[
  {
    "path": ".babelrc",
    "content": "{\r\n  \"presets\": [\r\n    [\r\n      \"@babel/preset-env\",\r\n      {\r\n        \"useBuiltIns\": \"entry\",\r\n        \"corejs\": \"3.22\"\r\n      }\r\n    ],\r\n    \"@babel/preset-typescript\"\r\n  ],\r\n  \"plugins\": [\r\n    \"html-tag-js/jsx/jsx-to-tag.js\",\r\n    \"html-tag-js/jsx/syntax-parser.js\",\r\n    \"@babel/plugin-transform-runtime\",\r\n    \"@babel/plugin-transform-block-scoping\"\r\n  ],\r\n  \"compact\": false,\r\n  \"sourceMaps\": \"inline\"\r\n}"
  },
  {
    "path": ".devcontainer/Dockerfile",
    "content": "# Acode Development Container - Standalone Docker Build\n# \n# This Dockerfile is for MANUAL Docker builds (docker build/run).\n# Usage:\n#   docker build -t acode-dev .devcontainer/\n#   docker run -it -v $(pwd):/workspaces/acode acode-dev\n\nFROM mcr.microsoft.com/devcontainers/java:1-21-bullseye\n\nARG ANDROID_PLATFORM=35\nARG ANDROID_BUILD_TOOLS=35.0.0\nARG CMDLINE_TOOLS_VERSION=11076708\nARG NODE_VERSION=22\nARG GRADLE_VERSION=8.11\n\nENV ANDROID_HOME=/opt/android-sdk\nENV ANDROID_SDK_ROOT=/opt/android-sdk\nENV GRADLE_HOME=/opt/gradle\nENV PATH=\"${PATH}:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${GRADLE_HOME}/bin\"\n\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n    wget \\\n    unzip \\\n    && rm -rf /var/lib/apt/lists/*\n\n# Install Gradle\nRUN wget -q \"https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip\" -O /tmp/gradle.zip \\\n    && unzip -q /tmp/gradle.zip -d /opt \\\n    && rm /tmp/gradle.zip \\\n    && ln -s /opt/gradle-${GRADLE_VERSION} ${GRADLE_HOME}\n\n# Install fnm and Node.js\nENV FNM_DIR=/usr/local/fnm\nENV PATH=\"${FNM_DIR}:${PATH}\"\nRUN curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir \"${FNM_DIR}\" --skip-shell \\\n    && eval \"$(${FNM_DIR}/fnm env)\" \\\n    && fnm install ${NODE_VERSION} \\\n    && fnm default ${NODE_VERSION} \\\n    && npm install -g pnpm\n\nENV PATH=\"${FNM_DIR}/aliases/default/bin:${PATH}\"\n\n# Install Android SDK\nRUN mkdir -p ${ANDROID_HOME}/cmdline-tools \\\n    && cd ${ANDROID_HOME}/cmdline-tools \\\n    && wget -q \"https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS_VERSION}_latest.zip\" -O cmdline-tools.zip \\\n    && unzip -q cmdline-tools.zip \\\n    && rm cmdline-tools.zip \\\n    && mv cmdline-tools latest \\\n    && yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --licenses 2>/dev/null || true \\\n    && ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --update \\\n    && ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager \\\n        \"platform-tools\" \\\n        \"platforms;android-${ANDROID_PLATFORM}\" \\\n        \"build-tools;${ANDROID_BUILD_TOOLS}\"\n\nWORKDIR /workspaces/acode\n\nLABEL maintainer=\"Acode Foundation\"\nLABEL description=\"Development environment for building Acode - Code Editor for Android\"\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n  \"name\": \"Acode Development\",\n  \"image\": \"mcr.microsoft.com/devcontainers/java:1-21-bullseye\",\n\n  \"features\": {\n    \"ghcr.io/devcontainers/features/java:1\": {\n      \"installGradle\": true,\n      \"installGroovy\": false,\n      \"installMaven\": false,\n      \"installAnt\": false,\n      \"version\": \"21\",\n      \"jdkDistro\": \"ms\",\n      \"gradleVersion\": \"latest\"\n    },\n    \"ghcr.io/nordcominc/devcontainer-features/android-sdk:1\": {\n      \"platform\": \"35\",\n      \"build_tools\": \"35.0.0\"\n    },\n    \"ghcr.io/devcontainers/features/node:1\": {\n      \"nodeGypDependencies\": false,\n      \"installYarnUsingApt\": false,\n      \"version\": \"lts\",\n      \"pnpmVersion\": \"latest\",\n      \"nvmVersion\": \"latest\"\n    }\n  },\n\n  \"postCreateCommand\": \"pnpm run setup\",\n\n  \"customizations\": {\n    \"vscode\": {\n      \"extensions\": [\"biomejs.biome\", \"redhat.java\"],\n      \"settings\": {\n        \"editor.formatOnSave\": true,\n        \"editor.defaultFormatter\": \"biomejs.biome\",\n        \"[java]\": {\n          \"editor.defaultFormatter\": \"redhat.java\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": ".dockerignore",
    "content": "# Dependencies\nnode_modules/\n.pnpm-store/\n\n# Build outputs\nplatforms/\nplugins/\nwww/css/build/\nwww/js/build/\n\n# IDE\n.vscode/\n.idea/\n\n# Git\n.git/\n.gitignore\n\n# Misc\n*.log\n*.apk\n*.aab\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.sh text eol=lf\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": ".github/CODEOWNERS @Acode-Foundation/acode \n\n# workflows\n.github/workflows/nightly-build.yml @unschooledgamer\n.github/workflows/on-demand-preview-releases-PR.yml @unschooledgamer\n.github/workflows/community-release-notifier.yml @unschooledgamer\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/0_bug_report.yml",
    "content": "name: Bug Report\ndescription: |\n  Use this template for bug reports.\nlabels: []\nbody:\n  - type: checkboxes\n    attributes:\n      label: Check for existing issues\n      description: Check the backlog of issues to reduce duplicates; if an issue already exists, place a 👍 on it.\n      options:\n        - label: Completed\n          required: true\n  - type: textarea\n    attributes:\n      label: Describe the bug / provide steps to reproduce it\n      description: A clear and concise description of what the bug is. If possible, then include a clip\n    validations:\n      required: true\n  - type: textarea\n    id: environment\n    attributes:\n      label: Environment\n      description: Provide your app version, Android version, webview version, etc (Search \"Copy Device Info\" in command palette to get all these info and share it)\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: If applicable, add mockups / screenshots regarding your vision\n      description: Drag issues into the text input below\n    validations:\n      required: false\n  - type: textarea\n    attributes:\n      label: If applicable, attach your Acode.log file to this issue.\n      description: |\n        Search for `Open Log File` in Acode command palette, it will open Acode.log file then share it\n        or Check this location for log file: `/storage/emulated/0/Android/com.foxdebug.acode{in case of free version there will be bit more}/files/Acode.log`\n      value: |\n        <details><summary>Acode.log</summary><pre>\n        <!-- Click below this line and paste or drag-and-drop your log-->\n\n        <!-- Click above this line and paste or drag-and-drop your log--></pre></details>\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1_feature_request.yml",
    "content": "name: Feature Request\ndescription: \"Suggest a new feature for Acode\"\nlabels: [\"enhancement\"]\nbody:\n  - type: checkboxes\n    attributes:\n      label: Check for existing issues\n      description: Check the backlog of issues to reduce duplicates; if an issue already exists, place a `+1` (👍) on it.\n      options:\n        - label: Completed\n          required: true\n  - type: textarea\n    attributes:\n      label: Describe the feature\n      description: A clear and concise description of what you want to happen.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: |\n        If applicable, add mockups / screenshots to help present your vision of the feature\n      description: Drag images into the text input below\n    validations:\n      required: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask Questions\n    url: https://github.com/Acode-Foundation/Acode/discussions\n    about: Ask any questions regarding Acode and its ecosystem\n  - name: Telegram Group\n    url: https://t.me/foxdebug_acode\n    about: Friendly Acode Community for helps regarding Acode, development and more\n  - name: Discord Server\n    url: https://discord.gg/vVxVWYUAWD\n    about: Acode discord server \n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for more information:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n# https://containers.dev/guide/dependabot\n\nversion: 2\nupdates:\n - package-ecosystem: \"devcontainers\"\n   directory: \"/\"\n   schedule:\n     interval: weekly\n \n - package-ecosystem: \"github-actions\"\n   directory: \"/\"\n   schedule:\n     interval: weekly\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "translations:\n  - any: \n    - changed-files: \n      - any-glob-to-any-file: 'src/lang/*.json'\n\ndocs:\n  - any: \n    - changed-files: \n      - any-glob-to-any-file: '**/*.md'\n\nenhancement:\n  - any:\n    - head-branch: ['^feature', 'feature', '^feat', '^add']"
  },
  {
    "path": ".github/workflows/add-pr-labels.yml",
    "content": "name: Add Pull Requests Labels\non: \n    pull_request_target: \n        types: [opened, closed, ready_for_review]\n\njobs:\n    add-labels:\n        timeout-minutes: 5\n        runs-on: ubuntu-latest\n        \n        if: github.event.action == 'opened'\n        permissions: \n            contents: read\n            pull-requests: write\n        \n        steps:\n            - uses: actions/labeler@v6\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n  pull_request:\n\njobs:\n  spell-check:\n    timeout-minutes: 5\n    name: Check spelling\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout Actions Repository\n        uses: actions/checkout@v6\n\n      - name: Check spelling\n        uses: crate-ci/typos@master\n        with:\n          config: ./_typos.toml\n\n  quality:\n    timeout-minutes: 5\n    name: Linting and formatting\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      - name: Setup Biome\n        uses: biomejs/setup-biome@v2\n\n      - name: Run Biome\n        run: biome ci .\n\n  translation-check:\n    name: Translation Check (On PR Only)\n    timeout-minutes: 5\n    runs-on: ubuntu-latest\n    permissions: \n      contents: read\n      pull-requests: read\n    if: github.event_name == 'pull_request'\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v6\n      - name: Use Node.js\n        uses: actions/setup-node@v6\n        with:\n          cache: npm\n          cache-dependency-path: '**/package-lock.json'\n\n      - name: Detect Changed Files\n        uses: dorny/paths-filter@v4\n        id: file-changes\n        with:\n          list-files: shell\n          filters: |\n            translation:\n              - 'src/lang/*.json'\n          token: ${{ secrets.GITHUB_TOKEN }}\n      - name: Translation Files Check (if changed)\n        if: steps.file-changes.outputs.translation == 'true'\n        run: |\n            npm ci --no-audit --no-fund\n            npm run lang check"
  },
  {
    "path": ".github/workflows/close-inactive-issues.yml",
    "content": "name: Close inactive issues\non:\n  schedule:\n    - cron: \"30 1 * * *\"\n\njobs:\n  close-issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@v10\n        with:\n          days-before-issue-stale: 60\n          days-before-issue-close: 14\n          stale-issue-label: \"stale\"\n          stale-issue-message: >\n            Hi there! 👋\n\n            We're working to clean up our issue tracker by closing older issues that might not be relevant anymore. If you are able to reproduce this issue in the latest version of Acode, please let us know by commenting on this issue(i.e Bump!), and we will keep it open. If you can't reproduce it, feel free to close the issue yourself. Otherwise, we'll close it in 14 days.\n\n            Thanks for your help!\n          close-issue-message: \"This issue was closed because it has been inactive for 14 days since being marked as stale.\"\n          exempt-all-milestones: true\n          days-before-pr-stale: -1\n          days-before-pr-close: -1\n          exempt-issue-labels: \"new plugin idea, todo, enhancement, bug, Low priority, documentation\"\n          operations-per-run: 100\n          repo-token: ${{ secrets.GITHUB_TOKEN }}\n          \n"
  },
  {
    "path": ".github/workflows/community-release-notifier.yml",
    "content": "name: community-release-notifier\n\non:\n  release:\n    types: [ released ]\n  workflow_call:\n    inputs:\n      tag_name:\n        required: true\n        description: \"Release tag_name\"\n        type: 'string'\n      url:\n        required: true\n        description: \"release URL\"\n        type: 'string'\n      body:\n        required: true\n        description: \"Release Body\"\n        type: 'string'\n        default: ''\n    secrets:\n      DISCORD_WEBHOOK_RELEASE_NOTES:\n        description: 'Discord Webhook for Notifying Releases to Discord'\n        required: true\n      TELEGRAM_BOT_TOKEN:\n        description: 'Telegram Bot Token'\n        required: true\n      TELEGRAM_CHAT_ID:\n        description: 'Telegram Chat ID (group/channel/supergroup)'\n        required: true\n      TELEGRAM_MESSAGE_THREAD_ID:\n        description: 'Topic / message_thread_id for Telegram forum/topic'\n        required: true\n\njobs:\n  notify:\n    if: github.repository_owner == 'Acode-Foundation'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Prepare release variables\n        id: vars\n        env:\n          INPUT_TAG: ${{ github.event.release.tag_name || inputs.tag_name }}\n          INPUT_URL: ${{ github.event.release.url || inputs.url }}\n          INPUT_BODY: ${{ github.event.release.body || inputs.body }}\n        run: |\n          TAG=\"$INPUT_TAG\"\n          URL=\"$INPUT_URL\"\n          \n          # Generate a random delimiter (hex string, safe and collision-resistant)\n          DELIMITER=$(openssl rand -hex 16 || head -c 16 /dev/urandom | xxd -p -c 16)\n          \n          # Escape problematic characters for MarkdownV2 (very conservative escaping)\n          # We escape: _ * [ ] ( ) ~ ` > # + - = | { } . ! \\\n          BODY_SAFE=$(printf '%s' \"$INPUT_BODY\" | \\\n            sed 's/[_*[\\]()~`>#+=|{}.!\\\\-]/\\\\&/g')\n          TAG_SAFE=$(printf '%s' \"$TAG\" | sed 's/[_*[\\]()~`>#+=|{}.!\\\\-]/\\\\&/g')\n\n          if [[ \"$TAG\" == *\"-nightly\"* ]]; then\n            SUFFIX=\" \\(Nightly Release\\)\"\n            SUFFIXPLAIN=\" (Nightly Release)\"\n          else\n            SUFFIX=\"\"\n            SUFFIXPLAIN=\"\"\n          fi\n\n          # Announcement line — also escape for safety\n          ANNOUNCE_SAFE=\"📢 Acode [$TAG_SAFE]($URL) was just Released 🎉${SUFFIX}\\\\!\"\n\n          echo \"announce=$ANNOUNCE_SAFE\" >> $GITHUB_OUTPUT\n          {\n            echo \"body_safe<<$DELIMITER\"\n            printf '%s\\n' \"$BODY_SAFE\"\n            echo \"$DELIMITER\"\n          } >> $GITHUB_OUTPUT\n\n          # Plain (MD) Announcement for Discord\n          ANNOUNCE_PLAIN=\"📢 Acode [$TAG](<$URL>) was just Released 🎉${SUFFIXPLAIN}!\"\n          echo \"announce_plain=$ANNOUNCE_PLAIN\" >> $GITHUB_OUTPUT\n          {\n            echo \"body_plain<<$DELIMITER\"\n            printf '%s\\n' \"$INPUT_BODY\"\n            echo \"$DELIMITER\"\n          } >> $GITHUB_OUTPUT\n\n      # ────────────────────────────────────────────────\n      # Truncate for Discord\n      # ────────────────────────────────────────────────\n      - name: Truncate message for Discord\n        id: truncate-discord\n        uses: 2428392/gh-truncate-string-action@b3ff790d21cf42af3ca7579146eedb93c8fb0757  # v1.4.1\n        env: \n          # https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/\n          FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        with:\n          maxLength: 2000\n          stringToTruncate: |\n            ${{ steps.vars.outputs.announce_plain }}\n\n            ${{ steps.vars.outputs.body_plain }}\n\n      # ────────────────────────────────────────────────\n      # Discord notification\n      # ────────────────────────────────────────────────\n      - name: Discord Webhook (Publishing)\n        uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645  # v7.0.0\n        env:\n          # https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/\n          FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n        with:\n          webhook-url: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}\n          content: ${{ steps.truncate-discord.outputs.string }}\n          flags: 4 # 1 << 2 - SUPPRESS_EMBEDS!\n\n      # ────────────────────────────────────────────────\n      # Telegram notification — MarkdownV2 + no link preview\n      # ────────────────────────────────────────────────\n      - name: Send to Telegram\n        #if: ${{ secrets.TELEGRAM_BOT_TOKEN != '' && secrets.TELEGRAM_CHAT_ID != '' && secrets.TELEGRAM_MESSAGE_THREAD_ID != '' }}\n        uses: Salmansha08/telegram-github-action@17c9ce6b4210d2659dca29d34028b02fa29d70ad  # or newer tag if available\n        with:\n          to: ${{ secrets.TELEGRAM_CHAT_ID }}\n          token: ${{ secrets.TELEGRAM_BOT_TOKEN }}\n          message: |\n            ${{ steps.vars.outputs.announce }}\n\n            ${{ steps.vars.outputs.body_safe }}\n          format: markdown\n          disable_web_page_preview: true\n          # Only needed for topic-enabled supergroups/channels\n          message_thread_id: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }}\n        continue-on-error: true\n"
  },
  {
    "path": ".github/workflows/nightly-build.yml",
    "content": "# Implement to use environment secrets someday, At the moment GH Actions doesn't support those in reusable workflows (ref: https://github.com/actions/runner/issues/1490)\n# at least that's what I found.\n  name: Nightly Build\n\n  on:\n    schedule:\n      # Fire every day at 7:00am UTC (Roughly before EU workday and after US workday)\n      - cron: \"0 7 * * *\"\n    push:\n      tags:\n        - nightly\n    workflow_call:\n      inputs:\n        is_PR:\n          default: false\n          type: boolean\n          description: If a Pull Request has triggered it.\n        PR_NUMBER:\n          required: true\n          type: number\n          description: The Pull Request that triggered this workflow\n        skip_tagging_and_releases:\n          required: false\n          default: true\n          type: boolean\n          description: Skips Tagging & releases, since workflow_call isn't available for github.event_name, default is true\n      outputs:\n        job_result:\n          description: \"Build job result\"\n          value: ${{ jobs.build.result }}\n\n    workflow_dispatch:\n      inputs:\n        skip_tagging_and_releases:\n          required: false\n          default: true\n          type: boolean\n          description: Skips Tagging & releases, since workflow_call isn't available for github.event_name, default is true\n\n  concurrency:\n    # Allow only one workflow per any non-`main` branch.\n    group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}-${{ inputs.is_PR && 'is_PR' || 'not_PR'}}\n    cancel-in-progress: true\n\n  permissions:\n    contents: read\n\n  env:\n    STORE_FILE_PATH: /tmp/app-debug.keystore\n    BUILD_JSON_PATH: build.json\n    VERSION_LABEL: ${{ (inputs.is_PR && 'pr') || 'nightly' }}\n    DISCORD_RELEASE_NOTIFIER_ENABLED: \"true\"\n  jobs:\n    build:\n      timeout-minutes: 60\n      runs-on: ubuntu-latest\n      if: github.repository_owner == 'Acode-Foundation'\n\n      permissions:\n#        contents write is needed to create Nightly Releases.\n        contents: write\n#        issues: write\n        pull-requests: write\n\n      outputs:\n         release_output_url: ${{ steps.release.outputs.url }} \n         updated_version: ${{ steps.update-version.outputs.UPDATED_VERSION}} \n         RELEASE_NOTES: ${{ env.RELEASE_NOTES }}\n      steps:\n        - name: Fast Fail if secrets are missing\n          if: ${{ env.KEYSTORE_CONTENT == '' || env.BUILD_JSON_CONTENT == '' }}\n          env:\n             KEYSTORE_CONTENT: ${{ secrets.KEYSTORE_CONTENT }} \n             BUILD_JSON_CONTENT: ${{ secrets.BUILD_JSON_CONTENT }} \n          run: |\n            echo \"::error title=Missing Secrets::KEYSTORE_CONTENT or BUILD_JSON_CONTENT secrets are missing! Aborting workflow.\"\n            exit 1\n        \n        - name: Logging & summaries\n          run: |\n            echo \"::group::Logging\" \n            echo \"🎯 github trigger event name: ${{ github.event_name }}\"\n            echo \"is_PR: ${{ inputs.is_PR }} \"\n            echo \"PR_NUMBER: ${{ inputs.PR_NUMBER }}\" \n            echo \"env: STORE_FILE_PATH: ${{ env.STORE_FILE_PATH }}\" \n            echo \"env: BUILD_JSON_PATH: ${{ env.BUILD_JSON_PATH }}\" \n            echo \"env: VERSION_LABEL: ${{ env. VERSION_LABEL }}\"\n            echo \"github sha: ${{ github.sha }}\" \n            echo \"should not skip tags, releases: ${{ ! inputs.skip_tagging_and_releases }} \" \n            echo \"🤐 env: NORMAL_APK_PATH: ${{ env.NORMAL_APK_PATH }}\"\n            echo \"🤐 env: FDROID_APK_PATH: ${{ env.FDROID_APK_PATH }}\"            \n            echo \"::endgroup::\" \n            \n            echo \"## 🚀 Build Type: ${{ env.VERSION_LABEL }}\" >> $GITHUB_STEP_SUMMARY\n            echo \"is_PR: ${{ inputs.is_PR || 'NOT a PR' }}\" >> $GITHUB_STEP_SUMMARY\n            echo \"PR_NUMBER: ${{ inputs.PR_NUMBER || 'not a PR' }}\" >> $GITHUB_STEP_SUMMARY\n            echo \"should not skip tags, releases: ${{ ! inputs.skip_tagging_and_releases }}\" >> $GITHUB_STEP_SUMMARY     \n\n        - name: Checkout Repository\n          uses: actions/checkout@v6\n          with:\n            fetch-depth: 0 # Required for tags\n            # persists credentials locally if tagging and releases are not skipped.\n            persist-credentials: ${{ ! inputs.skip_tagging_and_releases }}\n            ref: ${{ (inputs.is_PR && inputs.PR_NUMBER) && github.event.pull_request.head.sha || '' }}\n\n        - name: Set up Java 21\n          uses: actions/setup-java@v5\n          with:\n            distribution: 'temurin'\n            java-version: '21'\n            cache: ${{ (!(inputs.is_PR && inputs.PR_NUMBER) && github.ref == 'refs/heads/main' && 'gradle') || '' }}\n\n        - name: Set up Node.js\n          uses: actions/setup-node@v6\n          with:\n            node-version: 'lts/*' # or '18.x' for latest stable\n            cache: ${{ (!(inputs.is_PR && inputs.PR_NUMBER) && github.ref == 'refs/heads/main' && 'npm') || '' }}\n\n        - name: Add keystore and build.json from secrets\n          run: |\n            echo \"${{ secrets.KEYSTORE_CONTENT }}\" | base64 -d > ${{ env.STORE_FILE_PATH }}\n            echo \"${{ secrets.BUILD_JSON_CONTENT }}\" | base64 -d > ${{ env.BUILD_JSON_PATH }}\n            echo \"Keystore and build.json added successfully.\"\n\n        - name: Export Commit Hash & prev tag\n          run: |\n            echo \"GIT_COMMIT=$(git rev-parse HEAD)\" >> $GITHUB_ENV\n            echo \"PREV_TAG=$(git describe --tags --abbrev=0 || git rev-list --max-parents=0 HEAD)\" >> $GITHUB_ENV\n\n        - name: Extract versionCode and version from config.xml\n          id: extract_version\n          run: |\n            if [ ! -f config.xml ]; then\n            echo \"config.xml not found!\"\n            exit 1\n            fi\n            VERSION_CODE=$(grep 'versionCode=' config.xml | sed -E 's/.*versionCode=\"([0-9]+)\".*/\\1/')\n            VERSION=$(grep -oP 'version=\"\\K[0-9.]+' config.xml)\n            echo \"VERSION_CODE=$VERSION_CODE\" >> $GITHUB_OUTPUT\n            echo \"VERSION=$VERSION\" >> $GITHUB_OUTPUT\n            echo \"Extracted Version Code: $VERSION_CODE\"\n            echo \"Extracted Version: $VERSION\"\n\n        - name: Append Commit Hash to Version and Update config.xml\n          id: update-version \n          run: |\n            SHORT_COMMIT_HASH=$(echo \"${{ env.GIT_COMMIT }}\" | cut -c 1-7)\n            if ${{ inputs.is_PR  || false }}; then \n              PR_NUMBER=${{ inputs.PR_NUMBER }}\n            else\n              PR_NUMBER=\n            fi\n            UPDATED_VERSION=\"${{ steps.extract_version.outputs.VERSION }}-${{ env.VERSION_LABEL }}.${PR_NUMBER:-$SHORT_COMMIT_HASH}\"\n            echo \"Updated Version: $UPDATED_VERSION\"\n            \n            # Update config.xml with the new version\n            sed -i \"s/version=\\\"${{ steps.extract_version.outputs.VERSION }}\\\"/version=\\\"$UPDATED_VERSION\\\"/g\" config.xml\n            echo \"Updated version in config.xml\"\n            # Output the updated version\n            echo \"UPDATED_VERSION=$UPDATED_VERSION\" >> $GITHUB_ENV\n            echo \"UPDATED_VERSION=$UPDATED_VERSION\" >> $GITHUB_OUTPUT\n            echo \"NORMAL_APK_PATH=/tmp/acode-debug-normal-${UPDATED_VERSION}.apk\" >> $GITHUB_ENV\n            echo \"FDROID_APK_PATH=/tmp/acode-debug-fdroid-${UPDATED_VERSION}.apk\" >> $GITHUB_ENV\n\n        - name: Install Node.js Packages\n          run: npm install\n\n        - name: Install Cordova CLI\n          run: npm install -g cordova\n\n        - name: Run npm setup\n          run: npm run setup\n\n        - name: Run npm build paid dev apk\n          run: |\n             node utils/storage_manager.mjs y\n             npm run build paid dev apk\n             mv platforms/android/app/build/outputs/apk/debug/app-debug.apk ${{ env.NORMAL_APK_PATH }}\n             echo \"VERSION: $UPDATED_VERSION\" >> $GITHUB_STEP_SUMMARY\n             \n        - name: Upload APK Artifact\n          uses: actions/upload-artifact@v7\n          with:\n            name: app-debug-${{ env.GIT_COMMIT }}\n            path: ${{ env.NORMAL_APK_PATH }}\n        \n        - name: Run npm build paid dev apk fdroid (for F-Droid)\n          if: ${{ !inputs.is_PR }}\n          run: |\n            node utils/storage_manager.mjs y\n            npm run build paid dev apk fdroid\n            mv platforms/android/app/build/outputs/apk/debug/app-debug.apk ${{ env.FDROID_APK_PATH }}\n        \n        - name: Upload APK Artifact\n          uses: actions/upload-artifact@v7\n          if: ${{ !inputs.is_PR }}\n          with:\n            name: app-debug-fdroid-${{ env.GIT_COMMIT }}\n            path: ${{ env.FDROID_APK_PATH }}\n\n        - name: remove keystore and build.json\n          run: |\n            rm $STORE_FILE_PATH $BUILD_JSON_PATH\n            echo \"Keystore and build.json removed successfully.\"\n        \n        - name: Check Nightly Tag and Force Update\n          #if: github.event_name == 'push' && contains(github.event.ref, 'tags/nightly') == false\n          if: ${{ ! inputs.skip_tagging_and_releases }}\n          id: check-nightly-tag-force-update\n          run: |\n            # Check if the nightly tag exists and get the commit it points to\n            if git show-ref --quiet refs/tags/nightly; then\n              TAG_COMMIT=$(git rev-parse nightly)\n            else\n              TAG_COMMIT=\"\"\n            fi\n            \n            # If the current commit is the same as the tag, skip updating the tag\n            if [ \"$TAG_COMMIT\" != \"${{ env.GIT_COMMIT }}\" ]; then\n              echo \"releaseRequired=true\" >> $GITHUB_ENV\n              # export tag commit before updating for change logs comparing. \n              echo \"TAG_COMMIT=$TAG_COMMIT\" >> $GITHUB_ENV \n            \n              git config --global user.name 'GitHub Actions'\n              git config --global user.email 'github-actions@github.com'\n              git tag -f nightly\n              git push origin nightly --force\n            else\n              echo \"Nightly tag already points to this commit. Skipping update.\"\n            fi\n\n        # 🚨⚠️ WARNING: the GITHUB_TOKEN under this step, has access to write & read access to Contents, Pull Requests\n        # Which is why, it uses a fine-granted token with Read-Only Access to Public Repos Only.\n        - name: Generate Release Notes (Experimental)\n          if: ${{ success() && env.releaseRequired == 'true' }}\n          id: gen-release-notes\n          continue-on-error: true\n          run: |\n              RELEASE_NOTES=$(node utils/scripts/generate-release-notes.js ${{ github.repository_owner }} Acode ${{ github.sha }} --format md --from-tag ${{ env.TAG_COMMIT }} --important-only --quiet --changelog-only)\n              {\n                echo \"RELEASE_NOTES<<EOF\"\n                echo \"$RELEASE_NOTES\"\n                echo \"EOF\"\n              } >> $GITHUB_ENV        \n          env: \n            GITHUB_TOKEN: ${{ secrets.NIGHTLY_RELEASE_NOTES_GH_TOKEN }}\n        - name: Release Nightly Version\n#          Only run this step, if not called from another workflow. And a previous step is successful with releasedRequired=true\n          id: release\n          if: ${{ ! inputs.skip_tagging_and_releases && steps.check-nightly-tag-force-update.outcome == 'success' && env.releaseRequired == 'true' && !inputs.is_PR }}\n          uses: softprops/action-gh-release@v2\n          env:\n            FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n          with:\n            prerelease: true\n            name: ${{ env.UPDATED_VERSION }}\n            tag_name: ${{ env.UPDATED_VERSION }} \n            files: |\n              ${{ env.NORMAL_APK_PATH }}\n              ${{ env.FDROID_APK_PATH }}\n            body: |\n              Automated Nightly (pre-release) Releases for Today\n              \n              [Compare Changes](https://github.com/${{ github.repository }}/compare/${{ env.TAG_COMMIT }}...${{ github.sha }})\n\n              ${{ env.RELEASE_NOTES }}\n\n        - name: Update Last Comment by bot (If ran in PR)\n          if: inputs.is_PR\n          uses: marocchino/sticky-pull-request-comment@v3\n          with:\n             hide_and_recreate: true\n             hide_classify: \"OUTDATED\"\n             header: on-demand-build-status\n             message: |\n                   Preview Release for this, has been built.\n         \n                   [Click here to view that github actions build](https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }})\n             \n\n    community-release-notifier:\n      needs: build\n#      Run only if push tags, or triggered by a schedule\n      if: ${{ github.repository_owner == 'Acode-Foundation' && (contains(fromJSON('[\"push\", \"schedule\"]'), github.event_name) || ! inputs.skip_tagging_and_releases) && needs.build.outputs.release_output_url }}\n      uses: Acode-Foundation/acode/.github/workflows/community-release-notifier.yml@main\n      with:\n         tag_name: ${{ needs.build.outputs.updated_version }}\n         url: ${{ needs.build.outputs.release_output_url }}\n         body: ${{ needs.build.outputs.RELEASE_NOTES }}\n      secrets:\n        DISCORD_WEBHOOK_RELEASE_NOTES: ${{ secrets.DISCORD_WEBHOOK_RELEASE_NOTES }}\n        TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}\n        TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}\n        TELEGRAM_MESSAGE_THREAD_ID: ${{ secrets.TELEGRAM_MESSAGE_THREAD_ID }}\n"
  },
  {
    "path": ".github/workflows/on-demand-preview-releases-PR.yml",
    "content": "name: On Demand Preview Releases for PR\n#  avoids paths like .md, and anything in .github folder\non:\n  pull_request_target:\n    paths-ignore:\n      - '!*.md'\n#      - '.github/**'\n    types: [labeled, synchronize]\n\n# defined at workflow-level as the workflow, Requires these permissions to function.\npermissions:\n  contents: write\n  pull-requests: write\n  # All Pull Requests are issues, but not all issues are Pull Requests (like GitHub says 🙃)\n  issues: write\n\nconcurrency:\n  # Allow only one workflow per any non-`main` branch.\n  group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}\n  cancel-in-progress: true\n\njobs:\n  job_trigger:\n    name: Trigger Preview Release (if conditions met)\n    if: |\n        github.event.pull_request.draft == false\n        && (github.repository_owner == 'Acode-Foundation'\n        && (!contains(github.event.pull_request.labels.*.name, 'DO NOT PREVIEW RELEASE') \n        && (contains(github.event.pull_request.labels.*.name, 'Bypass check - PREVIEW RELEASE') \n        || contains(github.event.pull_request.labels.*.name, 'CI: RUN ON-DEMAND PREVIEW RELEASES')))\n        )\n\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6\n        with:\n          clean: false\n          fetch-depth: 0\n          persist-credentials: false\n          # Checkout pull request HEAD commit instead of merge commit\n          ref: ${{ github.event.pull_request.head.sha }}\n\n      - name: Remove Manually added PR Label\n        if: |\n          contains(github.event.pull_request.labels.*.name, 'CI: RUN ON-DEMAND PREVIEW RELEASES')\n\n        run: gh pr edit $PR --remove-label \"$labels\"\n        env:\n           PR: ${{ github.event.pull_request.number }}\n           labels: 'CI: RUN ON-DEMAND PREVIEW RELEASES'\n           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Add comment to PR\n        uses: marocchino/sticky-pull-request-comment@v3\n        with:\n          hide_and_recreate: true\n          hide_classify: \"OUTDATED\"\n          header: on-demand-build-status\n          message: |\n            ⚒️ Starting a workflow to build, Your On-Demand Preview Release/build for ${{ github.event.pull_request.html_url || github.event.pull_request.url }}.\n            \n            status: **\\`🟡 in_progress\\`**\n            \n            Kindly wait, this message may be updated with success or failure status.\n            \n            For Owners: Please [Click here to view that github actions](https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }})/\n\n        env:\n          PR: ${{ github.event.pull_request.number }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n  trigger_builder:\n      needs: job_trigger\n      secrets: inherit\n      uses: Acode-Foundation/acode/.github/workflows/nightly-build.yml@main\n      with:\n        is_PR: true\n        PR_NUMBER: ${{ github.event.pull_request.number }}\n        skip_tagging_and_releases: true\n\n  update_Last_Comment:\n      needs: [job_trigger,trigger_builder]\n      runs-on: ubuntu-latest\n      if: ${{ github.repository_owner == 'Acode-Foundation' && always() && contains(fromJSON('[\"failure\",\"cancelled\"]'), needs.trigger_builder.result) }}\n      steps:\n#        - name: Checkout code\n#          uses: actions/checkout@v4\n#          with:\n#            clean: false\n#            fetch-depth: 0\n\n        - name: Update Last Comment by bot (if Workflow Triggering failed)\n          uses: marocchino/sticky-pull-request-comment@v3\n          with:\n             hide_and_recreate: true\n             hide_classify: \"OUTDATED\"\n             header: on-demand-build-status\n             message: |\n                   🔴 (Workflow Trigger stopped), Your On-Demand Preview Release/build for ${{ github.event.pull_request.html_url || github.event.pull_request.url }}.\n                   status: **${{ needs.trigger_builder.result || 'failure'}}**         \n               \n                   ---\n                   For Owners: Please [Click here to view that github actions](https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }})\n"
  },
  {
    "path": ".gitignore",
    "content": "/node_modules\r\n/build.json\r\n/www/build\r\n/plugins\r\n/platforms\r\n/keystore.jks\r\n/platforms/android/debug-signing.properties\r\n/platforms/android/release-signing.properties\r\n**/*/.DS_Store\r\n.DS_Store\r\npnpm-lock.yaml\r\n.zed\r\n.idea\r\nace-builds\r\nfdroid.bool\r\n"
  },
  {
    "path": ".hintrc",
    "content": "{\n  \"extends\": [\n    \"development\"\n  ],\n  \"hints\": {\n    \"compat-api/css\": \"off\",\n    \"css-prefix-order\": \"off\",\n    \"meta-viewport\": \"off\",\n    \"detect-css-reflows/composite\": \"off\",\n    \"detect-css-reflows/paint\": \"off\"\n  }\n}"
  },
  {
    "path": ".prettierrc",
    "content": "{}\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"node\",\n      \"request\": \"launch\",\n      \"name\": \"Run Android\",\n      \"skipFiles\": [\n        \"<node_internals>/**\"\n      ],\n      \"program\": \"${workspaceFolder}\\\\utils\\\\run-android.js\",\n    }\n  ]\n}"
  },
  {
    "path": ".vscode/plugins.json",
    "content": "{\"plugins\":[\"cordova-clipboard\",\"cordova-plugin-buildinfo\",\"cordova-plugin-device\",\"cordova-plugin-file\",\"cordova-plugin-sftp\",\"cordova-plugin-server\",\"cordova-plugin-ftp\",\"cordova-plugin-sdcard\",\"cordova-plugin-browser\",\"cordova-plugin-iap\",\"cordova-plugin-system\",\"cordova-plugin-advanced-http\"]}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.formatOnSave\": true,\n  \"autoimport.doubleQuotes\": false,\n  \"java.configuration.updateBuildConfiguration\": \"disabled\",\n  \"prettier.requireConfig\": true,\n  \"javascript.format.enable\": true,\n  \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n  \"prettier.tabWidth\": 2,\n  \"prettier.useTabs\": false,\n  \"javascript.format.semicolons\": \"insert\",\n  \"[scss]\": {\n    \"editor.defaultFormatter\": \"vscode.css-language-features\"\n  },\n  \"emmet.syntaxProfiles\": {\n    \"xml\": {\n      \"attr_quotes\": \"single\"\n    },\n    \"html\": {\n      \"attr_quotes\": \"single\"\n    },\n    \"javascript\": {\n      \"attr_quotes\": \"single\",\n      \"attr_quotes_style\": \"single\",\n      \"self_closing_tag\": true\n    },\n    \"jsx\": {\n      \"attr_quotes\": \"single\"\n    }\n  },\n  \"cSpell.words\": [\n    \"abap\",\n    \"Acode\",\n    \"acodefree\",\n    \"actionscript\",\n    \"addedfolder\",\n    \"addedfolders\",\n    \"addstorage\",\n    \"admob\",\n    \"adoc\",\n    \"Ajit\",\n    \"alda\",\n    \"ANDROIDX\",\n    \"anglebracket\",\n    \"angularjs\",\n    \"antlr\",\n    \"anyscript\",\n    \"applescript\",\n    \"ARGB\",\n    \"Astro\",\n    \"Asuming\",\n    \"audiotrack\",\n    \"autocorrect\",\n    \"autohotkey\",\n    \"autoit\",\n    \"autosize\",\n    \"azurepipelines\",\n    \"babelrcpath\",\n    \"backbutton\",\n    \"bazel\",\n    \"biml\",\n    \"bitbucketpipeline\",\n    \"bithound\",\n    \"browserslist\",\n    \"buckbuild\",\n    \"buildinfo\",\n    \"cakefile\",\n    \"cakephp\",\n    \"canrun\",\n    \"canrw\",\n    \"Cascadia\",\n    \"changemode\",\n    \"cheader\",\n    \"cirru\",\n    \"clearclose\",\n    \"clojurescript\",\n    \"cloudfoundry\",\n    \"codacy\",\n    \"codeclimate\",\n    \"codecov\",\n    \"codekit\",\n    \"coffeelint\",\n    \"colorpicker\",\n    \"commitlintignore\",\n    \"commitlintrc\",\n    \"configpath\",\n    \"copyline\",\n    \"copylinesdown\",\n    \"copylinesup\",\n    \"cordova\",\n    \"corejs\",\n    \"cppheader\",\n    \"crossorigin\",\n    \"Csound\",\n    \"csscomb\",\n    \"csslint\",\n    \"cssmap\",\n    \"cuda\",\n    \"curlybracket\",\n    \"customise\",\n    \"cython\",\n    \"darcs\",\n    \"dartlang\",\n    \"deadlyjack\",\n    \"Debuggable\",\n    \"deviceready\",\n    \"dlang\",\n    \"dockertest\",\n    \"docpad\",\n    \"docz\",\n    \"dotjs\",\n    \"downloadget\",\n    \"doxygen\",\n    \"DPAD\",\n    \"dustjs\",\n    \"easylogic\",\n    \"Edifact\",\n    \"editmenu\",\n    \"endregion\",\n    \"ensime\",\n    \"ERUDA\",\n    \"filebrowser\",\n    \"filesize\",\n    \"Fira\",\n    \"firebasehosting\",\n    \"firestore\",\n    \"flac\",\n    \"Flix\",\n    \"floobits\",\n    \"Foxdebug\",\n    \"freemarker\",\n    \"gamemaker\",\n    \"gcode\",\n    \"getfilename\",\n    \"gitmodules\",\n    \"glsl\",\n    \"Gobstones\",\n    \"googleplay\",\n    \"gotoline\",\n    \"graphviz\",\n    \"greenkeeper\",\n    \"gridsome\",\n    \"guardfile\",\n    \"haml\",\n    \"HARDKEYBOARDHIDDEN\",\n    \"haxe\",\n    \"haxecheckstyle\",\n    \"haxedevelop\",\n    \"hideconsole\",\n    \"historyrestore\",\n    \"hjson\",\n    \"hlsl\",\n    \"homeassistant\",\n    \"htgroups\",\n    \"htmlhint\",\n    \"htmlpath\",\n    \"htpasswd\",\n    \"idris\",\n    \"idrisbin\",\n    \"idrispkg\",\n    \"imba\",\n    \"INAPP\",\n    \"infopath\",\n    \"informix\",\n    \"innosetup\",\n    \"inputhints\",\n    \"inputmethod\",\n    \"intoview\",\n    \"Invisibles\",\n    \"irid\",\n    \"jbuilder\",\n    \"JEXL\",\n    \"jsbeautify\",\n    \"jsbeautifyrc\",\n    \"jsmap\",\n    \"jsonld\",\n    \"jsonnet\",\n    \"JSSM\",\n    \"KEYBOARDHIDDEN\",\n    \"KHTML\",\n    \"kitchenci\",\n    \"kivy\",\n    \"Kumar\",\n    \"Laravel\",\n    \"lastfile\",\n    \"lenspalette\",\n    \"lintstagedrc\",\n    \"livescript\",\n    \"loaderror\",\n    \"Localname\",\n    \"Logi\",\n    \"logopath\",\n    \"Logtalk\",\n    \"lolcode\",\n    \"Lucene\",\n    \"lync\",\n    \"markdownlint\",\n    \"markojs\",\n    \"mathml\",\n    \"maxscript\",\n    \"menubutton\",\n    \"MIXAL\",\n    \"mjml\",\n    \"mlang\",\n    \"modeassoc\",\n    \"modelist\",\n    \"mojolicious\",\n    \"moleculer\",\n    \"moveline\",\n    \"movelinesdown\",\n    \"movelinesup\",\n    \"mson\",\n    \"NAVIGATIONHIDDEN\",\n    \"nestjs\",\n    \"njsproj\",\n    \"nodemon\",\n    \"NOKEYS\",\n    \"nord\",\n    \"Noto\",\n    \"NOTOUCH\",\n    \"NSIS\",\n    \"nsri\",\n    \"nunjs\",\n    \"nunjucks\",\n    \"nuxt\",\n    \"objectivecpp\",\n    \"OLED\",\n    \"oncanrun\",\n    \"onchangemode\",\n    \"onchangesession\",\n    \"ondisconnect\",\n    \"onenote\",\n    \"onexpand\",\n    \"onfold\",\n    \"onhide\",\n    \"onloaderror\",\n    \"onpurchase\",\n    \"onref\",\n    \"onremove\",\n    \"onrename\",\n    \"onrun\",\n    \"onsave\",\n    \"onscrollend\",\n    \"onscrollleft\",\n    \"onscrolltop\",\n    \"onsearch\",\n    \"onwilldisconnect\",\n    \"onwriteend\",\n    \"opencl\",\n    \"openfolder\",\n    \"opengl\",\n    \"orangered\",\n    \"ovpn\",\n    \"paket\",\n    \"partiql\",\n    \"PERSISTABLE\",\n    \"pgsql\",\n    \"photoshop\",\n    \"phpcsfixer\",\n    \"phps\",\n    \"phpt\",\n    \"phpunit\",\n    \"phraseapp\",\n    \"plantuml\",\n    \"platformio\",\n    \"Plore\",\n    \"plsql\",\n    \"poedit\",\n    \"Portugues\",\n    \"postcss\",\n    \"postcssconfig\",\n    \"postcssrc\",\n    \"powersave\",\n    \"Praat\",\n    \"praatscript\",\n    \"precommit\",\n    \"prefs\",\n    \"processinglang\",\n    \"procfile\",\n    \"Proggy\",\n    \"protobuf\",\n    \"protobug\",\n    \"PRQL\",\n    \"purescript\",\n    \"pyret\",\n    \"pyup\",\n    \"qlikview\",\n    \"qmldir\",\n    \"qsharp\",\n    \"QUICKTOOLS\",\n    \"rakefile\",\n    \"Raku\",\n    \"rakumod\",\n    \"rakutest\",\n    \"raml\",\n    \"rateapp\",\n    \"reactjs\",\n    \"reacttemplate\",\n    \"reactts\",\n    \"recents\",\n    \"rehype\",\n    \"remotefile\",\n    \"removeads\",\n    \"retext\",\n    \"rhtml\",\n    \"rnfr\",\n    \"rnto\",\n    \"robotframework\",\n    \"Roboto\",\n    \"rollup\",\n    \"rproj\",\n    \"rubocop\",\n    \"ruleset\",\n    \"saltstack\",\n    \"scilab\",\n    \"sconsole\",\n    \"SCROLLBARS\",\n    \"scrollleft\",\n    \"scrolltop\",\n    \"sdcard\",\n    \"sdlang\",\n    \"selectall\",\n    \"sequelize\",\n    \"settitle\",\n    \"shft\",\n    \"showconsole\",\n    \"silverstripe\",\n    \"smali\",\n    \"snapcraft\",\n    \"snyk\",\n    \"SPARQL\",\n    \"sqlserver\",\n    \"squarebracket\",\n    \"stargrade\",\n    \"stata\",\n    \"styl\",\n    \"stylable\",\n    \"stylelint\",\n    \"symfony\",\n    \"systemverilog\",\n    \"Tahoma\",\n    \"tera\",\n    \"Termux\",\n    \"terragrunt\",\n    \"testjs\",\n    \"testts\",\n    \"tfvars\",\n    \"themelist\",\n    \"ttcn\",\n    \"typescriptdef\",\n    \"ungap\",\n    \"unibeautify\",\n    \"unwatch\",\n    \"vala\",\n    \"vapi\",\n    \"vash\",\n    \"vbhtml\",\n    \"Verdana\",\n    \"verilog\",\n    \"vhdl\",\n    \"Visualforce\",\n    \"vsix\",\n    \"vsixmanifest\",\n    \"warningreport\",\n    \"watchmanconfig\",\n    \"webp\",\n    \"wercker\",\n    \"Wollok\",\n    \"wpgm\",\n    \"wpml\",\n    \"wtest\",\n    \"wxml\",\n    \"wxss\",\n    \"xquery\",\n    \"Zeek\"\n  ],\n  \"[javascript]\": {\n    \"editor.defaultFormatter\": \"biomejs.biome\"\n  }\n}\n"
  },
  {
    "path": ".vscode/typings/cordova/cordova.d.ts",
    "content": "// Type definitions for Apache Cordova\r\n// Project: http://cordova.apache.org\r\n// Definitions by: Microsoft Open Technologies Inc. <http://msopentech.com>\r\n// Definitions: https://github.com/borisyankov/DefinitelyTyped\r\n//\r\n// Copyright (c) Microsoft Open Technologies, Inc.\r\n// Licensed under the MIT license.\r\n\r\ninterface Cordova {\r\n    /** Invokes native functionality by specifying corresponding service name, action and optional parameters.\r\n     * @param success A success callback function.\r\n     * @param fail An error callback function.\r\n     * @param service The service name to call on the native side (corresponds to a native class).\r\n     * @param action The action name to call on the native side (generally corresponds to the native class method).\r\n     * @param args An array of arguments to pass into the native environment.\r\n     */\r\n    exec(success: () => any, fail: () => any, service: string, action: string, args?: string[]): void;\r\n    /** Gets the operating system name. */\r\n    platformId: string;\r\n    /** Gets Cordova framework version */\r\n    version: string;\r\n    /** Defines custom logic as a Cordova module. Other modules can later access it using module name provided. */\r\n    define(moduleName: string, factory: (require: any, exports: any, module: any) => any): void;\r\n    /** Access a Cordova module by name. */\r\n    require(moduleName: string): any;\r\n    /** Namespace for Cordova plugin functionality */\r\n    plugins:CordovaPlugins;\r\n}\r\n\r\ninterface CordovaPlugins {}\r\n\r\ninterface Document {\r\n    addEventListener(type: \"deviceready\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"pause\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"resume\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"backbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"menubutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"searchbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"startcallbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"endcallbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"volumedownbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    addEventListener(type: \"volumeupbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n\r\n    removeEventListener(type: \"deviceready\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"pause\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"resume\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"backbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"menubutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"searchbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"startcallbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"endcallbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"volumedownbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: \"volumeupbutton\", listener: (ev: Event) => any, useCapture?: boolean): void;\r\n\r\n    addEventListener(type: string, listener: (ev: Event) => any, useCapture?: boolean): void;\r\n    removeEventListener(type: string, listener: (ev: Event) => any, useCapture?: boolean): void;\r\n}\r\n\r\ninterface Window {\r\n  cordova:Cordova;\r\n}\r\n\r\n// cordova/argscheck module\r\ninterface ArgsCheck {\r\n    checkArgs(argsSpec: string, functionName: string, args: any[], callee?: any): void;\r\n    getValue(value?: any, defaultValue?: any): any;\r\n    enableChecks: boolean;\r\n}\r\n\r\n// cordova/urlutil module\r\ninterface UrlUtil {\r\n    makeAbsolute(url: string): string\r\n}\r\n\r\n/** Apache Cordova instance */\r\ndeclare var cordova: Cordova;\r\n\r\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change Log\n\n## v1.11.8 (967) \n\n### Features\n* feat: add acode cli by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1768\n* feat: Add FileTree component with virtual scrolling by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1774\n* feat(welcome): add welcome tab for first-time users by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1783\n* feat: Add visibility toggle for quicktools toggler on terminal input by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1766\n* feat: Add smart path shortening to terminal prompt by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1767\n* feat(settings): add option to preserve original item order by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1772\n* feat: add \"Open in Terminal\" option to folder context menu by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1809\n* feat: add proper fallback and saf uri handling for ischeck by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1814\n* feat: extensionless URL resolution in preview server by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1824\n* feat: improve DevContainer and Docker support for easier dev setup by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1823\n* feat: add eruda devtools for debugging by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1831\n* feat: Refactor li-icon set and add new icon by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1759\n* feat: Enhance `addIcon` to support svg currentColor by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1760\n* feat: Offload file change detection to background thread by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1802\n* feat: move service to background by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1754\n* feat: add quiet hours for ads by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1779\n* feat(auth): move authentication to native Android plugin with encrypted storage by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1794\n* feat: do not close milestone issues by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1799\n* feat: close search dialog with ctrl+f by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1807\n* feat: added executor tests by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1816\n* Added tests by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1813\n* CustomTabs web api for plugins by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1785\n* Implement plugin loading callbacks and tracking for installed plugins by @7HR4IZ3 in https://github.com/Acode-Foundation/Acode/pull/1558\n* Added Documents viewer plugin id to load pdf/excel/csv etc. files by @hackesofice in https://github.com/Acode-Foundation/Acode/pull/1833\n* Refactor quicktools settings by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1761\n\n### Fixes\n* fix: use correct timezone by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1716\n* fix: sanitize plugin readme before rendering by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1731\n* fix: make rm wrapper silent by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1727\n* fix: remove duplicate rm command by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1732\n* fix: sidebar app icon size and scrolling by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1733\n* fix: terminal bug by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1741\n* fix: sidebar apps active and removal things with fallback by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1749\n* fix: improve the backup and restore by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1744\n* fix: console by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1763\n* fix(terminal): cleanup broken tabs and show alerts on terminal errors by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1780\n* fix(plugin): sanitize markdown and prevent horizontal overflow by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1782\n* fix: run current File not respecting `preview mode` option. by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1805\n* fix: memory leak when toggling search panel by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1808\n* fix: prevent mixed content in SFTP/FTP cached files by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1815\n* Fix: Remove trimming of matched content in Executor.js by @dikidjatar in https://github.com/Acode-Foundation/Acode/pull/1798\n* revert: relative http paths by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1743\n\n### Chore & CI\n* Add F-droid Build in GH nightly releases by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1719\n* fix(CI): normal nightly & fdroid flavor build paths. by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1722\n* Update devcontainer.json and add Dependabot for devcontainer's version updates by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1742\n* enhance nightly build workflow by adding a step to generate release notes by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1747\n* chore: update nightly build workflow to use a fine-grained GITHUB_TOKEN for generating nightly release notes by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1751\n* chore: message type detection in release notes script by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1786\n* update(sftp): dependencies for SFTP plugin by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1792\n* chore(deps): bump tmp and cordova by @dependabot[bot] in https://github.com/Acode-Foundation/Acode/pull/1720\n* chore(deps): bump tough-cookie and cordova by @dependabot[bot] in https://github.com/Acode-Foundation/Acode/pull/1721\n* chore(deps): bump systeminformation from 5.27.11 to 5.27.14 by @dependabot[bot] in https://github.com/Acode-Foundation/Acode/pull/1750\n* chore(deps): bump tar from 7.5.2 to 7.5.3 by @dependabot[bot] in https://github.com/Acode-Foundation/Acode/pull/1829\n\n### Translations & i18n\n* `pl-pl.json` by @andrewczm in https://github.com/Acode-Foundation/Acode/pull/1709\n* `de-de.json` by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1714, https://github.com/Acode-Foundation/Acode/pull/1764\n* `cs-cz.json` by @Amereyeu in https://github.com/Acode-Foundation/Acode/pull/1752, https://github.com/Acode-Foundation/Acode/pull/1828\n* `hu-hu.json` by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1755, https://github.com/Acode-Foundation/Acode/pull/1762, https://github.com/Acode-Foundation/Acode/pull/1787, https://github.com/Acode-Foundation/Acode/pull/1810, https://github.com/Acode-Foundation/Acode/pull/1834\n* `zh-cn.json`, `zh-hant.json` by @LaunchLee in https://github.com/Acode-Foundation/Acode/pull/1769\n* `pt-br.json` by @sebastianjnuwu in https://github.com/Acode-Foundation/Acode/pull/1775\n* `uk-ua.json` by @PavloPogonets in https://github.com/Acode-Foundation/Acode/pull/1818, https://github.com/Acode-Foundation/Acode/pull/1821\n\n## v1.11.7 (966) \n\n* revert: sidebar/style.scss changes to fix collapse folders by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1572\n* Terminal Service by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1570\n* fix(i18n): fix typo in ./src/lang/id-id.json by @hyperz111 in https://github.com/Acode-Foundation/Acode/pull/1577\n* fix: browser download by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1587\n* Terminal initrc support by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1590\n* chore(i18n): update de-de.json by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1600\n* Updated & Added Missing ar-ye.json (Arabic) translations by @Hussain96o in https://github.com/Acode-Foundation/Acode/pull/1601\n* Restore terminal tabs  by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1592\n  - fix: terminal font issue\n  - Breaking change: Changed default terminal configs\n* feat: service on/off by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1602\n* fix: service stop on app exit by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1603\n* fix: ED25519 SSH keys not working by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1595\n* CI: Nightly Builds by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1612\n* style(terminal): Some touch selection handle enhancements by @peasneovoyager2banana2 in https://github.com/Acode-Foundation/Acode/pull/1611\n* fix: pip by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1617\n* feat: Enable all file access in nightly builds by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1618\n* Add support for renaming documents in provider by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1619\n* fix: support for Acode terminal SAF URIs by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1621\n* fix: rm command by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1623\n* feat: add 'check for app updates' setting to toggle the automatic behaviour by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1624\n* chore(i18n): update hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1626\n* Enforce Unix lf endings for shell scripts by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1637\n* AutoSuggest install command in terminal by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1638\n* feat: add plugin filtering by author and keywords by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1625\n  - Refactored filtering logic to support multi-page results and improved UI feedback for filter actions.\n* Translation: Update hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1640\n* fix: init-alpine by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1641\n* fix: ANSI escape sequence in init-alpine by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1643\n* chore(i18n): update de-de.json by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1648\n* fix: update proot binaries to support 16kb page size by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1649\n* chore(i18n): update id-id.json with new strings by @hyperz111 in https://github.com/Acode-Foundation/Acode/pull/1650\n* Translation: Update hu-Hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1653\n* Add confirmation prompt before closing terminal tabs by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1655\n* fix: compatibility for old android versions by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1656\n* fix: improve file sharing and URI handling by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1662\n  - Improved file sharing and fixed permission issue (also in case of open with , edit with)\n* fix: do not restore terminals if axs is dead by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1664\n* fix: .capitalize() removed because it changes the translations (also English) by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1665\n* fix: `switchFile` api to respect custom subtitle by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1672\n* Update zh-cn.json and zh-hant.json by @LaunchLee in https://github.com/Acode-Foundation/Acode/pull/1674\n* fix: Translation corrected in terminal settings by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1676\n* fix: Added missing translation for info window in file browser and app settings. by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1677\n* Translation: Update hungarian hu-HU.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1680\n* Update ads plugin and fix some issues of free version by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1683\n  - fix: system them on free version\n* fix: restore folds when formatting if available by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1682\n* fix: Added missing translation for info window in terminal settings by @Mr-Update in https://github.com/Acode-Foundation/Acode/pull/1681\n* Translation: Update hungarian hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1687\n* feat: Add clean install state functionality to app settings by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1690\n* Translation: Update hungarian hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1693\n\n\n## v1.11.6 (965)\n\n* fix: Terminal in F-Droid flavour by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1478\n* feat: update target SDK to API 35 and fix edge-to-edge compatibility by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1486\n* improve German translation by @brian200508 in https://github.com/Acode-Foundation/Acode/pull/1487\n* Translation: Update hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1489\n* chore(update IAP library to lateset version) by @deadlyjack in https://github.com/Acode-Foundation/Acode/pull/1405\n* feat: font manager ui for managing custom fonts by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1491\n* feat: add package updated time display on plugin page by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1494\n* chore: update id-id.json by @hyperz111 in https://github.com/Acode-Foundation/Acode/pull/1495\n* fix: document provider by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1497\n* feat(sponsor page) by @deadlyjack in https://github.com/Acode-Foundation/Acode/pull/1496\n* fix: load custom fonts on restart by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1499\n* Update hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1498\n* fix: resolve GitHub URI preview issues in server by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1501\n* fix: plugins page scrolling by @deadlyjack in https://github.com/Acode-Foundation/Acode/pull/1505\n* [chore] bump workflow dependencies to latest versions by @Jvr2022 in https://github.com/Acode-Foundation/Acode/pull/1506\n* fix: load base app stylesheet in custom editor tab by @deadlyjack in https://github.com/Acode-Foundation/Acode/pull/1507\n* update src/lang/pt-br.json by @sebastianjnuwu in https://github.com/Acode-Foundation/Acode/pull/1510\n* Add code formatter keybind by @UnschooledGamer in https://github.com/Acode-Foundation/Acode/pull/1511\n* Added hyphen in quicktools and more strings for i18n by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1516\n* chore(i18n): update id-id.json with new strings by @hyperz111 in https://github.com/Acode-Foundation/Acode/pull/1521\n* chore(i18n): update hu-hu.json with new strings by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1520\n* Add/Update Bengali (bn-BD) Translation File by @thebadhonbiswas in https://github.com/Acode-Foundation/Acode/pull/1522\n* feat: take confirmation before uninstalling terminal by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1528\n* fix ui issue by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1527\n* fix: disable changing editor theme when system is selected by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1529\n* fix: svg render issue by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1530\n* fix: encoding detection by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1533\n* fix: big screen ui and global search worker issue by @deadlyjack in https://github.com/Acode-Foundation/Acode/pull/1534\n* fix: improved terminal mounts by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1537\n* chore(i18n): refine Russian (ru-ru.json) localization with updated and new strings by @Nein-Ich-wurde-Gewinnen in https://github.com/Acode-Foundation/Acode/pull/1541\n* Fixed Plugin installation issues(in some cases) by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1546\n* feat: add option to auto detect encoding by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1547\n* fix: ftp infinite loading by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1552\n* fix: tab active state when switching tab with api by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1557\n* fix: resize of terminal and some small patches by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1562\n* fix: fdroid builds by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1565\n\n## v1.11.5 (963)\n\n* Alpine Linux Backend by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1401\n* fix: html escaping for console page by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1411\n* Create plugin for native libs by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1413\n* feat: terminal frontend by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1415\n* Update hu-hu.json translation by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1423, https://github.com/Acode-Foundation/Acode/pull/1425, https://github.com/Acode-Foundation/Acode/pull/1440\n* chore: update id-id.json translation by @hyperz111 in https://github.com/Acode-Foundation/Acode/pull/1424, https://github.com/Acode-Foundation/Acode/pull/1447\n* All file access permission for terminal by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1426\n* refactor: plugins page by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1428\n\t* Fixed infinite scrolling in case of filtering on both plugin page and sidebar\n    * Ui improvements on plugin page\n* Revamped few themes(fixes visibility issue) and Added few new ones  by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1430\n\t* New theme: Neon, Sunset, Glass, Glass Dark\n\t* Revamped themes: System (Dark & Light), Oled, Light\n\t* System theme option is available on free too.\n* fix: scaling issue by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1431\n* fix: quicktools toggler responsiveness(scaling) and migrate biome by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1442\n* chore: update ace to v1.43.2 by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1443\n* feat: add uninstall option in terminal setting and tab subtitle fixes by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1445\n* feat: alpine document provider(exposes terminal public dir to saf picker) by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1444\n* feat: improved logging + fix terminal installation failure by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1446\n* some terminal related quality improvements by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1449\n\t* fix: dont start selection in case of back gesture\n\t* remove useless border-radius from terminal container\n\t* fix: improve terminal cursor positioning for mobile keyboard events\n\t* fix: update terminal container background when theme changes\n* exclude alpine/public while backup by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1450\n* Custom port support in Terminal manager by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1459\n* 🌐 i18n(locale): update vietnamese translations by @Nekitori17 in https://github.com/Acode-Foundation/Acode/pull/1461\n* fix: infinite loading when previewing by @RohitKushvaha01 in https://github.com/Acode-Foundation/Acode/pull/1460\n* Feat/some improvements to terminal and its api by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1463\n* Fixes/sidebar extension state by clearing and active autocompletion box background  by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1466\n* feat: add terminal like search and replace history by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1467\n* fix: theme issue in terminal context menu\n* fix: prevent folder paste loops and improve SAF handling(termux) in cut operation by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1474\n\t- Add validation to prevent pasting folders into themselves or subdirectories\n\t- Implement special Termux SAF handling for cut operations with recursive file/folder moving\n\t- Fix createFileStructure to handle nested paths (foo/bar) for file:// URIs\n\n## v1.11.4 (962)\n\n* Fix renaming current Termux URI file whilst viewing/editing it by @peasneovoyager2banana2\n* fix: html preview on unsaved files by @RohitKushvaha01\n* Add markdown-it-footnote and task lists support by @UnschooledGamer\n* Fix quickTools height fallback by @bajrangCoder\n* Comment out column adjustment in touchHandler.js for weird selection by @bajrangCoder\n* Update Indonesian Translation by @hyperz111\n* feat: :sparkles: Native Websocket Plugin (uses okhttp) by @UnschooledGamer\n* fix: unintentional scrolling of tab container while dragging a tab by @peasneovoyager2banana2\n* feat. alert user when changing editor theme by @RohitKushvaha01\n* feat: Improve handling of intents before files are restored on startup by @bajrangCoder\n* fix: don't save state of SAFMode=\"single\" files by @bajrangCoder\n* fix: plugin passing undefined to plugin item on plugin page by @bajrangCoder\n* Integrated cordova-plugin-buildinfo as a local plugin by @RohitKushvaha01\n* fix: crash when running unsaved file by @RohitKushvaha01\n* feat: show open source plugin url on plugin page by @bajrangCoder\n* fix: Make \"None\" option clickable in select dialog on formatter settings by @bajrangCoder\n* fix: show confirm when copy/cut in sidebar instead of alert so user can proceed if they want by @bajrangCoder\n* feat: plugin enable/disable functionality and update translations by @UnschooledGamer\n* Update hu-hu.json by @summoner001\n* Advanced execution interface by @RohitKushvaha01\n\n## v1.11.3 (959)\n\n* feat: added system theme by @RohitKushvaha01\n* add: close-inactive-issues.yml by @UnschooledGamer\n* Added Executor by @RohitKushvaha01\n* fix: Executor Plugin's js module/interface path by @UnschooledGamer\n* fix: teardrop goes out of viewport when there is no gutter by @bajrangCoder\n* fix: infinite loading screen due to executor by @RohitKushvaha01\n* fix: infinite loading when previewing html files by @RohitKushvaha01\n* fix: apk related issues\n\n## v1.11.2 (958)\n\n* fix: cors related issues when installing plugins from remote by @bajrangCoder\n* fix: Acode ignoring main, readme and icon fields in plugin manifest by @alMukaafih\n* feat: inapp acode account login by @bajrangCoder\n* fix: launchApp function by @RohitKushvaha01\n* Update plugin documentation url by @RohitKushvaha01\n* Backup restore fixes by @bajrangCoder\n* file: re-emit switch and load file events after plugin load by @bajrangCoder\n* fix: File copy-paste retains paste option after file is copied. by @RohitKushvaha01\n* Fix sftp sidebar UI issue by @bajrangCoder\n* fix: http relative path by @RohitKushvaha01\n* Download support by @RohitKushvaha01\n* Add Custom File Type Handler API by @bajrangCoder\n* fix: applying folds on reopening the app by @bajrangCoder\n* feat(tabs): Implement Shadow DOM isolation for non-editor tabs by @bajrangCoder\n* remove invalid font by @bajrangCoder\n* Improve Indonesian Translation by @hyperz111\n* feat: improve changelogs page by @bajrangCoder\n* Refactor, feat: Select dialog by @overskul\n* Fix: Translate \"Donation Message\" and some words by @hyperz111\n* Fix: \"File Not Found\" error when previewing HTML files from a Termux directory by @RohitKushvaha01\n* Update zh-cn.json and zh-hant.json by @LaunchLee\n* fix: console not showing on unsaved html file by @RohitKushvaha01\n* File Menu & QuickTools Visibility for editor tabs by @bajrangCoder\n* fix: handle edge case for `hideQuickTools` property by @bajrangCoder\n* feat: add more new keys(<kbd>Home</kbd>,<kbd>End</kbd>, <kbd>PageUp</kbd>, <kbd>PageDown</kbd>, <kbd>Delete</kbd>) and symbols(`~` `Backtick`,`#` ,`$` ,`%` ,`^`) for quicktools by @bajrangCoder\n* Fixed select box issue and improved it by @bajrangCoder\n\t* Added `x` to delete recent files/folder from dialog\n* Update Hungarian translation by @summoner001\n* chore(i18n): update vi-vn.json by @Nekitori17\n* fix: file not found error by @RohitKushvaha01\n* feat: revamped file tree by @bajrangCoder\n* fix: pagedown key issue in editor by @bajrangCoder\n* feat: open files with arbitrary extension names by @RohitKushvaha01\n* add Hebrew language by @elid34\n* feat: add support for compound file paths like `.blade.php` by @bajrangCoder\n* support string content in tabs by @overskul\n* update Tagalog/Filipino language by @ychwah\n* fix: keyboard shortcuts leaks into ace editor by @RohitKushvaha01\n* Resizeable activity by @RohitKushvaha01\n* update ace v1.41.0 by @bajrangCoder\n* Fix html content access by @RohitKushvaha01\n* fix: path overlap issue in html viewer by @RohitKushvaha01\n* fix: only initiate iap stuff in case of paid plugin on sidebar by @bajrangCoder\n\n## v1.11.1 (957)\n\n### Features\n- **Syntax Highlighting**: Added syntax highlighting for code blocks on the plugin page using Ace Editor.\n- **Theme Settings Fallback**: Implemented a fallback mechanism on the theme settings page when a custom tab is opened.\n- **QuickTools Scroll Wheel Support**: Added support for scroll wheel events in QuickTools when in click mode.\n- **SFTP Improvements**:\n  - Enhanced logging for better debugging.\n  - Enabled Bouncy Castle for SFTP, resolving connection issues with certain key types.\n- **App Update Mechanism**:\n  - Now checks for updates after app startup.\n  - Uses a native approach instead of traditional fetch.\n\n### Fixes & Improvements\n- **SD Card Plugin**: Handled a few edge cases to prevent crashes.\n- **GitHub Plugin Palette Fix**: Resolved an issue where the color palette was breaking the GitHub plugin.\n- **Plugin Installation Fixes**:\n  - Updated the install button in the sidebar to prevent multiple installations.\n  - Fixed the installation mechanism in both the sidebar and the `installPlugin` API.\n- **Quality of Life Enhancements**:\n  - Various small improvements when installing/uninstalling plugins from the sidebar.\n\n#### ⚠️ Experimental Changes ⚠️\n- **Improved Plugin Loading**:\n  - Only **theme plugins** load on Acode startup for faster performance.\n  - All other plugins load **after** Acode startup.\n- **Important for Theme Developers**:\n  - Ensure your **plugin ID includes the word \"theme\"** to be correctly recognized as a theme plugin.\n  - No changes are needed for existing theme plugins.\n- **Potential Issues**: Since this is an experimental change, some features may break. Please report any issues encountered.\n\n## v1.11.0 (956)\n\n### Fixes\n* Fixed a typo: \"vission\" → \"vision\" by @ByteJoseph in #1125\n* Fixed heading and image alignment issues with `alignment=\"center\"` on the plugin page by @bajrangCoder in #1132\n* Fixed file listing in SFTP by @bajrangCoder in #1133\n* Fixed fallback to `*/*` when the `accept` attribute is absent in `<input type=\"file\">` in the in-app browser by @bajrangCoder in #1154\n* Fixed console not reappearing after page reload in the in-app browser by @bajrangCoder in #1155\n* Fixed infinite scroll on the plugin page to remove duplicates by @bajrangCoder in #1171\n* Fixed logger to limit its size in #1167\n* Fixed an issue where the info dialog wouldn't appear for non-editor tabs in #1167\n* Fixed incorrect file attributes in FTP by @bajrangCoder in #1194\n* Fixed the palette not opening when triggered from an existing palette by @bajrangCoder in #1197\n* Fixed triggering of infinite scroll on plugin page while searching by @bajrangCoder in #1200\n\n### Features\n* Improved tab view gesture handling to distinguish between scroll and swipe on the plugin page by @bajrangCoder in #1131\n* Added color preview for SVG files by @bajrangCoder in #1135\n* Implemented custom editor tab support by @bajrangCoder in #1136\n  * Now supports image, video, and audio previews directly in the editor instead of pop-ups\n  * Exposed API for plugin developers to add content in editor tabs via `EditorFile`\n* Redesigned the About page by @bajrangCoder in #1153\n* Added a plugin rebuild option for local plugins by @bajrangCoder in #1150\n  * Plugins can use `cordova.plugin.http` instead of `fetch` to avoid CORS issues when making network requests\n* Redesigned the Plugin page by @bajrangCoder in #1152\n  * Added new metadata fields in `plugin.json`: `license`, `changelog`, `keywords`, and `contributors`\n* Added a new option to open files in an external app from the file browser in #1163\n* Added minor sidebar UI tweaks and improved input element styling in #1164\n* Used theme colors in the extra cutout area in landscape mode instead of the default color in #1165\n* Improved file info dialog design in #1170\n* Added Eruda console support for external sites in Acode's built-in browser in #1172\n* Added a new editor setting: **\"Fade fold widgets\"** by @bajrangCoder in #1195\n* Added a command to change the editor and app theme via the command palette by @bajrangCoder in #1197\n* Added option to install plugins directly from sidebar extensions app by @bajrangCoder in #1200\n\n### Improvements\n* Improved paste operation with proper error handling in #1162\n* Rewritten SFTP implementation(#1167):\n  * Better performance and stability\n  * Improved symlink support with better visual distinction\n  * Proper error handling\n  * Enhanced file reading (downloading) and writing\n  * Uses native APIs for file system operations\n  * Better buffer handling—now even 30-40 minute videos load within seconds (faster than internal storage)\n  * Emoji support added\n* Improved the FTP client (#1193) by @bajrangCoder\n  * Supports emoji-named files\n  * Improved symlink handling\n  * Better handling of videos and images as binary files\n  * Displays file names instead of full paths\n* Reworked the plugin page UI—now displays essential info such as author, license, price, etc., in the list view by @bajrangCoder in #1196\n* Tweaked breadcrumbs in the file browser to follow the app theme in #1167\n* Updated the SSH library to `v3.1.2` in #1167\n* Removed deprecated APIs in #1167\n* Added experimental support for saving native logs in Acode Logger in #1167\n* Tweaked the donation page  #1188\n\n### Other Changes\n* **Chore**: Updated Ace Editor to `v1.39.0`\n  * Added CSV & TSV mode\n  * Improved search support for multi-line patterns (`\\n`, `\\t`)\n  * And more—see the Ace Changelog\n* Many translation updates for `hu-hu` by @summoner\n\n## v1.10.7 (955)\n\n* Update hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1092\n* build(deps): bump path-to-regexp and express by @dependabot in https://github.com/Acode-Foundation/Acode/pull/1093\n* Update hu-hu.json by @summoner001 in https://github.com/Acode-Foundation/Acode/pull/1094\n* Exclude python `venv` and php `vendor` folders to speedup startup by @Jobians in https://github.com/Acode-Foundation/Acode/pull/1096\n* Update de-de.json to 1.10.6.954 by @Micha-he in https://github.com/Acode-Foundation/Acode/pull/1097\n* Update pl-pl.json by @andrewczm in https://github.com/Acode-Foundation/Acode/pull/1099\n* minor fixes and tweaks by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1103\n  * Added new command(file explorer) to open file manager\n  * Added copy button to code blocks on plugin page\n  * some error logging for debugging\n  * few markdown tweaks\n* Updating Brazilian Portuguese language  by @sebastianjnuwu in https://github.com/Acode-Foundation/Acode/pull/1102\n* add className by @NezitX in https://github.com/Acode-Foundation/Acode/pull/1104\n* Add monochrome icon by @Npepperlinux in https://github.com/Acode-Foundation/Acode/pull/1108\n* fix: issue while making request to any ipv4, etc from app by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1107\n* Update zh-cn.json and zh-hant.json by @LaunchLee in https://github.com/Acode-Foundation/Acode/pull/1109\n* fix: show \"No matches found\" when palette has no matching items by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1110\n* build(deps): bump systeminformation from 5.21.8 to 5.23.14 by @dependabot in https://github.com/Acode-Foundation/Acode/pull/1111\n* Follow up for #1110 by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1112\n* fix: cleanup plugin install state on failed installations by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1113\n* feat: implement infinite scroll for plugin list by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1114\n* feat: add quick install button for plugins in sidebar by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1115\n* Plugin Dependencies by @alMukaafih in https://github.com/Acode-Foundation/Acode/pull/1030\n* updated install state to reflect fs behaviour of android by @alMukaafih in https://github.com/Acode-Foundation/Acode/pull/1118\n* fix: clean broken or half installed plugins on start by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1121\n* few improvements by @bajrangCoder in https://github.com/Acode-Foundation/Acode/pull/1122\n\n## v1.10.6 (954)\n\n### New Features\n- **Install State for Plugins**: Added an install state to improve plugin updates (#1026) by @alMukaafih, further enhanced by @bajrangCoder.\n- **Selection Mode in File Browser**: Introduced a selection mode in the file browser by @bajrangCoder.\n- **Persistent Notification System**: Added a persistent notification system with toast notifications by @bajrangCoder.\n- **In-App Browser Command**: Added a command to open an in-app browser with a given URL by @angeloyana.\n- **Font Size Shortcut Keys**: Added new key shortcuts for changing font size by @bajrangCoder:\n    - Increase Font Size: `Ctrl - +` or `Ctrl - =`\n    - Decrease Font Size: `Ctrl + -` or `Ctrl - _`\n- **Command Palette Enhancements**:\n    - \"Open Plugin Page\" command for quick access to plugin pages, especially for keyboard users.\n    - \"Copy Device Info\" command to share device information for troubleshooting.\n- **GitHub Alert Support**: Added GitHub alert support in plugin descriptions by @bajrangCoder.\n- **File Tab Drop Behavior**: Dropping a file tab into any input or editor now inserts its path by @bajrangCoder.\n- **File Browser Context Menu**: Added a \"Copy URI\" option in the file browser context menu by @bajrangCoder.\n- **Project Import as ZIP**: Added the option to import projects as ZIP files by @bajrangCoder.\n- **Backup Plugins**: You can now back up plugins, whether they are paid or free by @bajrangCoder.\n- **Task List Markdown Support**: Added support for task lists (`- [x]`) in the plugin page markdown by @bajrangCoder.\n- **Install as Plugin for ZIP Files**: Added the \"Install as Plugin\" option in the sidebar files section for ZIP files containing a `plugin.json` in the root directory by @bajrangCoder.\n- **`acode.installPlugin` API**: Introduced an API for plugins to install other plugins with user consent by @bajrangCoder(available from versionCode: `954`).\n- **View Changelogs in Settings**: Added an option in the settings page to view changelogs directly inside the app by @bajrangCoder.\n- **App Update Checker**: Implemented an app update checker that runs on startup by @bajrangCoder.\n- feat: copy/cut current line when selection is empty with same key shortcut by @angeloyana\n- feat: add symlinks support in SFTP by @bajrangCoder\n\n### Fixes\n- **Plugin Loading Failures**: Improved handling of plugin loading failures by @bajrangCoder:\n    - Prevents the app from crashing when plugins fail to load.\n    - Shows user feedback for each failed plugin while continuing to load others.\n- **Internal URL Navigation**: Replaced browser navigation with scroll behavior for internal links in plugin descriptions. Links to the same markdown now scroll instead of opening a browser by @bajrangCoder.\n- **Pro Version Ads Issue**: Fixed an issue where plugin context menu actions displayed ads even if the Pro version was purchased by @UnschooledGamer.\n- **Termux Private URI Operations**: Resolved issues with deleting folders/files and renaming files in Termux private URI file systems.\n- **Logger Enhancements**: Improved the logger to automatically detect failures and use `console.error` in Acode.\n- **Preview and Server Port Issue**: Fixed an issue where the browser did not open on the given port when the preview port and server port differed.\n- **Font Persistence**: Resolved an issue where fonts did not persist after restarting the app.\n- **Markdown Linking**: Fixed issues with linking to headings within the same page in markdown.\n- **Search Bar in File Browser**: Fixed a bug where the search bar in the file browser would get stuck and become unclosable.\n- **Theme Page Issues**: Addressed issues with theme plugins, including preview rendering and checkbox state changes.\n- **Formatter Mode Selection**: Fixed the formatter ignoring the selected mode for files by @alMukaafih.\n- fix: fetch plugin updates even if any fails by @UnschooledGamer\n- fix: plugin update page by removing unwanted option icon @bajrangCoder\n\n### Others\n- **Plugin Refactor**: Migrated the old plugin update icon to a new toast-like notification widget.\n\n### Translators\n- Updated translations for specific languages contributed by:\n    - **@Micha-he**: `de-de.json`\n    - **@LaunchLee**: `zh-cn.json` and `zh-hant.json`\n    - **@andrewczm**: `pl-pl.json`\n    - **@Nekitori17**: `vi-vn`\n    - **@s4ntiksu**: `ru-ru.json`\n    - **@summoner001**: `hu-hu.json`\n    - **@antikruk**: `be-by.json`\n\n---\n\n## [1.10.5] (953)\n\n- New\n  - Plugin Filtering System in #1011\n  - feat: Add more action menu in sidebar plugin in #1017\n  - Implement Logger system in #1020\n  - Feat: Use Current File for preview (Toggle option) in #1006\n  - updated ace editor to v1.36.2 in #1025\n\n- Fixes\n  - Update de-de.json in #1039\n  - fixed sidebar plugin search list scrolling in #1002\n  - Improve zh-TW traditional Chinese translation in #1004\n  - fix: Fixed save all changes option in #1010\n  - chore(i18n): vi-vn in #1016\n  - removed auto paste of url on plugin page in #1023\n  - fixed weird spacing issue on header #925 in #1024\n  - Update zh-cn.json and zh-hant.json in #1031\n  - Refactor Iap.java: Use WeakReference for Context and Activity to prevent memory leaks in #1040\n  - Updated Polish translation in #1043\n  - ru-ru improved in #1041\n  - Update pl-pl.json in #1044 & #1045\n  - fix: termux related fs operations failure in #1046\n\n## [1.10.4] (952)\n\n- New\n  - Nested Files/Folder Creation\n  - Updated ace to latest version\n  - Improved displaying of Download count on Plugins page as well as on Sidebar\n  - Enhanced search functionality to allow searching across all available plugins from the \"all\" section of the plugin page.\n  - Added a new option on Help page for submitting bug reports.\n- Fixes\n  - Fixed issue with the search bar on the plugin page\n  - Fixed issue with the search bar closing accidentally when clicking elsewhere on the screen\n\n## [1.10.2]\n\n- New\n  - [x] **Updated Ace editor** | 947\n    - Updated Ace editor to version 1.33.1.\n\n## [1.10.1]\n\n- New\n  - [x] **Updated Ace editor** | 946\n    - Updated Ace editor to version 1.32.9\n- Fixes\n  - [x] **Scrolling** | 946\n    - Fixed scrollbars not showing up properly.\n\n## [1.10.0]\n\n- New\n  - [x] **Ace editor** | 937\n    - Updated Ace editor to version 1.32.7\n- Fixes\n  - [x] **UI** | 944\n    - Fixed status and navigation text color not visible in light theme.\n  - [x] **Plugin** | 944\n    - Fixed updates for plugin not showing up.\n  - [x] **Markdown** | 945\n    - Fixed markdown preview not working properly.\n  - [x] **Text selection** | 937\n    - Fixed context menu not showing up when selecting all text from context menu after click and hold.\n\n## [1.9.0]\n\n- New\n  - [x] **New in app browser** | 324\n    - New in app browser with new UI.\n    - You can emulate multiple devices.\n  - [x] **New mode** | 933\n    - Zig mode\n    - Astro mode\n- Fixes\n  - [x] **File** | 931\n    - Fixed not able share, edit or open with other apps.\n    - Fixed file rename not working properly. | 934\n  - [x] **Preview** | 934\n    - Fixed preview where it opens browser two times.\n\n## [1.8.8]\n\n- New\n  - [x] **Natural Scrolling** | 331\n    - Acode now uses natural scrolling.\n- Fixes\n  - [x] **Selecting text** | 331\n    - Fixed an issue where selecting text using long press and adding text to selection behaves unexpectedly.\n  - [x] **Open folders** | 331\n    - Fixed an issue where removing folder from sidebar doesn't remove the selected folder.\n\n## [1.8.7]\n\n- New\n  - [x] **Updated Ace editor** | 318\n    - Updated Ace editor to version 1.28.0\n  - [x] **New problems page** | 318\n    - You can now see all the problems in one place.\n    - You can also see the problems in opened file.\n  - [x] **New settings** | 318\n    - New settings to toggle side buttons.\n  - [x] **New Plugin API** | 318\n    - `SideButton` is a new component that can be used to add side buttons.\n  - [x] **New theme color** | 318\n    - New `danger-color` and `danger-text-color` theme color.\n  - [x] **New key binding** | 318\n    - Use `Ctrl+Shift+X` key binding to open problems page.\n  - [x] **Plugin** | 320\n    - Install plugin directly from browser.\n  - [x] **Intent** | 323\n    - Plugin has now API to handle intent with uri acode://module/action/value.\n- Fixes\n  - [x] **Plugin page** | 318\n    - Improved plugin page UI.\n    - Shows plugin quickly when opened and loads new information in background.\n  - [x] **Unsaved changes** | 318\n    - Fixed unsaved changes not showing up in file when app restarted.\n  - [x] **Quicktools** | 319\n    - Fixed quicktools slides back when touch moved slightly.\n  - [x] **Settings** | 321\n    - Fixed settings not saving properly.\n  - [x] **Internal storage** | 322\n    - Fixed renaming file.\n  - [x] **Side buttons** | 323\n    - Fixed side buttons not shown properly.\n  - [x] **Open folders** | 330\n    - Fixed move file/folder not working properly.\n  - [x] **Editor** | 330\n    - Improved scrolling.\n  - [x] **Quicktools** | 330\n    - Improved quicktools.\n\n## [1.8.6] - Build 313\n\n- New\n  - [x] **Search in settings**\n    - You can now search for any settings in settings page.\n- Updates\n  - [x] **Language**\n    - Updated language pack for Russian, Spanish, Portuguese and Deutsche.\n  - [x] **Updated Ace editor**\n    - Updated Ace editor to version 1.5.0\n- Fixes\n  - [x] **Sidebar search**\n    - Fixed sidebar search not rendering words with special characters.\n  - [x] **Not Loading**\n    - Fixed app not loading on older devices.\n  - [x] **Scrolling**\n    - Fixed scrolling not working properly on some devices.\n  - [x] **Eruda console**\n    - Fixed eruda console not working properly.\n  - [x] **Scrollbar**\n    - Fixed scrollbar not working properly.\n  - [x] **Custom theme**\n    - Fixed custom theme not working properly.\n\n## [1.8.5] - Build 295\n\n- New\n  - [x] **Scroll speed**\n    - New 'Fast x2' scroll speed.\n  - [x] **Touch handling**\n    - Prevent accidental touch when tapping tear drop.\n  - [x] **Color Preview**\n    - You can now see color preview in css, scss, less, stylus and sass codes.\n    - No need to select the whole color.\n    - Enable/disable this feature from editor settings.\n  - [x] **Smaller app size**\n    - Reduced app size.\n  - [x] **Updated icon pack**\n    - Updated icon pack (mono color).\n  - [x] **Default file encoding**\n    - You can set default file encoding from settings.\n  - [x] **File encoding**\n    - Remember file encoding for each file.\n  - [x] **Sidebar apps**\n    - File list and extension list now remembers scroll position.\n  - [x] **File tab bar**\n    - When repositioning file tab bar, tab container will scroll when current tab is at the edge.\n- Fixes\n  - [x] **Touch handling**\n    - Fixed teardrop and text menu not updated when switching tabs.\n  - [x] **File encoding**\n    - Fixed file encoding not working properly.\n  - [x] **File icon**\n    - Fixed inconsistent file icon.\n  - [x] **JavaScript console**\n    - Fixed JavaScript console not opening.\n  - [x] **Ads**\n    - Fixed ads taking screen when keyboard is open.\n  - [x] **Insert file**\n    - Fixed 'insert file' in opened folder not working properly.\n\n## [1.8.4] - Build 283\n\n- New\n  - [x] **Updated Ace editor**\n    - Updated Ace editor to version 1.22.0\n  - [x] **Open files position**\n    - **Bottom** `beta`: This is new option for open files position. You can change\n      this from settings. This will open files in bottom of the screen.\n  - [x] **Search in All Files** `beta`\n    - This feature can be used to search and replace in all files in the opened projects.\n    - To use this feature, open side bar and click on the search icon.\n    - Note: This feature is in beta, so it may not work as expected.\n  - [x] **Fast File Listing in Find Files (Ctrl + P)**\n    - Loads files at startup and caches them for faster loading.\n    - Watches file being created or modified from sidebar and updates the list\n      accordingly.\n  - [x] **Ctrl Key Functionality**\n    - Keyboard shortcuts:\n      - Ctrl+S: Save\n      - Ctrl+Shift+P: Open the command palette. (Your shortcut may be different\n        depending on what is saved in .keybindings.json file.)\n  - [x] **Plugin API**\n    - `contextMenu` is a component that can be used to show context menu in your plugin page.\n  - [x] **Customisable quick tools**\n    - You can now customise quick tools from settings.\n- Fixes\n  - [x] **Scrolling Issue**\n    - Resolved an issue causing automatic scrolling from the cursor's\n      position when the back button is pressed with the soft keyboard up.\n    - Fixed a bug where scrollbar gets visible even when the file is not\n      scrollable.\n  - [x] **Active files in sidebar**\n    - Fixed active files taking whole height of sidebar.\n  - [x] **File opened using intent**\n    - Fixed file opened using intent is not set as active file.\n  - [x] **App doesn't load**\n    - Fixed an issue where the app wouldn't load when an error occurred.\n  - [x] **File tab bar**\n    - Changing file tab bar position will not make editor lose focus.\n  - [x] **Sidebar**\n    - Fixed sidebar resized unknowingly when dragged to open or close it.\n  - [x] **Close all tabs**\n    - Fixed \"close all\" action opens up multiple confirm dialog for every unsaved file.\n    - Now it will ask what to do with unsaved files.\n  - [x] **File changed alert**\n    - Fixed file changed alert showing up even when file is not changed.\n  - [x] **Explore**\n    - Fixed file not opening when opened from Explore.\n  - [x] **Extensions app in sidebar**\n    - Fixed extensions app's explore not rendering properly.\n  - [x] **Minor bugs**\n    - Fixed many minor bugs.\n    - Improved stability.\n\n## [1.8.3] - Build 278\n\n- [x] Bugs fixes\n\n## [1.8.2] - Build 276\n\n- [x] Updated ace editor to version v1.18.0\n- [x] Bugs fixes\n\n## [1.8.1] - Build 274\n\n- [x] Clicking on gutter will go to that line\n- [x] Plugin Stability improvement\n- [x] Updated ace editor\n- [x] Bugs fixes\n\n## [1.8.0] - Build 272\n\n- [x] Plugins can support themes, fonts and sidebar items\n- [x] Redesigned sidebar and quicktools\n- [x] UI improvements\n- [x] Bugs fixes\n\n## [1.7.2] - Build 268\n\n- [x] Added new command to toggle quick tools\n- [x] Added back search in quick tools\n- [x] Palette will close on ESC key\n- [x] Plugin settings UI\n- [x] Bugs fixes\n\n## [1.7.1] - Build 266\n\n- [x] Swipe to change tab in plugins page\n- [x] Fixed update paid plugin\n- [x] Bugs fixes\n\n## [1.7.0] - Build 262\n\n- [x] New medium size teardrop\n- [x] Fixed some indic languages rendering\n- [x] Fixed selection bug\n- [x] Support for paid plugins\n- [x] Quick tools improvement\n- [x] Settings will have info button, you can see more info about settings\n- [x] Updated plugin system\n- [x] You can see plugin review and rating\n- [x] GitHub is removed from app but, you can still use it from plugin\n- [x] Bugs fixes\n\n## [1.7.0] - Build 260\n\n- [x] Quick tools improvement\n- [x] Settings will have info button, you can see more info about settings\n- [x] Updated plugin system\n- [x] You can see plugin review and rating\n- [x] GitHub is removed from app but, you can still use it from plugin\n- [x] Bugs fixes\n\n## [1.6.1] - Build 245\n\n- [x] Updated plugin CDN\n\n## [1.6.0] - Build 244\n\n- [x] Updated plugin APIs\n- [x] Updated text menu\n- [x] Install plugin form device\n- [x] Updated ace editor\n\n## [1.6.0] - Build 239\n\n- [x] Fixed horizontal scroll bug\n- [x] Updated ace editor\n\n## [1.6.0] - Build 235\n\n- [x] Retry FTP/SFTP connection when fail\n- [x] Supports plugin\n- [x] Updated custom theme settings\n- [x] Set custom port for preview in app settings\n- [x] New option find file\n- [x] Stability improvement\n- [x] UI improvement\n- [x] Fixed keyboard issues\n- [x] Fixed tap hold to select text\n- [x] Fixed loading files using FTP/SFTP\n- [x] Fixed File checking\n- [x] Fixed settings\n- [x] Various fixes\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at Acode.CODE_OF_CONDUCT.md\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Corrections Issues\n\n**Community Impact**: Must Not Use of inappropriate coding language or other hacker behavior deemed\nAny professional or manipulative findings like ease dropping hackers, malware bots, or faulty A.I. that breaks code for attacks against citizens cut-off for those programs and users are rendered fictitious businesses spammers using meta-trans-users causing confusion and corruption problems recirculating systems. Without help fixing these misconducts are responsible with talk back in any encryptions Acode intelligence blocking them from entry to another account. Issues and comments are welcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Guidance\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent view\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent free service for bandwidth/LTE technology, for opening arguments within discussions hearing any testing that class forms, and any sort of arbitration claims processed are settled. Giving back to private sectors,to public interaction within\nthe community, state, country, and never excludes global conglomerates due to consumer reports, feedbacks, sharing viable information that interests any disadvantages against\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement reversal legislation relativity](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Acode\n\nThank you for your interest in contributing to Acode! This guide will help you get started with development.\n\n## Quick Start Options\n\n### Option 1: DevContainer (Recommended)\n\n1. Install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) in VS Code or other editors that support DevContainers.\n\n2. Clone and open the repository:\n   ```bash\n   git clone https://github.com/Acode-Foundation/Acode.git\n   code Acode\n   ```\n\n3. When VS Code prompts \"Reopen in Container\", click it\n   - Or use Command Palette (Cmd/Ctrl+Shift+P) → \"Dev Containers: Reopen in Container\"\n\n4. Wait for the container to build (~5-10 minutes first time, subsequent opens are instant)\n\n5. Once ready, build the APK:\n   ```bash\n   pnpm run build paid dev apk\n   ```\n\n   > Use any package manager (pnpm, bun, npm, yarn, etc.)\n\n### Option 2: Docker CLI (For Any Editor)\n\nIf your editor doesn't support DevContainers, you can use Docker directly:\n\n```bash\n# Clone the repository\ngit clone https://github.com/Acode-Foundation/Acode.git\ncd Acode\n\n# Build the Docker image from our Dockerfile\ndocker build -t acode-dev .devcontainer/\n\n# Run the container with your code mounted\ndocker run -it --rm \\\n  -v \"$(pwd):/workspaces/acode\" \\\n  -w /workspaces/acode \\\n  acode-dev \\\n  bash\n\n# Inside the container, run setup and build\n# bun run setup && bun run build paid dev apk\npnpm run setup\npnpm run build paid dev apk # or pnpm run build p d\n```\n\n**Keep container running for repeated use:**\n```bash\n# Start container in background\ndocker run -d --name acode-dev \\\n  -v \"$(pwd):/workspaces/acode\" \\\n  -w /workspaces/acode \\\n  acode-dev \\\n  sleep infinity\n\n# Execute commands in the running container\ndocker exec -it acode-dev bash -c \"pnpm run setup\"\ndocker exec -it acode-dev pnpm run build paid dev apk\n\n# Stop and remove when done\ndocker stop acode-dev && docker rm acode-dev\n```\n\n---\n\n## 🛠️ Manual Setup (Without Docker)\n\nIf you prefer not to use Docker at all:\n\n### Prerequisites\n\n| Requirement | Version |\n|------------|---------|\n| **Node.js** | 18+ (22 recommended) |\n| **pnpm** or **bun** | Latest |\n| **Java JDK** | 17+ (21 recommended) |\n| **Android SDK** | API 35 | \n| **Gradle** | 8.x |\n\n### Environment Setup\n\nAdd these to your shell profile (`~/.bashrc`, `~/.zshrc`, or `~/.config/fish/config.fish`):\n\n**macOS:**\n```bash\nexport ANDROID_HOME=\"$HOME/Library/Android/sdk\"\nexport PATH=\"$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin\"\n```\n\n**Linux:**\n```bash\nexport ANDROID_HOME=\"$HOME/Android/Sdk\"\nexport PATH=\"$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/cmdline-tools/latest/bin\"\n```\n\nSome more environment variables, check [cordova docs](https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html).\n\n### Build Steps\n\n```bash\n# Clone the repository\ngit clone https://github.com/Acode-Foundation/Acode.git\ncd Acode\n\n# Install dependencies and set up Cordova\npnpm run setup\n\n# Build the APK\npnpm run build paid dev apk # or pnpm run build p d\n```\n\nThe APK will be at: `platforms/android/app/build/outputs/apk/debug/app-debug.apk`\n\n\n## 📝 Contribution Guidelines\n\n### Before Submitting a PR\n\n1. **Fork** the repository and create a branch from `main`\n2. **Make changes** - keep commits focused and atomic\n3. **Check code quality:**\n   ```bash\n   pnpm run check\n   ```\n4. **Test** on a device or emulator if possible\n\n### Pull Request Checklist\n\n- [ ] Clear description of changes\n- [ ] Reference to related issue (if applicable)\n- [ ] Screenshots/GIFs for UI changes\n- [ ] Passing CI checks\n\n### Code Style\n\nWe use [Biome](https://biomejs.dev/) for linting and formatting:\n- Run `pnpm run check` before committing\n- Install the Biome VS Code extension for auto-formatting\n\n### Commit Messages\n\nUse clear, descriptive messages:\n```\nfeat: add dark mode toggle to settings\nfix: resolve crash when opening large files\ndocs: update build instructions\nrefactor: simplify file loading logic\n```\n\n## 🌍 Adding Translations\n\n1. Create a JSON file in `src/lang/` (e.g., `fr-fr.json` for French)\n2. Add it to `src/lib/lang.js`\n3. Use the translation utilities:\n   ```bash\n   pnpm run lang add       # Add new string\n   pnpm run lang remove    # Remove string\n   pnpm run lang search    # Search strings\n   pnpm run lang update    # Update translations\n   ```\n\n## ℹ️ Adding New Icons (to the existing font family)\n> [!NOTE]\n> Acode uses SVG and converts them into a font family, to be used inside the editor and generally for plugin devs.\n> \n> **Plugin-specific icons SHOULD NOT be added into the editor. Only generally helpful icons SHOULD BE added**\n\nMany font editing software and web-based tools exist for this purpose. Some of them are listed below.\n\n| Name | Platform |\n|------|----------|\n| https://icomoon.io/ | Free (Web-Based, PWA-supported, Offline-supported) |\n| https://fontforge.org/ | Open-Source (Linux, Mac, Windows) |\n\n### Steps in Icomoon to add new Icons\n\n1. Download the `code-editor-icon.icomoon.json` file from https://github.com/Acode-Foundation/Acode/tree/main/utils\n2. Go to https://icomoon.io/ > Import\n3. Import the `code-editor-icon.icomoon.json` downloaded (in step 1)\n4. All icons will be displayed after importing.\n5. Import the SVG icon created/downloaded to be added to the Font Family.\n6. On the right side, press **enable Show Characters** & **Show Names** to view the Unicode character & Name for that icon.\n7. Provided the newly added SVG icon with a name (in the name box).\n8. Repeat Step 5 and Step 7 until all needed new icons are added.\n9. Press the export icon from the top left-hand side.\n10. Press the download button, and a zip file will be downloaded.\n11. Go to the Projects section of [icomoon](https://icomoon.io/new-app), uncollapse/expand the Project named `code-editor-icon`  and press the **save** button (this downloads the project file named: `code-editor-icon.icomoon.json`)\n\n### Updating Project files for Icon Contribution\n1. Extract the downloaded zip file; navigate to the `fonts` folder inside it.\n2. Rename `code-editor-icon.ttf` to `icons.ttf`.\n3. Copy & paste the renamed `icons.ttf` into https://github.com/Acode-Foundation/Acode/tree/main/src/res/icons\n4. Copy and paste the `code-editor-icon.icomoon.json` file (downloaded in the adding icons steps) onto https://github.com/Acode-Foundation/Acode/tree/main/utils (yes, replace it with the newer one; we downloaded!)\n4. Commit the changes **ON A NEW branch** (by following: [Commit Messages guide](#commit-messages))\n\n## 🔌 Plugin Development\n\nTo create plugins for Acode:\n- [Plugin Starter Repository](https://github.com/Acode-Foundation/acode-plugin)\n- [Plugin Documentation](https://docs.acode.app/)\n"
  },
  {
    "path": "_typos.toml",
    "content": "[default]\ncheck-filename = true\n\n[files]\nextend-exclude = [\n    \"node_modules\",\n    \"www\",\n    \"*.yaml\",\n    \".vscode\",\n    \"fastlane\",\n    \"hooks\",\n    \"*.gradle\",\n    \"plugins\",\n    \"platforms\",\n    \"src/lang/ar-ye.json\",\n    \"src/lang/be-by.json\",\n    \"src/lang/bn-bd.json\",\n    \"src/lang/cs-cz.json\",\n    \"src/lang/de-de.json\",\n    \"src/lang/es-sv.json\",\n    \"src/lang/fr-fr.json\",\n    \"src/lang/hi-in.json\",\n    \"src/lang/hu-hu.json\",\n    \"src/lang/id-id.json\",\n    \"src/lang/ir-fa.json\",\n    \"src/lang/it-it.json\",\n    \"src/lang/ja-jp.json\",\n    \"src/lang/ko-kr.json\",\n    \"src/lang/ml-in.json\",\n    \"src/lang/mm-unicode.json\",\n    \"src/lang/mm-zawgyi.json\",\n    \"src/lang/pl-pl.json\",\n    \"src/lang/pt-br.json\",\n    \"src/lang/pu-in.json\",\n    \"src/lang/ru-ru.json\",\n    \"src/lang/tl-ph.json\",\n    \"src/lang/tr-tr.json\",\n    \"src/lang/uk-ua.json\",\n    \"src/lang/uz-uz.json\",\n    \"src/lang/vi-vn.json\",\n    \"src/lang/zh-cn.json\",\n    \"src/lang/zh-hant.json\",\n    \"src/lang/zh-tw.json\",\n]\n\n[default.extend-words]\n# temporary thing to staisfy the linter\nstrech = \"strech\"\ncontaienr = \"contaienr\"\nformate = \"formate\"\ncollapsable = \"collapsable\"\nstyl = \"styl\"\nIZ = \"IZ\"\nshft = \"shft\"\nmultline = \"multline\"\n"
  },
  {
    "path": "biome.json",
    "content": "{\n\t\"$schema\": \"https://biomejs.dev/schemas/2.4.11/schema.json\",\n\t\"formatter\": {\n\t\t\"enabled\": true,\n\t\t\"indentStyle\": \"tab\"\n\t},\n\t\"assist\": {\n\t\t\"actions\": {\n\t\t\t\"source\": {\n\t\t\t\t\"organizeImports\": \"on\"\n\t\t\t}\n\t\t}\n\t},\n\t\"linter\": {\n\t\t\"enabled\": true,\n\t\t\"rules\": {\n\t\t\t\"recommended\": false,\n\t\t\t\"complexity\": {\n\t\t\t\t\"noForEach\": \"off\",\n\t\t\t\t\"noStaticOnlyClass\": \"error\",\n\t\t\t\t\"noUselessSwitchCase\": \"error\",\n\t\t\t\t\"useFlatMap\": \"error\"\n\t\t\t},\n\t\t\t\"style\": {\n\t\t\t\t\"noNegationElse\": \"off\",\n\t\t\t\t\"useForOf\": \"error\",\n\t\t\t\t\"useNodejsImportProtocol\": \"error\",\n\t\t\t\t\"useNumberNamespace\": \"error\"\n\t\t\t},\n\t\t\t\"suspicious\": {\n\t\t\t\t\"noDoubleEquals\": \"error\",\n\t\t\t\t\"noThenProperty\": \"error\",\n\t\t\t\t\"useIsArray\": \"error\"\n\t\t\t}\n\t\t}\n\t},\n\t\"javascript\": {\n\t\t\"globals\": [\"Global1\"]\n\t},\n\t\"files\": {\n\t\t\"includes\": [\n\t\t\t\"**/src/**/*\",\n\t\t\t\"**/utils/**/*.js\",\n\t\t\t\"!**/www/build/**/*\",\n\t\t\t\"**/www/res/**/*.css\",\n\t\t\t\"**/src/plugins/terminal/**\",\n\t\t\t\"!**/ace-builds\",\n\t\t\t\"!**/src/plugins/**/*\",\n\t\t\t\"!**/plugins/**/*\",\n\t\t\t\"!**/hooks/**/*\",\n\t\t\t\"!**/fastlane/**/*\",\n\t\t\t\"!**/res/**/*\",\n\t\t\t\"!**/platforms/**/*\"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "build-extras.gradle",
    "content": "android {\n    packagingOptions {\n        pickFirst 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'\n    }\n}\n\nconfigurations {\n  all {\n      exclude module: 'commons-logging'\n      exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'\n      exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on'\n      exclude group: 'org.bouncycastle', module: 'bcpkix-jdk18on'\n      exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on'\n  }\n}"
  },
  {
    "path": "config.xml",
    "content": "<?xml version='1.0' encoding='utf-8' ?>\r\n<widget id=\"com.foxdebug.acode\" android-versionCode=\"967\" version=\"1.11.8\"\r\n    xmlns=\"http://www.w3.org/ns/widgets\"\r\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    xmlns:cdv=\"http://cordova.apache.org/ns/1.0\">\r\n    <name>Acode</name>\r\n    <description>\r\n        Light weight code editor and web IDE for android.\r\n    </description>\r\n    <author email=\"ajit@foxdebug.com\" href=\"https://foxdebug.com\">\r\n       Foxdebug\r\n    </author>\r\n    <content src=\"index.html\" />\r\n    <access launch-external=\"yes\" origin=\"*\" />\r\n    <allow-navigation href=\"https://*/*\" />\r\n    <allow-navigation href=\"http://*/*\" />\r\n    <allow-intent href=\"http://*/*\" />\r\n    <allow-intent href=\"https://*/*\" />\r\n    <allow-intent href=\"tel:*\" />\r\n    <allow-intent href=\"sms:*\" />\r\n    <allow-intent href=\"mailto:*\" />\r\n    <allow-intent href=\"geo:*\" />\r\n\r\n    <platform name=\"android\">\r\n        <allow-intent href=\"market:*\" />\r\n        <preference name=\"fullscreen\" value=\"false\"/>\r\n        <preference name=\"SplashScreen\" value=\"none\"/>\r\n        <preference name=\"ShowTitle\" value=\"true\"/>\r\n        <preference name=\"DisallowOverscroll\" value=\"true\" />\r\n        <preference name=\"BackgroundColor\" value=\"0xFF313131\" />\r\n        <preference name=\"AndroidWindowSplashScreenBackground\" value=\"@color/ic_splash_background\" />\r\n        <preference name=\"AndroidWindowSplashScreenAnimatedIcon\" value=\"res/android/drawable/ic_launcher_foreground.xml\" />\r\n        <preference name=\"AndroidPostSplashScreenTheme\" value=\"@style/Theme.App.Activity\" />\r\n        <preference name=\"AndroidPersistentFileLocation\" value=\"Compatibility\" />\r\n        <preference name=\"AndroidLaunchMode\" value=\"singleTask\" />\r\n        <preference name=\"prerendered-icon\" value=\"false\" />\r\n        <preference name=\"androidxEnabled\" value=\"true\" />\r\n        <preference name=\"GradlePluginKotlinEnabled\" value=\"true\" />\r\n        <preference name=\"android-targetSdkVersion\" value=\"36\" />\r\n\r\n        <!-- Cordova does not merge this unless both of these are present -->\r\n        <edit-config file=\"AndroidManifest.xml\" mode=\"merge\" target=\"/manifest/application\">\r\n            <application \r\n                android:usesCleartextTraffic=\"true\"\r\n                android:networkSecurityConfig=\"@xml/network_security_config\"\r\n                android:hardwareAccelerated=\"true\"\r\n                android:largeHeap=\"true\"\r\n                android:requestLegacyExternalStorage=\"true\"\r\n                android:enableOnBackInvokedCallback=\"false\" />\r\n        </edit-config>\r\n\r\n        <!-- Cordova does not merge this unless both of these are present -->\r\n        <edit-config file=\"app/src/main/AndroidManifest.xml\" mode=\"merge\" target=\"/manifest/application\">\r\n            <application android:networkSecurityConfig=\"@xml/network_security_config\" />\r\n            <application android:usesCleartextTraffic=\"true\" />\r\n            <application android:hardwareAccelerated=\"true\" />\r\n            <application android:largeHeap=\"true\" />\r\n            <application android:requestLegacyExternalStorage=\"true\"/>\r\n            <application android:enableOnBackInvokedCallback=\"false\" />\r\n        </edit-config>\r\n\r\n        <edit-config file=\"app/src/main/AndroidManifest.xml\" mode=\"merge\" target=\"/manifest/application/activity[@android:name='MainActivity']\">\r\n            <activity android:resizeableActivity=\"true\" />\r\n        </edit-config>\r\n\r\n        <config-file parent=\"./application/activity\" target=\"AndroidManifest.xml\">\r\n            <intent-filter>\r\n                <action android:name=\"android.intent.action.VIEW\" />\r\n                <action android:name=\"android.intent.action.EDIT\" />\r\n                <category android:name=\"android.intent.category.DEFAULT\" />\r\n                <category android:name=\"android.intent.category.LAUNCHER\" />\r\n                <data android:mimeType=\"*/*\"/>\r\n            </intent-filter>\r\n            <!-- Allow app to open using url from browser -->\r\n            <intent-filter android:autoVerify=\"true\">\r\n                <action android:name=\"android.intent.action.VIEW\" />\r\n                <category android:name=\"android.intent.category.DEFAULT\" />\r\n                <category android:name=\"android.intent.category.BROWSABLE\" />\r\n                <data android:scheme=\"acode\" />\r\n            </intent-filter>\r\n        </config-file>\r\n\r\n        <config-file target=\"AndroidManifest.xml\" parent=\"/manifest\">\r\n            <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\r\n            <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\r\n            <uses-permission android:name=\"android.permission.WRITE_MEDIA_STORAGE\" />\r\n            <uses-permission android:name=\"android.permission.VIBRATE\" />\r\n        </config-file>\r\n\r\n        <hook type=\"before_prepare\" src=\"hooks/restore-cordova-resources.js\" />\r\n        <hook type=\"before_prepare\" src=\"hooks/modify-java-files.js\" />\r\n        <hook type=\"after_prepare\" src=\"hooks/post-process.js\" />\r\n    </platform>\r\n    <preference name=\"AndroidBlacklistSecureSocketProtocols\" value=\"SSLv3,TLSv1\" />\r\n</widget>\r\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/full_description.txt",
    "content": "Welcome to Acode!\n\nA powerful, lightweight code editor, and web IDE for Android. Now enhanced with cutting-edge features and updates to transform your coding experience.\n\nWhat's New?\n\nStep into the future of coding with our innovative Plugin System. This brand new feature supports a wide range of plugins, boosting the functionality of Acode to meet all your development needs. With over 30 plugins already available in the Plugin Store, the possibilities are endless.\n\nLatest Updates Include:\n\n- Enhanced Ace Editor: Now updated to version 1.22.0 for more efficient editing.\n- Search in All Files: Our beta feature lets you search and replace text in all files within your opened projects.\n- Customizable Quick Tools: Personalize your quick tools to enhance your workflow.\n- Fast File Listing in Find Files (Ctrl + P): Acode now loads and caches files at startup, leading to faster file listing.\n- Ctrl Key Functionality: Take advantage of keyboard shortcuts for actions such as save (Ctrl+S) and open command palette (Ctrl+Shift+P).\n\nWhy Choose Acode?\n\nAcode lets you build and run websites directly within your browser, debug with ease using the integrated console, and edit a wide range of source files - from Python and CSS to Java, JavaScript, Dart, and more.\n\nKey Features:\n\n- Universal File Editor: Edit any file directly from your device.\n- GitHub Integration: Seamlessly sync your projects with GitHub.\n- FTP/SFTP Support: Manage your files efficiently with FTP/SFTP.\n- Extensive Syntax Highlighting: Supports over 100 programming languages.\n- Personalized Themes: Choose from dozens of unique themes to match your style.\n- User-Friendly Interface: Navigate with ease through our intuitive design.\n- In-App Preview: Instantly view your HTML/MarkDown files within the app.\n- Interactive JavaScript Console: Debug JavaScript code right from the console.\n- In-App File Browser: Access your files directly within Acode.\n- Open Source: Benefit from our transparent and community-driven project.\n- High Performance: Supports files with over 50,000 lines, ensuring smooth workflow.\n- Multi-File Support: Work on multiple files simultaneously for productive multitasking.\n- Customizable Interface: Adapt Acode to your personal coding style.\n- Keyboard Shortcuts: Speed up your coding with handy shortcuts.\n- File Recovery: Never lose your work with our reliable file recovery feature.\n- File Management: Keep your projects organized with effective file management.\n\nStart your streamlined coding journey with Acode today. Join our ever-growing community of developers and experience the difference for yourself!"
  },
  {
    "path": "fastlane/metadata/android/en-US/short_description.txt",
    "content": "A lightweight but powerful text/code editor."
  },
  {
    "path": "fastlane/metadata/android/en-US/title.txt",
    "content": "Acode editor - Android code editor"
  },
  {
    "path": "hooks/README.md",
    "content": "<!--\r\n#\r\n# Licensed to the Apache Software Foundation (ASF) under one\r\n# or more contributor license agreements.  See the NOTICE file\r\n# distributed with this work for additional information\r\n# regarding copyright ownership.  The ASF licenses this file\r\n# to you under the Apache License, Version 2.0 (the\r\n# \"License\"); you may not use this file except in compliance\r\n# with the License.  You may obtain a copy of the License at\r\n#\r\n# http://www.apache.org/licenses/LICENSE-2.0\r\n#\r\n# Unless required by applicable law or agreed to in writing,\r\n# software distributed under the License is distributed on an\r\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\n#  KIND, either express or implied.  See the License for the\r\n# specific language governing permissions and limitations\r\n# under the License.\r\n#\r\n-->\r\n# Cordova Hooks\r\n\r\nCordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system  to customize cordova commands. See Hooks Guide for more details:  http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide.\r\n"
  },
  {
    "path": "hooks/modify-java-files.js",
    "content": "const path = require('path');\nconst fs = require('fs');\nconst prettier = require('prettier');\n\nmain();\n\nasync function main() {\n  const patchVersion = '2';\n  const flagFile = path.resolve(__dirname, '../platforms/android/.flag_done');\n  if (fs.existsSync(flagFile)) {\n    const appliedVersion = fs.readFileSync(flagFile, 'utf8').trim();\n    if (appliedVersion === patchVersion) {\n      return;\n    }\n  }\n\n  const base = path.resolve(__dirname, `../platforms/android/CordovaLib/src/org/apache/cordova`);\n  const files = {\n    'SystemWebView.java': `${base}/engine/SystemWebView.java`,\n    'SystemWebViewEngine.java': `${base}/engine/SystemWebViewEngine.java`,\n    'CordovaWebViewEngine.java': `${base}/CordovaWebViewEngine.java`,\n    'CordovaWebView.java': `${base}/CordovaWebView.java`,\n    'CordovaWebViewImpl.java': `${base}/CordovaWebViewImpl.java`,\n  };\n\n  const interfaceMethod = {\n    name: 'setInputType',\n    modifier: 'public',\n    returnType: 'void',\n    params: [\n      {\n        type: 'int',\n        name: 'type',\n      }\n    ],\n  };\n\n  const nativeContextMenuInterfaceMethod = {\n    name: 'setNativeContextMenuDisabled',\n    modifier: 'public',\n    returnType: 'void',\n    params: [\n      {\n        type: 'boolean',\n        name: 'disabled',\n      }\n    ],\n  };\n\n  const setInputTypeMethod = {\n    name: 'setInputType',\n    modifier: 'public',\n    returnType: 'void',\n    params: [\n      {\n        type: 'int',\n        name: 'type',\n      }\n    ],\n    body: ['webView.setInputType(type);'],\n  };\n\n  const setNativeContextMenuDisabledMethod = {\n    name: 'setNativeContextMenuDisabled',\n    modifier: 'public',\n    returnType: 'void',\n    params: [\n      {\n        type: 'boolean',\n        name: 'disabled',\n      }\n    ],\n    body: ['webView.setNativeContextMenuDisabled(disabled);'],\n  };\n\n  const contentToAdd = {\n    'SystemWebView.java': {\n      'import': [\n        'android.graphics.Rect',\n        'android.os.Build',\n        'android.text.InputType',\n        'android.view.ActionMode',\n        'android.view.inputmethod.InputConnection',\n        'android.view.inputmethod.EditorInfo',\n        'android.view.Menu',\n        'android.view.MenuItem',\n        'android.view.View',\n      ],\n      'fields': [\n        {\n          type: 'int',\n          name: 'type',\n          modifier: 'private',\n          value: '-1',\n        },\n        {\n          type: 'int',\n          name: 'NO_SUGGESTIONS',\n          modifier: 'private',\n          value: '0',\n        },\n        {\n          type: 'int',\n          name: 'NO_SUGGESTIONS_AGGRESSIVE',\n          modifier: 'private',\n          value: '1',\n        },\n        {\n          type: 'boolean',\n          name: 'nativeContextMenuDisabled',\n          modifier: 'private',\n          value: 'false',\n        },\n      ],\n      methods: [\n        {\n          ...setInputTypeMethod,\n          body: [`this.type = type;`]\n        },\n        {\n          name: 'setNativeContextMenuDisabled',\n          modifier: 'public',\n          returnType: 'void',\n          params: [\n            {\n              type: 'boolean',\n              name: 'disabled',\n            }\n          ],\n          body: [`this.nativeContextMenuDisabled = disabled;`],\n        },\n        {\n          name: 'onCreateInputConnection',\n          modifier: 'public',\n          returnType: 'InputConnection',\n          params: [\n            {\n              type: 'EditorInfo',\n              name: 'outAttrs',\n            }\n          ],\n          body: [\n            `InputConnection ic = super.onCreateInputConnection(outAttrs);\n            if (type == NO_SUGGESTIONS) {\n              outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;\n            } else if (type == NO_SUGGESTIONS_AGGRESSIVE) {\n              outAttrs.inputType =\n                InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS |\n                InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;\n            } else {\n              outAttrs.inputType |= InputType.TYPE_NULL;\n            }\n\n            return ic;`,\n          ],\n          notation: '@Override',\n        },\n        {\n          name: 'startActionMode',\n          modifier: 'public',\n          returnType: 'ActionMode',\n          params: [\n            {\n              type: 'ActionMode.Callback',\n              name: 'callback',\n            }\n          ],\n          body: [\n            `return suppressActionMode(super.startActionMode(wrapActionModeCallback(callback)));`,\n          ],\n          notation: '@Override',\n        },\n        {\n          name: 'startActionMode',\n          modifier: 'public',\n          returnType: 'ActionMode',\n          params: [\n            {\n              type: 'ActionMode.Callback',\n              name: 'callback',\n            },\n            {\n              type: 'int',\n              name: 'type',\n            }\n          ],\n          body: [\n            `return suppressActionMode(super.startActionMode(wrapActionModeCallback(callback), type));`,\n          ],\n          notation: '@Override',\n        },\n        {\n          name: 'startActionModeForChild',\n          modifier: 'public',\n          returnType: 'ActionMode',\n          params: [\n            {\n              type: 'View',\n              name: 'originalView',\n            },\n            {\n              type: 'ActionMode.Callback',\n              name: 'callback',\n            }\n          ],\n          body: [\n            `return suppressActionMode(super.startActionModeForChild(originalView, wrapActionModeCallback(callback)));`,\n          ],\n          notation: '@Override',\n        },\n        {\n          name: 'startActionModeForChild',\n          modifier: 'public',\n          returnType: 'ActionMode',\n          params: [\n            {\n              type: 'View',\n              name: 'originalView',\n            },\n            {\n              type: 'ActionMode.Callback',\n              name: 'callback',\n            },\n            {\n              type: 'int',\n              name: 'type',\n            }\n          ],\n          body: [\n            `return suppressActionMode(super.startActionModeForChild(originalView, wrapActionModeCallback(callback), type));`,\n          ],\n          notation: '@Override',\n        },\n        {\n          name: 'wrapActionModeCallback',\n          modifier: 'private',\n          returnType: 'ActionMode.Callback',\n          params: [\n            {\n              type: 'ActionMode.Callback',\n              name: 'callback',\n            }\n          ],\n          body: [\n            `if (!nativeContextMenuDisabled || callback == null) {\n              return callback;\n            }\n            return new ActionMode.Callback2() {\n              @Override\n              public boolean onCreateActionMode(ActionMode mode, Menu menu) {\n                boolean created = callback.onCreateActionMode(mode, menu);\n                if (created) {\n                  suppressActionModeUi(mode, menu);\n                }\n                return created;\n              }\n\n              @Override\n              public boolean onPrepareActionMode(ActionMode mode, Menu menu) {\n                boolean prepared = callback.onPrepareActionMode(mode, menu);\n                suppressActionModeUi(mode, menu);\n                return prepared;\n              }\n\n              @Override\n              public boolean onActionItemClicked(ActionMode mode, MenuItem item) {\n                return callback.onActionItemClicked(mode, item);\n              }\n\n              @Override\n              public void onDestroyActionMode(ActionMode mode) {\n                callback.onDestroyActionMode(mode);\n              }\n\n              @Override\n              public void onGetContentRect(ActionMode mode, View view, Rect outRect) {\n                if (callback instanceof ActionMode.Callback2) {\n                  ((ActionMode.Callback2) callback).onGetContentRect(mode, view, outRect);\n                  return;\n                }\n                super.onGetContentRect(mode, view, outRect);\n              }\n            };`,\n          ],\n        },\n        {\n          name: 'suppressActionMode',\n          modifier: 'private',\n          returnType: 'ActionMode',\n          params: [\n            {\n              type: 'ActionMode',\n              name: 'mode',\n            }\n          ],\n          body: [\n            `if (mode == null || !nativeContextMenuDisabled) {\n              return mode;\n            }\n            suppressActionModeUi(mode, mode.getMenu());\n            return mode;`,\n          ],\n        },\n        {\n          name: 'suppressActionModeUi',\n          modifier: 'private',\n          returnType: 'void',\n          params: [\n            {\n              type: 'ActionMode',\n              name: 'mode',\n            },\n            {\n              type: 'Menu',\n              name: 'menu',\n            }\n          ],\n          body: [\n            `if (mode == null || !nativeContextMenuDisabled || menu == null) {\n              return;\n            }\n            menu.clear();\n            mode.setTitle(null);\n            mode.setSubtitle(null);\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n              post(() -> {\n                if (!nativeContextMenuDisabled) {\n                  return;\n                }\n                try {\n                  mode.hide(0);\n                } catch (Throwable ignored) {\n                }\n              });\n            }`,\n          ],\n        },\n      ]\n    },\n    'SystemWebViewEngine.java': {\n      methods: [\n        setInputTypeMethod,\n        setNativeContextMenuDisabledMethod,\n      ]\n    },\n    'CordovaWebViewEngine.java': {\n      methods: [\n        interfaceMethod,\n        nativeContextMenuInterfaceMethod,\n      ]\n    },\n    'CordovaWebView.java': {\n      methods: [\n        interfaceMethod,\n        nativeContextMenuInterfaceMethod,\n      ]\n    },\n    'CordovaWebViewImpl.java': {\n      methods: [\n        {\n          ...setInputTypeMethod,\n          body: [`engine.setInputType(type);`]\n        },\n        {\n          ...setNativeContextMenuDisabledMethod,\n          body: [`engine.setNativeContextMenuDisabled(disabled);`]\n        }\n      ]\n    }\n  };\n\n  const fileContent = {};\n\n  for (let file in files) {\n    fileContent[file] = fs.readFileSync(files[file], 'utf8');\n  }\n\n  for (let file in contentToAdd) {\n    const content = fileContent[file];\n    const contentToAddTo = contentToAdd[file];\n    const text = removeComments(content);\n    let newContent = await format(text);\n    if (contentToAddTo.import) {\n      const imports = contentToAddTo.import.map(importStr => {\n        return `import ${importStr};`;\n      }).join('\\n');\n\n      newContent = newContent.replace(\n        /^(\\s*)(import.*;)/m,\n        `$1${imports}\\n$2`\n      );\n    }\n    if (contentToAddTo.fields) {\n      const fields = contentToAddTo.fields.map(field => {\n        return getFieldString(field);\n      }).join('\\n');\n      newContent = newContent.replace(\n        /^(\\s*)(\\w+\\s+\\w+\\s*;)/m,\n        `$1${fields}\\n$2`\n      );\n    }\n    if (contentToAddTo.methods) {\n      const methods = contentToAddTo.methods.map(method => {\n        return getMethodString(method);\n      }).join('\\n');\n\n      if (isInterface(file, content)) {\n        const regex = getInterfaceDeclarationRegex(file);\n        newContent = newContent.replace(\n          regex,\n          `$1${methods}\\n$2`\n        );\n      } else {\n        let regex = getConstructorRegex(file);\n        if (regex.test(newContent)) {\n          newContent = newContent.replace(\n            regex,\n            `$1${methods}\\n$2`\n          );\n        } else {\n          regex = getClassDeclarationRegex(file);\n          newContent = newContent.replace(\n            regex,\n            `$1${methods}\\n$2`\n          );\n        }\n      }\n    }\n\n    newContent = await format(newContent);\n    fs.writeFile(files[file], newContent, err => {\n      if (err) {\n        console.log(err);\n        process.exit(1);\n      }\n\n      console.log(`${files[file]} updated`);\n    });\n  }\n\n  fs.writeFile(flagFile, patchVersion, err => {\n    if (err) {\n      console.log(err);\n      process.exit(1);\n    }\n\n    console.log(`${flagFile} updated`);\n  });\n\n  async function format(content) {\n    return prettier.format(content, {\n      plugins: ['prettier-plugin-java'],\n      parser: 'java',\n      tabWidth: 2,\n      printWidth: Infinity\n    });\n  }\n\n  function getMethodString(method) {\n    const params = method.params.map(param => {\n      return `${param.type} ${param.name}`;\n    }).join(', ');\n\n    let str = `${method.modifier} ${method.returnType} ${method.name}(${params})`;\n    if (method.notation) {\n      str = `\\n${method.notation}\\n${str}`;\n    }\n    if (method.body) {\n      return str + `{${method.body.join('')}}`;\n    }\n    return str + ';';\n  }\n\n  function getFieldString(field) {\n    return `${field.modifier} ${field.type} ${field.name}${field.value ? ` = ${field.value}` : ''};`;\n  }\n\n  function isInterface(filename, content) {\n    return content.indexOf(`interface ${filename.split('.')[0]}`) > -1;\n  }\n\n  function getConstructorRegex(filename) {\n    return new RegExp(`([^]*${filename.split('.')[0]}\\\\s*\\\\(.*\\\\)\\\\s*{[^}]*})([^]*)`, 'm');\n  }\n\n  function getInterfaceDeclarationRegex(filename) {\n    return new RegExp(`([^]*interface\\\\s+${filename.split('.')[0]}[\\\\s\\\\w]*{)([^]*})`, 'm');\n  }\n\n  function getClassDeclarationRegex(filename) {\n    return new RegExp(`([^]*class\\\\s+${filename.split('.')[0]}[\\\\s\\\\w]*{)([^]*})`, 'm');\n  }\n\n  function removeComments(content) {\n    return content.replace(/\\/\\*[\\s\\S]*?\\*\\/|([^\\\\:]|^)\\/\\/.*$/gm, '');\n  }\n}\n"
  },
  {
    "path": "hooks/move-files.js",
    "content": "const path = require('path');\nconst fs = require('fs');\n\nconst gradleFilePath = path.resolve(__dirname, '../build-extras.gradle');\nconst newGradleFilePath = path.resolve(\n  __dirname,\n  '../platforms/android/app/build-extras.gradle'\n);\nconst buildFilePath = path.resolve(__dirname, '../build.json');\nconst newBuildFilePath = path.resolve(\n  __dirname,\n  '../platforms/android/build.json'\n);\n\nconst repeatChar = (char, times) => {\n  let res = '';\n  while (--times >= 0) res += char;\n  return res;\n};\n\nlet msg;\nif (!fs.existsSync(newBuildFilePath) && fs.existsSync(buildFilePath)) {\n  msg = '== Moved build.json ==';\n  console.log(repeatChar('=', msg.length));\n  console.log(msg);\n  console.log(repeatChar('=', msg.length));\n  fs.copyFileSync(buildFilePath, newBuildFilePath);\n}\n\nif (!fs.existsSync(newGradleFilePath) && fs.existsSync(gradleFilePath)) {\n  msg = '== Moved build-extras.gradle ==';\n  console.log(repeatChar('=', msg.length));\n  console.log(msg);\n  console.log(repeatChar('=', msg.length));\n  fs.copyFileSync(gradleFilePath, newGradleFilePath);\n}\n"
  },
  {
    "path": "hooks/post-process.js",
    "content": "/* eslint-disable no-console */\nconst path = require('path');\nconst fs = require('fs');\nconst { execSync } = require('child_process');\n\nconst buildFilePath = path.resolve(__dirname, '../build.json');\nconst copyToPath = path.resolve(__dirname, '../platforms/android/build.json');\nconst gradleFilePath = path.resolve(__dirname, '../build-extras.gradle');\nconst androidGradleFilePath = path.resolve(\n  __dirname,\n  '../platforms/android/app/build-extras.gradle'\n);\nconst resPath = path.resolve(__dirname, '../platforms/android/app/src/main/res/');\nconst localResPath = path.resolve(__dirname, '../res/android/');\n\nif (\n  !fs.existsSync(copyToPath)\n  && fs.existsSync(buildFilePath)\n) fs.copyFileSync(buildFilePath, copyToPath);\n\nif (fs.existsSync(androidGradleFilePath)) fs.unlinkSync(androidGradleFilePath);\nfs.copyFileSync(gradleFilePath, androidGradleFilePath);\n\n// Cordova Android 15 generates `cdv_*` resources and version-qualified value\n// directories that are required later in the build. Keep the generated tree and\n// only overlay this project's custom resources on top of it.\ncopyDirRecursively(localResPath, resPath);\nenableLegacyJni();\nenableStaticContext();\npatchTargetSdkVersion();\nenableKeyboardWorkaround();\n\n\nfunction getTmpDir() {\n  const tmpdirEnv = process.env.TMPDIR;\n\n  if (tmpdirEnv) {\n    try {\n      fs.accessSync(tmpdirEnv, fs.constants.R_OK | fs.constants.W_OK);\n      return tmpdirEnv;\n    } catch {\n      // TMPDIR exists but not accessible\n    }\n  }\n\n  try {\n    fs.accessSync(\"/tmp\", fs.constants.R_OK | fs.constants.W_OK);\n    return \"/tmp\";\n  } catch {\n    console.log(\"Error: No usable temporary directory found (TMPDIR or /tmp not accessible).\");\n    return null;\n    // process.exit(1);\n  }\n}\n\nfunction patchTargetSdkVersion() {\n  const prefix = execSync('npm prefix').toString().trim();\n  const gradleFile = path.join(prefix, 'platforms/android/app/build.gradle');\n\n  if (!fs.existsSync(gradleFile)) {\n    console.warn('[Cordova Hook] ⚠️ build.gradle not found');\n    return;\n  }\n\n  let content = fs.readFileSync(gradleFile, 'utf-8');\n\n  const sdkRegex = /targetSdkVersion\\s+(cordovaConfig\\.SDK_VERSION|\\d+)/;\n\n  if (sdkRegex.test(content)) {\n    let api = \"36\";\n    const tmp = getTmpDir();\n    if (tmp == null) {\n      console.warn(\"---------------------------------------------------------------------------------\\n\\n\\n\\n\");\n      console.warn(`⚠️ fdroid.bool not found`);\n      console.warn(\"⚠️ Fdroid flavour will be built\");\n      api = \"28\";\n      console.warn(\"\\n\\n\\n\\n---------------------------------------------------------------------------------\");\n    } else {\n      const froidFlag = path.join(getTmpDir(), 'fdroid.bool');\n\n      if (fs.existsSync(froidFlag)) {\n        const fdroid = fs.readFileSync(froidFlag, 'utf-8').trim();\n        if (fdroid == \"true\") {\n          api = \"28\";\n        }\n      } else {\n        console.warn(\"---------------------------------------------------------------------------------\\n\\n\\n\\n\");\n        console.warn(`⚠️ fdroid.bool not found`);\n        console.warn(\"⚠️ Fdroid flavour will be built\");\n        api = \"28\";\n        console.warn(\"\\n\\n\\n\\n---------------------------------------------------------------------------------\");\n        //process.exit(1);\n      }\n    }\n\n\n    content = content.replace(sdkRegex, 'targetSdkVersion ' + api);\n    fs.writeFileSync(gradleFile, content, 'utf-8');\n    console.log('[Cordova Hook] ✅ Patched targetSdkVersion to ' + api);\n  } else {\n    console.warn('[Cordova Hook] ⚠️ targetSdkVersion not found');\n  }\n}\n\n\nfunction enableLegacyJni() {\n  const prefix = execSync('npm prefix').toString().trim();\n  const gradleFile = path.join(prefix, 'platforms/android/app/build.gradle');\n\n  if (!fs.existsSync(gradleFile)) return;\n\n  let content = fs.readFileSync(gradleFile, 'utf-8');\n  // Check for correct block to avoid duplicate insertion\n  if (content.includes('useLegacyPackaging = true')) return;\n\n  // Inject under android block with correct Groovy syntax\n  content = content.replace(/android\\s*{/, match => {\n    return (\n      match +\n      `\n    packagingOptions {\n        jniLibs {\n            useLegacyPackaging = true\n        }\n    }`\n    );\n  });\n\n  fs.writeFileSync(gradleFile, content, 'utf-8');\n  console.log('[Cordova Hook] ✅ Enabled legacy JNI packaging');\n}\n\nfunction enableStaticContext() {\n  try {\n    const prefix = execSync('npm prefix').toString().trim();\n    const mainActivityPath = path.join(\n      prefix,\n      'platforms/android/app/src/main/java/com/foxdebug/acode/MainActivity.java'\n    );\n\n    if (!fs.existsSync(mainActivityPath)) {\n      return;\n    }\n\n    let content = fs.readFileSync(mainActivityPath, 'utf-8');\n\n    // Skip if fully patched\n    if (\n      content.includes('WeakReference<Context>') &&\n      content.includes('public static Context getContext()') &&\n      content.includes('weakContext = new WeakReference<>(this);')\n    ) {\n      return;\n    }\n\n    // Add missing imports\n    if (!content.includes('import java.lang.ref.WeakReference;')) {\n      content = content.replace(\n        /import org\\.apache\\.cordova\\.\\*;/,\n        match =>\n          match +\n          '\\nimport android.content.Context;\\nimport java.lang.ref.WeakReference;'\n      );\n    }\n\n    // Inject static field and method into class body\n    content = content.replace(\n      /public class MainActivity extends CordovaActivity\\s*\\{/,\n      match =>\n        match +\n        `\\n\\n    private static WeakReference<Context> weakContext;\\n\\n` +\n        `    public static Context getContext() {\\n` +\n        `        return weakContext != null ? weakContext.get() : null;\\n` +\n        `    }\\n`\n    );\n\n    // Insert weakContext assignment inside onCreate\n    content = content.replace(\n      /super\\.onCreate\\(savedInstanceState\\);/,\n      `super.onCreate(savedInstanceState);\\n        weakContext = new WeakReference<>(this);`\n    );\n\n    fs.writeFileSync(mainActivityPath, content, 'utf-8');\n  } catch (err) {\n    console.error('[Cordova Hook] ❌ Failed to patch MainActivity:', err.message);\n  }\n}\n\nfunction enableKeyboardWorkaround() {\n  try{\n    const prefix = execSync('npm prefix').toString().trim();\n    const mainActivityPath = path.join(\n      prefix,\n      'platforms/android/app/src/main/java/com/foxdebug/acode/MainActivity.java'\n    );\n\n    if (!fs.existsSync(mainActivityPath)) {\n      return;\n    }\n\n    let content = fs.readFileSync(mainActivityPath, 'utf-8');\n\n    // Skip if already patched\n    if (content.includes('SoftInputAssist')) {\n      return;\n    }\n\n    // Add import\n    if (!content.includes('import com.foxdebug.system.SoftInputAssist;')) {\n      content = content.replace(\n        /import java.lang.ref.WeakReference;|import org\\.apache\\.cordova\\.\\*;/,\n        match =>\n          match + '\\nimport com.foxdebug.system.SoftInputAssist;'\n      );\n    }\n\n    // Declare field\n    if (!content.includes('private SoftInputAssist softInputAssist;')) {\n      content = content.replace(\n        /public class MainActivity extends CordovaActivity\\s*\\{/,\n        match =>\n          match +\n          `\\n\\n    private SoftInputAssist softInputAssist;\\n`\n      );\n    }\n\n    // Initialize in onCreate\n    content = content.replace(\n      /loadUrl\\(launchUrl\\);/,\n      `loadUrl(launchUrl);\\n\\n        softInputAssist = new SoftInputAssist(this);`\n    );\n\n    fs.writeFileSync(mainActivityPath, content, 'utf-8');\n    console.log('[Cordova Hook] ✅ Enabled keyboard workaround');\n  } catch (err) {\n    console.error('[Cordova Hook] ❌ Failed to enable keyboard workaround:', err.message);\n  }\n}\n\n\n/**\n * Copy directory recursively\n * @param {string} src Source directory\n * @param {string} dest Destination directory\n * @param {string[]} skip Files to not copy\n */\nfunction copyDirRecursively(src, dest, skip = [], currPath = '') {\n  const exists = fs.existsSync(src);\n  const stats = exists && fs.statSync(src);\n  const isDirectory = exists && stats.isDirectory();\n\n  if (!exists) {\n    console.log(`File ${src} does not exist`);\n    return;\n  }\n\n  if (!fs.existsSync(dest) && isDirectory) {\n    fs.mkdirSync(dest);\n  }\n\n  if (exists && isDirectory) {\n    fs.mkdirSync(dest, { recursive: true });\n    fs.readdirSync(src).forEach((childItemName) => {\n      const relativePath = path.join(currPath, childItemName);\n      if (childItemName.startsWith('.')) return;\n      if (skip.includes(childItemName) || skip.includes(relativePath)) return;\n      copyDirRecursively(\n        path.join(src, childItemName),\n        path.join(dest, childItemName),\n        skip,\n        relativePath,\n      );\n    });\n  } else {\n    removeConflictingResourceFiles(src, dest);\n    fs.copyFileSync(src, dest);\n\n    // log\n    const message = `copied: ${path.basename(src)}`;\n    console.log('\\x1b[32m%s\\x1b[0m', message); // green\n  }\n}\n\nfunction removeConflictingResourceFiles(src, dest) {\n  const parentDir = path.dirname(dest);\n\n  if (!fs.existsSync(parentDir)) {\n    return;\n  }\n\n  const resourceDirName = path.basename(parentDir);\n  if (!resourceDirName.startsWith('mipmap') && !resourceDirName.startsWith('drawable')) {\n    return;\n  }\n\n  const srcExt = path.extname(src);\n  const resourceName = path.basename(src, srcExt);\n\n  for (const existingName of fs.readdirSync(parentDir)) {\n    const existingPath = path.join(parentDir, existingName);\n    if (existingPath === dest || !fs.statSync(existingPath).isFile()) {\n      continue;\n    }\n\n    const existingExt = path.extname(existingName);\n    const existingResourceName = path.basename(existingName, existingExt);\n\n    if (existingResourceName !== resourceName || existingExt === srcExt) {\n      continue;\n    }\n\n    fs.rmSync(existingPath);\n    console.log('\\x1b[31m%s\\x1b[0m', `deleted conflicting resource: ${existingName}`);\n  }\n}\n"
  },
  {
    "path": "hooks/restore-cordova-resources.js",
    "content": "const fs = require(\"fs\");\nconst path = require(\"path\");\n\nconst templateResPath = path.resolve(\n  __dirname,\n  \"../node_modules/cordova-android/templates/project/res\",\n);\nconst androidResPath = path.resolve(\n  __dirname,\n  \"../platforms/android/app/src/main/res\",\n);\n\nif (!fs.existsSync(templateResPath) || !fs.existsSync(androidResPath)) {\n  process.exit(0);\n}\n\nrestoreCordovaResourceFiles(templateResPath);\n\nfunction restoreCordovaResourceFiles(currentPath) {\n  for (const entry of fs.readdirSync(currentPath, { withFileTypes: true })) {\n    const absolutePath = path.join(currentPath, entry.name);\n\n    if (entry.isDirectory()) {\n      restoreCordovaResourceFiles(absolutePath);\n      continue;\n    }\n\n    if (!shouldRestore(absolutePath)) {\n      continue;\n    }\n\n    const relativePath = path.relative(templateResPath, absolutePath);\n    const destinationPath = path.join(androidResPath, relativePath);\n\n    if (fs.existsSync(destinationPath)) {\n      continue;\n    }\n\n    fs.mkdirSync(path.dirname(destinationPath), { recursive: true });\n    fs.copyFileSync(absolutePath, destinationPath);\n    console.log(`[Cordova Hook] Restored ${relativePath}`);\n  }\n}\n\nfunction shouldRestore(filePath) {\n  const fileName = path.basename(filePath);\n\n  return fileName.startsWith(\"cdv_\") || fileName === \"ic_cdv_splashscreen.xml\";\n}\n"
  },
  {
    "path": "jsconfig.json",
    "content": "{\n  \"exclude\": [\"**/node_modules\", \"**/platforms\", \"**/www\", \"www/js/ace/**/*\"],\n  \"compilerOptions\": {\n    \"baseUrl\": \"./src\",\n    \"paths\": {\n      \"*\": [\"*\"]\n    }\n  },\n  \"include\": [\"src/**/*\"],\n  \"typeAcquisition\": {\n    \"enable\": true\n  }\n}\n"
  },
  {
    "path": "license.txt",
    "content": "Copyright 2020 Foxdebug(Ajit Kumar)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software\nand associated documentation files (the \"Software\"), to deal in the Software without \nrestriction, including without limitation the rights to use, copy, modify, merge, publish, \ndistribute, sublicense, and/or sell copies of the Software, and to permit persons to whom \nthe Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or \nsubstantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, \nINCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR \nPURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE \nFOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR \nOTHERWISE,ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \nDEALINGS IN THE SOFTWARE."
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"com.foxdebug.acode\",\n  \"displayName\": \"Acode\",\n  \"version\": \"1.11.8\",\n  \"description\": \"Acode is a code editor for android\",\n  \"scripts\": {\n    \"lang\": \"node ./utils/lang.js\",\n    \"build\": \"sh utils/scripts/build.sh\",\n    \"start\": \"sh utils/scripts/start.sh\",\n    \"clean\": \"sh utils/scripts/clean.sh android android\",\n    \"plugin\": \"sh utils/scripts/plugin.sh\",\n    \"setup\": \"node ./utils/setup.js\",\n    \"lint\": \"biome lint --write\",\n    \"format\": \"biome format --write\",\n    \"check\": \"biome check --write\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"updateAce\": \"node ./utils/updateAce.js\"\n  },\n  \"keywords\": [\n    \"ecosystem:cordova\"\n  ],\n  \"author\": \"Foxdebug (Ajit Kumar)\",\n  \"license\": \"MIT\",\n  \"cordova\": {\n    \"plugins\": {\n      \"cordova-clipboard\": {},\n      \"cordova-plugin-device\": {},\n      \"cordova-plugin-file\": {},\n      \"cordova-plugin-server\": {},\n      \"cordova-plugin-ftp\": {},\n      \"cordova-plugin-sdcard\": {},\n      \"cordova-plugin-iap\": {},\n      \"cordova-plugin-advanced-http\": {\n        \"ANDROIDBLACKLISTSECURESOCKETPROTOCOLS\": \"SSLv3,TLSv1\"\n      },\n      \"cordova-plugin-websocket\": {},\n      \"cordova-plugin-buildinfo\": {},\n      \"cordova-plugin-browser\": {},\n      \"cordova-plugin-sftp\": {},\n      \"com.foxdebug.acode.rk.exec.proot\": {},\n      \"com.foxdebug.acode.rk.exec.terminal\": {},\n      \"com.foxdebug.acode.rk.customtabs\": {},\n      \"com.foxdebug.acode.rk.plugin.plugincontext\": {},\n      \"com.foxdebug.acode.rk.auth\": {},\n      \"cordova-plugin-system\": {}\n    },\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/deadlyjack/acode.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/deadlyjack/acode/issues\"\n  },\n  \"homepage\": \"https://github.com/deadlyjack/acode#readme\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.28.5\",\n    \"@babel/plugin-transform-runtime\": \"^7.28.5\",\n    \"@babel/preset-env\": \"^7.28.5\",\n    \"@babel/preset-typescript\": \"^7.28.5\",\n    \"@babel/runtime\": \"^7.28.4\",\n    \"@babel/runtime-corejs3\": \"^7.28.4\",\n    \"@biomejs/biome\": \"2.4.11\",\n    \"@rspack/cli\": \"^1.7.0\",\n    \"@rspack/core\": \"^1.7.0\",\n    \"@types/ace\": \"^0.0.52\",\n    \"@types/url-parse\": \"^1.4.11\",\n    \"autoprefixer\": \"^10.4.22\",\n    \"babel-loader\": \"^10.0.0\",\n    \"com.foxdebug.acode.rk.auth\": \"file:src/plugins/auth\",\n    \"com.foxdebug.acode.rk.customtabs\": \"file:src/plugins/custom-tabs\",\n    \"com.foxdebug.acode.rk.exec.proot\": \"file:src/plugins/proot\",\n    \"com.foxdebug.acode.rk.exec.terminal\": \"file:src/plugins/terminal\",\n    \"com.foxdebug.acode.rk.plugin.plugincontext\": \"file:src/plugins/pluginContext\",\n    \"cordova-android\": \"^15.0.0\",\n    \"cordova-clipboard\": \"^1.3.0\",\n    \"cordova-plugin-advanced-http\": \"^3.3.1\",\n    \"cordova-plugin-browser\": \"file:src/plugins/browser\",\n    \"cordova-plugin-buildinfo\": \"file:src/plugins/cordova-plugin-buildinfo\",\n    \"cordova-plugin-device\": \"^2.1.0\",\n    \"cordova-plugin-file\": \"^8.1.3\",\n    \"cordova-plugin-ftp\": \"file:src/plugins/ftp\",\n    \"cordova-plugin-iap\": \"file:src/plugins/iap\",\n    \"cordova-plugin-sdcard\": \"file:src/plugins/sdcard\",\n    \"cordova-plugin-server\": \"file:src/plugins/server\",\n    \"cordova-plugin-sftp\": \"file:src/plugins/sftp\",\n    \"cordova-plugin-system\": \"file:src/plugins/system\",\n    \"cordova-plugin-websocket\": \"file:src/plugins/websocket\",\n    \"css-loader\": \"^7.1.2\",\n    \"mini-css-extract-plugin\": \"^2.9.4\",\n    \"path-browserify\": \"^1.0.1\",\n    \"postcss-loader\": \"^8.2.0\",\n    \"prettier\": \"^3.7.4\",\n    \"prettier-plugin-java\": \"^2.7.7\",\n    \"raw-loader\": \"^4.0.2\",\n    \"sass\": \"^1.94.2\",\n    \"sass-loader\": \"^16.0.6\",\n    \"style-loader\": \"^4.0.0\",\n    \"terminal\": \"^0.1.4\",\n    \"ts-loader\": \"^9.5.4\",\n    \"typescript\": \"^5.9.3\",\n    \"vscode-languageserver-types\": \"^3.17.5\"\n  },\n  \"dependencies\": {\n    \"@codemirror/autocomplete\": \"^6.20.1\",\n    \"@codemirror/commands\": \"^6.10.3\",\n    \"@codemirror/lang-angular\": \"^0.1.4\",\n    \"@codemirror/lang-cpp\": \"^6.0.3\",\n    \"@codemirror/lang-css\": \"^6.3.1\",\n    \"@codemirror/lang-go\": \"^6.0.1\",\n    \"@codemirror/lang-html\": \"^6.4.11\",\n    \"@codemirror/lang-java\": \"^6.0.2\",\n    \"@codemirror/lang-javascript\": \"^6.2.5\",\n    \"@codemirror/lang-jinja\": \"^6.0.0\",\n    \"@codemirror/lang-json\": \"^6.0.2\",\n    \"@codemirror/lang-less\": \"^6.0.2\",\n    \"@codemirror/lang-liquid\": \"^6.3.2\",\n    \"@codemirror/lang-markdown\": \"^6.5.0\",\n    \"@codemirror/lang-php\": \"^6.0.2\",\n    \"@codemirror/lang-python\": \"^6.2.1\",\n    \"@codemirror/lang-rust\": \"^6.0.2\",\n    \"@codemirror/lang-sass\": \"^6.0.2\",\n    \"@codemirror/lang-sql\": \"^6.10.0\",\n    \"@codemirror/lang-vue\": \"^0.1.3\",\n    \"@codemirror/lang-wast\": \"^6.0.2\",\n    \"@codemirror/lang-xml\": \"^6.1.0\",\n    \"@codemirror/lang-yaml\": \"^6.1.2\",\n    \"@codemirror/language\": \"^6.12.2\",\n    \"@codemirror/language-data\": \"^6.5.2\",\n    \"@codemirror/legacy-modes\": \"^6.5.2\",\n    \"@codemirror/lint\": \"^6.9.5\",\n    \"@codemirror/lsp-client\": \"^6.2.2\",\n    \"@codemirror/search\": \"^6.6.0\",\n    \"@codemirror/state\": \"^6.6.0\",\n    \"@codemirror/theme-one-dark\": \"^6.1.3\",\n    \"@codemirror/view\": \"^6.40.0\",\n    \"@deadlyjack/ajax\": \"^1.2.6\",\n    \"@emmetio/codemirror6-plugin\": \"^0.4.0\",\n    \"@lezer/highlight\": \"^1.2.3\",\n    \"@ungap/custom-elements\": \"^1.3.0\",\n    \"@xterm/addon-attach\": \"^0.11.0\",\n    \"@xterm/addon-fit\": \"^0.10.0\",\n    \"@xterm/addon-image\": \"^0.8.0\",\n    \"@xterm/addon-search\": \"^0.15.0\",\n    \"@xterm/addon-unicode11\": \"^0.8.0\",\n    \"@xterm/addon-web-links\": \"^0.11.0\",\n    \"@xterm/addon-webgl\": \"^0.18.0\",\n    \"@xterm/xterm\": \"^5.5.0\",\n    \"acorn\": \"^8.15.0\",\n    \"autosize\": \"^6.0.1\",\n    \"codemirror\": \"^6.0.2\",\n    \"cordova\": \"13.0.0\",\n    \"core-js\": \"^3.47.0\",\n    \"dayjs\": \"^1.11.19\",\n    \"dompurify\": \"^3.3.2\",\n    \"escape-string-regexp\": \"^5.0.0\",\n    \"esprima\": \"^4.0.1\",\n    \"filesize\": \"^11.0.13\",\n    \"html-tag-js\": \"^2.4.16\",\n    \"jszip\": \"^3.10.1\",\n    \"katex\": \"^0.16.39\",\n    \"markdown-it\": \"^14.1.1\",\n    \"markdown-it-anchor\": \"^9.2.0\",\n    \"markdown-it-emoji\": \"^3.0.0\",\n    \"markdown-it-footnote\": \"^4.0.0\",\n    \"markdown-it-github-alerts\": \"^1.0.0\",\n    \"markdown-it-task-lists\": \"^2.1.1\",\n    \"markdown-it-texmath\": \"^1.0.0\",\n    \"mermaid\": \"^11.13.0\",\n    \"mime-types\": \"^3.0.1\",\n    \"mustache\": \"^4.2.0\",\n    \"picomatch\": \"^4.0.4\",\n    \"url-parse\": \"^1.5.10\",\n    \"vanilla-picker\": \"^2.12.3\",\n    \"yargs\": \"^18.0.0\"\n  },\n  \"overrides\": {\n    \"@codemirror/autocomplete\": \"^6.20.1\",\n    \"@codemirror/commands\": \"^6.10.3\",\n    \"@codemirror/language\": \"^6.12.2\",\n    \"@codemirror/lint\": \"^6.9.5\",\n    \"@codemirror/search\": \"^6.6.0\",\n    \"@codemirror/state\": \"^6.6.0\",\n    \"@codemirror/view\": \"^6.40.0\"\n  },\n  \"browserslist\": \"cover 100%,not android < 5\"\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\r\n    plugins: [\r\n        require('autoprefixer')({})\r\n    ]\r\n};"
  },
  {
    "path": "readme.md",
    "content": "# Acode - Code Editor for Android\n\n<p align=\"center\">\n  <img src='res/logo_1.png' width='250'>\n</p>\n\n[![](https://img.shields.io/endpoint?logo=telegram&label=Acode&style=flat&url=https%3A%2F%2Facode.app%2Fapi%2Ftelegram-members-count)](https://t.me/foxdebug_acode) [![](https://dcbadge.vercel.app/api/server/vVxVWYUAWD?style=flat)](https://discord.gg/vVxVWYUAWD)\n\n## • Overview\n\nWelcome to Acode Editor - a powerful and versatile code editing tool designed specifically for Android devices. Whether you're working on HTML, CSS, JavaScript, or other programming languages, Acode empowers you to code on-the-go with confidence.\n\n## • Features\n\n- Edit and create websites, and instantly preview them in a browser.\n- Seamlessly modify source files for various languages like Python, Java, JavaScript, and more.\n- Built-in javascript console\n- Enjoy multi-language editing support with easy management tools.\n- Enjoy a large collections of community plugins to enhance your coding experience.\n\n## • Installation\n\nYou can get Acode Editor from popular platforms:\n\n[<img src=\"https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png\" alt=\"Get it on Google Play\" height=\"60\">](https://play.google.com/store/apps/details?id=com.foxdebug.acodefree) [<img src=\"https://fdroid.gitlab.io/artwork/badge/get-it-on.png\" alt=\"Get it on F-Droid\" height=\"60\"/>](https://www.f-droid.org/packages/com.foxdebug.acode/)\n\n## • Project Structure\n\n<pre>\nAcode/\n|\n|- src/   - Core code and language files\n|\n|- www/   - Public documents, compiled files, and HTML templates\n|\n|- utils/ - CLI tools for building, string manipulation, and more\n</pre>\n\n## • Multi-language Support\n\nEnhance Acode's capabilities by adding new languages easily. Just create a file with the language code (e.g., en-us for English) in [`src/lang/`](https://github.com/Acode-Foundation/Acode/tree/main/src/lang) and include it in [`src/lib/lang.js`](https://github.com/Acode-Foundation/Acode/blob/main/src/lib/lang.js). Manage strings across languages effortlessly using utility commands:\n\n```shell\npnpm run lang add\npnpm run lang remove\npnpm run lang search\npnpm run lang update\n```\n\n## • Contributing & Building the Application\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for detailed instructions.\n\n## • Contributors\n\n<a href=\"https://github.com/Acode-Foundation/Acode/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=Acode-Foundation/Acode\" />\n</a>\n\n## • Developing a Plugin for Acode\n\nFor comprehensive documentation on creating plugins for Acode Editor, visit the [repository](https://github.com/Acode-Foundation/acode-plugin).\n\nFor plugin development information, refer to: [Acode Plugin Documentation](https://docs.acode.app/)\n\n## Star History\n\n<a href=\"https://star-history.com/#Acode-Foundation/Acode&Date\">\n <picture>\n   <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=Acode-Foundation/Acode&type=Date&theme=dark\" />\n   <source media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=Acode-Foundation/Acode&type=Date\" />\n   <img alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=Acode-Foundation/Acode&type=Date\" />\n </picture>\n</a>\n"
  },
  {
    "path": "res/android/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#3DDC84\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "res/android/drawable/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"544.37\"\n    android:viewportHeight=\"452.63\">\n  <group android:scaleX=\"0.55\"\n      android:scaleY=\"0.4573112\"\n      android:translateX=\"122.48325\"\n      android:translateY=\"122.81861\">\n    <path\n        android:pathData=\"m216.9,55.7c1.54,-9.11 9.61,-17.98 16.49,-17.98 10.54,0 32.1,0 42.28,0 9.31,0 17.81,11.45 15,25.95 -16.32,84.09 -48.75,253.82 -63.92,334.21 -2.15,11.4 -10.51,17.02 -16.04,17.02 -8.59,0 -25.2,0 -36.49,0 -7.06,0 -17.09,-9.66 -15.71,-17.67 18.15,-105.18 43.83,-255.62 58.39,-341.53z\"\n        android:fillColor=\"#3499FE\"/>\n    <path\n        android:pathData=\"m327.13,55.71c-1.54,-9.11 -9.61,-17.98 -16.49,-17.98 -10.54,0 -32.1,0 -42.28,0 -9.31,0 -17.81,11.45 -15,25.95 16.32,84.09 48.75,253.82 63.92,334.21 2.15,11.4 10.51,17.02 16.04,17.02 8.59,0 25.2,0 36.49,0 7.06,0 17.09,-9.66 15.71,-17.67 -18.15,-105.18 -43.83,-255.62 -58.39,-341.53z\"\n        android:fillColor=\"#3499FE\"/>\n    <path\n        android:pathData=\"m438.86,233.19c-4.64,-4.84 -53.34,-55.72 -70.25,-72.53 -6.23,-6.19 -6.23,-16.45 -3.45,-23.32 2.17,-5.36 8.75,-11.32 15.15,-11.29 11.27,0.05 35.05,0 46.31,0 4.16,0 7.87,2.18 11.59,5.95 20.31,20.55 59,60.43 80.61,83.36v0c1.44,1.52 2.93,2.88 4.1,4.12 3.54,3.75 4.64,8.13 4.84,11.56 0.09,0.93 0.13,1.8 0.15,2.58 0.09,3.59 -0.81,8.86 -4.99,13.27 -1.17,1.24 -2.66,2.6 -4.1,4.12v0c-21.61,22.93 -60.3,62.81 -80.61,83.36 -3.72,3.77 -7.43,5.95 -11.59,5.95 -11.26,0 -35.04,-0.05 -46.31,0 -6.4,0.03 -12.98,-5.93 -15.15,-11.29 -2.78,-6.87 -2.78,-17.13 3.45,-23.32 16.92,-16.79 65.62,-67.67 70.25,-72.52z\"\n        android:fillColor=\"#3499FE\"/>\n    <path\n        android:pathData=\"m105.51,233.77c4.64,-4.84 53.34,-55.72 70.25,-72.53 6.23,-6.19 6.23,-16.45 3.45,-23.32 -2.17,-5.36 -8.75,-11.32 -15.15,-11.29 -11.27,0.05 -35.05,0 -46.31,0 -4.16,0 -7.87,2.18 -11.59,5.95 -20.31,20.55 -59,60.43 -80.61,83.36v0c-1.44,1.52 -2.93,2.88 -4.1,4.12 -3.54,3.75 -4.64,8.13 -4.84,11.56 -0.09,0.93 -0.13,1.8 -0.15,2.58 -0.09,3.59 0.81,8.86 4.99,13.27 1.17,1.24 2.66,2.6 4.1,4.12v0c21.61,22.93 60.3,62.81 80.61,83.36 3.72,3.77 7.43,5.95 11.59,5.95 11.26,0 35.04,-0.05 46.31,0 6.4,0.03 12.98,-5.93 15.15,-11.29 2.78,-6.87 2.78,-17.13 -3.45,-23.32 -16.91,-16.8 -65.61,-67.68 -70.25,-72.52z\"\n        android:fillColor=\"#3499FE\"/>\n  </group>\n</vector>\n"
  },
  {
    "path": "res/android/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "res/android/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\"/>\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "res/android/values/colors.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n  <color name=\"teardrop\">#FFFFFF</color>\n</resources>\n"
  },
  {
    "path": "res/android/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#3a3e54</color>\n    <color name=\"ic_splash_background\">#3a3e54</color>\n</resources>"
  },
  {
    "path": "res/android/values/themes.xml",
    "content": "<?xml version='1.0' encoding='utf-8'?>\n<resources>\n    <style name=\"Theme.App.Activity\" parent=\"Theme.AppCompat.DayNight.NoActionBar\">\n        <item name=\"colorAccent\">@color/teardrop</item>\n        <item name=\"android:colorAccent\">@color/teardrop</item>\n        <item name=\"colorControlActivated\">@color/teardrop</item>\n        <item name=\"android:colorControlActivated\">@color/teardrop</item>\n        <item name=\"android:windowOptOutEdgeToEdgeEnforcement\">true</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "res/android/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n  <base-config cleartextTrafficPermitted=\"true\">\n    <trust-anchors>\n      <certificates src=\"system\" />\n    </trust-anchors>\n  </base-config>\n</network-security-config>"
  },
  {
    "path": "rspack.config.js",
    "content": "const path = require('path');\nconst { rspack } = require('@rspack/core');\n\nmodule.exports = (env, options) => {\n  const { mode = 'development' } = options;\n  const prod = mode === 'production';\n\n  const rules = [\n    // TypeScript/TSX files - Custom JSX loader + SWC\n    {\n      test: /\\.tsx?$/,\n      exclude: /node_modules/,\n      use: [\n        {\n          loader: 'builtin:swc-loader',\n          options: {\n            jsc: {\n              parser: {\n                syntax: 'typescript',\n                tsx: false,\n              },\n              transform: {\n                // react: {\n                //   pragma: 'tag',\n                //   pragmaFrag: 'Array',\n                //   throwIfNamespace: false,\n                //   development: false,\n                //   useBuiltins: false,\n                //   runtime: 'classic',\n                // },\n              },\n              target: 'es2015',\n            },\n          },\n        },\n        path.resolve(__dirname, 'utils/custom-loaders/html-tag-jsx-loader.js'),\n      ],\n    },\n    // JavaScript files\n    {\n      test: /\\.m?js$/,\n      oneOf: [\n        // Node modules - use builtin:swc-loader only\n        {\n          include: /node_modules/,\n          use: [\n            {\n              loader: 'builtin:swc-loader',\n              options: {\n                jsc: {\n                  parser: {\n                    syntax: 'ecmascript',\n                  },\n                  target: 'es2015',\n                },\n              },\n            },\n          ],\n        },\n        // Source JS files - Custom JSX loader + SWC (JSX will be removed first)\n        {\n          use: [\n            {\n              loader: 'builtin:swc-loader',\n              options: {\n                jsc: {\n                  parser: {\n                    syntax: 'ecmascript',\n                    jsx: false,\n                  },\n                  target: 'es2015',\n                },\n              },\n            },\n            path.resolve(__dirname, 'utils/custom-loaders/html-tag-jsx-loader.js'),\n          ],\n        },\n      ],\n    },\n    // Handlebars and Markdown files\n    {\n      test: /\\.(hbs|md)$/,\n      type: 'asset/source',\n    },\n    // Module CSS/SCSS (with .m prefix)\n    {\n      test: /\\.m\\.(sa|sc|c)ss$/,\n      use: [\n        'raw-loader',\n        'postcss-loader',\n        'sass-loader',\n      ],\n      type: 'javascript/auto',\n    },\n    // Asset files\n    {\n      test: /\\.(png|svg|jpg|jpeg|ico|ttf|webp|eot|woff|webm|mp4|wav)(\\?.*)?$/,\n      type: 'asset/resource',\n    },\n    // Regular CSS/SCSS files\n    {\n      test: /(?<!\\.m)\\.(sa|sc|c)ss$/,\n      use: [\n        rspack.CssExtractRspackPlugin.loader,\n        'css-loader',\n        'postcss-loader',\n        'sass-loader',\n      ],\n      type: 'javascript/auto',\n    },\n  ];\n\n  const main = {\n    mode,\n    entry: {\n      main: './src/main.js',\n      console: './src/lib/console.js',\n      searchInFilesWorker: './src/sidebarApps/searchInFiles/worker.js',\n    },\n    output: {\n      path: path.resolve(__dirname, 'www/build/'),\n      filename: '[name].js',\n      chunkFilename: '[name].chunk.js',\n      assetModuleFilename: '[name][ext]',\n      publicPath: '/build/',\n      clean: true,\n    },\n    module: {\n      rules,\n    },\n    resolve: {\n      extensions: ['.ts', '.tsx', '.js', '.mjs', '.json'],\n      fallback: {\n        path: require.resolve('path-browserify'),\n        crypto: false,\n      },\n      modules: ['node_modules', 'src'],\n    },\n    plugins: [\n      new rspack.CssExtractRspackPlugin({\n        filename: '[name].css',\n      }),\n    ],\n  };\n\n  return [main];\n};\n"
  },
  {
    "path": "src/cm/baseExtensions.ts",
    "content": "import { closeBrackets, completionKeymap } from \"@codemirror/autocomplete\";\nimport { defaultKeymap, history, historyKeymap } from \"@codemirror/commands\";\nimport {\n\tbracketMatching,\n\tdefaultHighlightStyle,\n\tfoldGutter,\n\tindentOnInput,\n\tsyntaxHighlighting,\n} from \"@codemirror/language\";\nimport { highlightSelectionMatches } from \"@codemirror/search\";\nimport type { Extension } from \"@codemirror/state\";\nimport { EditorState } from \"@codemirror/state\";\nimport {\n\tcrosshairCursor,\n\tdrawSelection,\n\tdropCursor,\n\thighlightActiveLine,\n\thighlightActiveLineGutter,\n\thighlightSpecialChars,\n\tkeymap,\n\trectangularSelection,\n\ttooltips,\n} from \"@codemirror/view\";\n\n/**\n * Base extensions roughly matching the useful parts of CodeMirror's basicSetup\n */\nexport default function createBaseExtensions(): Extension[] {\n\treturn [\n\t\thighlightActiveLineGutter(),\n\t\thighlightSpecialChars(),\n\t\thistory(),\n\t\tfoldGutter(),\n\t\tdrawSelection(),\n\t\tdropCursor(),\n\t\tEditorState.allowMultipleSelections.of(true),\n\t\tindentOnInput(),\n\t\tsyntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n\t\tbracketMatching(),\n\t\tcloseBrackets(),\n\t\trectangularSelection(),\n\t\tcrosshairCursor(),\n\t\thighlightActiveLine(),\n\t\thighlightSelectionMatches(),\n\t\tkeymap.of([...completionKeymap, ...defaultKeymap, ...historyKeymap]),\n\t\t// This prevents tooltips from being going out of the editor area\n\t\ttooltips({\n\t\t\ttooltipSpace: (view) => {\n\t\t\t\tconst rect = view.dom.getBoundingClientRect();\n\t\t\t\treturn {\n\t\t\t\t\ttop: rect.top,\n\t\t\t\t\tleft: rect.left,\n\t\t\t\t\tbottom: window.innerHeight,\n\t\t\t\t\tright: window.innerWidth,\n\t\t\t\t};\n\t\t\t},\n\t\t}),\n\t];\n}\n"
  },
  {
    "path": "src/cm/colorView.ts",
    "content": "import type { Range, Text } from \"@codemirror/state\";\nimport type { DecorationSet, ViewUpdate } from \"@codemirror/view\";\nimport {\n\tDecoration,\n\tEditorView,\n\tViewPlugin,\n\tWidgetType,\n} from \"@codemirror/view\";\nimport pickColor from \"dialogs/color\";\nimport color from \"utils/color\";\nimport { colorRegex, HEX } from \"utils/color/regex\";\n\ninterface ColorWidgetState {\n\tfrom: number;\n\tto: number;\n\tcolorType: string;\n\talpha?: string;\n}\n\ninterface ColorWidgetParams extends ColorWidgetState {\n\tcolor: string;\n\tcolorRaw: string;\n}\n\n// WeakMap to carry state from widget DOM back into handler\nconst colorState = new WeakMap<HTMLElement, ColorWidgetState>();\n\nconst HEX_RE = new RegExp(HEX, \"gi\");\n\nconst RGBG = new RegExp(colorRegex.anyGlobal);\n\nconst enumColorType = { hex: \"hex\", rgb: \"rgb\", hsl: \"hsl\", named: \"named\" };\n\nconst disallowedBoundaryBefore = new Set([\"-\", \".\", \"/\", \"#\"]);\nconst disallowedBoundaryAfter = new Set([\"-\", \".\", \"/\"]);\nconst ignoredLeadingWords = new Set([\"url\"]);\n\nfunction isWhitespace(char: string): boolean {\n\treturn (\n\t\tchar === \" \" ||\n\t\tchar === \"\\t\" ||\n\t\tchar === \"\\n\" ||\n\t\tchar === \"\\r\" ||\n\t\tchar === \"\\f\"\n\t);\n}\n\nfunction isAlpha(char: string): boolean {\n\tif (!char) return false;\n\tconst code = char.charCodeAt(0);\n\treturn (\n\t\t(code >= 65 && code <= 90) || // A-Z\n\t\t(code >= 97 && code <= 122)\n\t);\n}\n\nfunction charAt(doc: Text, index: number): string {\n\tif (index < 0 || index >= doc.length) return \"\";\n\treturn doc.sliceString(index, index + 1);\n}\n\nfunction findPrevNonWhitespace(doc: Text, index: number): number {\n\tfor (let i = index - 1; i >= 0; i--) {\n\t\tif (!isWhitespace(charAt(doc, i))) return i;\n\t}\n\treturn -1;\n}\n\nfunction findNextNonWhitespace(doc: Text, index: number): number {\n\tfor (let i = index; i < doc.length; i++) {\n\t\tif (!isWhitespace(charAt(doc, i))) return i;\n\t}\n\treturn doc.length;\n}\n\nfunction readWordBefore(doc: Text, index: number): string {\n\tlet pos = index;\n\twhile (pos >= 0 && isWhitespace(charAt(doc, pos))) pos--;\n\tif (pos < 0) return \"\";\n\tif (charAt(doc, pos) === \"(\") {\n\t\tpos--;\n\t}\n\twhile (pos >= 0 && isWhitespace(charAt(doc, pos))) pos--;\n\tconst end = pos;\n\twhile (pos >= 0 && isAlpha(charAt(doc, pos))) pos--;\n\tconst start = pos + 1;\n\tif (end < start) return \"\";\n\treturn doc.sliceString(start, end + 1).toLowerCase();\n}\n\nfunction shouldRenderColor(doc: Text, start: number, end: number): boolean {\n\tconst immediatePrev = charAt(doc, start - 1);\n\tif (disallowedBoundaryBefore.has(immediatePrev)) return false;\n\n\tconst immediateNext = charAt(doc, end);\n\tif (disallowedBoundaryAfter.has(immediateNext)) return false;\n\n\tconst prevNonWhitespaceIndex = findPrevNonWhitespace(doc, start);\n\tif (prevNonWhitespaceIndex !== -1) {\n\t\tconst prevNonWhitespaceChar = charAt(doc, prevNonWhitespaceIndex);\n\t\tif (disallowedBoundaryBefore.has(prevNonWhitespaceChar)) return false;\n\t\tconst prevWord = readWordBefore(doc, prevNonWhitespaceIndex);\n\t\tif (ignoredLeadingWords.has(prevWord)) return false;\n\t}\n\n\tconst nextNonWhitespaceIndex = findNextNonWhitespace(doc, end);\n\tif (nextNonWhitespaceIndex < doc.length) {\n\t\tconst nextNonWhitespaceChar = charAt(doc, nextNonWhitespaceIndex);\n\t\tif (disallowedBoundaryAfter.has(nextNonWhitespaceChar)) return false;\n\t}\n\n\treturn true;\n}\n\nclass ColorWidget extends WidgetType {\n\tstate: ColorWidgetState;\n\tcolor: string;\n\tcolorRaw: string;\n\n\tconstructor({ color, colorRaw, ...state }: ColorWidgetParams) {\n\t\tsuper();\n\t\tthis.state = state; // from, to, colorType, alpha\n\t\tthis.color = color; // hex for input value\n\t\tthis.colorRaw = colorRaw; // original css color string\n\t}\n\n\teq(other: ColorWidget): boolean {\n\t\treturn (\n\t\t\tother.state.colorType === this.state.colorType &&\n\t\t\tother.color === this.color &&\n\t\t\tother.state.from === this.state.from &&\n\t\t\tother.state.to === this.state.to &&\n\t\t\t(other.state.alpha || \"\") === (this.state.alpha || \"\")\n\t\t);\n\t}\n\n\ttoDOM(): HTMLElement {\n\t\tconst wrapper = document.createElement(\"span\");\n\t\twrapper.className = \"cm-color-chip\";\n\t\twrapper.style.display = \"inline-block\";\n\t\twrapper.style.width = \"0.9em\";\n\t\twrapper.style.height = \"0.9em\";\n\t\twrapper.style.borderRadius = \"2px\";\n\t\twrapper.style.verticalAlign = \"middle\";\n\t\twrapper.style.margin = \"0 2px\";\n\t\twrapper.style.boxSizing = \"border-box\";\n\t\twrapper.style.border = \"1px solid rgba(0,0,0,0.2)\";\n\t\twrapper.style.backgroundColor = this.colorRaw;\n\t\twrapper.dataset[\"color\"] = this.color;\n\t\twrapper.dataset[\"colorraw\"] = this.colorRaw;\n\t\twrapper.style.cursor = \"pointer\";\n\t\tcolorState.set(wrapper, this.state);\n\t\treturn wrapper;\n\t}\n\n\tignoreEvent(): boolean {\n\t\treturn false;\n\t}\n}\n\nfunction colorDecorations(view: EditorView): DecorationSet {\n\tconst deco: Range<Decoration>[] = [];\n\tconst ranges = view.visibleRanges;\n\tconst doc = view.state.doc;\n\tfor (const { from, to } of ranges) {\n\t\tconst text = doc.sliceString(from, to);\n\t\t// Any color using global matcher from utils (captures named/rgb/rgba/hsl/hsla/hex)\n\t\tRGBG.lastIndex = 0;\n\t\tfor (let m: RegExpExecArray | null; (m = RGBG.exec(text)); ) {\n\t\t\tconst raw = m[2];\n\t\t\tconst start = from + m.index + m[1].length;\n\t\t\tconst end = start + raw.length;\n\t\t\tif (!shouldRenderColor(doc, start, end)) continue;\n\t\t\tconst c = color(raw);\n\t\t\tconst colorHex = c.hex.toString(false);\n\t\t\tdeco.push(\n\t\t\t\tDecoration.widget({\n\t\t\t\t\twidget: new ColorWidget({\n\t\t\t\t\t\tfrom: start,\n\t\t\t\t\t\tto: end,\n\t\t\t\t\t\tcolor: colorHex,\n\t\t\t\t\t\tcolorRaw: raw,\n\t\t\t\t\t\tcolorType: enumColorType.named,\n\t\t\t\t\t}),\n\t\t\t\t\tside: -1,\n\t\t\t\t}).range(start),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn Decoration.set(deco, true);\n}\n\nclass ColorViewPlugin {\n\tdecorations: DecorationSet;\n\traf = 0;\n\tpendingView: EditorView | null = null;\n\n\tconstructor(view: EditorView) {\n\t\tthis.decorations = colorDecorations(view);\n\t}\n\n\tupdate(update: ViewUpdate): void {\n\t\tif (update.docChanged || update.viewportChanged) {\n\t\t\tthis.scheduleDecorations(update.view);\n\t\t}\n\t\tconst readOnly = update.view.contentDOM.ariaReadOnly === \"true\";\n\t\tconst editable = update.view.contentDOM.contentEditable === \"true\";\n\t\tconst canBeEdited = readOnly === false && editable;\n\t\tthis.changePicker(update.view, canBeEdited);\n\t}\n\n\tscheduleDecorations(view: EditorView): void {\n\t\tthis.pendingView = view;\n\t\tif (this.raf) return;\n\t\t// Color chips are decorative, so batch rapid viewport/doc changes into\n\t\t// one animation frame instead of rebuilding on every intermediate update.\n\t\tthis.raf = requestAnimationFrame(() => {\n\t\t\tthis.raf = 0;\n\t\t\tconst pendingView = this.pendingView;\n\t\t\tthis.pendingView = null;\n\t\t\tif (!pendingView) return;\n\t\t\tthis.decorations = colorDecorations(pendingView);\n\t\t});\n\t}\n\n\tchangePicker(view: EditorView, canBeEdited: boolean): void {\n\t\tconst doms = view.contentDOM.querySelectorAll(\"input[type=color]\");\n\t\tdoms.forEach((inp) => {\n\t\t\tconst input = inp as HTMLInputElement;\n\t\t\tif (canBeEdited) {\n\t\t\t\tinput.removeAttribute(\"disabled\");\n\t\t\t} else {\n\t\t\t\tinput.setAttribute(\"disabled\", \"\");\n\t\t\t}\n\t\t});\n\t}\n\n\tdestroy(): void {\n\t\tif (this.raf) {\n\t\t\tcancelAnimationFrame(this.raf);\n\t\t\tthis.raf = 0;\n\t\t}\n\t\tthis.pendingView = null;\n\t}\n}\n\nexport const colorView = (showPicker = true) =>\n\tViewPlugin.fromClass(ColorViewPlugin, {\n\t\tdecorations: (v) => v.decorations,\n\t\teventHandlers: {\n\t\t\tclick: (e: PointerEvent, view: EditorView): boolean => {\n\t\t\t\tconst target = e.target as HTMLElement | null;\n\t\t\t\tconst chip = target?.closest?.(\".cm-color-chip\") as HTMLElement | null;\n\t\t\t\tif (!chip) return false;\n\t\t\t\t// Respect read-only and setting toggle\n\t\t\t\tconst readOnly = view.contentDOM.ariaReadOnly === \"true\";\n\t\t\t\tconst editable = view.contentDOM.contentEditable === \"true\";\n\t\t\t\tconst canBeEdited = !readOnly && editable;\n\t\t\t\tif (!canBeEdited) return true;\n\t\t\t\tconst data = colorState.get(chip);\n\t\t\t\tif (!data) return false;\n\n\t\t\t\tpickColor(chip.dataset.colorraw || chip.dataset.color || \"\")\n\t\t\t\t\t.then((picked: string | null) => {\n\t\t\t\t\t\tif (!picked) return;\n\t\t\t\t\t\tview.dispatch({\n\t\t\t\t\t\t\tchanges: { from: data.from, to: data.to, insert: picked },\n\t\t\t\t\t\t});\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t/* ignore */\n\t\t\t\t\t});\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t});\n\nexport default colorView;\n"
  },
  {
    "path": "src/cm/commandRegistry.js",
    "content": "import fsOperation from \"fileSystem\";\nimport * as cmCommands from \"@codemirror/commands\";\nimport {\n\tcopyLineDown,\n\tcopyLineUp,\n\tcursorCharLeft,\n\tcursorCharRight,\n\tcursorDocEnd,\n\tcursorDocStart,\n\tcursorGroupLeft,\n\tcursorGroupRight,\n\tcursorLineDown,\n\tcursorLineEnd,\n\tcursorLineStart,\n\tcursorLineUp,\n\tcursorMatchingBracket,\n\tcursorPageDown,\n\tcursorPageUp,\n\tdeleteCharBackward,\n\tdeleteCharForward,\n\tdeleteGroupBackward,\n\tdeleteGroupForward,\n\tdeleteLine,\n\tdeleteLineBoundaryForward,\n\tdeleteToLineEnd,\n\tdeleteToLineStart,\n\tindentLess,\n\tindentMore,\n\tindentSelection,\n\tinsertBlankLine,\n\tinsertNewlineAndIndent,\n\tlineComment,\n\tlineUncomment,\n\tmoveLineDown,\n\tmoveLineUp,\n\tredo,\n\tselectAll,\n\tselectCharLeft,\n\tselectCharRight,\n\tselectDocEnd,\n\tselectDocStart,\n\tselectGroupLeft,\n\tselectGroupRight,\n\tselectLine,\n\tselectLineDown,\n\tselectLineEnd,\n\tselectLineStart,\n\tselectLineUp,\n\tselectMatchingBracket,\n\tselectPageDown,\n\tselectPageUp,\n\tsimplifySelection,\n\ttoggleBlockComment,\n\tundo,\n} from \"@codemirror/commands\";\nimport { indentUnit as indentUnitFacet } from \"@codemirror/language\";\nimport {\n\tcloseLintPanel,\n\tforceLinting,\n\tnextDiagnostic,\n\topenLintPanel,\n\tpreviousDiagnostic,\n} from \"@codemirror/lint\";\nimport {\n\tLSPPlugin,\n\tcloseReferencePanel as lspCloseReferencePanel,\n\tfindReferences as lspFindReferences,\n\tformatDocument as lspFormatDocument,\n\tjumpToDeclaration as lspJumpToDeclaration,\n\tjumpToDefinition as lspJumpToDefinition,\n\tjumpToImplementation as lspJumpToImplementation,\n\tjumpToTypeDefinition as lspJumpToTypeDefinition,\n} from \"@codemirror/lsp-client\";\nimport { Compartment, EditorSelection } from \"@codemirror/state\";\nimport { keymap } from \"@codemirror/view\";\nimport {\n\trenameSymbol as acodeRenameSymbol,\n\tclearDiagnosticsEffect,\n\tclientManager,\n\tnextSignature as lspNextSignature,\n\tprevSignature as lspPrevSignature,\n\tshowSignatureHelp as lspShowSignatureHelp,\n} from \"cm/lsp\";\nimport {\n\tcloseReferencesPanel as acodeCloseReferencesPanel,\n\tfindAllReferences as acodeFindAllReferences,\n\tfindAllReferencesInTab as acodeFindAllReferencesInTab,\n} from \"cm/lsp/references\";\nimport { showDocumentSymbols } from \"components/symbolsPanel\";\nimport toast from \"components/toast\";\nimport prompt from \"dialogs/prompt\";\nimport actions from \"handlers/quickTools\";\nimport keyBindings from \"lib/keyBindings\";\nimport settings from \"lib/settings\";\nimport Url from \"utils/Url\";\n\nconst commandKeymapCompartment = new Compartment();\n\n/**\n * @typedef {import(\"@codemirror/view\").EditorView} EditorView\n */\n\n/**\n * @typedef {{\n *  name: string;\n *  description?: string;\n *  readOnly?: boolean;\n *  run: (view?: EditorView | null) => boolean | void;\n *  requiresView?: boolean;\n *  defaultDescription?: string;\n *  defaultKey?: string | null;\n *  key?: string | null;\n * }} CommandEntry\n */\n\n/** @type {Map<string, CommandEntry>} */\nconst commandMap = new Map();\n\n/** @type {Record<string, any>} */\nlet resolvedKeyBindings = keyBindings;\n\n/** @type {Record<string, any>} */\nlet cachedResolvedKeyBindings = {};\n\nlet resolvedKeyBindingsVersion = 0;\n\n/** @type {import(\"@codemirror/view\").KeyBinding[]} */\nlet cachedKeymap = [];\n\nconst ARROW_KEY_MAP = {\n\tleft: \"ArrowLeft\",\n\tright: \"ArrowRight\",\n\tup: \"ArrowUp\",\n\tdown: \"ArrowDown\",\n};\n\nconst SPECIAL_KEY_MAP = {\n\tesc: \"Escape\",\n\tescape: \"Escape\",\n\treturn: \"Enter\",\n\tenter: \"Enter\",\n\tspace: \"Space\",\n\tdel: \"Delete\",\n\tdelete: \"Delete\",\n\tbackspace: \"Backspace\",\n\ttab: \"Tab\",\n\thome: \"Home\",\n\tend: \"End\",\n\tpageup: \"PageUp\",\n\tpagedown: \"PageDown\",\n\tinsert: \"Insert\",\n};\n\nconst MODIFIER_MAP = {\n\tctrl: \"Mod\",\n\tcontrol: \"Mod\",\n\tcmd: \"Mod\",\n\tmeta: \"Mod\",\n\tshift: \"Shift\",\n\talt: \"Alt\",\n\toption: \"Alt\",\n};\n\nconst CODEMIRROR_COMMAND_ENTRIES = Object.entries(cmCommands).filter(\n\t([, value]) => typeof value === \"function\",\n);\n\nconst CODEMIRROR_COMMAND_MAP = new Map(\n\tCODEMIRROR_COMMAND_ENTRIES.map(([name, fn]) => [name, fn]),\n);\n\nregisterCoreCommands();\nregisterLspCommands();\nregisterLintCommands();\nregisterCommandsFromKeyBindings();\nrebuildKeymap();\n\nfunction registerCoreCommands() {\n\taddCommand({\n\t\tname: \"focusEditor\",\n\t\tdescription: \"Focus editor\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tresolvedView?.focus();\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"findFile\",\n\t\tdescription: \"Find file in workspace\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"find-file\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"closeCurrentTab\",\n\t\tdescription: \"Close current tab\",\n\t\treadOnly: false,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"close-current-tab\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"closeAllTabs\",\n\t\tdescription: \"Close all tabs\",\n\t\treadOnly: false,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"close-all-tabs\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"togglePinnedTab\",\n\t\tdescription: \"Pin or unpin current tab\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"toggle-pin-tab\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"newFile\",\n\t\tdescription: \"Create new file\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"new-file\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"openFile\",\n\t\tdescription: \"Open a file\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open-file\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"openFolder\",\n\t\tdescription: \"Open a folder\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open-folder\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"saveFile\",\n\t\tdescription: \"Save current file\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"save\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"saveFileAs\",\n\t\tdescription: \"Save as current file\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"save-as\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"saveAllChanges\",\n\t\tdescription: \"Save all changes\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"save-all-changes\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"nextFile\",\n\t\tdescription: \"Open next file tab\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"next-file\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"prevFile\",\n\t\tdescription: \"Open previous file tab\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"prev-file\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"showSettingsMenu\",\n\t\tdescription: \"Show settings menu\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open\", \"settings\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"renameFile\",\n\t\tdescription: \"Rename active file\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"rename\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"run\",\n\t\tdescription: \"Preview HTML and MarkDown\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"run\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"openInAppBrowser\",\n\t\tdescription: \"Open In-App Browser\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun: openInAppBrowserCommand,\n\t});\n\taddCommand({\n\t\tname: \"toggleFullscreen\",\n\t\tdescription: \"Toggle full screen mode\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"toggle-fullscreen\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"toggleSidebar\",\n\t\tdescription: \"Toggle sidebar\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"toggle-sidebar\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"toggleMenu\",\n\t\tdescription: \"Toggle main menu\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"toggle-menu\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"toggleEditMenu\",\n\t\tdescription: \"Toggle edit menu\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"toggle-editmenu\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectall\",\n\t\tdescription: \"Select all\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn selectAll(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"gotoline\",\n\t\tdescription: \"Go to line...\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"goto\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"find\",\n\t\tdescription: \"Find\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"find\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"copy\",\n\t\tdescription: \"Copy\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: copyCommand,\n\t});\n\taddCommand({\n\t\tname: \"cut\",\n\t\tdescription: \"Cut\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun: cutCommand,\n\t});\n\taddCommand({\n\t\tname: \"paste\",\n\t\tdescription: \"Paste\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun: pasteCommand,\n\t});\n\taddCommand({\n\t\tname: \"problems\",\n\t\tdescription: \"Show errors and warnings\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open\", \"problems\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"replace\",\n\t\tdescription: \"Replace\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"replace\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"openCommandPalette\",\n\t\tdescription: \"Open command palette\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"command-palette\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"modeSelect\",\n\t\tdescription: \"Change language mode...\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"syntax\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"toggleQuickTools\",\n\t\tdescription: \"Toggle quick tools\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tactions(\"toggle\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectWord\",\n\t\tdescription: \"Select current word\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun: selectWordCommand,\n\t});\n\taddCommand({\n\t\tname: \"openLogFile\",\n\t\tdescription: \"Open Log File\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open-log-file\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"increaseFontSize\",\n\t\tdescription: \"Increase font size\",\n\t\treadOnly: false,\n\t\trequiresView: false,\n\t\trun: () => adjustFontSize(1),\n\t});\n\taddCommand({\n\t\tname: \"decreaseFontSize\",\n\t\tdescription: \"Decrease font size\",\n\t\treadOnly: false,\n\t\trequiresView: false,\n\t\trun: () => adjustFontSize(-1),\n\t});\n\taddCommand({\n\t\tname: \"openPluginsPage\",\n\t\tdescription: \"Open Plugins Page\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open\", \"plugins\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"openFileExplorer\",\n\t\tdescription: \"File Explorer\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"open\", \"file_browser\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"copyDeviceInfo\",\n\t\tdescription: \"Copy Device info\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"copy-device-info\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"changeAppTheme\",\n\t\tdescription: \"Change App Theme\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"change-app-theme\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"changeEditorTheme\",\n\t\tdescription: \"Change Editor Theme\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"change-editor-theme\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"openTerminal\",\n\t\tdescription: \"Open Terminal\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"new-terminal\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"acode:showWelcome\",\n\t\tdescription: \"Show Welcome\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"welcome\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"run-tests\",\n\t\tdescription: \"Run Tests\",\n\t\tkey: \"Ctrl-Shift-T\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\tacode.exec(\"run-tests\");\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"dev:openInspector\",\n\t\tdescription: \"Open Inspector\",\n\t\trun() {\n\t\t\tacode.exec(\"open-inspector\");\n\t\t\treturn true;\n\t\t},\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t});\n\taddCommand({\n\t\tname: \"dev:toggleDevTools\",\n\t\tdescription: \"Toggle Developer Tools\",\n\t\trun() {\n\t\t\tacode.exec(\"toggle-inspector\");\n\t\t\treturn true;\n\t\t},\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\tkey: \"Ctrl-Shift-I\",\n\t});\n\n\t// Additional editor-centric helpers mapped to CodeMirror primitives that have existing key bindings in defaults.\n\taddCommand({\n\t\tname: \"duplicateSelection\",\n\t\tdescription: \"Duplicate selection\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn copyLineDown(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"copylinesdown\",\n\t\tdescription: \"Copy lines down\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn copyLineDown(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"copylinesup\",\n\t\tdescription: \"Copy lines up\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn copyLineUp(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"movelinesdown\",\n\t\tdescription: \"Move lines down\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn moveLineDown(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"movelinesup\",\n\t\tdescription: \"Move lines up\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn moveLineUp(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"removeline\",\n\t\tdescription: \"Remove line\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn deleteLine(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"insertlineafter\",\n\t\tdescription: \"Insert line after\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn insertBlankLine(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectline\",\n\t\tdescription: \"Select line\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn selectLine(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectlinesdown\",\n\t\tdescription: \"Select line down\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn selectLineDown(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectlinesup\",\n\t\tdescription: \"Select line up\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn selectLineUp(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectlinestart\",\n\t\tdescription: \"Select line start\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn selectLineStart(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"selectlineend\",\n\t\tdescription: \"Select line end\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn selectLineEnd(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"indent\",\n\t\tdescription: \"Indent\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\tconst { state } = resolvedView;\n\t\t\tconst hasSelection = state.selection.ranges.some((range) => !range.empty);\n\t\t\tif (hasSelection) {\n\t\t\t\treturn indentMore(resolvedView);\n\t\t\t}\n\t\t\tconst indentString =\n\t\t\t\tstate.facet(indentUnitFacet) ||\n\t\t\t\t(settings?.value?.softTab\n\t\t\t\t\t? \" \".repeat(Math.max(1, Number(settings?.value?.tabSize) || 2))\n\t\t\t\t\t: \"\\t\");\n\t\t\tconst insert = indentString && indentString.length ? indentString : \"\\t\";\n\t\t\tresolvedView.dispatch(\n\t\t\t\tstate.changeByRange((range) => ({\n\t\t\t\t\tchanges: { from: range.from, to: range.to, insert },\n\t\t\t\t\trange: EditorSelection.cursor(range.from + insert.length),\n\t\t\t\t})),\n\t\t\t);\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"outdent\",\n\t\tdescription: \"Outdent\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn indentLess(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"indentselection\",\n\t\tdescription: \"Indent selection\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn indentSelection(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"newline\",\n\t\tdescription: \"Insert newline\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn insertNewlineAndIndent(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"joinlines\",\n\t\tdescription: \"Join lines\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn deleteLineBoundaryForward(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"deletetolinestart\",\n\t\tdescription: \"Delete to line start\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn deleteToLineStart(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"deletetolineend\",\n\t\tdescription: \"Delete to line end\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn deleteToLineEnd(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"togglecomment\",\n\t\tdescription: \"Toggle comment\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn lineComment(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"comment\",\n\t\tdescription: \"Add line comment\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn lineComment(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"uncomment\",\n\t\tdescription: \"Remove line comment\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn lineUncomment(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"toggleBlockComment\",\n\t\tdescription: \"Toggle block comment\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn toggleBlockComment(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"undo\",\n\t\tdescription: \"Undo\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn undo(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"redo\",\n\t\tdescription: \"Redo\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn redo(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"simplifySelection\",\n\t\tdescription: \"Simplify selection\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn simplifySelection(resolvedView);\n\t\t},\n\t});\n}\n\nfunction registerLspCommands() {\n\taddCommand({\n\t\tname: \"formatDocument\",\n\t\tdescription: \"Format document (Language Server)\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspFormatDocument),\n\t});\n\taddCommand({\n\t\tname: \"renameSymbol\",\n\t\tdescription: \"Rename symbol (Language Server)\",\n\t\treadOnly: false,\n\t\trequiresView: true,\n\t\trun: runLspCommand(acodeRenameSymbol),\n\t});\n\taddCommand({\n\t\tname: \"showSignatureHelp\",\n\t\tdescription: \"Show signature help\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspShowSignatureHelp),\n\t});\n\taddCommand({\n\t\tname: \"nextSignature\",\n\t\tdescription: \"Next signature\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspNextSignature, { silentOnMissing: true }),\n\t});\n\taddCommand({\n\t\tname: \"prevSignature\",\n\t\tdescription: \"Previous signature\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspPrevSignature, { silentOnMissing: true }),\n\t});\n\taddCommand({\n\t\tname: \"jumpToDefinition\",\n\t\tdescription: \"Go to definition (Language Server)\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspJumpToDefinition),\n\t});\n\taddCommand({\n\t\tname: \"jumpToDeclaration\",\n\t\tdescription: \"Go to declaration (Language Server)\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspJumpToDeclaration),\n\t});\n\taddCommand({\n\t\tname: \"jumpToTypeDefinition\",\n\t\tdescription: \"Go to type definition (Language Server)\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspJumpToTypeDefinition),\n\t});\n\taddCommand({\n\t\tname: \"jumpToImplementation\",\n\t\tdescription: \"Go to implementation (Language Server)\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun: runLspCommand(lspJumpToImplementation),\n\t});\n\taddCommand({\n\t\tname: \"findReferences\",\n\t\tdescription: \"Find all references (Language Server)\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\tasync run(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\tconst plugin = LSPPlugin.get(resolvedView);\n\t\t\tif (!plugin) {\n\t\t\t\tnotifyLspUnavailable();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn acodeFindAllReferences(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"closeReferencePanel\",\n\t\tdescription: \"Close references panel\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\trun() {\n\t\t\treturn acodeCloseReferencesPanel();\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"findReferencesInTab\",\n\t\tdescription: \"Find all references in new tab (Language Server)\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\tasync run(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\tconst plugin = LSPPlugin.get(resolvedView);\n\t\t\tif (!plugin) {\n\t\t\t\tnotifyLspUnavailable();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn acodeFindAllReferencesInTab(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"restartAllLspServers\",\n\t\tdescription: \"Restart all running LSP servers\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\tasync run() {\n\t\t\tconst activeClients = clientManager.getActiveClients();\n\t\t\tif (!activeClients.length) {\n\t\t\t\ttoast(\"No LSP servers are currently running\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst count = activeClients.length;\n\t\t\ttoast(`Restarting ${count} LSP server${count > 1 ? \"s\" : \"\"}...`);\n\n\t\t\t// Dispose all clients (also clears diagnostics)\n\t\t\tawait clientManager.dispose();\n\n\t\t\t// Trigger reconnect for active file\n\t\t\teditorManager?.restartLsp?.();\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"stopAllLspServers\",\n\t\tdescription: \"Stop all running LSP servers\",\n\t\treadOnly: true,\n\t\trequiresView: false,\n\t\tasync run() {\n\t\t\tconst activeClients = clientManager.getActiveClients();\n\t\t\tif (!activeClients.length) {\n\t\t\t\ttoast(\"No LSP servers are currently running\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tconst count = activeClients.length;\n\n\t\t\t// Dispose all clients\n\t\t\tawait clientManager.dispose();\n\t\t\ttoast(`Stopped ${count} LSP server${count > 1 ? \"s\" : \"\"}`);\n\t\t\treturn true;\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"documentSymbols\",\n\t\tdescription: \"Go to Symbol in Document...\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\tasync run(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn showDocumentSymbols(resolvedView);\n\t\t},\n\t});\n}\n\nfunction registerLintCommands() {\n\taddCommand({\n\t\tname: \"openLintPanel\",\n\t\tdescription: \"Open lint panel\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn openLintPanel(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"closeLintPanel\",\n\t\tdescription: \"Close lint panel\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn closeLintPanel(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"nextDiagnostic\",\n\t\tdescription: \"Go to next diagnostic\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn nextDiagnostic(resolvedView);\n\t\t},\n\t});\n\taddCommand({\n\t\tname: \"previousDiagnostic\",\n\t\tdescription: \"Go to previous diagnostic\",\n\t\treadOnly: true,\n\t\trequiresView: true,\n\t\trun(view) {\n\t\t\tconst resolvedView = resolveView(view);\n\t\t\tif (!resolvedView) return false;\n\t\t\treturn previousDiagnostic(resolvedView);\n\t\t},\n\t});\n}\n\nfunction registerCommandsFromKeyBindings() {\n\tObject.entries(keyBindings).forEach(([name, binding]) => {\n\t\tif (commandMap.has(name)) return;\n\t\tconst description = binding?.description || humanizeCommandName(name);\n\t\tconst readOnly = binding?.readOnly ?? false;\n\t\tconst requiresView = !!binding?.editorOnly;\n\t\tconst commandFn = CODEMIRROR_COMMAND_MAP.get(name);\n\n\t\tif (binding?.action) {\n\t\t\taddCommand({\n\t\t\t\tname,\n\t\t\t\tdescription,\n\t\t\t\treadOnly,\n\t\t\t\trequiresView,\n\t\t\t\trun(view) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (requiresView) {\n\t\t\t\t\t\t\tconst resolvedView = resolveView(view);\n\t\t\t\t\t\t\tif (!resolvedView) return false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tacode.exec(binding.action);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(`Failed to execute action ${binding.action}`, error);\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (commandFn) {\n\t\t\taddCommand({\n\t\t\t\tname,\n\t\t\t\tdescription,\n\t\t\t\treadOnly,\n\t\t\t\trequiresView: true,\n\t\t\t\trun(view) {\n\t\t\t\t\tconst resolvedView = resolveView(view);\n\t\t\t\t\tif (!resolvedView) return false;\n\t\t\t\t\treturn commandFn(resolvedView);\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t});\n}\n\nfunction addCommand(entry) {\n\tconst command = {\n\t\t...entry,\n\t\tdefaultDescription: entry.description || entry.name,\n\t\tdefaultKey: entry.key ?? null,\n\t\tkey: entry.key ?? null,\n\t};\n\tcommandMap.set(entry.name, command);\n}\n\nfunction resolveView(view) {\n\treturn view || editorManager?.editor || null;\n}\n\nfunction notifyLspUnavailable() {\n\ttoast?.(\"Language server not available\");\n}\n\nfunction runLspCommand(commandFn, options = {}) {\n\treturn (view) => {\n\t\tconst resolvedView = resolveView(view);\n\t\tif (!resolvedView) return false;\n\t\tconst plugin = LSPPlugin.get(resolvedView);\n\t\tif (!plugin) {\n\t\t\tif (!options?.silentOnMissing) {\n\t\t\t\tnotifyLspUnavailable();\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tconst result = commandFn(resolvedView);\n\t\treturn result !== false;\n\t};\n}\n\nfunction humanizeCommandName(name) {\n\treturn name\n\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n\t\t.replace(/_/g, \" \")\n\t\t.replace(/^./, (char) => char.toUpperCase());\n}\n\nfunction copyCommand(view) {\n\tconst resolvedView = resolveView(view);\n\tif (!resolvedView) return false;\n\tconst { state } = resolvedView;\n\tconst texts = state.selection.ranges.map((range) => {\n\t\tif (range.empty) {\n\t\t\tconst line = state.doc.lineAt(range.head);\n\t\t\treturn state.doc.sliceString(line.from, line.to);\n\t\t}\n\t\treturn state.doc.sliceString(range.from, range.to);\n\t});\n\tconst textToCopy = texts.join(\"\\n\");\n\tcordova.plugins.clipboard.copy(textToCopy);\n\ttoast?.(strings?.[\"copied to clipboard\"] || \"Copied to clipboard\");\n\treturn true;\n}\n\nfunction cutCommand(view) {\n\tconst resolvedView = resolveView(view);\n\tif (!resolvedView) return false;\n\tconst { state } = resolvedView;\n\tconst ranges = state.selection.ranges;\n\tconst segments = [];\n\tlet changes = [];\n\tranges.forEach((range) => {\n\t\tif (range.empty) {\n\t\t\tconst line = state.doc.lineAt(range.head);\n\t\t\tsegments.push(state.doc.sliceString(line.from, line.to));\n\t\t\tchanges.push({ from: line.from, to: line.to, insert: \"\" });\n\t\t\treturn;\n\t\t}\n\t\tsegments.push(state.doc.sliceString(range.from, range.to));\n\t\tchanges.push({ from: range.from, to: range.to, insert: \"\" });\n\t});\n\tcordova.plugins.clipboard.copy(segments.join(\"\\n\"));\n\tresolvedView.dispatch({\n\t\tchanges,\n\t\tselection: EditorSelection.single(\n\t\t\tchanges[0]?.from ?? state.selection.main.from,\n\t\t),\n\t});\n\treturn true;\n}\n\nfunction pasteCommand(view) {\n\tconst resolvedView = resolveView(view);\n\tif (!resolvedView) return false;\n\tcordova.plugins.clipboard.paste((text = \"\") => {\n\t\tconst insertText = String(text);\n\t\tresolvedView.dispatch(\n\t\t\tresolvedView.state.changeByRange((range) => ({\n\t\t\t\tchanges: { from: range.from, to: range.to, insert: insertText },\n\t\t\t\trange: EditorSelection.cursor(range.from + insertText.length),\n\t\t\t})),\n\t\t);\n\t});\n\treturn true;\n}\n\nfunction selectWordCommand(view) {\n\tconst resolvedView = resolveView(view);\n\tif (!resolvedView) return false;\n\tconst { state } = resolvedView;\n\tconst ranges = state.selection.ranges.map((range) => {\n\t\tconst word = state.wordAt(range.head);\n\t\tif (word) return EditorSelection.range(word.from, word.to);\n\t\tconst line = state.doc.lineAt(range.head);\n\t\treturn EditorSelection.range(line.from, line.to);\n\t});\n\tresolvedView.dispatch({\n\t\tselection: EditorSelection.create(ranges, state.selection.mainIndex),\n\t});\n\treturn true;\n}\n\nasync function openInAppBrowserCommand() {\n\tconst url = await prompt(\"Enter url\", \"\", \"url\", {\n\t\tplaceholder: \"http://\",\n\t\tmatch: /^https?:\\/\\/.+/,\n\t});\n\tif (url) acode.exec(\"open-inapp-browser\", url);\n\treturn true;\n}\n\nfunction adjustFontSize(delta) {\n\tconst current = settings?.value?.fontSize || \"12px\";\n\tconst numeric = Number.parseInt(current, 10) || 12;\n\tconst next = Math.max(1, numeric + delta);\n\tsettings.value.fontSize = `${next}px`;\n\tsettings.update(false);\n\treturn true;\n}\n\nfunction parseKeyString(keyString) {\n\tif (!keyString) return [];\n\treturn String(keyString)\n\t\t.split(\"|\")\n\t\t.map((combo) => combo.trim())\n\t\t.filter(Boolean);\n}\n\nfunction hasOwnBindingOverride(name) {\n\treturn Object.prototype.hasOwnProperty.call(resolvedKeyBindings ?? {}, name);\n}\n\nfunction resolveBindingInfo(name) {\n\tconst baseBinding = keyBindings[name] ?? null;\n\tif (!hasOwnBindingOverride(name)) return baseBinding;\n\n\tconst override = resolvedKeyBindings?.[name];\n\tif (override === null) {\n\t\treturn baseBinding ? { ...baseBinding, key: null } : { key: null };\n\t}\n\n\tif (!override || typeof override !== \"object\") {\n\t\treturn baseBinding;\n\t}\n\n\treturn baseBinding ? { ...baseBinding, ...override } : override;\n}\n\nfunction buildResolvedKeyBindingsSnapshot() {\n\tconst bindingNames = new Set([\n\t\t...Object.keys(keyBindings),\n\t\t...Object.keys(resolvedKeyBindings ?? {}),\n\t]);\n\n\treturn Object.fromEntries(\n\t\tArray.from(bindingNames, (name) => [name, resolveBindingInfo(name)]).filter(\n\t\t\t([, binding]) => binding,\n\t\t),\n\t);\n}\n\nfunction toCodeMirrorKey(combo) {\n\tif (!combo) return null;\n\tconst parts = combo\n\t\t.split(\"-\")\n\t\t.map((part) => part.trim())\n\t\t.filter(Boolean);\n\tconst modifiers = [];\n\tlet key = null;\n\n\tparts.forEach((part, index) => {\n\t\tconst lower = part.toLowerCase();\n\t\tif (MODIFIER_MAP[lower]) {\n\t\t\tconst mod = MODIFIER_MAP[lower];\n\t\t\tif (!modifiers.includes(mod)) modifiers.push(mod);\n\t\t\treturn;\n\t\t}\n\n\t\tif (ARROW_KEY_MAP[lower]) {\n\t\t\tkey = ARROW_KEY_MAP[lower];\n\t\t\treturn;\n\t\t}\n\n\t\tif (SPECIAL_KEY_MAP[lower]) {\n\t\t\tkey = SPECIAL_KEY_MAP[lower];\n\t\t\treturn;\n\t\t}\n\n\t\tif (part.length === 1 && /[a-z]/i.test(part)) {\n\t\t\tkey = part.length === 1 ? part.toLowerCase() : part;\n\t\t\treturn;\n\t\t}\n\n\t\tkey = part;\n\t});\n\n\tif (!key) return modifiers.join(\"-\") || null;\n\treturn modifiers.length ? `${modifiers.join(\"-\")}-${key}` : key;\n}\n\nfunction rebuildKeymap() {\n\tconst bindings = [];\n\tcachedResolvedKeyBindings = buildResolvedKeyBindingsSnapshot();\n\tcommandMap.forEach((command, name) => {\n\t\tconst bindingInfo = resolveBindingInfo(name);\n\t\tcommand.description =\n\t\t\tbindingInfo?.description || command.defaultDescription;\n\t\tconst keySource =\n\t\t\tbindingInfo && Object.prototype.hasOwnProperty.call(bindingInfo, \"key\")\n\t\t\t\t? bindingInfo.key\n\t\t\t\t: (command.defaultKey ?? null);\n\t\tcommand.key = keySource;\n\t\tconst combos = parseKeyString(keySource);\n\t\tcombos.forEach((combo) => {\n\t\t\tconst cmKey = toCodeMirrorKey(combo);\n\t\t\tif (!cmKey) return;\n\t\t\tbindings.push({\n\t\t\t\tkey: cmKey,\n\t\t\t\trun: (view) => executeCommand(name, view),\n\t\t\t\tpreventDefault: true,\n\t\t\t});\n\t\t});\n\t});\n\tcachedKeymap = bindings;\n\tresolvedKeyBindingsVersion += 1;\n\treturn bindings;\n}\n\nfunction resolveCommand(name) {\n\treturn commandMap.get(name) || null;\n}\n\nfunction commandRunsInReadOnly(command, view) {\n\tif (!view) return command.readOnly;\n\treturn view.state?.readOnly ? !!command.readOnly : true;\n}\n\nexport function executeCommand(name, view, args) {\n\tconst command = resolveCommand(name);\n\tif (!command) return false;\n\tconst targetView = command.requiresView\n\t\t? resolveView(view)\n\t\t: resolveView(view) || null;\n\tif (command.requiresView && !targetView) return false;\n\tif (!commandRunsInReadOnly(command, targetView)) return false;\n\ttry {\n\t\tconst result = command.run(targetView, args);\n\t\treturn result !== false;\n\t} catch (error) {\n\t\tconsole.error(`Failed to execute command ${name}`, error);\n\t\treturn false;\n\t}\n}\n\nexport function getRegisteredCommands() {\n\treturn Array.from(commandMap.values()).map((command) => ({\n\t\tname: command.name,\n\t\tdescription: command.description || command.defaultDescription,\n\t\tkey: command.key || null,\n\t}));\n}\n\nexport function getResolvedKeyBindings() {\n\treturn cachedResolvedKeyBindings;\n}\n\nexport function getResolvedKeyBindingsVersion() {\n\treturn resolvedKeyBindingsVersion;\n}\n\nexport function getCommandKeymapExtension() {\n\treturn commandKeymapCompartment.of(keymap.of(cachedKeymap));\n}\n\nexport async function setKeyBindings(view) {\n\tawait loadCustomKeyBindings();\n\tconst bindings = rebuildKeymap();\n\tconst resolvedView = resolveView(view);\n\tapplyCommandKeymap(resolvedView, bindings);\n}\n\nasync function loadCustomKeyBindings() {\n\ttry {\n\t\tconst bindingsFile = fsOperation(KEYBINDING_FILE);\n\t\tif (await bindingsFile.exists()) {\n\t\t\tconst bindings = await bindingsFile.readFile(\"json\");\n\t\t\tif (bindings && typeof bindings === \"object\") {\n\t\t\t\tresolvedKeyBindings = bindings;\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new Error(\"Key binding file not found\");\n\t\t}\n\t} catch (error) {\n\t\tawait resetKeyBindings();\n\t\tresolvedKeyBindings = keyBindings;\n\t}\n}\n\nexport async function resetKeyBindings() {\n\ttry {\n\t\tconst fs = fsOperation(KEYBINDING_FILE);\n\t\tconst fileName = Url.basename(KEYBINDING_FILE);\n\t\tconst content = JSON.stringify(keyBindings, undefined, 2);\n\t\tif (!(await fs.exists())) {\n\t\t\tawait fsOperation(DATA_STORAGE).createFile(fileName, content);\n\t\t\treturn;\n\t\t}\n\t\tawait fs.writeFile(content);\n\t} catch (error) {\n\t\twindow.log?.(\"error\", \"Reset Keybinding failed!\");\n\t\twindow.log?.(\"error\", error);\n\t}\n}\n\nexport { commandKeymapCompartment };\n\nexport function registerExternalCommand(descriptor = {}) {\n\tconst normalized = normalizeExternalCommand(descriptor);\n\tif (!normalized) return null;\n\n\tconst { name } = normalized;\n\tif (commandMap.has(name)) {\n\t\tcommandMap.delete(name);\n\t}\n\n\taddCommand(normalized);\n\tconst stored = commandMap.get(name);\n\tif (stored) {\n\t\tstored.key = normalized.key ?? stored.key;\n\t}\n\n\trebuildKeymap();\n\treturn stored;\n}\n\nexport function removeExternalCommand(name) {\n\tif (!name) return false;\n\tconst exists = commandMap.has(name);\n\tif (!exists) return false;\n\tcommandMap.delete(name);\n\trebuildKeymap();\n\treturn true;\n}\n\nexport function refreshCommandKeymap(view) {\n\tconst resolvedView = resolveView(view);\n\tapplyCommandKeymap(resolvedView);\n}\n\nfunction normalizeExternalCommand(descriptor) {\n\tconst name =\n\t\ttypeof descriptor?.name === \"string\" ? descriptor.name.trim() : \"\";\n\tif (!name) {\n\t\tconsole.warn(\"Command registration skipped: missing name\", descriptor);\n\t\treturn null;\n\t}\n\tconst exec = typeof descriptor?.exec === \"function\" ? descriptor.exec : null;\n\tif (!exec) {\n\t\tconsole.warn(\n\t\t\t`Command registration skipped for \"${name}\": exec must be a function.`,\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst requiresView = descriptor?.requiresView ?? true;\n\tconst key = normalizeExternalKey(descriptor?.bindKey);\n\n\treturn {\n\t\tname,\n\t\tdescription: descriptor?.description || humanizeCommandName(name),\n\t\treadOnly: descriptor?.readOnly ?? true,\n\t\trequiresView,\n\t\tkey,\n\t\trun(view, args) {\n\t\t\ttry {\n\t\t\t\tconst resolvedView = resolveView(view);\n\t\t\t\tif (requiresView && !resolvedView) return false;\n\t\t\t\tconst result = exec(resolvedView || null, args);\n\t\t\t\treturn result !== false;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Command \\\"${name}\\\" failed`, error);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction normalizeExternalKey(bindKey) {\n\tif (!bindKey) return null;\n\tif (typeof bindKey === \"string\") return bindKey;\n\tconst combos = [];\n\tif (typeof bindKey === \"object\") {\n\t\tconst pushCombo = (combo) => {\n\t\t\tif (typeof combo === \"string\" && combo.trim()) combos.push(combo.trim());\n\t\t};\n\t\tpushCombo(bindKey.win);\n\t\tpushCombo(bindKey.linux);\n\t\tpushCombo(bindKey.mac);\n\t}\n\treturn combos.length ? combos.join(\"|\") : null;\n}\n\nfunction applyCommandKeymap(view, bindings = cachedKeymap) {\n\tif (!view) return;\n\tview.dispatch({\n\t\teffects: commandKeymapCompartment.reconfigure(\n\t\t\tkeymap.of(bindings ?? cachedKeymap),\n\t\t),\n\t});\n}\n"
  },
  {
    "path": "src/cm/editorUtils.ts",
    "content": "import { foldEffect, foldedRanges } from \"@codemirror/language\";\nimport type { EditorState, StateEffect } from \"@codemirror/state\";\nimport { EditorSelection } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\n\nexport interface FoldSpan {\n\tfromLine: number;\n\tfromCol: number;\n\ttoLine: number;\n\ttoCol: number;\n}\nexport interface SelectionRange {\n\tfrom: number;\n\tto: number;\n}\nexport interface SerializedSelection {\n\tranges: SelectionRange[];\n\tmainIndex: number;\n}\nexport interface ScrollPosition {\n\tscrollTop: number;\n\tscrollLeft: number;\n}\n\n/**\n * Get all folded ranges from CodeMirror editor state\n */\nexport function getAllFolds(state: EditorState): FoldSpan[] {\n\tconst doc = state.doc;\n\tconst folds: FoldSpan[] = [];\n\n\tfoldedRanges(state).between(0, doc.length, (from, to) => {\n\t\tconst fromPos = doc.lineAt(from);\n\t\tconst toPos = doc.lineAt(to);\n\t\tfolds.push({\n\t\t\tfromLine: fromPos.number,\n\t\t\tfromCol: from - fromPos.from,\n\t\t\ttoLine: toPos.number,\n\t\t\ttoCol: to - toPos.from,\n\t\t});\n\t});\n\n\treturn folds;\n}\n\n/**\n * Get current selection from editor view\n */\nexport function getSelection(view: EditorView): SerializedSelection {\n\tconst sel = view.state.selection;\n\treturn {\n\t\tranges: sel.ranges.map((r) => ({ from: r.from, to: r.to })),\n\t\tmainIndex: sel.mainIndex,\n\t};\n}\n\n/**\n * Get scroll position from editor view\n */\nexport function getScrollPosition(view: EditorView): ScrollPosition {\n\tconst { scrollTop, scrollLeft } = view.scrollDOM;\n\treturn { scrollTop, scrollLeft };\n}\n\n/**\n * Set scroll position in CodeMirror editor view\n */\nexport function setScrollPosition(\n\tview: EditorView,\n\tscrollTop?: number,\n\tscrollLeft?: number,\n): void {\n\tconst scroller = view.scrollDOM;\n\n\tif (typeof scrollTop === \"number\") {\n\t\tscroller.scrollTop = scrollTop;\n\t}\n\n\tif (typeof scrollLeft === \"number\") {\n\t\tscroller.scrollLeft = scrollLeft;\n\t}\n}\n\n/**\n * Restore selection to editor view\n */\nexport function restoreSelection(\n\tview: EditorView,\n\tsel: SerializedSelection | null | undefined,\n): void {\n\tif (!sel || !sel.ranges || !sel.ranges.length) return;\n\tconst len = view.state.doc.length;\n\n\tconst ranges = sel.ranges\n\t\t.map((r) => {\n\t\t\tconst from = Math.max(0, Math.min(len, r.from | 0));\n\t\t\tconst to = Math.max(0, Math.min(len, r.to | 0));\n\t\t\treturn EditorSelection.range(from, to);\n\t\t})\n\t\t.filter(Boolean);\n\n\tif (!ranges.length) return;\n\n\tconst mainIndex =\n\t\tsel.mainIndex >= 0 && sel.mainIndex < ranges.length ? sel.mainIndex : 0;\n\n\tview.dispatch({\n\t\tselection: EditorSelection.create(ranges, mainIndex),\n\t\tscrollIntoView: true,\n\t});\n}\n\n/**\n * Restore folds to CodeMirror editor\n */\nexport function restoreFolds(\n\tview: EditorView,\n\tfolds: FoldSpan[] | null | undefined,\n): void {\n\tif (!Array.isArray(folds) || folds.length === 0) return;\n\n\tfunction lineColToOffset(\n\t\tdoc: EditorState[\"doc\"],\n\t\tline: number,\n\t\tcol: number,\n\t): number {\n\t\tconst ln = doc.line(line);\n\t\treturn Math.min(ln.from + col, ln.to);\n\t}\n\n\tfunction loadFolds(\n\t\tstate: EditorState,\n\t\tsaved: FoldSpan[],\n\t): StateEffect<{ from: number; to: number }>[] {\n\t\tconst doc = state.doc;\n\t\tconst effects: StateEffect<{ from: number; to: number }>[] = [];\n\n\t\tfor (const f of saved) {\n\t\t\t// Validate line numbers\n\t\t\tif (f.fromLine < 1 || f.fromLine > doc.lines) continue;\n\t\t\tif (f.toLine < 1 || f.toLine > doc.lines) continue;\n\n\t\t\tconst from = lineColToOffset(doc, f.fromLine, f.fromCol);\n\t\t\tconst to = lineColToOffset(doc, f.toLine, f.toCol);\n\t\t\tif (to > from) {\n\t\t\t\teffects.push(foldEffect.of({ from, to }));\n\t\t\t}\n\t\t}\n\t\treturn effects;\n\t}\n\n\tconst restoreEffects = loadFolds(view.state, folds);\n\tif (restoreEffects.length) {\n\t\tview.dispatch({ effects: restoreEffects });\n\t}\n}\n\n/**\n * Clear selection, keeping only cursor position\n */\nexport function clearSelection(view: EditorView): void {\n\tview.dispatch({\n\t\tselection: EditorSelection.single(view.state.selection.main.head),\n\t\tscrollIntoView: true,\n\t});\n\t// Also clear the global DOM selection to prevent native selection handles/menus persisting\n\ttry {\n\t\tdocument.getSelection()?.removeAllRanges();\n\t} catch (_) {\n\t\t// Ignore errors\n\t}\n}\n\nexport default {\n\tgetAllFolds,\n\tgetSelection,\n\tgetScrollPosition,\n\tsetScrollPosition,\n\trestoreSelection,\n\trestoreFolds,\n\tclearSelection,\n};\n"
  },
  {
    "path": "src/cm/indentGuides.ts",
    "content": "import { getIndentUnit, syntaxTree } from \"@codemirror/language\";\nimport type { Extension } from \"@codemirror/state\";\nimport { EditorState, RangeSetBuilder } from \"@codemirror/state\";\nimport {\n\tDecoration,\n\ttype DecorationSet,\n\tEditorView,\n\tViewPlugin,\n\ttype ViewUpdate,\n} from \"@codemirror/view\";\nimport type { SyntaxNode } from \"@lezer/common\";\n\n/**\n * Configuration options for indent guides\n */\nexport interface IndentGuidesConfig {\n\t/** Whether to highlight the guide at the cursor's indent level */\n\thighlightActiveGuide?: boolean;\n\t/** Whether to hide guides on blank lines */\n\thideOnBlankLines?: boolean;\n}\n\nconst defaultConfig: Required<IndentGuidesConfig> = {\n\thighlightActiveGuide: true,\n\thideOnBlankLines: false,\n};\n\nconst GUIDE_MARK_CLASS = \"cm-indent-guides\";\n\n/**\n * Get the tab size from editor state\n */\nfunction getTabSize(state: EditorState): number {\n\tconst tabSize = state.facet(EditorState.tabSize);\n\treturn Number.isFinite(tabSize) && tabSize > 0 ? tabSize : 4;\n}\n\n/**\n * Resolve the indentation width used for guide spacing.\n */\nfunction getIndentUnitColumns(state: EditorState): number {\n\tconst width = getIndentUnit(state);\n\tif (Number.isFinite(width) && width > 0) return width;\n\treturn getTabSize(state);\n}\n\n/**\n * Calculate the visual indentation of a line\n */\nfunction getLineIndentation(line: string, tabSize: number): number {\n\tlet columns = 0;\n\tfor (const ch of line) {\n\t\tif (ch === \" \") {\n\t\t\tcolumns++;\n\t\t} else if (ch === \"\\t\") {\n\t\t\tcolumns += tabSize - (columns % tabSize);\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn columns;\n}\n\n/**\n * Check if a line is blank\n */\nfunction isBlankLine(line: string): boolean {\n\treturn /^\\s*$/.test(line);\n}\n\n/**\n * Count the leading indentation characters of a line.\n */\nfunction getLeadingWhitespaceLength(line: string): number {\n\tlet count = 0;\n\tfor (const ch of line) {\n\t\tif (ch === \" \" || ch === \"\\t\") {\n\t\t\tcount++;\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\treturn count;\n}\n\n/**\n * Node types that represent scope blocks in various languages\n */\nconst SCOPE_NODE_TYPES = new Set([\n\t\"Block\",\n\t\"ObjectExpression\",\n\t\"ArrayExpression\",\n\t\"ArrowFunction\",\n\t\"FunctionDeclaration\",\n\t\"FunctionExpression\",\n\t\"ClassBody\",\n\t\"ClassDeclaration\",\n\t\"MethodDeclaration\",\n\t\"SwitchBody\",\n\t\"IfStatement\",\n\t\"WhileStatement\",\n\t\"ForStatement\",\n\t\"ForInStatement\",\n\t\"ForOfStatement\",\n\t\"TryStatement\",\n\t\"CatchClause\",\n\t\"Object\",\n\t\"Array\",\n\t\"Element\",\n\t\"SelfClosingTag\",\n\t\"RuleSet\",\n\t\"DeclarationList\",\n\t\"Body\",\n\t\"Suite\",\n\t\"Program\",\n\t\"Script\",\n\t\"Module\",\n]);\n\n/**\n * Information about the active scope for highlighting\n */\ninterface ActiveScope {\n\tlevel: number;\n\tstartLine: number;\n\tendLine: number;\n}\n\n/**\n * Find the active scope using syntax tree analysis\n */\nfunction getActiveScope(\n\tview: EditorView,\n\tindentUnit: number,\n): ActiveScope | null {\n\tconst { state } = view;\n\tconst { main } = state.selection;\n\tconst cursorPos = main.head;\n\n\tconst tree = syntaxTree(state);\n\tif (!tree || tree.length === 0) {\n\t\treturn getActiveScopeByIndentation(state, indentUnit);\n\t}\n\n\tlet scopeNode: SyntaxNode | null = null;\n\tlet node: SyntaxNode | null = tree.resolveInner(cursorPos, 0);\n\n\twhile (node) {\n\t\tif (SCOPE_NODE_TYPES.has(node.name)) {\n\t\t\tscopeNode = node;\n\t\t\tbreak;\n\t\t}\n\t\tnode = node.parent;\n\t}\n\n\tif (!scopeNode) {\n\t\treturn null;\n\t}\n\n\tconst startLine = state.doc.lineAt(scopeNode.from);\n\tconst endLine = state.doc.lineAt(scopeNode.to);\n\tlet contentStartLine = startLine.number;\n\tif (startLine.number < endLine.number) {\n\t\tcontentStartLine = startLine.number + 1;\n\t}\n\n\tconst tabSize = getTabSize(state);\n\tlet level = 0;\n\n\tfor (let ln = contentStartLine; ln <= endLine.number; ln++) {\n\t\tconst line = state.doc.line(ln);\n\t\tif (!isBlankLine(line.text)) {\n\t\t\tconst indent = getLineIndentation(line.text, tabSize);\n\t\t\tlevel = Math.floor(indent / indentUnit);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (level <= 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tlevel,\n\t\tstartLine: startLine.number,\n\t\tendLine: endLine.number,\n\t};\n}\n\n/**\n * Fallback: Find active scope by indentation when no syntax tree is available\n */\nfunction getActiveScopeByIndentation(\n\tstate: EditorState,\n\tindentUnit: number,\n): ActiveScope | null {\n\tconst { main } = state.selection;\n\tconst cursorLine = state.doc.lineAt(main.head);\n\tconst tabSize = getTabSize(state);\n\n\tlet cursorIndent = getLineIndentation(cursorLine.text, tabSize);\n\n\tif (isBlankLine(cursorLine.text)) {\n\t\tfor (let lineNum = cursorLine.number - 1; lineNum >= 1; lineNum--) {\n\t\t\tconst prevLine = state.doc.line(lineNum);\n\t\t\tif (!isBlankLine(prevLine.text)) {\n\t\t\t\tcursorIndent = getLineIndentation(prevLine.text, tabSize);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst cursorLevel = Math.floor(cursorIndent / indentUnit);\n\tif (cursorLevel <= 0) return null;\n\n\tlet startLine = cursorLine.number;\n\tfor (let lineNum = cursorLine.number - 1; lineNum >= 1; lineNum--) {\n\t\tconst line = state.doc.line(lineNum);\n\t\tif (isBlankLine(line.text)) continue;\n\t\tconst lineLevel = Math.floor(\n\t\t\tgetLineIndentation(line.text, tabSize) / indentUnit,\n\t\t);\n\t\tif (lineLevel < cursorLevel) break;\n\t\tstartLine = lineNum;\n\t}\n\n\tlet endLine = cursorLine.number;\n\tfor (\n\t\tlet lineNum = cursorLine.number + 1;\n\t\tlineNum <= state.doc.lines;\n\t\tlineNum++\n\t) {\n\t\tconst line = state.doc.line(lineNum);\n\t\tif (isBlankLine(line.text)) {\n\t\t\tendLine = lineNum;\n\t\t\tcontinue;\n\t\t}\n\t\tconst lineLevel = Math.floor(\n\t\t\tgetLineIndentation(line.text, tabSize) / indentUnit,\n\t\t);\n\t\tif (lineLevel < cursorLevel) break;\n\t\tendLine = lineNum;\n\t}\n\n\treturn { level: cursorLevel, startLine, endLine };\n}\n\nfunction buildGuideStyle(\n\tlevels: number,\n\tguideStepPx: number,\n\tactiveGuideIndex: number,\n): string {\n\tconst images = [];\n\tconst positions = [];\n\tconst sizes = [];\n\n\tfor (let i = 0; i < levels; i++) {\n\t\tconst color =\n\t\t\ti === activeGuideIndex\n\t\t\t\t? \"var(--indent-guide-active-color)\"\n\t\t\t\t: \"var(--indent-guide-color)\";\n\t\timages.push(`linear-gradient(${color}, ${color})`);\n\t\tpositions.push(`${i * guideStepPx}px 0`);\n\t\tsizes.push(\"1px 100%\");\n\t}\n\n\treturn [\n\t\t`background-image:${images.join(\",\")}`,\n\t\t\"background-repeat:no-repeat\",\n\t\t`background-position:${positions.join(\",\")}`,\n\t\t`background-size:${sizes.join(\",\")}`,\n\t].join(\";\");\n}\n\n/**\n * Build decorations for indent guides\n */\nfunction buildDecorations(\n\tview: EditorView,\n\tconfig: Required<IndentGuidesConfig>,\n): DecorationSet {\n\tconst builder = new RangeSetBuilder<Decoration>();\n\tconst { state } = view;\n\tconst tabSize = getTabSize(state);\n\tconst indentUnit = getIndentUnitColumns(state);\n\tconst guideStepPx = Math.max(view.defaultCharacterWidth * indentUnit, 1);\n\n\tconst activeScope = config.highlightActiveGuide\n\t\t? getActiveScope(view, indentUnit)\n\t\t: null;\n\n\tfor (const { from: blockFrom, to: blockTo } of view.visibleRanges) {\n\t\tconst startLine = state.doc.lineAt(blockFrom);\n\t\tconst endLine = state.doc.lineAt(blockTo);\n\n\t\tfor (let lineNum = startLine.number; lineNum <= endLine.number; lineNum++) {\n\t\t\tconst line = state.doc.line(lineNum);\n\t\t\tconst lineText = line.text;\n\n\t\t\tif (config.hideOnBlankLines && isBlankLine(lineText)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst indentColumns = getLineIndentation(lineText, tabSize);\n\t\t\tconst levels = Math.floor(indentColumns / indentUnit);\n\t\t\tif (levels <= 0) continue;\n\t\t\tconst leadingWhitespaceLength = getLeadingWhitespaceLength(lineText);\n\t\t\tif (leadingWhitespaceLength <= 0) continue;\n\n\t\t\tlet activeGuideIndex = -1;\n\t\t\tif (\n\t\t\t\tactiveScope &&\n\t\t\t\tlineNum >= activeScope.startLine &&\n\t\t\t\tlineNum <= activeScope.endLine &&\n\t\t\t\tlevels >= activeScope.level\n\t\t\t) {\n\t\t\t\tactiveGuideIndex = activeScope.level - 1;\n\t\t\t}\n\n\t\t\tbuilder.add(\n\t\t\t\tline.from,\n\t\t\t\tline.from + leadingWhitespaceLength,\n\t\t\t\tDecoration.mark({\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tclass: GUIDE_MARK_CLASS,\n\t\t\t\t\t\tstyle: buildGuideStyle(levels, guideStepPx, activeGuideIndex),\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn builder.finish();\n}\n\n/**\n * ViewPlugin for indent guides\n */\nfunction createIndentGuidesPlugin(\n\tconfig: Required<IndentGuidesConfig>,\n): ViewPlugin<{\n\tdecorations: DecorationSet;\n\tupdate(update: ViewUpdate): void;\n}> {\n\treturn ViewPlugin.fromClass(\n\t\tclass {\n\t\t\tdecorations: DecorationSet;\n\t\t\traf = 0;\n\t\t\tpendingView: EditorView | null = null;\n\n\t\t\tconstructor(view: EditorView) {\n\t\t\t\tthis.decorations = buildDecorations(view, config);\n\t\t\t}\n\n\t\t\tupdate(update: ViewUpdate): void {\n\t\t\t\tif (\n\t\t\t\t\t!update.docChanged &&\n\t\t\t\t\t!update.viewportChanged &&\n\t\t\t\t\t!update.geometryChanged &&\n\t\t\t\t\t!(config.highlightActiveGuide && update.selectionSet)\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.scheduleBuild(update.view);\n\t\t\t}\n\n\t\t\tscheduleBuild(view: EditorView): void {\n\t\t\t\tthis.pendingView = view;\n\t\t\t\tif (this.raf) return;\n\t\t\t\t// Guide rebuilding is cosmetic and can be expensive on large\n\t\t\t\t// viewports, so we intentionally collapse bursts into one frame.\n\t\t\t\tthis.raf = requestAnimationFrame(() => {\n\t\t\t\t\tthis.raf = 0;\n\t\t\t\t\tconst pendingView = this.pendingView;\n\t\t\t\t\tthis.pendingView = null;\n\t\t\t\t\tif (!pendingView) return;\n\t\t\t\t\tthis.decorations = buildDecorations(pendingView, config);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tdestroy(): void {\n\t\t\t\tif (this.raf) {\n\t\t\t\t\tcancelAnimationFrame(this.raf);\n\t\t\t\t\tthis.raf = 0;\n\t\t\t\t}\n\t\t\t\tthis.pendingView = null;\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tdecorations: (v) => v.decorations,\n\t\t},\n\t);\n}\n\n/**\n * Theme for indent guides.\n * Uses a single span around leading indentation instead of per-guide widgets.\n */\nconst indentGuidesTheme = EditorView.baseTheme({\n\t\".cm-indent-guides\": {\n\t\tdisplay: \"inline-block\",\n\t\tverticalAlign: \"top\",\n\t},\n\t\"&\": {\n\t\t\"--indent-guide-color\": \"rgba(128, 128, 128, 0.25)\",\n\t\t\"--indent-guide-active-color\": \"rgba(128, 128, 128, 0.7)\",\n\t},\n\t\"&light\": {\n\t\t\"--indent-guide-color\": \"rgba(0, 0, 0, 0.1)\",\n\t\t\"--indent-guide-active-color\": \"rgba(0, 0, 0, 0.4)\",\n\t},\n\t\"&dark\": {\n\t\t\"--indent-guide-color\": \"rgba(255, 255, 255, 0.1)\",\n\t\t\"--indent-guide-active-color\": \"rgba(255, 255, 255, 0.4)\",\n\t},\n});\n\nexport function indentGuides(config: IndentGuidesConfig = {}): Extension {\n\tconst mergedConfig: Required<IndentGuidesConfig> = {\n\t\t...defaultConfig,\n\t\t...config,\n\t};\n\n\treturn [createIndentGuidesPlugin(mergedConfig), indentGuidesTheme];\n}\n\nexport function indentGuidesExtension(\n\tenabled: boolean,\n\tconfig: IndentGuidesConfig = {},\n): Extension {\n\tif (!enabled) return [];\n\treturn indentGuides(config);\n}\n\nexport default indentGuides;\n"
  },
  {
    "path": "src/cm/lsp/api.ts",
    "content": "import { defineBundle, defineServer, installers } from \"./providerUtils\";\nimport {\n\tgetServerBundle,\n\tlistServerBundles,\n\tregisterServerBundle,\n\tunregisterServerBundle,\n} from \"./serverCatalog\";\nimport {\n\tgetServer,\n\tgetServersForLanguage,\n\tlistServers,\n\tonRegistryChange,\n\ttype RegisterServerOptions,\n\tregisterServer,\n\ttype ServerUpdater,\n\tunregisterServer,\n\tupdateServer,\n} from \"./serverRegistry\";\nimport type {\n\tLspServerBundle,\n\tLspServerDefinition,\n\tLspServerManifest,\n} from \"./types\";\n\nexport { defineBundle, defineServer, installers };\n\nexport type LspRegistrationEntry = LspServerManifest | LspServerBundle;\n\nfunction isBundleEntry(entry: LspRegistrationEntry): entry is LspServerBundle {\n\treturn typeof (entry as LspServerBundle)?.getServers === \"function\";\n}\n\nexport function register(\n\tentry: LspRegistrationEntry,\n\toptions?: RegisterServerOptions & { replace?: boolean },\n): LspServerDefinition | LspServerBundle {\n\tif (isBundleEntry(entry)) {\n\t\treturn registerServerBundle(entry, options);\n\t}\n\n\treturn registerServer(entry, options);\n}\n\nexport function upsert(\n\tentry: LspRegistrationEntry,\n): LspServerDefinition | LspServerBundle {\n\treturn register(entry, { replace: true });\n}\n\nexport const servers = {\n\tget(id: string): LspServerDefinition | null {\n\t\treturn getServer(id);\n\t},\n\tlist(): LspServerDefinition[] {\n\t\treturn listServers();\n\t},\n\tlistForLanguage(\n\t\tlanguageId: string,\n\t\toptions?: { includeDisabled?: boolean },\n\t): LspServerDefinition[] {\n\t\treturn getServersForLanguage(languageId, options);\n\t},\n\tupdate(id: string, updater: ServerUpdater): LspServerDefinition | null {\n\t\treturn updateServer(id, updater);\n\t},\n\tunregister(id: string): boolean {\n\t\treturn unregisterServer(id);\n\t},\n\tonChange(listener: Parameters<typeof onRegistryChange>[0]): () => void {\n\t\treturn onRegistryChange(listener);\n\t},\n};\n\nexport const bundles = {\n\tlist(): LspServerBundle[] {\n\t\treturn listServerBundles();\n\t},\n\tgetForServer(id: string): LspServerBundle | null {\n\t\treturn getServerBundle(id);\n\t},\n\tunregister(id: string): boolean {\n\t\treturn unregisterServerBundle(id);\n\t},\n};\n\nconst lspApi = {\n\tdefineServer,\n\tdefineBundle,\n\tregister,\n\tupsert,\n\tinstallers,\n\tservers,\n\tbundles,\n};\n\nexport default lspApi;\n"
  },
  {
    "path": "src/cm/lsp/clientManager.ts",
    "content": "import { getIndentUnit, indentUnit } from \"@codemirror/language\";\nimport type { LSPClientExtension } from \"@codemirror/lsp-client\";\nimport {\n\tfindReferencesKeymap,\n\tformatKeymap,\n\tjumpToDefinitionKeymap,\n\tLSPClient,\n\tLSPPlugin,\n\tserverCompletion,\n\tserverDiagnostics,\n} from \"@codemirror/lsp-client\";\nimport { EditorState, Extension, MapMode } from \"@codemirror/state\";\nimport { EditorView, keymap } from \"@codemirror/view\";\nimport lspStatusBar from \"components/lspStatusBar\";\nimport NotificationManager from \"lib/notificationManager\";\nimport Uri from \"utils/Uri\";\nimport { clearDiagnosticsEffect } from \"./diagnostics\";\nimport { supportsBuiltinFormatting } from \"./formattingSupport\";\nimport { inlayHintsExtension } from \"./inlayHints\";\nimport { acodeRenameKeymap } from \"./rename\";\nimport { ensureServerRunning } from \"./serverLauncher\";\nimport serverRegistry from \"./serverRegistry\";\nimport { hoverTooltips, signatureHelp } from \"./tooltipExtensions\";\nimport { createTransport } from \"./transport\";\nimport type {\n\tBuiltinExtensionsConfig,\n\tClientManagerOptions,\n\tClientState,\n\tDocumentUriContext,\n\tFileMetadata,\n\tFormattingOptions,\n\tLspServerDefinition,\n\tNormalizedRootUri,\n\tParsedUri,\n\tRootUriContext,\n\tTextEdit,\n\tTransport,\n\tTransportHandle,\n} from \"./types\";\nimport AcodeWorkspace from \"./workspace\";\n\nfunction asArray<T>(value: T | T[] | null | undefined): T[] {\n\tif (!value) return [];\n\treturn Array.isArray(value) ? value : [value];\n}\n\nfunction pluginKey(\n\tserverId: string,\n\trootUri: string | null | undefined,\n\tuseWorkspaceFolders?: boolean,\n): string {\n\t// For workspace folders mode, use just the server ID (one client per server type)\n\tif (useWorkspaceFolders) {\n\t\treturn serverId;\n\t}\n\treturn `${serverId}::${rootUri ?? \"__global__\"}`;\n}\n\nfunction safeString(value: unknown): string {\n\treturn value != null ? String(value) : \"\";\n}\n\nfunction isVerboseLspLoggingEnabled(): boolean {\n\tconst buildInfo = (globalThis as { BuildInfo?: { debug?: boolean } })\n\t\t.BuildInfo;\n\treturn !!buildInfo?.debug;\n}\n\nfunction logLspInfo(...args: unknown[]): void {\n\tif (!isVerboseLspLoggingEnabled()) return;\n\tconsole.info(...args);\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction resolveInitializationOptions(\n\tserver: LspServerDefinition,\n\tclientConfig: Record<string, unknown>,\n): Record<string, unknown> | undefined {\n\tconst serverOptions = isPlainObject(server.initializationOptions)\n\t\t? server.initializationOptions\n\t\t: null;\n\tconst clientOptions = isPlainObject(clientConfig.initializationOptions)\n\t\t? clientConfig.initializationOptions\n\t\t: null;\n\n\tif (serverOptions && clientOptions) {\n\t\treturn {\n\t\t\t...serverOptions,\n\t\t\t...clientOptions,\n\t\t};\n\t}\n\n\treturn serverOptions || clientOptions || undefined;\n}\n\ninterface InternalLSPRequest<Result> {\n\tpromise: Promise<Result>;\n}\n\ntype RequestInnerFn = <Params, Result>(\n\tmethod: string,\n\tparams: Params,\n\tmapped?: boolean,\n) => InternalLSPRequest<Result>;\n\nfunction connectClient(\n\tclient: ExtendedLSPClient,\n\ttransport: Transport,\n\tinitializationOptions?: Record<string, unknown>,\n): void {\n\tif (!initializationOptions || !Object.keys(initializationOptions).length) {\n\t\tclient.connect(transport);\n\t\treturn;\n\t}\n\n\tconst patchedClient = client as unknown as {\n\t\trequestInner: RequestInnerFn;\n\t};\n\tconst originalRequestInner = patchedClient.requestInner.bind(\n\t\tpatchedClient,\n\t) as RequestInnerFn;\n\n\tpatchedClient.requestInner = function patchedRequestInner<Params, Result>(\n\t\tmethod: string,\n\t\tparams: Params,\n\t\tmapped?: boolean,\n\t): InternalLSPRequest<Result> {\n\t\tif (method === \"initialize\" && isPlainObject(params)) {\n\t\t\tparams = {\n\t\t\t\t...params,\n\t\t\t\tinitializationOptions,\n\t\t\t} as Params;\n\t\t}\n\t\treturn originalRequestInner<Params, Result>(method, params, mapped);\n\t};\n\n\ttry {\n\t\tclient.connect(transport);\n\t} finally {\n\t\tpatchedClient.requestInner = originalRequestInner;\n\t}\n}\n\ninterface BuiltinExtensionsResult {\n\textensions: Extension[];\n\tdiagnosticsExtension: Extension | LSPClientExtension | null;\n}\n\nfunction buildBuiltinExtensions(\n\tconfig: BuiltinExtensionsConfig = {},\n): BuiltinExtensionsResult {\n\tconst {\n\t\thover: includeHover = true,\n\t\tcompletion: includeCompletion = true,\n\t\tsignature: includeSignature = true,\n\t\tkeymaps: includeKeymaps = true,\n\t\tdiagnostics: includeDiagnostics = true,\n\t\tinlayHints: includeInlayHints = false,\n\t\tformatting: includeFormatting = true,\n\t} = config;\n\n\tconst extensions: Extension[] = [];\n\tlet diagnosticsExtension: Extension | LSPClientExtension | null = null;\n\n\tif (includeCompletion) extensions.push(serverCompletion());\n\tif (includeHover) extensions.push(hoverTooltips());\n\tif (includeKeymaps) {\n\t\tconst bindings = [\n\t\t\t...(includeFormatting ? formatKeymap : []),\n\t\t\t...acodeRenameKeymap,\n\t\t\t...jumpToDefinitionKeymap,\n\t\t\t...findReferencesKeymap,\n\t\t];\n\t\tif (bindings.length) {\n\t\t\textensions.push(keymap.of(bindings));\n\t\t}\n\t}\n\tif (includeSignature) extensions.push(signatureHelp());\n\tif (includeDiagnostics) {\n\t\tconst diagExt = serverDiagnostics();\n\t\tdiagnosticsExtension = diagExt;\n\t\textensions.push(diagExt as Extension);\n\t}\n\tif (includeInlayHints) {\n\t\tconst hintsExt = inlayHintsExtension();\n\t\textensions.push(hintsExt as LSPClientExtension as Extension);\n\t}\n\n\treturn { extensions, diagnosticsExtension };\n}\n\ninterface LSPError extends Error {\n\tcode?: string;\n}\n\ninterface InitContext {\n\tkey: string;\n\tnormalizedRootUri: string | null;\n\toriginalRootUri: string | null;\n}\n\ninterface ExtendedLSPClient extends LSPClient {\n\t__acodeLoggedInfo?: boolean;\n}\n\nexport class LspClientManager {\n\toptions: ClientManagerOptions;\n\n\t#clients: Map<string, ClientState>;\n\t#pendingClients: Map<string, Promise<ClientState>>;\n\n\tconstructor(options: ClientManagerOptions = {}) {\n\t\tthis.options = { ...options };\n\t\tthis.#clients = new Map();\n\t\tthis.#pendingClients = new Map();\n\t}\n\n\tsetOptions(next: Partial<ClientManagerOptions>): void {\n\t\tthis.options = { ...this.options, ...next };\n\t}\n\n\tgetActiveClients(): ClientState[] {\n\t\treturn Array.from(this.#clients.values());\n\t}\n\n\tasync getExtensionsForFile(metadata: FileMetadata): Promise<Extension[]> {\n\t\tconst {\n\t\t\turi: originalUri,\n\t\t\tlanguageId,\n\t\t\tlanguageName,\n\t\t\tview,\n\t\t\tfile,\n\t\t\trootUri,\n\t\t} = metadata;\n\n\t\tconst effectiveLang = safeString(languageId ?? languageName).toLowerCase();\n\t\tif (!effectiveLang) return [];\n\n\t\tconst servers = serverRegistry.getServersForLanguage(effectiveLang);\n\t\tif (!servers.length) return [];\n\n\t\tconst lspExtensions: Extension[] = [];\n\t\tconst diagnosticsUiExtension = this.options.diagnosticsUiExtension;\n\n\t\tfor (const server of servers) {\n\t\t\tconst normalizedUri = await this.#resolveDocumentUri(server, {\n\t\t\t\turi: originalUri,\n\t\t\t\tfile,\n\t\t\t\tview,\n\t\t\t\tlanguageId: effectiveLang,\n\t\t\t\trootUri,\n\t\t\t});\n\t\t\tif (!normalizedUri) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Cannot resolve document URI for LSP server ${server.id}: ${originalUri}`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet targetLanguageId = effectiveLang;\n\t\t\tif (server.resolveLanguageId) {\n\t\t\t\ttry {\n\t\t\t\t\tconst resolved = server.resolveLanguageId({\n\t\t\t\t\t\tlanguageId: effectiveLang,\n\t\t\t\t\t\tlanguageName,\n\t\t\t\t\t\turi: normalizedUri,\n\t\t\t\t\t\tfile,\n\t\t\t\t\t});\n\t\t\t\t\tif (resolved) targetLanguageId = safeString(resolved);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`LSP server ${server.id} failed to resolve language id for ${normalizedUri}`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst clientState = await this.#ensureClient(server, {\n\t\t\t\t\turi: normalizedUri,\n\t\t\t\t\tfile,\n\t\t\t\t\tview,\n\t\t\t\t\tlanguageId: targetLanguageId,\n\t\t\t\t\trootUri,\n\t\t\t\t});\n\t\t\t\tconst plugin = clientState.client.plugin(\n\t\t\t\t\tnormalizedUri,\n\t\t\t\t\ttargetLanguageId,\n\t\t\t\t);\n\t\t\t\tconst aliases =\n\t\t\t\t\toriginalUri && originalUri !== normalizedUri ? [originalUri] : [];\n\t\t\t\tclientState.attach(normalizedUri, view as EditorView, aliases);\n\t\t\t\tlspExtensions.push(plugin);\n\t\t\t} catch (error) {\n\t\t\t\tconst lspError = error as LSPError;\n\t\t\t\tif (lspError?.code === \"LSP_SERVER_UNAVAILABLE\") {\n\t\t\t\t\tconsole.info(\n\t\t\t\t\t\t`Skipping LSP client for ${server.id}: ${lspError.message}`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to initialize LSP client for ${server.id}`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (diagnosticsUiExtension && lspExtensions.length) {\n\t\t\tlspExtensions.push(...asArray(diagnosticsUiExtension));\n\t\t}\n\n\t\treturn lspExtensions;\n\t}\n\n\tasync formatDocument(\n\t\tmetadata: FileMetadata,\n\t\toptions: FormattingOptions = {},\n\t): Promise<boolean> {\n\t\tconst { uri: originalUri, languageId, languageName, view, file } = metadata;\n\t\tconst effectiveLang = safeString(languageId ?? languageName).toLowerCase();\n\t\tif (!effectiveLang || !view) return false;\n\n\t\tconst servers = serverRegistry.getServersForLanguage(effectiveLang);\n\t\tif (!servers.length) return false;\n\n\t\tfor (const server of servers) {\n\t\t\tif (!supportsBuiltinFormatting(server)) continue;\n\t\t\ttry {\n\t\t\t\tconst normalizedUri = await this.#resolveDocumentUri(server, {\n\t\t\t\t\turi: originalUri,\n\t\t\t\t\tfile,\n\t\t\t\t\tview,\n\t\t\t\t\tlanguageId: effectiveLang,\n\t\t\t\t\trootUri: metadata.rootUri,\n\t\t\t\t});\n\t\t\t\tif (!normalizedUri) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Cannot resolve document URI for formatting with ${server.id}: ${originalUri}`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst context: RootUriContext = {\n\t\t\t\t\turi: normalizedUri,\n\t\t\t\t\tlanguageId: effectiveLang,\n\t\t\t\t\tview,\n\t\t\t\t\tfile,\n\t\t\t\t\trootUri: metadata.rootUri,\n\t\t\t\t};\n\t\t\t\tconst state = await this.#ensureClient(server, context);\n\t\t\t\tconst capabilities = state.client.serverCapabilities;\n\t\t\t\tif (!capabilities?.documentFormattingProvider) continue;\n\t\t\t\tstate.attach(normalizedUri, view);\n\t\t\t\tconst plugin = LSPPlugin.get(view);\n\t\t\t\tif (!plugin) continue;\n\t\t\t\tplugin.client.sync();\n\t\t\t\tconst edits = await state.client.request<\n\t\t\t\t\t{ textDocument: { uri: string }; options: FormattingOptions },\n\t\t\t\t\tTextEdit[] | null\n\t\t\t\t>(\"textDocument/formatting\", {\n\t\t\t\t\ttextDocument: { uri: normalizedUri },\n\t\t\t\t\toptions: buildFormattingOptions(view, options),\n\t\t\t\t});\n\t\t\t\tif (!edits || !edits.length) {\n\t\t\t\t\tplugin.client.sync();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconst applied = applyTextEdits(plugin, view, edits);\n\t\t\t\tif (applied) {\n\t\t\t\t\tplugin.client.sync();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`LSP formatting failed for ${server.id}`, error);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tdetach(uri: string, view: EditorView): void {\n\t\tfor (const state of this.#clients.values()) {\n\t\t\tstate.detach(uri, view);\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\ttry {\n\t\t\tinterface FileWithSession {\n\t\t\t\tid?: string;\n\t\t\t\ttype?: string;\n\t\t\t\tsession?: EditorState;\n\t\t\t}\n\n\t\t\tinterface EditorManagerLike {\n\t\t\t\tfiles?: FileWithSession[];\n\t\t\t\teditor?: EditorView;\n\t\t\t\tactiveFile?: FileWithSession;\n\t\t\t}\n\n\t\t\tconst em = (globalThis as Record<string, unknown>).editorManager as\n\t\t\t\t| EditorManagerLike\n\t\t\t\t| undefined;\n\n\t\t\tif (em?.editor) {\n\t\t\t\ttry {\n\t\t\t\t\tem.editor.dispatch({ effects: clearDiagnosticsEffect() });\n\t\t\t\t\tif (em.activeFile?.type === \"editor\") {\n\t\t\t\t\t\tem.activeFile.session = em.editor.state;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t/* View may be disposed */\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (em?.files) {\n\t\t\t\tfor (const file of em.files) {\n\t\t\t\t\tif (file?.type !== \"editor\" || file.id === em.activeFile?.id)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tconst session = file.session;\n\t\t\t\t\tif (session && typeof session.update === \"function\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfile.session = session.update({\n\t\t\t\t\t\t\t\teffects: clearDiagnosticsEffect(),\n\t\t\t\t\t\t\t}).state;\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t/* State update failed */\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t/* Ignore errors */\n\t\t}\n\n\t\tconst disposeOps: Promise<void>[] = [];\n\t\tfor (const [key, state] of this.#clients.entries()) {\n\t\t\tdisposeOps.push(state.dispose());\n\t\t\tthis.#clients.delete(key);\n\t\t}\n\t\tawait Promise.allSettled(disposeOps);\n\t}\n\n\tasync #ensureClient(\n\t\tserver: LspServerDefinition,\n\t\tcontext: RootUriContext,\n\t): Promise<ClientState> {\n\t\tconst useWsFolders = server.useWorkspaceFolders === true;\n\t\tconst resolvedRoot = await this.#resolveRootUri(server, context);\n\t\tconst { normalizedRootUri, originalRootUri } = normalizeRootUriForServer(\n\t\t\tserver,\n\t\t\tresolvedRoot,\n\t\t);\n\n\t\t// For workspace folders mode, use a shared key based on server ID only\n\t\tconst key = pluginKey(server.id, normalizedRootUri, useWsFolders);\n\n\t\t// Return existing client if already initialized\n\t\tif (this.#clients.has(key)) {\n\t\t\tconst existing = this.#clients.get(key)!;\n\t\t\t// For workspace folders mode, add the new folder to the existing server\n\t\t\tif (useWsFolders && normalizedRootUri) {\n\t\t\t\tconst workspace = existing.client.workspace as AcodeWorkspace | null;\n\t\t\t\tif (workspace && !workspace.hasWorkspaceFolder(normalizedRootUri)) {\n\t\t\t\t\tworkspace.addWorkspaceFolder(normalizedRootUri);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn existing;\n\t\t}\n\n\t\t// If initialization is already in progress, wait for it\n\t\tif (this.#pendingClients.has(key)) {\n\t\t\treturn this.#pendingClients.get(key)!;\n\t\t}\n\n\t\t// Create and track the pending initialization\n\t\tconst initPromise = this.#initializeClient(server, context, {\n\t\t\tkey,\n\t\t\tnormalizedRootUri: useWsFolders ? null : normalizedRootUri,\n\t\t\toriginalRootUri: useWsFolders ? null : originalRootUri,\n\t\t});\n\t\tthis.#pendingClients.set(key, initPromise);\n\n\t\ttry {\n\t\t\treturn await initPromise;\n\t\t} finally {\n\t\t\tthis.#pendingClients.delete(key);\n\t\t}\n\t}\n\n\tasync #initializeClient(\n\t\tserver: LspServerDefinition,\n\t\tcontext: RootUriContext,\n\t\tinitContext: InitContext,\n\t): Promise<ClientState> {\n\t\tconst { key, normalizedRootUri, originalRootUri } = initContext;\n\n\t\tconst workspaceOptions = {\n\t\t\tdisplayFile: this.options.displayFile,\n\t\t\topenFile: this.options.openFile,\n\t\t\tresolveLanguageId: this.options.resolveLanguageId,\n\t\t};\n\n\t\tconst clientConfig = { ...(server.clientConfig ?? {}) };\n\t\tconst initializationOptions = resolveInitializationOptions(\n\t\t\tserver,\n\t\t\tclientConfig as Record<string, unknown>,\n\t\t);\n\t\tconst builtinConfig = clientConfig.builtinExtensions ?? {};\n\t\tconst useDefaultExtensions = clientConfig.useDefaultExtensions !== false;\n\t\tconst { extensions: defaultExtensions, diagnosticsExtension } =\n\t\t\tuseDefaultExtensions\n\t\t\t\t? buildBuiltinExtensions({\n\t\t\t\t\t\thover: builtinConfig.hover !== false,\n\t\t\t\t\t\tcompletion: builtinConfig.completion !== false,\n\t\t\t\t\t\tsignature: builtinConfig.signature !== false,\n\t\t\t\t\t\tkeymaps: builtinConfig.keymaps !== false,\n\t\t\t\t\t\tdiagnostics: builtinConfig.diagnostics !== false,\n\t\t\t\t\t\tinlayHints: builtinConfig.inlayHints === true,\n\t\t\t\t\t\tformatting: builtinConfig.formatting !== false,\n\t\t\t\t\t})\n\t\t\t\t: { extensions: [], diagnosticsExtension: null };\n\n\t\tconst extraExtensions = asArray(this.options.clientExtensions);\n\t\tconst serverExtensions = asArray(clientConfig.extensions);\n\n\t\tinterface ExtensionWithCapabilities {\n\t\t\tclientCapabilities?: {\n\t\t\t\ttextDocument?: {\n\t\t\t\t\tpublishDiagnostics?: unknown;\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\tconst wantsCustomDiagnostics = [\n\t\t\t...extraExtensions,\n\t\t\t...serverExtensions,\n\t\t].some((ext) => {\n\t\t\tconst extWithCaps = ext as ExtensionWithCapabilities;\n\t\t\treturn !!extWithCaps?.clientCapabilities?.textDocument\n\t\t\t\t?.publishDiagnostics;\n\t\t});\n\n\t\tconst filteredBuiltins =\n\t\t\twantsCustomDiagnostics && diagnosticsExtension\n\t\t\t\t? defaultExtensions.filter((ext) => ext !== diagnosticsExtension)\n\t\t\t\t: defaultExtensions;\n\n\t\tconst progressCapabilities: LSPClientExtension = {\n\t\t\tclientCapabilities: {\n\t\t\t\twindow: {\n\t\t\t\t\tworkDoneProgress: true,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst mergedExtensions = [\n\t\t\t...filteredBuiltins,\n\t\t\t...extraExtensions,\n\t\t\t...serverExtensions,\n\t\t\tprogressCapabilities,\n\t\t];\n\t\tclientConfig.extensions = mergedExtensions;\n\n\t\tconst existingHandlers = clientConfig.notificationHandlers ?? {};\n\n\t\ttype LogLevel = \"error\" | \"warn\" | \"log\" | \"info\";\n\t\tinterface LogMessageParams {\n\t\t\ttype?: number;\n\t\t\tmessage?: string;\n\t\t}\n\t\tinterface ShowMessageParams {\n\t\t\ttype?: number;\n\t\t\tmessage?: string;\n\t\t}\n\n\t\tclientConfig.notificationHandlers = {\n\t\t\t...existingHandlers,\n\t\t\t\"window/logMessage\": (_client: LSPClient, params: unknown): boolean => {\n\t\t\t\tconst logParams = params as LogMessageParams;\n\t\t\t\tif (!logParams?.message) return false;\n\t\t\t\tconst { type, message } = logParams;\n\t\t\t\tlet level: LogLevel = \"info\";\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\tlevel = \"error\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tlevel = \"warn\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\tlevel = \"log\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tlevel = \"info\";\n\t\t\t\t}\n\t\t\t\tconst logFn = console[level] ?? console.info;\n\t\t\t\tlogFn(`[LSP:${server.id}] ${message}`);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\t\"window/showMessage\": (_client: LSPClient, params: unknown): boolean => {\n\t\t\t\tconst showParams = params as ShowMessageParams;\n\t\t\t\tif (!showParams?.message) return false;\n\t\t\t\tconst { type, message } = showParams;\n\t\t\t\tconst serverLabel = server.label || server.id;\n\n\t\t\t\t// Helper to clean and truncate message for notifications\n\t\t\t\tconst cleanMessage = (msg: string, maxLen = 150): string => {\n\t\t\t\t\t// Take only first line\n\t\t\t\t\tlet cleaned = msg.split(\"\\n\")[0].trim();\n\t\t\t\t\tif (cleaned.length > maxLen) {\n\t\t\t\t\t\tcleaned = cleaned.slice(0, maxLen - 3) + \"...\";\n\t\t\t\t\t}\n\t\t\t\t\treturn cleaned;\n\t\t\t\t};\n\n\t\t\t\t// Use notifications for errors and warnings\n\t\t\t\tif (type === 1 || type === 2) {\n\t\t\t\t\tconst notificationManager = new NotificationManager();\n\t\t\t\t\tnotificationManager.pushNotification({\n\t\t\t\t\t\ttitle: serverLabel,\n\t\t\t\t\t\tmessage: cleanMessage(message),\n\t\t\t\t\t\ticon: type === 1 ? \"error\" : \"warningreport_problem\",\n\t\t\t\t\t\ttype: type === 1 ? \"error\" : \"warning\",\n\t\t\t\t\t});\n\t\t\t\t\tlogLspInfo(`[LSP:${server.id}] ${message}`);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// For info/log messages, use status bar briefly\n\t\t\t\tlspStatusBar.show({\n\t\t\t\t\tmessage: cleanMessage(message, 80),\n\t\t\t\t\ttitle: serverLabel,\n\t\t\t\t\ttype: \"info\",\n\t\t\t\t\ticon: type === 4 ? \"autorenew\" : \"info\",\n\t\t\t\t\tduration: 5000,\n\t\t\t\t});\n\t\t\t\tlogLspInfo(`[LSP:${server.id}] ${message}`);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\t\"$/progress\": (_client: LSPClient, params: unknown): boolean => {\n\t\t\t\tinterface ProgressParams {\n\t\t\t\t\ttoken?: string | number;\n\t\t\t\t\tvalue?: {\n\t\t\t\t\t\tkind?: \"begin\" | \"report\" | \"end\";\n\t\t\t\t\t\ttitle?: string;\n\t\t\t\t\t\tmessage?: string;\n\t\t\t\t\t\tpercentage?: number;\n\t\t\t\t\t\tcancellable?: boolean;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tconst progressParams = params as ProgressParams;\n\t\t\t\tif (!progressParams?.value) return false;\n\n\t\t\t\tconst { kind, title, message, percentage } = progressParams.value;\n\t\t\t\tconst displayTitle = title || server.label || server.id;\n\t\t\t\t// Use server ID + token as unique status ID for concurrent progress tracking\n\t\t\t\tconst progressToken = progressParams.token;\n\t\t\t\tconst statusId = `${server.id}-progress-${progressToken ?? \"default\"}`;\n\n\t\t\t\tif (kind === \"begin\") {\n\t\t\t\t\tlspStatusBar.show({\n\t\t\t\t\t\tid: statusId,\n\t\t\t\t\t\tmessage: message || title || \"Starting...\",\n\t\t\t\t\t\ttitle: displayTitle,\n\t\t\t\t\t\ttype: \"info\",\n\t\t\t\t\t\ticon: \"autorenew\",\n\t\t\t\t\t\tduration: false,\n\t\t\t\t\t\tshowProgress: typeof percentage === \"number\",\n\t\t\t\t\t\tprogress: percentage,\n\t\t\t\t\t});\n\t\t\t\t} else if (kind === \"report\") {\n\t\t\t\t\tlspStatusBar.update({\n\t\t\t\t\t\tid: statusId,\n\t\t\t\t\t\tmessage: message,\n\t\t\t\t\t\tprogress: percentage,\n\t\t\t\t\t});\n\t\t\t\t} else if (kind === \"end\") {\n\t\t\t\t\t// Just hide the progress item silently, no \"Complete\" message\n\t\t\t\t\tlspStatusBar.hideById(statusId);\n\t\t\t\t}\n\n\t\t\t\tlogLspInfo(\n\t\t\t\t\t`[LSP:${server.id}] Progress: ${kind} - ${message || title || \"\"} ${typeof percentage === \"number\" ? `(${percentage}%)` : \"\"}`,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\t\"$/typescriptVersion\": (_client: LSPClient, params: unknown): boolean => {\n\t\t\t\tinterface TypeScriptVersionParams {\n\t\t\t\t\tversion?: string;\n\t\t\t\t\tsource?: string;\n\t\t\t\t}\n\t\t\t\tconst versionParams = params as TypeScriptVersionParams;\n\t\t\t\tif (!versionParams?.version) return false;\n\n\t\t\t\tconst serverLabel = server.label || server.id;\n\t\t\t\tconst source = versionParams.source || \"bundled\";\n\t\t\t\tlogLspInfo(\n\t\t\t\t\t`[LSP:${server.id}] TypeScript ${versionParams.version} (${source})`,\n\t\t\t\t);\n\n\t\t\t\t// Show briefly in status bar\n\t\t\t\tlspStatusBar.show({\n\t\t\t\t\tmessage: `TypeScript ${versionParams.version}`,\n\t\t\t\t\ttitle: serverLabel,\n\t\t\t\t\ttype: \"info\",\n\t\t\t\t\ticon: \"code\",\n\t\t\t\t\tduration: 3000,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t},\n\t\t};\n\n\t\t// Log unhandled notifications to help debug what servers are sending\n\t\tconst unhandledNotificationKey =\n\t\t\t\"unhandledNotification\" as keyof typeof clientConfig;\n\t\tif (!(unhandledNotificationKey in clientConfig)) {\n\t\t\t(\n\t\t\t\tclientConfig as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\t(client: LSPClient, method: string, params: unknown) => void\n\t\t\t\t>\n\t\t\t).unhandledNotification = (\n\t\t\t\t_client: LSPClient,\n\t\t\t\tmethod: string,\n\t\t\t\tparams: unknown,\n\t\t\t) => {\n\t\t\t\tlogLspInfo(\n\t\t\t\t\t`[LSP:${server.id}] Unhandled notification: ${method}`,\n\t\t\t\t\tparams,\n\t\t\t\t);\n\t\t\t};\n\t\t}\n\n\t\tif (!clientConfig.workspace) {\n\t\t\tclientConfig.workspace = (client: LSPClient) =>\n\t\t\t\tnew AcodeWorkspace(client, workspaceOptions);\n\t\t}\n\n\t\tif (normalizedRootUri && !clientConfig.rootUri) {\n\t\t\tclientConfig.rootUri = normalizedRootUri;\n\t\t}\n\n\t\tif (!normalizedRootUri && clientConfig.rootUri) {\n\t\t\tdelete clientConfig.rootUri;\n\t\t}\n\n\t\tif (server.startupTimeout && !clientConfig.timeout) {\n\t\t\tclientConfig.timeout = server.startupTimeout;\n\t\t}\n\n\t\tlet transportHandle: TransportHandle | undefined;\n\t\tlet client: ExtendedLSPClient | undefined;\n\n\t\ttry {\n\t\t\t// Get session from server ID for auto-port discovery\n\t\t\tconst session = server.id;\n\t\t\tconst serverResult = await ensureServerRunning(server, session);\n\n\t\t\t// Use discovered port if available (for auto-port discovery)\n\t\t\tconst dynamicPort = serverResult.discoveredPort;\n\n\t\t\ttransportHandle = createTransport(server, {\n\t\t\t\t...context,\n\t\t\t\trootUri: normalizedRootUri ?? null,\n\t\t\t\toriginalRootUri: originalRootUri ?? undefined,\n\t\t\t\tdynamicPort,\n\t\t\t});\n\t\t\tawait transportHandle.ready;\n\t\t\tclient = new LSPClient(clientConfig) as ExtendedLSPClient;\n\t\t\tconnectClient(client, transportHandle.transport, initializationOptions);\n\t\t\tawait client.initializing;\n\t\t\tif (!client.__acodeLoggedInfo) {\n\t\t\t\t// Log root URI info to console\n\t\t\t\tif (normalizedRootUri) {\n\t\t\t\t\tif (originalRootUri && originalRootUri !== normalizedRootUri) {\n\t\t\t\t\t\tlogLspInfo(\n\t\t\t\t\t\t\t`[LSP:${server.id}] root ${normalizedRootUri} (from ${originalRootUri})`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlogLspInfo(`[LSP:${server.id}] root`, normalizedRootUri);\n\t\t\t\t\t}\n\t\t\t\t} else if (originalRootUri) {\n\t\t\t\t\tlogLspInfo(`[LSP:${server.id}] root ignored`, originalRootUri);\n\t\t\t\t}\n\t\t\t\tif (initializationOptions) {\n\t\t\t\t\tlogLspInfo(\n\t\t\t\t\t\t`[LSP:${server.id}] initializationOptions keys`,\n\t\t\t\t\t\tObject.keys(initializationOptions),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlogLspInfo(`[LSP:${server.id}] initialized`);\n\t\t\t\tclient.__acodeLoggedInfo = true;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\ttransportHandle?.dispose?.();\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst state = this.#createClientState({\n\t\t\tkey,\n\t\t\tserver,\n\t\t\tclient,\n\t\t\ttransportHandle,\n\t\t\tnormalizedRootUri,\n\t\t\toriginalRootUri,\n\t\t});\n\n\t\tthis.#clients.set(key, state);\n\t\treturn state;\n\t}\n\n\t#createClientState(params: {\n\t\tkey: string;\n\t\tserver: LspServerDefinition;\n\t\tclient: LSPClient;\n\t\ttransportHandle: TransportHandle;\n\t\tnormalizedRootUri: string | null;\n\t\toriginalRootUri: string | null;\n\t}): ClientState {\n\t\tconst {\n\t\t\tkey,\n\t\t\tserver,\n\t\t\tclient,\n\t\t\ttransportHandle,\n\t\t\tnormalizedRootUri,\n\t\t\toriginalRootUri,\n\t\t} = params;\n\t\tconst fileRefs = new Map<string, Set<EditorView>>();\n\t\tconst uriAliases = new Map<string, string>();\n\t\tconst effectiveRoot = normalizedRootUri ?? originalRootUri ?? null;\n\n\t\tconst attach = (\n\t\t\turi: string,\n\t\t\tview: EditorView,\n\t\t\taliases: string[] = [],\n\t\t): void => {\n\t\t\tconst existing = fileRefs.get(uri) ?? new Set();\n\t\t\texisting.add(view);\n\t\t\tfileRefs.set(uri, existing);\n\t\t\turiAliases.set(uri, uri);\n\t\t\tfor (const alias of aliases) {\n\t\t\t\tif (!alias || alias === uri) continue;\n\t\t\t\turiAliases.set(alias, uri);\n\t\t\t}\n\t\t\tconst suffix = effectiveRoot ? ` (root ${effectiveRoot})` : \"\";\n\t\t\tlogLspInfo(`[LSP:${server.id}] attached to ${uri}${suffix}`);\n\t\t};\n\n\t\tconst detach = (uri: string, view?: EditorView): void => {\n\t\t\tconst actualUri = uriAliases.get(uri) ?? uri;\n\t\t\tconst existing = fileRefs.get(actualUri);\n\t\t\tif (!existing) return;\n\t\t\tif (view) existing.delete(view);\n\t\t\tif (!view || !existing.size) {\n\t\t\t\tfileRefs.delete(actualUri);\n\t\t\t\tfor (const [alias, target] of uriAliases.entries()) {\n\t\t\t\t\tif (target === actualUri) {\n\t\t\t\t\t\turiAliases.delete(alias);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\t// Only pass uri to closeFile - view is not needed for closing\n\t\t\t\t\t// and passing it may cause issues if the view is already disposed\n\t\t\t\t\t(client.workspace as AcodeWorkspace)?.closeFile?.(actualUri);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(`Failed to close LSP file ${actualUri}`, error);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!fileRefs.size) {\n\t\t\t\tthis.options.onClientIdle?.({\n\t\t\t\t\tserver,\n\t\t\t\t\tclient,\n\t\t\t\t\trootUri: effectiveRoot,\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\n\t\tconst dispose = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tclient.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(`Error disconnecting LSP client ${server.id}`, error);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait transportHandle.dispose?.();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(`Error disposing LSP transport ${server.id}`, error);\n\t\t\t}\n\t\t\tthis.#clients.delete(key);\n\t\t};\n\n\t\treturn {\n\t\t\tserver,\n\t\t\tclient,\n\t\t\ttransport: transportHandle,\n\t\t\trootUri: effectiveRoot,\n\t\t\tattach,\n\t\t\tdetach,\n\t\t\tdispose,\n\t\t};\n\t}\n\n\tasync #resolveRootUri(\n\t\tserver: LspServerDefinition,\n\t\tcontext: RootUriContext,\n\t): Promise<string | null> {\n\t\tif (typeof server.rootUri === \"function\") {\n\t\t\ttry {\n\t\t\t\tconst value = await server.rootUri(context?.uri ?? \"\", context);\n\t\t\t\tif (value) return safeString(value);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(`Server root resolver failed for ${server.id}`, error);\n\t\t\t}\n\t\t}\n\n\t\tif (context?.rootUri) return safeString(context.rootUri);\n\n\t\tif (typeof this.options.resolveRoot === \"function\") {\n\t\t\ttry {\n\t\t\t\tconst value = await this.options.resolveRoot(context);\n\t\t\t\tif (value) return safeString(value);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\"Global LSP root resolver failed\", error);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tasync #resolveDocumentUri(\n\t\tserver: LspServerDefinition,\n\t\tcontext: RootUriContext,\n\t): Promise<string | null> {\n\t\tconst originalUri = context?.uri;\n\t\tif (!originalUri) return null;\n\n\t\tlet normalizedUri = normalizeDocumentUri(originalUri);\n\t\tif (!normalizedUri) {\n\t\t\t// Fall back to cache file path for providers that do not expose a file:// URI.\n\t\t\tconst cacheFile = context.file?.cacheFile;\n\t\t\tif (cacheFile && typeof cacheFile === \"string\") {\n\t\t\t\tnormalizedUri = buildFileUri(cacheFile.replace(/^file:\\/\\//, \"\"));\n\t\t\t\tif (normalizedUri) {\n\t\t\t\t\tconsole.info(\n\t\t\t\t\t\t`LSP using cache path for unrecognized URI: ${originalUri} -> ${normalizedUri}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (typeof server.documentUri === \"function\") {\n\t\t\ttry {\n\t\t\t\tconst value = await server.documentUri(originalUri, {\n\t\t\t\t\t...context,\n\t\t\t\t\tnormalizedUri,\n\t\t\t\t} as DocumentUriContext);\n\t\t\t\tif (value) return safeString(value);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Server document URI resolver failed for ${server.id}`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn normalizedUri;\n\t}\n}\n\ninterface Change {\n\tfrom: number;\n\tto: number;\n\tinsert: string;\n}\n\nfunction applyTextEdits(\n\tplugin: LSPPlugin,\n\tview: EditorView,\n\tedits: TextEdit[],\n): boolean {\n\tconst changes: Change[] = [];\n\tfor (const edit of edits) {\n\t\tif (!edit?.range) continue;\n\t\tlet fromBase: number;\n\t\tlet toBase: number;\n\t\ttry {\n\t\t\tfromBase = plugin.fromPosition(edit.range.start, plugin.syncedDoc);\n\t\t\ttoBase = plugin.fromPosition(edit.range.end, plugin.syncedDoc);\n\t\t} catch (_) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst fromResult = plugin.unsyncedChanges.mapPos(\n\t\t\tfromBase,\n\t\t\t1,\n\t\t\tMapMode.TrackDel,\n\t\t);\n\t\tconst toResult = plugin.unsyncedChanges.mapPos(\n\t\t\ttoBase,\n\t\t\t-1,\n\t\t\tMapMode.TrackDel,\n\t\t);\n\t\tif (fromResult == null || toResult == null) continue;\n\t\tconst insert =\n\t\t\ttypeof edit.newText === \"string\"\n\t\t\t\t? edit.newText.replace(/\\r\\n/g, \"\\n\")\n\t\t\t\t: \"\";\n\t\tchanges.push({ from: fromResult, to: toResult, insert });\n\t}\n\tif (!changes.length) return false;\n\tchanges.sort((a, b) => a.from - b.from || a.to - b.to);\n\tview.dispatch({ changes });\n\treturn true;\n}\n\nfunction buildFormattingOptions(\n\tview: EditorView,\n\toverrides: FormattingOptions = {},\n): FormattingOptions {\n\tconst state = view?.state;\n\tif (!state) return { ...overrides };\n\n\tconst unitValue = state.facet(indentUnit);\n\tconst unit =\n\t\ttypeof unitValue === \"string\" && unitValue.length\n\t\t\t? unitValue\n\t\t\t: String(unitValue ?? \"\\t\");\n\tlet tabSize = getIndentUnit(state);\n\tif (\n\t\ttypeof tabSize !== \"number\" ||\n\t\t!Number.isFinite(tabSize) ||\n\t\ttabSize <= 0\n\t) {\n\t\ttabSize = resolveIndentWidth(unit);\n\t}\n\tconst insertSpaces = !unit.includes(\"\\t\");\n\n\treturn {\n\t\ttabSize,\n\t\tinsertSpaces,\n\t\t...overrides,\n\t};\n}\n\nfunction resolveIndentWidth(unit: string): number {\n\tif (typeof unit !== \"string\" || !unit.length) return 4;\n\tlet width = 0;\n\tfor (const ch of unit) {\n\t\tif (ch === \"\\t\") return 4;\n\t\twidth += 1;\n\t}\n\treturn width || 4;\n}\n\nconst defaultManager = new LspClientManager();\n\nexport default defaultManager;\n\nfunction normalizeRootUriForServer(\n\t_server: LspServerDefinition,\n\trootUri: string | null,\n): NormalizedRootUri {\n\tif (!rootUri || typeof rootUri !== \"string\") {\n\t\treturn { normalizedRootUri: null, originalRootUri: null };\n\t}\n\tconst schemeMatch = /^([a-zA-Z][\\w+\\-.]*):/.exec(rootUri);\n\tconst scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;\n\n\t// Already a file:// URI - use as-is\n\tif (scheme === \"file\") {\n\t\treturn { normalizedRootUri: rootUri, originalRootUri: rootUri };\n\t}\n\n\t// Try to convert content:// URIs to file:// URIs\n\tif (scheme === \"content\") {\n\t\tconst fileUri = contentUriToFileUri(rootUri);\n\t\tif (fileUri) {\n\t\t\treturn { normalizedRootUri: fileUri, originalRootUri: rootUri };\n\t\t}\n\t\t// Can't convert to file:// - server won't work properly\n\t\treturn { normalizedRootUri: null, originalRootUri: rootUri };\n\t}\n\n\t// Unknown scheme - try to use as-is\n\treturn { normalizedRootUri: rootUri, originalRootUri: rootUri };\n}\n\nfunction normalizeDocumentUri(uri: string | null | undefined): string | null {\n\tif (!uri || typeof uri !== \"string\") return null;\n\n\tconst schemeMatch = /^([a-zA-Z][\\w+\\-.]*):/.exec(uri);\n\tconst scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;\n\n\t// Already a file:// URI or untitled use as-is\n\tif (scheme === \"file\" || scheme === \"untitled\") {\n\t\treturn uri;\n\t}\n\n\t// Convert content:// URIs to file:// URIs\n\tif (scheme === \"content\") {\n\t\tconst fileUri = contentUriToFileUri(uri);\n\t\tif (fileUri) {\n\t\t\treturn fileUri;\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Unknown scheme\n\treturn uri;\n}\n\nfunction contentUriToFileUri(uri: string): string | null {\n\ttry {\n\t\tconst parsed = Uri.parse(uri) as ParsedUri | null;\n\t\tif (!parsed || typeof parsed !== \"object\") return null;\n\t\tconst { docId, rootUri, isFileUri } = parsed;\n\t\tif (!docId) return null;\n\n\t\tif (isFileUri && rootUri) {\n\t\t\treturn rootUri;\n\t\t}\n\n\t\tconst providerMatch =\n\t\t\t/^content:\\/\\/com\\.((?![:<>\"/\\\\|?*]).*?)\\.documents\\//.exec(\n\t\t\t\trootUri ?? \"\",\n\t\t\t);\n\t\tconst providerId = providerMatch ? providerMatch[1] : null;\n\n\t\tlet normalized = docId.trim();\n\t\tif (!normalized) return null;\n\n\t\tswitch (providerId) {\n\t\t\tcase \"foxdebug.acode\":\n\t\t\tcase \"foxdebug.acodefree\":\n\t\t\t\tnormalized = normalized.replace(/:+$/, \"\");\n\t\t\t\tif (!normalized) return null;\n\t\t\t\tif (normalized.startsWith(\"raw:/\")) {\n\t\t\t\t\tnormalized = normalized.slice(4);\n\t\t\t\t} else if (normalized.startsWith(\"raw:\")) {\n\t\t\t\t\tnormalized = normalized.slice(4);\n\t\t\t\t}\n\t\t\t\tif (!normalized.startsWith(\"/\")) return null;\n\t\t\t\treturn buildFileUri(normalized);\n\t\t\tcase \"android.externalstorage\":\n\t\t\t\tnormalized = normalized.replace(/:+$/, \"\");\n\t\t\t\tif (!normalized) return null;\n\n\t\t\t\tif (normalized.startsWith(\"/\")) {\n\t\t\t\t\treturn buildFileUri(normalized);\n\t\t\t\t}\n\n\t\t\t\tif (normalized.startsWith(\"raw:/\")) {\n\t\t\t\t\treturn buildFileUri(normalized.slice(4));\n\t\t\t\t}\n\n\t\t\t\tif (normalized.startsWith(\"raw:\")) {\n\t\t\t\t\treturn buildFileUri(normalized.slice(4));\n\t\t\t\t}\n\n\t\t\t\tconst separator = normalized.indexOf(\":\");\n\t\t\t\tif (separator === -1) return null;\n\n\t\t\t\tconst root = normalized.slice(0, separator);\n\t\t\t\tconst remainder = normalized.slice(separator + 1);\n\t\t\t\tif (!remainder) return null;\n\n\t\t\t\tswitch (root) {\n\t\t\t\t\tcase \"primary\":\n\t\t\t\t\t\treturn buildFileUri(`/storage/emulated/0/${remainder}`);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (/^[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}$/.test(root)) {\n\t\t\t\t\t\t\treturn buildFileUri(`/storage/${root}/${remainder}`);\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t} catch (_) {\n\t\treturn null;\n\t}\n}\n\nfunction buildFileUri(pathname: string): string | null {\n\tif (!pathname) return null;\n\tconst normalized = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n\tconst encoded = encodeURI(normalized).replace(/#/g, \"%23\");\n\treturn `file://${encoded}`;\n}\n"
  },
  {
    "path": "src/cm/lsp/codeActions.ts",
    "content": "import { LSPPlugin } from \"@codemirror/lsp-client\";\nimport { EditorView } from \"@codemirror/view\";\nimport toast from \"components/toast\";\nimport select from \"dialogs/select\";\nimport type {\n\tCodeAction,\n\tCodeActionContext,\n\tCodeActionKind,\n\tCommand,\n\tDiagnostic,\n\tRange as LspRange,\n\tWorkspaceEdit,\n} from \"vscode-languageserver-types\";\nimport type { Position, Range } from \"./types\";\nimport type AcodeWorkspace from \"./workspace\";\n\ntype CodeActionResponse = (CodeAction | Command)[] | null;\n\nconst CODE_ACTION_KINDS = {\n\tQUICK_FIX: \"quickfix\",\n\tREFACTOR: \"refactor\",\n\tREFACTOR_EXTRACT: \"refactor.extract\",\n\tREFACTOR_INLINE: \"refactor.inline\",\n\tREFACTOR_REWRITE: \"refactor.rewrite\",\n\tSOURCE: \"source\",\n\tSOURCE_ORGANIZE_IMPORTS: \"source.organizeImports\",\n\tSOURCE_FIX_ALL: \"source.fixAll\",\n} as const;\n\nconst CODE_ACTION_ICONS: Record<string, string> = {\n\tquickfix: \"build\",\n\trefactor: \"code\",\n\t\"refactor.extract\": \"call_split\",\n\t\"refactor.inline\": \"call_merge\",\n\t\"refactor.rewrite\": \"edit\",\n\tsource: \"settings\",\n\t\"source.organizeImports\": \"sort\",\n\t\"source.fixAll\": \"done_all\",\n};\n\nfunction getCodeActionIcon(kind?: CodeActionKind): string {\n\tif (!kind) return \"icon zap\";\n\tfor (const [prefix, icon] of Object.entries(CODE_ACTION_ICONS)) {\n\t\tif (kind.startsWith(prefix)) return icon;\n\t}\n\treturn \"icon zap\";\n}\n\nfunction formatCodeActionKind(kind?: CodeActionKind): string {\n\tif (!kind) return \"\";\n\treturn kind\n\t\t.split(\".\")\n\t\t.map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n\t\t.join(\" › \");\n}\n\nfunction isCommand(item: CodeAction | Command): item is Command {\n\treturn (\n\t\t\"command\" in item && typeof item.command === \"string\" && !(\"edit\" in item)\n\t);\n}\n\nfunction lspPositionToOffset(\n\tdoc: { line: (n: number) => { from: number } },\n\tpos: Position,\n): number {\n\treturn doc.line(pos.line + 1).from + pos.character;\n}\n\nasync function requestCodeActions(\n\tplugin: LSPPlugin,\n\trange: LspRange,\n\tdiagnostics: Diagnostic[] = [],\n): Promise<CodeActionResponse> {\n\tconst context: CodeActionContext = {\n\t\tdiagnostics,\n\t\ttriggerKind: 1, // CodeActionTriggerKind.Invoked\n\t};\n\n\treturn plugin.client.request<\n\t\t{\n\t\t\ttextDocument: { uri: string };\n\t\t\trange: LspRange;\n\t\t\tcontext: CodeActionContext;\n\t\t},\n\t\tCodeActionResponse\n\t>(\"textDocument/codeAction\", {\n\t\ttextDocument: { uri: plugin.uri },\n\t\trange,\n\t\tcontext,\n\t});\n}\n\nasync function resolveCodeAction(\n\tplugin: LSPPlugin,\n\taction: CodeAction,\n): Promise<CodeAction> {\n\t// If action already has an edit, no need to resolve\n\tif (action.edit) return action;\n\n\tconst capabilities = plugin.client.serverCapabilities;\n\tconst provider = capabilities?.codeActionProvider;\n\tconst supportsResolve =\n\t\ttypeof provider === \"object\" &&\n\t\tprovider !== null &&\n\t\t\"resolveProvider\" in provider &&\n\t\tprovider.resolveProvider === true;\n\n\tif (!supportsResolve) return action;\n\n\t// Resolve to get the edit property (lazy computation per LSP 3.16+)\n\ttry {\n\t\tconst resolved = await plugin.client.request<CodeAction, CodeAction>(\n\t\t\t\"codeAction/resolve\",\n\t\t\taction,\n\t\t);\n\t\treturn resolved ?? action;\n\t} catch (error) {\n\t\tconsole.warn(\"[LSP:CodeAction] Failed to resolve:\", error);\n\t\treturn action;\n\t}\n}\n\nasync function executeCommand(\n\tplugin: LSPPlugin,\n\tcommand: Command,\n): Promise<boolean> {\n\ttry {\n\t\tawait plugin.client.request<\n\t\t\t{ command: string; arguments?: unknown[] },\n\t\t\tunknown\n\t\t>(\"workspace/executeCommand\", {\n\t\t\tcommand: command.command,\n\t\t\targuments: command.arguments,\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\t// -32601 = Method not implemented (expected for some LSP servers)\n\t\tconst lspError = error as { code?: number };\n\t\tif (lspError?.code !== -32601) {\n\t\t\tconsole.warn(\"[LSP:CodeAction] Command execution failed:\", error);\n\t\t}\n\t\treturn false;\n\t}\n}\n\ninterface LspChange {\n\trange: Range;\n\tnewText: string;\n}\n\nasync function applyChangesToFile(\n\tworkspace: AcodeWorkspace,\n\turi: string,\n\tchanges: LspChange[],\n\tmapping: { mapPosition: (uri: string, pos: Position) => number },\n): Promise<boolean> {\n\tconst file = workspace.getFile(uri);\n\tif (file) {\n\t\tconst view = file.getView();\n\t\tif (view) {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: changes.map((c) => ({\n\t\t\t\t\tfrom: mapping.mapPosition(uri, c.range.start),\n\t\t\t\t\tto: mapping.mapPosition(uri, c.range.end),\n\t\t\t\t\tinsert: c.newText,\n\t\t\t\t})),\n\t\t\t\tuserEvent: \"codeAction\",\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tconst displayedView = await workspace.displayFile(uri);\n\tif (!displayedView?.state?.doc) {\n\t\tconsole.warn(`[LSP:CodeAction] Could not open file: ${uri}`);\n\t\treturn false;\n\t}\n\n\tdisplayedView.dispatch({\n\t\tchanges: changes.map((c) => ({\n\t\t\tfrom: lspPositionToOffset(displayedView.state.doc, c.range.start),\n\t\t\tto: lspPositionToOffset(displayedView.state.doc, c.range.end),\n\t\t\tinsert: c.newText,\n\t\t})),\n\t\tuserEvent: \"codeAction\",\n\t});\n\treturn true;\n}\n\nasync function applyWorkspaceEdit(\n\tview: EditorView,\n\tedit: WorkspaceEdit,\n): Promise<boolean> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) return false;\n\n\tconst workspace = plugin.client.workspace as AcodeWorkspace;\n\tif (!workspace) return false;\n\n\tlet filesChanged = 0;\n\n\tconst result = await plugin.client.withMapping(async (mapping) => {\n\t\t// Handle simple changes format\n\t\tif (edit.changes) {\n\t\t\tfor (const uri in edit.changes) {\n\t\t\t\tconst changes = edit.changes[uri] as LspChange[];\n\t\t\t\tif (\n\t\t\t\t\tchanges.length &&\n\t\t\t\t\t(await applyChangesToFile(workspace, uri, changes, mapping))\n\t\t\t\t) {\n\t\t\t\t\tfilesChanged++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle documentChanges format (supports versioned edits)\n\t\tif (edit.documentChanges) {\n\t\t\tfor (const docChange of edit.documentChanges) {\n\t\t\t\tif (\"textDocument\" in docChange && \"edits\" in docChange) {\n\t\t\t\t\tconst uri = docChange.textDocument.uri;\n\t\t\t\t\tconst edits = docChange.edits as LspChange[];\n\t\t\t\t\tif (\n\t\t\t\t\t\tedits.length &&\n\t\t\t\t\t\t(await applyChangesToFile(workspace, uri, edits, mapping))\n\t\t\t\t\t) {\n\t\t\t\t\t\tfilesChanged++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn filesChanged;\n\t});\n\n\treturn (result ?? 0) > 0;\n}\n\n/**\n * Apply a code action following the LSP spec:\n * \"If both edit and command are supplied, first the edit is applied, then the command is executed\"\n */\nasync function applyCodeAction(\n\tview: EditorView,\n\taction: CodeAction,\n): Promise<boolean> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) return false;\n\n\tplugin.client.sync();\n\n\t// Resolve to get the edit if not already present\n\tconst resolved = await resolveCodeAction(plugin, action);\n\tlet success = false;\n\n\t// Step 1: Apply workspace edit if present\n\tif (resolved.edit) {\n\t\tsuccess = await applyWorkspaceEdit(view, resolved.edit);\n\t}\n\n\t// Step 2: Execute command if present (after edit per LSP spec)\n\tif (resolved.command) {\n\t\tconst commandSuccess = await executeCommand(plugin, resolved.command);\n\t\tsuccess = success || commandSuccess;\n\t}\n\n\tplugin.client.sync();\n\treturn success;\n}\n\nexport interface CodeActionItem {\n\ttitle: string;\n\tkind?: CodeActionKind;\n\ticon: string;\n\tisPreferred?: boolean;\n\tdisabled?: boolean;\n\tdisabledReason?: string;\n\taction: CodeAction | Command;\n}\n\nexport async function fetchCodeActions(\n\tview: EditorView,\n): Promise<CodeActionItem[]> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) return [];\n\n\tconst capabilities = plugin.client.serverCapabilities;\n\tif (!capabilities?.codeActionProvider) return [];\n\n\tconst { from, to } = view.state.selection.main;\n\tconst range: LspRange = {\n\t\tstart: plugin.toPosition(from),\n\t\tend: plugin.toPosition(to),\n\t};\n\n\tplugin.client.sync();\n\n\ttry {\n\t\tconst response = await requestCodeActions(plugin, range);\n\t\tif (!response?.length) return [];\n\n\t\tconst items: CodeActionItem[] = response.map((item) => {\n\t\t\tif (isCommand(item)) {\n\t\t\t\treturn { title: item.title, icon: \"terminal\", action: item };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttitle: item.title,\n\t\t\t\tkind: item.kind,\n\t\t\t\ticon: getCodeActionIcon(item.kind),\n\t\t\t\tisPreferred: item.isPreferred,\n\t\t\t\tdisabled: !!item.disabled,\n\t\t\t\tdisabledReason: item.disabled?.reason,\n\t\t\t\taction: item,\n\t\t\t};\n\t\t});\n\n\t\t// Sort: preferred first, then quickfixes, then alphabetically\n\t\titems.sort((a, b) => {\n\t\t\tif (a.isPreferred && !b.isPreferred) return -1;\n\t\t\tif (!a.isPreferred && b.isPreferred) return 1;\n\t\t\tif (a.kind?.startsWith(\"quickfix\") && !b.kind?.startsWith(\"quickfix\"))\n\t\t\t\treturn -1;\n\t\t\tif (!a.kind?.startsWith(\"quickfix\") && b.kind?.startsWith(\"quickfix\"))\n\t\t\t\treturn 1;\n\t\t\treturn a.title.localeCompare(b.title);\n\t\t});\n\n\t\treturn items;\n\t} catch (error) {\n\t\tconsole.error(\"[LSP:CodeAction] Failed to fetch:\", error);\n\t\treturn [];\n\t}\n}\n\nexport async function executeCodeAction(\n\tview: EditorView,\n\titem: CodeActionItem,\n): Promise<boolean> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) return false;\n\n\ttry {\n\t\tplugin.client.sync();\n\n\t\t// Handle standalone Command (not CodeAction)\n\t\tif (isCommand(item.action)) {\n\t\t\treturn executeCommand(plugin, item.action);\n\t\t}\n\n\t\t// Handle CodeAction\n\t\treturn applyCodeAction(view, item.action);\n\t} catch (error) {\n\t\tconsole.error(\"[LSP:CodeAction] Failed to execute:\", error);\n\t\treturn false;\n\t}\n}\n\nexport function supportsCodeActions(view: EditorView): boolean {\n\tconst plugin = LSPPlugin.get(view);\n\treturn !!plugin?.client.serverCapabilities?.codeActionProvider;\n}\n\nexport async function showCodeActionsMenu(view: EditorView): Promise<boolean> {\n\tif (!supportsCodeActions(view)) return false;\n\n\tconst items = await fetchCodeActions(view);\n\tif (!items.length) {\n\t\ttoast(\"No code actions available\");\n\t\treturn false;\n\t}\n\n\tconst selectItems = items.map((item, i) => ({\n\t\tvalue: String(i),\n\t\ttext: item.title,\n\t\ticon: item.icon,\n\t\tdisabled: item.disabled,\n\t}));\n\n\ttry {\n\t\tconst result = await select(\n\t\t\tstrings[\"code actions\"] || \"Code Actions\",\n\t\t\tselectItems as unknown as string[],\n\t\t\t{ hideOnSelect: true },\n\t\t);\n\n\t\tif (result !== null && result !== undefined) {\n\t\t\tconst index = Number.parseInt(String(result), 10);\n\t\t\tif (!Number.isNaN(index) && index >= 0 && index < items.length) {\n\t\t\t\tawait executeCodeAction(view, items[index]);\n\t\t\t\tview.focus();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// User cancelled selection\n\t}\n\n\tview.focus();\n\treturn false;\n}\n\nexport async function performQuickFix(view: EditorView): Promise<boolean> {\n\tconst items = await fetchCodeActions(view);\n\tif (!items.length) return false;\n\n\t// Find preferred action or first quickfix\n\tconst quickFix =\n\t\titems.find((i) => i.isPreferred) ??\n\t\titems.find((i) => i.kind?.startsWith(\"quickfix\"));\n\n\tif (quickFix) {\n\t\treturn executeCodeAction(view, quickFix);\n\t}\n\n\t// Fall back to showing menu\n\treturn showCodeActionsMenu(view);\n}\n\nexport { CODE_ACTION_KINDS, formatCodeActionKind, getCodeActionIcon };\n"
  },
  {
    "path": "src/cm/lsp/diagnostics.ts",
    "content": "import { Diagnostic, linter, lintGutter } from \"@codemirror/lint\";\nimport type { LSPClient } from \"@codemirror/lsp-client\";\nimport { LSPPlugin } from \"@codemirror/lsp-client\";\nimport type { Extension } from \"@codemirror/state\";\nimport {\n\tEditorState,\n\tMapMode,\n\tStateEffect,\n\tStateField,\n} from \"@codemirror/state\";\nimport { type EditorView, ViewPlugin } from \"@codemirror/view\";\nimport type {\n\tLSPClientWithWorkspace,\n\tLSPPluginAPI,\n\tLspDiagnostic,\n\tPublishDiagnosticsParams,\n\tRawDiagnostic,\n} from \"./types\";\n\nconst setPublishedDiagnostics = StateEffect.define<LspDiagnostic[]>();\nlet diagnosticsEventTimer: ReturnType<typeof setTimeout> | null = null;\nlet diagnosticsViewCount = 0;\n\nexport const LSP_DIAGNOSTICS_EVENT = \"acode:lsp-diagnostics-updated\";\n\nfunction isCoarsePointerDevice(): boolean {\n\tif (typeof window !== \"undefined\") {\n\t\ttry {\n\t\t\tif (window.matchMedia?.(\"(pointer: coarse)\").matches) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t// Ignore matchMedia failures and fall back to maxTouchPoints.\n\t\t}\n\t}\n\n\treturn (\n\t\ttypeof navigator !== \"undefined\" &&\n\t\tNumber(navigator.maxTouchPoints || 0) > 0\n\t);\n}\n\nfunction emitDiagnosticsUpdated(): void {\n\tif (\n\t\ttypeof document === \"undefined\" ||\n\t\ttypeof document.dispatchEvent !== \"function\"\n\t) {\n\t\treturn;\n\t}\n\n\tlet event: CustomEvent | Event;\n\ttry {\n\t\tevent = new CustomEvent(LSP_DIAGNOSTICS_EVENT);\n\t} catch (_) {\n\t\ttry {\n\t\t\tevent = document.createEvent(\"CustomEvent\");\n\t\t\t(event as CustomEvent).initCustomEvent(\n\t\t\t\tLSP_DIAGNOSTICS_EVENT,\n\t\t\t\tfalse,\n\t\t\t\tfalse,\n\t\t\t\tundefined,\n\t\t\t);\n\t\t} catch (_) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tdocument.dispatchEvent(event);\n}\n\nfunction clearScheduledDiagnosticsUpdated(): void {\n\tif (diagnosticsEventTimer == null) return;\n\tclearTimeout(diagnosticsEventTimer);\n\tdiagnosticsEventTimer = null;\n}\n\nconst lspPublishedDiagnostics = StateField.define<LspDiagnostic[]>({\n\tcreate(): LspDiagnostic[] {\n\t\treturn [];\n\t},\n\tupdate(value: LspDiagnostic[], tr): LspDiagnostic[] {\n\t\tfor (const effect of tr.effects) {\n\t\t\tif (effect.is(setPublishedDiagnostics)) {\n\t\t\t\tvalue = effect.value;\n\t\t\t}\n\t\t}\n\t\treturn value;\n\t},\n});\n\ntype DiagnosticSeverity = \"error\" | \"warning\" | \"info\" | \"hint\";\nconst severities: DiagnosticSeverity[] = [\n\t\"hint\",\n\t\"error\",\n\t\"warning\",\n\t\"info\",\n\t\"hint\",\n];\n\nfunction collectLspDiagnostics(\n\tplugin: LSPPluginAPI,\n\tdiagnostics: RawDiagnostic[],\n): LspDiagnostic[] {\n\tconst items: LspDiagnostic[] = [];\n\tconst { syncedDoc } = plugin;\n\n\tfor (const diagnostic of diagnostics) {\n\t\tlet from: number;\n\t\tlet to: number;\n\t\ttry {\n\t\t\tconst mappedFrom = plugin.fromPosition(\n\t\t\t\tdiagnostic.range.start,\n\t\t\t\tplugin.syncedDoc,\n\t\t\t);\n\t\t\tconst mappedTo = plugin.fromPosition(\n\t\t\t\tdiagnostic.range.end,\n\t\t\t\tplugin.syncedDoc,\n\t\t\t);\n\t\t\tconst fromResult = plugin.unsyncedChanges.mapPos(mappedFrom);\n\t\t\tconst toResult = plugin.unsyncedChanges.mapPos(mappedTo);\n\t\t\tif (fromResult === null || toResult === null) continue;\n\t\t\tfrom = fromResult;\n\t\t\tto = toResult;\n\t\t} catch (_) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (to > syncedDoc.length) continue;\n\n\t\tconst severity = severities[diagnostic.severity ?? 0] ?? \"info\";\n\t\tconst source = diagnostic.code\n\t\t\t? `${diagnostic.source ? `${diagnostic.source}-` : \"\"}${diagnostic.code}`\n\t\t\t: undefined;\n\n\t\titems.push({\n\t\t\tfrom,\n\t\t\tto,\n\t\t\tseverity,\n\t\t\tmessage: diagnostic.message,\n\t\t\tsource,\n\t\t});\n\t}\n\n\treturn items;\n}\n\nfunction storeLspDiagnostics(\n\titems: LspDiagnostic[],\n): StateEffect<LspDiagnostic[]> {\n\treturn setPublishedDiagnostics.of(items);\n}\n\nfunction sameDiagnostics(\n\tcurrent: readonly LspDiagnostic[],\n\tnext: readonly LspDiagnostic[],\n): boolean {\n\tif (current.length !== next.length) return false;\n\tfor (let index = 0; index < current.length; index++) {\n\t\tconst left = current[index];\n\t\tconst right = next[index];\n\t\tif (\n\t\t\tleft.from !== right.from ||\n\t\t\tleft.to !== right.to ||\n\t\t\tleft.severity !== right.severity ||\n\t\t\tleft.message !== right.message ||\n\t\t\tleft.source !== right.source\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nfunction scheduleDiagnosticsUpdated(): void {\n\tif (diagnosticsEventTimer != null) return;\n\tdiagnosticsEventTimer = setTimeout(() => {\n\t\tdiagnosticsEventTimer = null;\n\t\tif (diagnosticsViewCount > 0) {\n\t\t\temitDiagnosticsUpdated();\n\t\t}\n\t}, 32);\n}\n\nconst diagnosticsLifecyclePlugin = ViewPlugin.fromClass(\n\tclass {\n\t\tconstructor() {\n\t\t\tdiagnosticsViewCount++;\n\t\t}\n\n\t\tdestroy(): void {\n\t\t\tdiagnosticsViewCount = Math.max(0, diagnosticsViewCount - 1);\n\t\t\tif (!diagnosticsViewCount) {\n\t\t\t\tclearScheduledDiagnosticsUpdated();\n\t\t\t}\n\t\t}\n\t},\n);\n\nfunction mapDiagnostics(\n\tplugin: LSPPluginAPI,\n\tstate: EditorState,\n): Diagnostic[] {\n\tconst stored = state.field(lspPublishedDiagnostics);\n\tconst changes = plugin.unsyncedChanges;\n\tconst mapped: Diagnostic[] = [];\n\n\tfor (const diagnostic of stored) {\n\t\tlet from: number | null;\n\t\tlet to: number | null;\n\t\ttry {\n\t\t\tfrom = changes.mapPos(diagnostic.from, 1, MapMode.TrackDel);\n\t\t\tto = changes.mapPos(diagnostic.to, -1, MapMode.TrackDel);\n\t\t} catch (_) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (from != null && to != null) {\n\t\t\tmapped.push({ ...diagnostic, from, to });\n\t\t}\n\t}\n\n\treturn mapped;\n}\n\nfunction lspLinterSource(view: EditorView): Diagnostic[] {\n\tconst plugin = LSPPlugin.get(view) as LSPPluginAPI | null;\n\tif (!plugin) return [];\n\treturn mapDiagnostics(plugin, view.state);\n}\n\nexport function lspDiagnosticsClientExtension(): {\n\tclientCapabilities: Record<string, unknown>;\n\tnotificationHandlers: Record<\n\t\tstring,\n\t\t(client: LSPClient, params: PublishDiagnosticsParams) => boolean\n\t>;\n} {\n\treturn {\n\t\tclientCapabilities: {\n\t\t\ttextDocument: {\n\t\t\t\tpublishDiagnostics: {\n\t\t\t\t\trelatedInformation: true,\n\t\t\t\t\tcodeDescriptionSupport: true,\n\t\t\t\t\tdataSupport: true,\n\t\t\t\t\tversionSupport: true,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tnotificationHandlers: {\n\t\t\t\"textDocument/publishDiagnostics\": (\n\t\t\t\tclient: LSPClient,\n\t\t\t\tparams: PublishDiagnosticsParams,\n\t\t\t): boolean => {\n\t\t\t\tconst clientWithWorkspace = client as unknown as LSPClientWithWorkspace;\n\t\t\t\tconst file = clientWithWorkspace.workspace.getFile(params.uri);\n\t\t\t\tif (\n\t\t\t\t\t!file ||\n\t\t\t\t\t(params.version != null && params.version !== file.version)\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconst view = file.getView();\n\t\t\t\tif (!view) return true;\n\t\t\t\tconst plugin = LSPPlugin.get(view) as LSPPluginAPI | null;\n\t\t\t\tif (!plugin) return true;\n\n\t\t\t\tconst diagnostics = collectLspDiagnostics(plugin, params.diagnostics);\n\t\t\t\tconst current = view.state.field(lspPublishedDiagnostics, false) ?? [];\n\t\t\t\tif (sameDiagnostics(current, diagnostics)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tview.dispatch({\n\t\t\t\t\teffects: storeLspDiagnostics(diagnostics),\n\t\t\t\t});\n\t\t\t\tscheduleDiagnosticsUpdated();\n\t\t\t\treturn true;\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport function lspDiagnosticsUiExtension(includeGutter = true): Extension[] {\n\tconst diagnosticsMarkerFilter = isCoarsePointerDevice()\n\t\t? () => []\n\t\t: undefined;\n\tconst diagnosticsTooltipFilter = isCoarsePointerDevice()\n\t\t? () => []\n\t\t: undefined;\n\tconst extensions: Extension[] = [\n\t\tdiagnosticsLifecyclePlugin,\n\t\tlspPublishedDiagnostics,\n\t\tlinter(lspLinterSource, {\n\t\t\tneedsRefresh(update) {\n\t\t\t\treturn update.transactions.some((tr) =>\n\t\t\t\t\ttr.effects.some((effect) => effect.is(setPublishedDiagnostics)),\n\t\t\t\t);\n\t\t\t},\n\t\t\tmarkerFilter: diagnosticsMarkerFilter,\n\t\t\ttooltipFilter: diagnosticsTooltipFilter,\n\t\t\t// keep panel closed by default\n\t\t\tautoPanel: false,\n\t\t}),\n\t];\n\tif (includeGutter) {\n\t\textensions.splice(\n\t\t\t1,\n\t\t\t0,\n\t\t\tlintGutter({\n\t\t\t\ttooltipFilter: diagnosticsTooltipFilter,\n\t\t\t}),\n\t\t);\n\t}\n\treturn extensions;\n}\n\ninterface DiagnosticsExtension {\n\tclientCapabilities: Record<string, unknown>;\n\tnotificationHandlers: Record<\n\t\tstring,\n\t\t(client: LSPClient, params: PublishDiagnosticsParams) => boolean\n\t>;\n\teditorExtension: Extension[];\n}\n\nexport function lspDiagnosticsExtension(\n\tincludeGutter = true,\n): DiagnosticsExtension {\n\treturn {\n\t\t...lspDiagnosticsClientExtension(),\n\t\teditorExtension: lspDiagnosticsUiExtension(includeGutter),\n\t};\n}\n\nexport default lspDiagnosticsExtension;\n\nexport function clearDiagnosticsEffect(): StateEffect<LspDiagnostic[]> {\n\treturn setPublishedDiagnostics.of([]);\n}\n\nexport function getLspDiagnostics(state: EditorState | null): LspDiagnostic[] {\n\tif (!state || typeof state.field !== \"function\") return [];\n\ttry {\n\t\tconst stored = state.field(lspPublishedDiagnostics, false);\n\t\tif (!stored || !Array.isArray(stored)) return [];\n\t\treturn stored.map((diagnostic) => ({ ...diagnostic }));\n\t} catch (_) {\n\t\treturn [];\n\t}\n}\n"
  },
  {
    "path": "src/cm/lsp/documentSymbols.ts",
    "content": "/**\n * LSP Document Symbols Extension for CodeMirror\n *\n * Provides document symbol information (functions, classes, variables, etc.) from language servers.\n */\n\nimport { LSPPlugin } from \"@codemirror/lsp-client\";\nimport type { EditorView } from \"@codemirror/view\";\nimport type {\n\tDocumentSymbol,\n\tPosition,\n\tRange,\n\tSymbolInformation,\n\tSymbolKind,\n} from \"vscode-languageserver-types\";\nimport type { LSPPluginAPI } from \"./types\";\n\ninterface DocumentSymbolParams {\n\ttextDocument: { uri: string };\n}\n\nexport interface ProcessedSymbol {\n\tname: string;\n\tkind: SymbolKind;\n\tkindName: string;\n\tdetail?: string;\n\trange: {\n\t\tstartLine: number;\n\t\tstartCharacter: number;\n\t\tendLine: number;\n\t\tendCharacter: number;\n\t};\n\tselectionRange: {\n\t\tstartLine: number;\n\t\tstartCharacter: number;\n\t\tendLine: number;\n\t\tendCharacter: number;\n\t};\n\tchildren?: ProcessedSymbol[];\n\tdepth: number;\n\tcontainerName?: string;\n}\n\nexport interface FlatSymbol {\n\tname: string;\n\tkind: SymbolKind;\n\tkindName: string;\n\tdetail?: string;\n\tline: number;\n\tcharacter: number;\n\tendLine: number;\n\tendCharacter: number;\n\tcontainerName?: string;\n\tdepth: number;\n}\n\nconst SYMBOL_KIND_NAMES: Record<SymbolKind, string> = {\n\t1: \"File\",\n\t2: \"Module\",\n\t3: \"Namespace\",\n\t4: \"Package\",\n\t5: \"Class\",\n\t6: \"Method\",\n\t7: \"Property\",\n\t8: \"Field\",\n\t9: \"Constructor\",\n\t10: \"Enum\",\n\t11: \"Interface\",\n\t12: \"Function\",\n\t13: \"Variable\",\n\t14: \"Constant\",\n\t15: \"String\",\n\t16: \"Number\",\n\t17: \"Boolean\",\n\t18: \"Array\",\n\t19: \"Object\",\n\t20: \"Key\",\n\t21: \"Null\",\n\t22: \"EnumMember\",\n\t23: \"Struct\",\n\t24: \"Event\",\n\t25: \"Operator\",\n\t26: \"TypeParameter\",\n};\n\nconst SYMBOL_KIND_ICONS: Record<SymbolKind, string> = {\n\t1: \"insert_drive_file\",\n\t2: \"view_module\",\n\t3: \"view_module\",\n\t4: \"folder\",\n\t5: \"class\",\n\t6: \"functions\",\n\t7: \"label\",\n\t8: \"label\",\n\t9: \"functions\",\n\t10: \"list\",\n\t11: \"category\",\n\t12: \"functions\",\n\t13: \"code\",\n\t14: \"lock\",\n\t15: \"text_fields\",\n\t16: \"pin\",\n\t17: \"toggle_on\",\n\t18: \"data_array\",\n\t19: \"data_object\",\n\t20: \"key\",\n\t21: \"not_interested\",\n\t22: \"list\",\n\t23: \"data_object\",\n\t24: \"bolt\",\n\t25: \"calculate\",\n\t26: \"text_fields\",\n};\n\nexport function getSymbolKindName(kind: SymbolKind): string {\n\treturn SYMBOL_KIND_NAMES[kind] || \"Unknown\";\n}\n\nexport function getSymbolKindIcon(kind: SymbolKind): string {\n\treturn SYMBOL_KIND_ICONS[kind] || \"code\";\n}\n\nfunction isDocumentSymbol(\n\titem: DocumentSymbol | SymbolInformation,\n): item is DocumentSymbol {\n\treturn \"selectionRange\" in item;\n}\n\nfunction processDocumentSymbol(\n\tsymbol: DocumentSymbol,\n\tdepth = 0,\n\tcontainerName?: string,\n): ProcessedSymbol {\n\tconst processed: ProcessedSymbol = {\n\t\tname: symbol.name,\n\t\tkind: symbol.kind,\n\t\tkindName: getSymbolKindName(symbol.kind),\n\t\tdetail: symbol.detail,\n\t\trange: {\n\t\t\tstartLine: symbol.range.start.line,\n\t\t\tstartCharacter: symbol.range.start.character,\n\t\t\tendLine: symbol.range.end.line,\n\t\t\tendCharacter: symbol.range.end.character,\n\t\t},\n\t\tselectionRange: {\n\t\t\tstartLine: symbol.selectionRange.start.line,\n\t\t\tstartCharacter: symbol.selectionRange.start.character,\n\t\t\tendLine: symbol.selectionRange.end.line,\n\t\t\tendCharacter: symbol.selectionRange.end.character,\n\t\t},\n\t\tdepth,\n\t\tcontainerName,\n\t};\n\n\tif (symbol.children && symbol.children.length > 0) {\n\t\tprocessed.children = symbol.children.map((child) =>\n\t\t\tprocessDocumentSymbol(child, depth + 1, symbol.name),\n\t\t);\n\t}\n\n\treturn processed;\n}\n\nfunction processSymbolInformation(\n\tsymbol: SymbolInformation,\n\tdepth = 0,\n): ProcessedSymbol {\n\treturn {\n\t\tname: symbol.name,\n\t\tkind: symbol.kind,\n\t\tkindName: getSymbolKindName(symbol.kind),\n\t\trange: {\n\t\t\tstartLine: symbol.location.range.start.line,\n\t\t\tstartCharacter: symbol.location.range.start.character,\n\t\t\tendLine: symbol.location.range.end.line,\n\t\t\tendCharacter: symbol.location.range.end.character,\n\t\t},\n\t\tselectionRange: {\n\t\t\tstartLine: symbol.location.range.start.line,\n\t\t\tstartCharacter: symbol.location.range.start.character,\n\t\t\tendLine: symbol.location.range.end.line,\n\t\t\tendCharacter: symbol.location.range.end.character,\n\t\t},\n\t\tcontainerName: symbol.containerName,\n\t\tdepth,\n\t};\n}\n\nfunction flattenSymbols(\n\tsymbols: ProcessedSymbol[],\n\tresult: FlatSymbol[] = [],\n): FlatSymbol[] {\n\tfor (const symbol of symbols) {\n\t\tresult.push({\n\t\t\tname: symbol.name,\n\t\t\tkind: symbol.kind,\n\t\t\tkindName: symbol.kindName,\n\t\t\tdetail: symbol.detail,\n\t\t\tline: symbol.selectionRange.startLine,\n\t\t\tcharacter: symbol.selectionRange.startCharacter,\n\t\t\tendLine: symbol.selectionRange.endLine,\n\t\t\tendCharacter: symbol.selectionRange.endCharacter,\n\t\t\tcontainerName: symbol.containerName,\n\t\t\tdepth: symbol.depth,\n\t\t});\n\n\t\tif (symbol.children) {\n\t\t\tflattenSymbols(symbol.children, result);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport async function fetchDocumentSymbols(\n\tview: EditorView,\n): Promise<ProcessedSymbol[] | null> {\n\tconst plugin = LSPPlugin.get(view) as LSPPluginAPI | null;\n\tif (!plugin) {\n\t\treturn null;\n\t}\n\n\tconst client = plugin.client;\n\tconst capabilities = client.serverCapabilities;\n\n\tif (!capabilities?.documentSymbolProvider) {\n\t\treturn null;\n\t}\n\n\tclient.sync();\n\n\tconst params: DocumentSymbolParams = {\n\t\ttextDocument: { uri: plugin.uri },\n\t};\n\n\ttry {\n\t\tconst response = await client.request<\n\t\t\tDocumentSymbolParams,\n\t\t\t(DocumentSymbol | SymbolInformation)[] | null\n\t\t>(\"textDocument/documentSymbol\", params);\n\n\t\tif (!response || response.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (isDocumentSymbol(response[0])) {\n\t\t\treturn (response as DocumentSymbol[]).map((sym) =>\n\t\t\t\tprocessDocumentSymbol(sym),\n\t\t\t);\n\t\t}\n\n\t\treturn (response as SymbolInformation[]).map((sym) =>\n\t\t\tprocessSymbolInformation(sym),\n\t\t);\n\t} catch (error) {\n\t\tconsole.warn(\"Failed to fetch document symbols:\", error);\n\t\treturn null;\n\t}\n}\n\nexport async function getDocumentSymbolsFlat(\n\tview: EditorView,\n): Promise<FlatSymbol[]> {\n\tconst symbols = await fetchDocumentSymbols(view);\n\tif (!symbols) {\n\t\treturn [];\n\t}\n\n\treturn flattenSymbols(symbols);\n}\n\nexport async function navigateToSymbol(\n\tview: EditorView,\n\tsymbol: FlatSymbol | ProcessedSymbol,\n): Promise<boolean> {\n\ttry {\n\t\tconst doc = view.state.doc;\n\t\tlet targetLine: number;\n\t\tlet targetChar: number;\n\n\t\tif (\"line\" in symbol) {\n\t\t\ttargetLine = symbol.line;\n\t\t\ttargetChar = symbol.character;\n\t\t} else {\n\t\t\ttargetLine = symbol.selectionRange.startLine;\n\t\t\ttargetChar = symbol.selectionRange.startCharacter;\n\t\t}\n\n\t\tconst lineNumber = targetLine + 1;\n\t\tif (lineNumber < 1 || lineNumber > doc.lines) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst line = doc.line(lineNumber);\n\t\tconst pos = Math.min(line.from + targetChar, line.to);\n\n\t\tview.dispatch({\n\t\t\tselection: { anchor: pos },\n\t\t\tscrollIntoView: true,\n\t\t});\n\n\t\tview.focus();\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.warn(\"Failed to navigate to symbol:\", error);\n\t\treturn false;\n\t}\n}\n\nexport function supportsDocumentSymbols(view: EditorView): boolean {\n\tconst plugin = LSPPlugin.get(view) as LSPPluginAPI | null;\n\tif (!plugin?.client.connected) {\n\t\treturn false;\n\t}\n\n\treturn !!plugin.client.serverCapabilities?.documentSymbolProvider;\n}\n\nexport interface DocumentSymbolsResult {\n\tsymbols: ProcessedSymbol[];\n\tflat: FlatSymbol[];\n}\n\nexport async function getDocumentSymbols(\n\tview: EditorView,\n): Promise<DocumentSymbolsResult | null> {\n\tconst symbols = await fetchDocumentSymbols(view);\n\tif (symbols === null) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsymbols,\n\t\tflat: flattenSymbols(symbols),\n\t};\n}\n\nexport { SymbolKind } from \"vscode-languageserver-types\";\n"
  },
  {
    "path": "src/cm/lsp/formatter.ts",
    "content": "import type { EditorView } from \"@codemirror/view\";\nimport { getModes } from \"cm/modelist\";\nimport toast from \"components/toast\";\nimport lspClientManager from \"./clientManager\";\nimport { supportsBuiltinFormatting } from \"./formattingSupport\";\nimport serverRegistry from \"./serverRegistry\";\nimport type { AcodeApi, FileMetadata } from \"./types\";\n\ninterface Mode {\n\tname?: string;\n\textensions?: string;\n}\n\ninterface EditorManagerWithLsp {\n\teditor?: EditorView;\n\tactiveFile?: AcodeFile;\n\tgetLspMetadata?: (file: AcodeFile) => FileMetadata | null;\n}\n\nfunction getActiveMetadata(\n\tmanager: EditorManagerWithLsp | undefined,\n\tfile: AcodeFile | undefined,\n): (FileMetadata & { view?: EditorView }) | null {\n\tif (!manager?.getLspMetadata || !file) return null;\n\tconst metadata = manager.getLspMetadata(file);\n\tif (!metadata) return null;\n\treturn {\n\t\t...metadata,\n\t\tview: manager.editor,\n\t};\n}\n\nexport function registerLspFormatter(acode: AcodeApi): void {\n\tconst languages = new Set<string>();\n\tserverRegistry.listServers().forEach((server) => {\n\t\tif (!supportsBuiltinFormatting(server)) return;\n\t\t(server.languages || []).forEach((lang) => {\n\t\t\tif (lang) languages.add(String(lang));\n\t\t});\n\t});\n\tconst extensions = languages.size\n\t\t? collectFormatterExtensions(languages)\n\t\t: [\"*\"];\n\n\tacode.registerFormatter(\n\t\t\"lsp\",\n\t\textensions,\n\t\tasync () => {\n\t\t\tconst manager = window.editorManager as EditorManagerWithLsp | undefined;\n\t\t\tconst file = manager?.activeFile;\n\t\t\tconst metadata = getActiveMetadata(manager, file);\n\t\t\tif (!metadata) {\n\t\t\t\ttoast(\"LSP formatter unavailable\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tconst languageId = metadata.languageId;\n\t\t\tif (!languageId) {\n\t\t\t\ttoast(\"Unknown language for LSP formatting\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tconst servers = serverRegistry\n\t\t\t\t.getServersForLanguage(languageId)\n\t\t\t\t.filter(supportsBuiltinFormatting);\n\t\t\tif (!servers.length) {\n\t\t\t\ttoast(\"No LSP formatter available\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tconst fullMetadata = {\n\t\t\t\t...metadata,\n\t\t\t\tlanguageName: metadata.languageName || languageId,\n\t\t\t};\n\t\t\tconst success = await lspClientManager.formatDocument(fullMetadata);\n\t\t\tif (!success) {\n\t\t\t\ttoast(\"LSP formatter failed\");\n\t\t\t}\n\t\t\treturn success;\n\t\t},\n\t\t\"Language Server\",\n\t);\n}\n\nfunction collectFormatterExtensions(languages: Set<string>): string[] {\n\tconst extensions = new Set<string>();\n\tconst modeMap = new Map<string, Mode>();\n\n\ttry {\n\t\tconst modes = getModes() as Mode[];\n\t\tmodes.forEach((mode) => {\n\t\t\tconst key = String(mode?.name ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\t\tif (key) modeMap.set(key, mode);\n\t\t});\n\t} catch (_) {\n\t\t// Ignore mode loading errors\n\t}\n\n\tlanguages.forEach((language) => {\n\t\tconst key = String(language ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tif (!key) return;\n\t\textensions.add(key);\n\t\tconst mode = modeMap.get(key);\n\t\tif (!mode?.extensions) return;\n\t\tString(mode.extensions)\n\t\t\t.split(\"|\")\n\t\t\t.forEach((part) => {\n\t\t\t\tconst ext = part.trim();\n\t\t\t\tif (ext && !ext.startsWith(\"^\")) {\n\t\t\t\t\textensions.add(ext);\n\t\t\t\t}\n\t\t\t});\n\t});\n\n\tif (!extensions.size) {\n\t\treturn [\"*\"];\n\t}\n\n\treturn Array.from(extensions);\n}\n"
  },
  {
    "path": "src/cm/lsp/formattingSupport.ts",
    "content": "import type { LspServerDefinition } from \"./types\";\n\nexport function supportsBuiltinFormatting(\n\tserver: LspServerDefinition,\n): boolean {\n\treturn server.clientConfig?.builtinExtensions?.formatting !== false;\n}\n"
  },
  {
    "path": "src/cm/lsp/index.ts",
    "content": "export {\n\tbundles,\n\tdefault as lspApi,\n\tdefineBundle,\n\tdefineServer,\n\tinstallers,\n\tregister,\n\tservers,\n\tupsert,\n} from \"./api\";\nexport { default as clientManager, LspClientManager } from \"./clientManager\";\nexport type { CodeActionItem } from \"./codeActions\";\nexport {\n\tCODE_ACTION_KINDS,\n\texecuteCodeAction,\n\tfetchCodeActions,\n\tformatCodeActionKind,\n\tgetCodeActionIcon,\n\tperformQuickFix,\n\tshowCodeActionsMenu,\n\tsupportsCodeActions,\n} from \"./codeActions\";\nexport {\n\tclearDiagnosticsEffect,\n\tgetLspDiagnostics,\n\tLSP_DIAGNOSTICS_EVENT,\n\tlspDiagnosticsClientExtension,\n\tlspDiagnosticsExtension,\n\tlspDiagnosticsUiExtension,\n} from \"./diagnostics\";\nexport type {\n\tDocumentSymbolsResult,\n\tFlatSymbol,\n\tProcessedSymbol,\n} from \"./documentSymbols\";\nexport {\n\tfetchDocumentSymbols,\n\tgetDocumentSymbols,\n\tgetDocumentSymbolsFlat,\n\tgetSymbolKindIcon,\n\tgetSymbolKindName,\n\tnavigateToSymbol,\n\tSymbolKind,\n\tsupportsDocumentSymbols,\n} from \"./documentSymbols\";\nexport { registerLspFormatter } from \"./formatter\";\nexport type { InlayHintsConfig } from \"./inlayHints\";\nexport {\n\tinlayHintsClientExtension,\n\tinlayHintsEditorExtension,\n\tinlayHintsExtension,\n} from \"./inlayHints\";\nexport {\n\tcloseReferencesPanel,\n\tfindAllReferences,\n\tfindAllReferencesInTab,\n} from \"./references\";\nexport {\n\tacodeRenameExtension,\n\tacodeRenameKeymap,\n\trenameSymbol,\n} from \"./rename\";\nexport {\n\tensureServerRunning,\n\tresetManagedServers,\n\tstopManagedServer,\n} from \"./serverLauncher\";\nexport { default as serverRegistry } from \"./serverRegistry\";\nexport {\n\tnextSignature,\n\tprevSignature,\n\tshowSignatureHelp,\n} from \"./tooltipExtensions\";\nexport { createTransport } from \"./transport\";\n\nexport type {\n\tBuiltinExtensionsConfig,\n\tClientManagerOptions,\n\tClientState,\n\tDiagnosticRelatedInformation,\n\tDocumentUriContext,\n\tFileMetadata,\n\tFormattingOptions,\n\tLSPClientWithWorkspace,\n\tLSPDiagnostic,\n\tLSPFormattingOptions,\n\tLSPPluginAPI,\n\tLspDiagnostic,\n\tLspServerDefinition,\n\tPosition,\n\tRange,\n\tTextEdit,\n\tTransportDescriptor,\n\tTransportHandle,\n\tWorkspaceOptions,\n} from \"./types\";\nexport { default as AcodeWorkspace } from \"./workspace\";\n"
  },
  {
    "path": "src/cm/lsp/inlayHints.ts",
    "content": "/**\n * LSP Inlay Hints Extension for CodeMirror\n *\n * Provides inline hints (type annotations, parameter names, etc.) from language servers.\n */\n\nimport type { LSPClient, LSPClientExtension } from \"@codemirror/lsp-client\";\nimport { LSPPlugin } from \"@codemirror/lsp-client\";\nimport type { Extension, Range } from \"@codemirror/state\";\nimport { RangeSet, StateEffect, StateField } from \"@codemirror/state\";\nimport {\n\tDecoration,\n\ttype DecorationSet,\n\tEditorView,\n\tViewPlugin,\n\ttype ViewUpdate,\n\tWidgetType,\n} from \"@codemirror/view\";\nimport type {\n\tInlayHint,\n\tInlayHintLabelPart,\n\tPosition,\n} from \"vscode-languageserver-types\";\nimport type { LSPPluginAPI } from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface InlayHintParams {\n\ttextDocument: { uri: string };\n\trange: { start: Position; end: Position };\n}\n\ninterface ProcessedHint {\n\tpos: number;\n\tlabel: string;\n\tpaddingLeft?: boolean;\n\tpaddingRight?: boolean;\n\ttooltip?: string;\n}\n\nexport interface InlayHintsConfig {\n\tenabled?: boolean;\n\tdebounceMs?: number;\n\tshowTypes?: boolean;\n\tshowParameters?: boolean;\n\tmaxHints?: number;\n}\n\n// LSP InlayHintKind constants\nconst TYPE_HINT = 1;\nconst PARAM_HINT = 2;\n\n// ============================================================================\n// State\n// ============================================================================\n\nconst setHints = StateEffect.define<ProcessedHint[]>();\n\nconst hintsField = StateField.define<ProcessedHint[]>({\n\tcreate: () => [],\n\tupdate(hints, tr) {\n\t\tfor (const e of tr.effects) {\n\t\t\tif (e.is(setHints)) return e.value;\n\t\t}\n\t\treturn hints;\n\t},\n});\n\n// ============================================================================\n// Widget\n// ============================================================================\n\nclass HintWidget extends WidgetType {\n\tconstructor(\n\t\treadonly label: string,\n\t\treadonly padLeft: boolean,\n\t\treadonly padRight: boolean,\n\t\treadonly tooltip: string | undefined,\n\t) {\n\t\tsuper();\n\t}\n\n\teq(other: HintWidget): boolean {\n\t\treturn (\n\t\t\tthis.label === other.label &&\n\t\t\tthis.padLeft === other.padLeft &&\n\t\t\tthis.padRight === other.padRight\n\t\t);\n\t}\n\n\ttoDOM(): HTMLSpanElement {\n\t\tconst el = document.createElement(\"span\");\n\t\tel.className = `cm-inlay-hint${this.padLeft ? \" cm-inlay-hint-pl\" : \"\"}${this.padRight ? \" cm-inlay-hint-pr\" : \"\"}`;\n\t\tel.textContent = this.label;\n\t\tif (this.tooltip) el.title = this.tooltip;\n\t\treturn el;\n\t}\n\n\tignoreEvent(): boolean {\n\t\treturn true;\n\t}\n}\n\n// ============================================================================\n// Decorations\n// ============================================================================\n\nfunction buildDecos(hints: ProcessedHint[], docLen: number): DecorationSet {\n\tif (!hints.length) return Decoration.none;\n\n\tconst decos: Range<Decoration>[] = [];\n\tfor (const h of hints) {\n\t\tif (h.pos < 0 || h.pos > docLen) continue;\n\t\tdecos.push(\n\t\t\tDecoration.widget({\n\t\t\t\twidget: new HintWidget(\n\t\t\t\t\th.label,\n\t\t\t\t\th.paddingLeft ?? false,\n\t\t\t\t\th.paddingRight ?? false,\n\t\t\t\t\th.tooltip,\n\t\t\t\t),\n\t\t\t\tside: 1,\n\t\t\t}).range(h.pos),\n\t\t);\n\t}\n\treturn RangeSet.of(decos, true);\n}\n\n// ============================================================================\n// Plugin\n// ============================================================================\n\nfunction createPlugin(config: InlayHintsConfig) {\n\tconst delay = config.debounceMs ?? 200;\n\tconst max = config.maxHints ?? 500;\n\tconst showTypes = config.showTypes !== false;\n\tconst showParams = config.showParameters !== false;\n\n\treturn ViewPlugin.fromClass(\n\t\tclass {\n\t\t\tdecorations: DecorationSet = Decoration.none;\n\t\t\ttimer: ReturnType<typeof setTimeout> | null = null;\n\t\t\treqId = 0;\n\n\t\t\tconstructor(private view: EditorView) {\n\t\t\t\tthis.fetch();\n\t\t\t}\n\n\t\t\tupdate(update: ViewUpdate): void {\n\t\t\t\tif (\n\t\t\t\t\tupdate.transactions.some((t) => t.effects.some((e) => e.is(setHints)))\n\t\t\t\t) {\n\t\t\t\t\tthis.decorations = buildDecos(\n\t\t\t\t\t\tupdate.state.field(hintsField, false) ?? [],\n\t\t\t\t\t\tupdate.state.doc.length,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (update.docChanged || update.viewportChanged) {\n\t\t\t\t\tthis.schedule();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tschedule(): void {\n\t\t\t\tif (this.timer) clearTimeout(this.timer);\n\t\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\t\tthis.timer = null;\n\t\t\t\t\tthis.fetch();\n\t\t\t\t}, delay);\n\t\t\t}\n\n\t\t\tasync fetch(): Promise<void> {\n\t\t\t\tconst lsp = LSPPlugin.get(this.view) as LSPPluginAPI | null;\n\t\t\t\tif (!lsp?.client.connected) return;\n\n\t\t\t\tconst caps = lsp.client.serverCapabilities;\n\t\t\t\tif (!caps?.inlayHintProvider) return;\n\n\t\t\t\tlsp.client.sync();\n\t\t\t\tconst id = ++this.reqId;\n\t\t\t\tconst doc = this.view.state.doc;\n\n\t\t\t\t// Visible range with buffer\n\t\t\t\tconst { from, to } = this.view.viewport;\n\t\t\t\tconst buf = 20;\n\t\t\t\tconst startLn = Math.max(1, doc.lineAt(Math.max(0, from)).number - buf);\n\t\t\t\tconst endLn = Math.min(\n\t\t\t\t\tdoc.lines,\n\t\t\t\t\tdoc.lineAt(Math.min(doc.length, to)).number + buf,\n\t\t\t\t);\n\n\t\t\t\ttry {\n\t\t\t\t\tconst hints = await lsp.client.request<\n\t\t\t\t\t\tInlayHintParams,\n\t\t\t\t\t\tInlayHint[] | null\n\t\t\t\t\t>(\"textDocument/inlayHint\", {\n\t\t\t\t\t\ttextDocument: { uri: lsp.uri },\n\t\t\t\t\t\trange: {\n\t\t\t\t\t\t\tstart: lsp.toPosition(doc.line(startLn).from),\n\t\t\t\t\t\t\tend: lsp.toPosition(doc.line(endLn).to),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tif (id !== this.reqId) return;\n\n\t\t\t\t\tconst processed = this.process(lsp, hints ?? [], doc.length);\n\t\t\t\t\tthis.view.dispatch({ effects: setHints.of(processed) });\n\t\t\t\t} catch {\n\t\t\t\t\t// Non-critical - silently ignore\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprocess(\n\t\t\t\tlsp: LSPPluginAPI,\n\t\t\t\thints: InlayHint[],\n\t\t\t\tdocLen: number,\n\t\t\t): ProcessedHint[] {\n\t\t\t\tconst result: ProcessedHint[] = [];\n\n\t\t\t\tfor (const h of hints) {\n\t\t\t\t\tif (h.kind === TYPE_HINT && !showTypes) continue;\n\t\t\t\t\tif (h.kind === PARAM_HINT && !showParams) continue;\n\n\t\t\t\t\tlet pos: number;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tpos = lsp.fromPosition(h.position, lsp.syncedDoc);\n\t\t\t\t\t\tconst mapped = lsp.unsyncedChanges.mapPos(pos);\n\t\t\t\t\t\tif (mapped === null) continue;\n\t\t\t\t\t\tpos = mapped;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (pos < 0 || pos > docLen) continue;\n\n\t\t\t\t\tconst label =\n\t\t\t\t\t\ttypeof h.label === \"string\"\n\t\t\t\t\t\t\t? h.label\n\t\t\t\t\t\t\t: Array.isArray(h.label)\n\t\t\t\t\t\t\t\t? h.label.map((p: InlayHintLabelPart) => p.value).join(\"\")\n\t\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\tif (!label) continue;\n\n\t\t\t\t\tconst tooltip =\n\t\t\t\t\t\ttypeof h.tooltip === \"string\"\n\t\t\t\t\t\t\t? h.tooltip\n\t\t\t\t\t\t\t: h.tooltip &&\n\t\t\t\t\t\t\t\t\ttypeof h.tooltip === \"object\" &&\n\t\t\t\t\t\t\t\t\t\"value\" in h.tooltip\n\t\t\t\t\t\t\t\t? (h.tooltip as { value: string }).value\n\t\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\tresult.push({\n\t\t\t\t\t\tpos,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tpaddingLeft: h.paddingLeft,\n\t\t\t\t\t\tpaddingRight: h.paddingRight,\n\t\t\t\t\t\ttooltip,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (result.length >= max) break;\n\t\t\t\t}\n\n\t\t\t\treturn result.sort((a, b) => a.pos - b.pos);\n\t\t\t}\n\n\t\t\tdestroy(): void {\n\t\t\t\tif (this.timer) clearTimeout(this.timer);\n\t\t\t}\n\t\t},\n\t\t{ decorations: (v) => v.decorations },\n\t);\n}\n\n// ============================================================================\n// Styles\n// ============================================================================\n\nconst styles = EditorView.baseTheme({\n\t\".cm-inlay-hint\": {\n\t\tdisplay: \"inline-block\",\n\t\tfontFamily: \"inherit\",\n\t\tfontSize: \"0.9em\",\n\t\tfontStyle: \"italic\",\n\t\tborderRadius: \"3px\",\n\t\tpadding: \"0 3px\",\n\t\tmargin: \"0 2px\",\n\t\tverticalAlign: \"baseline\",\n\t\tpointerEvents: \"none\",\n\t},\n\t\"&light .cm-inlay-hint\": {\n\t\tcolor: \"#6a737d\",\n\t\tbackgroundColor: \"rgba(27, 31, 35, 0.05)\",\n\t},\n\t\"&dark .cm-inlay-hint\": {\n\t\tcolor: \"#6a9955\",\n\t\tbackgroundColor: \"rgba(255, 255, 255, 0.05)\",\n\t},\n\t\".cm-inlay-hint-pl\": { marginLeft: \"4px\" },\n\t\".cm-inlay-hint-pr\": { marginRight: \"4px\" },\n});\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport function inlayHintsClientExtension(): LSPClientExtension {\n\treturn {\n\t\tclientCapabilities: {\n\t\t\ttextDocument: {\n\t\t\t\tinlayHint: {\n\t\t\t\t\tdynamicRegistration: true,\n\t\t\t\t\tresolveSupport: {\n\t\t\t\t\t\tproperties: [\n\t\t\t\t\t\t\t\"tooltip\",\n\t\t\t\t\t\t\t\"textEdits\",\n\t\t\t\t\t\t\t\"label.tooltip\",\n\t\t\t\t\t\t\t\"label.location\",\n\t\t\t\t\t\t\t\"label.command\",\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport function inlayHintsEditorExtension(\n\tconfig: InlayHintsConfig = {},\n): Extension {\n\tif (config.enabled === false) return [];\n\treturn [hintsField, createPlugin(config), styles];\n}\n\nexport function inlayHintsExtension(\n\tconfig: InlayHintsConfig = {},\n): LSPClientExtension & { editorExtension: Extension } {\n\treturn {\n\t\t...inlayHintsClientExtension(),\n\t\teditorExtension: inlayHintsEditorExtension(config),\n\t};\n}\n\nexport default inlayHintsExtension;\n"
  },
  {
    "path": "src/cm/lsp/installRuntime.ts",
    "content": "function getExecutor(): Executor {\n\tconst executor = (globalThis as unknown as { Executor?: Executor }).Executor;\n\tif (!executor) {\n\t\tthrow new Error(\"Executor plugin is not available\");\n\t}\n\treturn executor;\n}\n\nfunction getBackgroundExecutor(): Executor {\n\tconst executor = getExecutor();\n\treturn executor.BackgroundExecutor ?? executor;\n}\n\nexport function quoteArg(value: unknown): string {\n\tconst str = String(value ?? \"\");\n\tif (!str.length) return \"''\";\n\tif (/^[A-Za-z0-9_@%+=:,./-]+$/.test(str)) return str;\n\treturn `'${str.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatCommand(\n\tcommand: string | string[] | null | undefined,\n): string {\n\tif (Array.isArray(command)) {\n\t\treturn command.map((part) => quoteArg(part)).join(\" \");\n\t}\n\tif (typeof command === \"string\") {\n\t\treturn command.trim();\n\t}\n\treturn \"\";\n}\n\nfunction wrapShellCommand(command: string): string {\n\tconst script = command.trim();\n\treturn `sh -lc ${quoteArg(`set -e\\n${script}`)}`;\n}\n\nexport async function runQuickCommand(command: string): Promise<string> {\n\tconst wrapped = wrapShellCommand(command);\n\treturn getBackgroundExecutor().execute(wrapped, true);\n}\n\nexport async function runForegroundCommand(command: string): Promise<string> {\n\tconst wrapped = wrapShellCommand(command);\n\treturn getExecutor().execute(wrapped, true);\n}\n"
  },
  {
    "path": "src/cm/lsp/installerUtils.ts",
    "content": "const ARCH_ALIASES = {\n\taarch64: [\"aarch64\", \"arm64\", \"arm64-v8a\"],\n\tx86_64: [\"x86_64\", \"amd64\"],\n\tarmv7: [\"armv7\", \"armv7l\", \"armeabi-v7a\"],\n} as const;\n\nexport type NormalizedArch = keyof typeof ARCH_ALIASES;\n\nexport function normalizeArchitecture(arch: string | null | undefined): string {\n\tconst normalized = String(arch || \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\n\tfor (const [canonical, aliases] of Object.entries(ARCH_ALIASES)) {\n\t\tif (aliases.includes(normalized as never)) {\n\t\t\treturn canonical;\n\t\t}\n\t}\n\n\treturn normalized;\n}\n\nexport function getArchitectureMatchers(\n\tassets: Record<string, string> | undefined | null,\n): Array<{ canonicalArch: string; aliases: string[]; asset: string }> {\n\tif (!assets || typeof assets !== \"object\") return [];\n\n\tconst resolved = new Map<string, { aliases: string[]; asset: string }>();\n\tfor (const [rawArch, rawAsset] of Object.entries(assets)) {\n\t\tconst asset = String(rawAsset || \"\").trim();\n\t\tif (!asset) continue;\n\n\t\tconst canonicalArch = normalizeArchitecture(rawArch);\n\t\tif (!canonicalArch) continue;\n\n\t\tconst aliases = (\n\t\t\tARCH_ALIASES[canonicalArch as NormalizedArch] || [canonicalArch]\n\t\t).map((value) => String(value));\n\t\tresolved.set(canonicalArch, { aliases, asset });\n\t}\n\n\treturn Array.from(resolved.entries()).map(([canonicalArch, value]) => ({\n\t\tcanonicalArch,\n\t\taliases: value.aliases,\n\t\tasset: value.asset,\n\t}));\n}\n\nexport function buildShellArchCase(\n\tassets: Record<string, string> | undefined | null,\n\tquote: (value: unknown) => string,\n): string {\n\treturn getArchitectureMatchers(assets)\n\t\t.map(\n\t\t\t({ aliases, asset }) =>\n\t\t\t\t`\\t${aliases.join(\"|\")}) ASSET=${quote(asset)} ;;`,\n\t\t)\n\t\t.join(\"\\n\");\n}\n"
  },
  {
    "path": "src/cm/lsp/providerUtils.ts",
    "content": "import type {\n\tBridgeConfig,\n\tInstallCheckResult,\n\tLauncherInstallConfig,\n\tLspServerBundle,\n\tLspServerManifest,\n\tTransportDescriptor,\n} from \"./types\";\n\nexport interface ManagedServerOptions {\n\tid: string;\n\tlabel: string;\n\tlanguages: string[];\n\tenabled?: boolean;\n\tuseWorkspaceFolders?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\ttransport?: Partial<TransportDescriptor>;\n\tbridge?: Partial<BridgeConfig> | null;\n\tinstaller?: LauncherInstallConfig;\n\tcheckCommand?: string;\n\tversionCommand?: string;\n\tupdateCommand?: string;\n\tuninstallCommand?: string;\n\tstartupTimeout?: number;\n\tinitializationOptions?: Record<string, unknown>;\n\tclientConfig?: LspServerManifest[\"clientConfig\"];\n\tresolveLanguageId?: LspServerManifest[\"resolveLanguageId\"];\n\trootUri?: LspServerManifest[\"rootUri\"];\n\tdocumentUri?: LspServerManifest[\"documentUri\"];\n\tcapabilityOverrides?: Record<string, unknown>;\n}\n\nexport interface BundleHooks {\n\tgetExecutable?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t) => string | null | undefined;\n\tcheckInstallation?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t) => Promise<InstallCheckResult | null | undefined>;\n\tinstallServer?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t\tmode: \"install\" | \"update\" | \"reinstall\",\n\t\toptions?: { promptConfirm?: boolean },\n\t) => Promise<boolean>;\n}\n\nexport function defineBundle(options: {\n\tid: string;\n\tlabel?: string;\n\tservers: LspServerManifest[];\n\thooks?: BundleHooks;\n}): LspServerBundle {\n\tconst { id, label, servers, hooks } = options;\n\treturn {\n\t\tid,\n\t\tlabel,\n\t\tgetServers: () => servers,\n\t\t...hooks,\n\t};\n}\n\nexport function defineServer(options: ManagedServerOptions): LspServerManifest {\n\tconst {\n\t\tid,\n\t\tlabel,\n\t\tlanguages,\n\t\tenabled = true,\n\t\tuseWorkspaceFolders = false,\n\t\tcommand,\n\t\targs,\n\t\ttransport,\n\t\tbridge,\n\t\tinstaller,\n\t\tcheckCommand,\n\t\tversionCommand,\n\t\tupdateCommand,\n\t\tuninstallCommand,\n\t\tstartupTimeout,\n\t\tinitializationOptions,\n\t\tclientConfig,\n\t\tresolveLanguageId,\n\t\trootUri,\n\t\tdocumentUri,\n\t\tcapabilityOverrides,\n\t} = options;\n\n\tconst bridgeCommand = command || bridge?.command;\n\treturn {\n\t\tid,\n\t\tlabel,\n\t\tlanguages,\n\t\tenabled,\n\t\tuseWorkspaceFolders,\n\t\ttransport: {\n\t\t\tkind: \"websocket\",\n\t\t\t...(transport || {}),\n\t\t} as TransportDescriptor,\n\t\tlauncher: {\n\t\t\tcheckCommand,\n\t\t\tversionCommand,\n\t\t\tupdateCommand,\n\t\t\tuninstallCommand,\n\t\t\tinstall: installer,\n\t\t\tbridge: bridgeCommand\n\t\t\t\t? {\n\t\t\t\t\t\tkind: \"axs\",\n\t\t\t\t\t\tcommand: bridgeCommand,\n\t\t\t\t\t\targs: args || bridge?.args,\n\t\t\t\t\t\tport: bridge?.port,\n\t\t\t\t\t\tsession: bridge?.session,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t},\n\t\tstartupTimeout,\n\t\tinitializationOptions,\n\t\tclientConfig,\n\t\tresolveLanguageId,\n\t\trootUri,\n\t\tdocumentUri,\n\t\tcapabilityOverrides,\n\t};\n}\n\nexport const installers = {\n\tapk(options: {\n\t\tpackages: string[];\n\t\texecutable: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"apk\",\n\t\t\tsource: options.source || \"apk\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable,\n\t\t\tpackages: options.packages,\n\t\t};\n\t},\n\tnpm(options: {\n\t\tpackages: string[];\n\t\texecutable: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t\tglobal?: boolean;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"npm\",\n\t\t\tsource: options.source || \"npm\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable,\n\t\t\tpackages: options.packages,\n\t\t\tglobal: options.global,\n\t\t};\n\t},\n\tpip(options: {\n\t\tpackages: string[];\n\t\texecutable: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t\tbreakSystemPackages?: boolean;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"pip\",\n\t\t\tsource: options.source || \"pip\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable,\n\t\t\tpackages: options.packages,\n\t\t\tbreakSystemPackages: options.breakSystemPackages,\n\t\t};\n\t},\n\tcargo(options: {\n\t\tpackages: string[];\n\t\texecutable: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"cargo\",\n\t\t\tsource: options.source || \"cargo\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable,\n\t\t\tpackages: options.packages,\n\t\t};\n\t},\n\tmanual(options: {\n\t\tbinaryPath: string;\n\t\texecutable?: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"manual\",\n\t\t\tsource: options.source || \"manual\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable || options.binaryPath,\n\t\t\tbinaryPath: options.binaryPath,\n\t\t};\n\t},\n\tshell(options: {\n\t\tcommand: string;\n\t\texecutable: string;\n\t\tupdateCommand?: string;\n\t\tuninstallCommand?: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"shell\",\n\t\t\tsource: options.source || \"custom\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable,\n\t\t\tcommand: options.command,\n\t\t\tupdateCommand: options.updateCommand,\n\t\t\tuninstallCommand: options.uninstallCommand,\n\t\t};\n\t},\n\tgithubRelease(options: {\n\t\trepo: string;\n\t\tbinaryPath: string;\n\t\texecutable?: string;\n\t\tassetNames: Record<string, string>;\n\t\textractFile?: string;\n\t\tarchiveType?: \"zip\" | \"binary\";\n\t\tlabel?: string;\n\t\tsource?: string;\n\t}): LauncherInstallConfig {\n\t\treturn {\n\t\t\tkind: \"github-release\",\n\t\t\tsource: options.source || \"github-release\",\n\t\t\tlabel: options.label,\n\t\t\texecutable: options.executable || options.binaryPath,\n\t\t\trepo: options.repo,\n\t\t\tassetNames: options.assetNames,\n\t\t\textractFile: options.extractFile,\n\t\t\tarchiveType: options.archiveType,\n\t\t\tbinaryPath: options.binaryPath,\n\t\t};\n\t},\n};\n"
  },
  {
    "path": "src/cm/lsp/references.ts",
    "content": "import fsOperation from \"fileSystem\";\nimport { LSPPlugin } from \"@codemirror/lsp-client\";\nimport type { EditorView } from \"@codemirror/view\";\nimport {\n\topenReferencesTab,\n\tshowReferencesPanel,\n} from \"components/referencesPanel\";\nimport settings from \"lib/settings\";\n\ninterface Position {\n\tline: number;\n\tcharacter: number;\n}\n\ninterface Range {\n\tstart: Position;\n\tend: Position;\n}\n\ninterface Location {\n\turi: string;\n\trange: Range;\n}\n\ninterface ReferenceWithContext extends Location {\n\tlineText?: string;\n}\n\ninterface ReferenceParams {\n\ttextDocument: { uri: string };\n\tposition: Position;\n\tcontext: { includeDeclaration: boolean };\n}\n\nasync function fetchLineText(uri: string, line: number): Promise<string> {\n\ttry {\n\t\tinterface EditorManagerLike {\n\t\t\tgetFile?: (uri: string, type: string) => EditorFileLike | null;\n\t\t}\n\n\t\tinterface EditorFileLike {\n\t\t\tsession?: {\n\t\t\t\tdoc?: {\n\t\t\t\t\tline?: (n: number) => { text?: string } | null;\n\t\t\t\t\ttoString?: () => string;\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\tconst em = (globalThis as Record<string, unknown>).editorManager as\n\t\t\t| EditorManagerLike\n\t\t\t| undefined;\n\n\t\tconst openFile = em?.getFile?.(uri, \"uri\");\n\t\tif (openFile?.session?.doc) {\n\t\t\tconst doc = openFile.session.doc;\n\t\t\tif (typeof doc.line === \"function\") {\n\t\t\t\tconst lineObj = doc.line(line + 1);\n\t\t\t\tif (lineObj && typeof lineObj.text === \"string\") {\n\t\t\t\t\treturn lineObj.text;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (typeof doc.toString === \"function\") {\n\t\t\t\tconst content = doc.toString();\n\t\t\t\tconst lines = content.split(\"\\n\");\n\t\t\t\tif (lines[line] !== undefined) {\n\t\t\t\t\treturn lines[line];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst fs = fsOperation(uri);\n\t\tif (fs && (await fs.exists())) {\n\t\t\tconst encoding =\n\t\t\t\t(settings as { value?: { defaultFileEncoding?: string } })?.value\n\t\t\t\t\t?.defaultFileEncoding || \"utf-8\";\n\t\t\tconst content = await fs.readFile(encoding);\n\t\t\tif (typeof content === \"string\") {\n\t\t\t\tconst lines = content.split(\"\\n\");\n\t\t\t\tif (lines[line] !== undefined) {\n\t\t\t\t\treturn lines[line];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.warn(`Failed to fetch line text for ${uri}:${line}`, error);\n\t}\n\treturn \"\";\n}\n\nfunction getWordAtCursor(view: EditorView): string {\n\tconst { state } = view;\n\tconst pos = state.selection.main.head;\n\tconst word = state.wordAt(pos);\n\tif (word) {\n\t\treturn state.doc.sliceString(word.from, word.to);\n\t}\n\treturn \"\";\n}\n\nasync function fetchReferences(\n\tview: EditorView,\n): Promise<{ symbolName: string; references: ReferenceWithContext[] } | null> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) {\n\t\treturn null;\n\t}\n\n\tconst client = plugin.client;\n\tconst capabilities = client.serverCapabilities;\n\n\tif (!capabilities?.referencesProvider) {\n\t\tconst toast = (globalThis as Record<string, unknown>).toast as\n\t\t\t| ((msg: string) => void)\n\t\t\t| undefined;\n\t\ttoast?.(\"Language server does not support find references\");\n\t\treturn null;\n\t}\n\n\tconst { state } = view;\n\tconst pos = state.selection.main.head;\n\tconst line = state.doc.lineAt(pos);\n\tconst lineNumber = line.number - 1;\n\tconst character = pos - line.from;\n\tconst uri = plugin.uri;\n\n\tconst symbolName = getWordAtCursor(view);\n\n\tclient.sync();\n\n\tconst params: ReferenceParams = {\n\t\ttextDocument: { uri },\n\t\tposition: { line: lineNumber, character },\n\t\tcontext: { includeDeclaration: true },\n\t};\n\n\tconst locations = await client.request<ReferenceParams, Location[] | null>(\n\t\t\"textDocument/references\",\n\t\tparams,\n\t);\n\n\tif (!locations || locations.length === 0) {\n\t\treturn { symbolName, references: [] };\n\t}\n\n\tconst refsWithContext: ReferenceWithContext[] = await Promise.all(\n\t\tlocations.map(async (loc) => {\n\t\t\tconst lineText = await fetchLineText(loc.uri, loc.range.start.line);\n\t\t\treturn {\n\t\t\t\t...loc,\n\t\t\t\tlineText,\n\t\t\t};\n\t\t}),\n\t);\n\n\treturn { symbolName, references: refsWithContext };\n}\n\nexport async function findAllReferences(view: EditorView): Promise<boolean> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) {\n\t\treturn false;\n\t}\n\n\tconst symbolName = getWordAtCursor(view);\n\tconst panel = showReferencesPanel({ symbolName });\n\n\ttry {\n\t\tconst result = await fetchReferences(view);\n\t\tif (result === null) {\n\t\t\tpanel.setError(\"Failed to fetch references\");\n\t\t\treturn false;\n\t\t}\n\t\tpanel.setReferences(result.references);\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(\"Find references failed:\", error);\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : \"Unknown error occurred\";\n\t\tpanel.setError(errorMessage);\n\t\treturn false;\n\t}\n}\n\nexport async function findAllReferencesInTab(\n\tview: EditorView,\n): Promise<boolean> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) {\n\t\tconst toast = (globalThis as Record<string, unknown>).toast as\n\t\t\t| ((msg: string) => void)\n\t\t\t| undefined;\n\t\ttoast?.(\"Language server not available\");\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst result = await fetchReferences(view);\n\t\tif (result === null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (result.references.length === 0) {\n\t\t\tconst toast = (globalThis as Record<string, unknown>).toast as\n\t\t\t\t| ((msg: string) => void)\n\t\t\t\t| undefined;\n\t\t\ttoast?.(\"No references found\");\n\t\t\treturn true;\n\t\t}\n\n\t\topenReferencesTab({\n\t\t\tsymbolName: result.symbolName,\n\t\t\treferences: result.references,\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(\"Find references in tab failed:\", error);\n\t\treturn false;\n\t}\n}\n\nexport function closeReferencesPanel(): boolean {\n\tconst { hideReferencesPanel } = require(\"components/referencesPanel\");\n\thideReferencesPanel();\n\treturn true;\n}\n"
  },
  {
    "path": "src/cm/lsp/rename.ts",
    "content": "import { LSPPlugin } from \"@codemirror/lsp-client\";\nimport {\n\ttype Command,\n\tEditorView,\n\ttype KeyBinding,\n\tkeymap,\n} from \"@codemirror/view\";\nimport prompt from \"dialogs/prompt\";\nimport type * as lsp from \"vscode-languageserver-protocol\";\nimport type AcodeWorkspace from \"./workspace\";\n\ninterface RenameParams {\n\tnewName: string;\n\tposition: lsp.Position;\n\ttextDocument: { uri: string };\n}\n\ninterface TextDocumentEdit {\n\trange: lsp.Range;\n\tnewText: string;\n}\n\ninterface PrepareRenameResponse {\n\trange?: lsp.Range;\n\tplaceholder?: string;\n\tdefaultBehavior?: boolean;\n}\n\ninterface LspChange {\n\trange: lsp.Range;\n\tnewText: string;\n}\n\nfunction getRename(plugin: LSPPlugin, pos: number, newName: string) {\n\treturn plugin.client.request<RenameParams, lsp.WorkspaceEdit | null>(\n\t\t\"textDocument/rename\",\n\t\t{\n\t\t\tnewName,\n\t\t\tposition: plugin.toPosition(pos),\n\t\t\ttextDocument: { uri: plugin.uri },\n\t\t},\n\t);\n}\n\nfunction getPrepareRename(plugin: LSPPlugin, pos: number) {\n\treturn plugin.client.request<\n\t\t{ position: lsp.Position; textDocument: { uri: string } },\n\t\tPrepareRenameResponse | lsp.Range | null\n\t>(\"textDocument/prepareRename\", {\n\t\tposition: plugin.toPosition(pos),\n\t\ttextDocument: { uri: plugin.uri },\n\t});\n}\n\nasync function performRename(view: EditorView): Promise<boolean> {\n\tconst wordRange = view.state.wordAt(view.state.selection.main.head);\n\tconst plugin = LSPPlugin.get(view);\n\n\tif (!plugin) {\n\t\treturn false;\n\t}\n\n\tconst capabilities = plugin.client.serverCapabilities;\n\tconst renameProvider = capabilities?.renameProvider;\n\n\tif (renameProvider === false || renameProvider === undefined) {\n\t\treturn false;\n\t}\n\n\tif (!wordRange) {\n\t\treturn false;\n\t}\n\n\tconst word = view.state.sliceDoc(wordRange.from, wordRange.to);\n\tlet initialValue = word;\n\tlet canRename = true;\n\n\tconst supportsPrepare =\n\t\ttypeof renameProvider === \"object\" &&\n\t\trenameProvider !== null &&\n\t\t\"prepareProvider\" in renameProvider &&\n\t\trenameProvider.prepareProvider === true;\n\n\tif (supportsPrepare) {\n\t\ttry {\n\t\t\tplugin.client.sync();\n\t\t\tconst prepareResult = await getPrepareRename(plugin, wordRange.from);\n\t\t\tif (prepareResult === null) {\n\t\t\t\tcanRename = false;\n\t\t\t} else if (typeof prepareResult === \"object\" && prepareResult !== null) {\n\t\t\t\tif (\"placeholder\" in prepareResult && prepareResult.placeholder) {\n\t\t\t\t\tinitialValue = prepareResult.placeholder;\n\t\t\t\t} else if (\n\t\t\t\t\t\"defaultBehavior\" in prepareResult &&\n\t\t\t\t\tprepareResult.defaultBehavior\n\t\t\t\t) {\n\t\t\t\t\tinitialValue = word;\n\t\t\t\t} else if (\"start\" in prepareResult && \"end\" in prepareResult) {\n\t\t\t\t\tconst from = plugin.fromPosition(prepareResult.start);\n\t\t\t\t\tconst to = plugin.fromPosition(prepareResult.end);\n\t\t\t\t\tinitialValue = view.state.sliceDoc(from, to);\n\t\t\t\t} else if (\"range\" in prepareResult && prepareResult.range) {\n\t\t\t\t\tconst from = plugin.fromPosition(prepareResult.range.start);\n\t\t\t\t\tconst to = plugin.fromPosition(prepareResult.range.end);\n\t\t\t\t\tinitialValue = view.state.sliceDoc(from, to);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[LSP:Rename] prepareRename failed, using word:\", error);\n\t\t}\n\t}\n\n\tif (!canRename) {\n\t\tconst alert = (await import(\"dialogs/alert\")).default;\n\t\talert(\"Rename\", \"Cannot rename this symbol.\");\n\t\treturn true;\n\t}\n\n\tconst newName = await prompt(\n\t\tstrings[\"new name\"] || \"New name\",\n\t\tinitialValue,\n\t\t\"text\",\n\t\t{\n\t\t\trequired: true,\n\t\t\tplaceholder: strings[\"enter new name\"] || \"Enter new name\",\n\t\t},\n\t);\n\n\tif (newName === null || newName === initialValue) {\n\t\treturn true;\n\t}\n\n\ttry {\n\t\tawait doRename(view, String(newName), wordRange.from);\n\t} catch (error) {\n\t\tconsole.error(\"[LSP:Rename] Rename failed:\", error);\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : \"Failed to rename symbol\";\n\t\tconst alert = (await import(\"dialogs/alert\")).default;\n\t\talert(\"Rename Error\", errorMessage);\n\t}\n\n\treturn true;\n}\n\nfunction lspPositionToOffset(\n\tdoc: { line: (n: number) => { from: number } },\n\tpos: lsp.Position,\n): number {\n\tconst line = doc.line(pos.line + 1);\n\treturn line.from + pos.character;\n}\n\nasync function applyChangesToFile(\n\tworkspace: AcodeWorkspace,\n\turi: string,\n\tlspChanges: LspChange[],\n\tmapping: { mapPosition: (uri: string, pos: lsp.Position) => number },\n): Promise<boolean> {\n\tconst file = workspace.getFile(uri);\n\n\tif (file) {\n\t\tconst view = file.getView();\n\t\tif (view) {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: lspChanges.map((change) => ({\n\t\t\t\t\tfrom: mapping.mapPosition(uri, change.range.start),\n\t\t\t\t\tto: mapping.mapPosition(uri, change.range.end),\n\t\t\t\t\tinsert: change.newText,\n\t\t\t\t})),\n\t\t\t\tuserEvent: \"rename\",\n\t\t\t});\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tconst displayedView = await workspace.displayFile(uri);\n\tif (!displayedView?.state?.doc) {\n\t\tconsole.warn(`[LSP:Rename] Could not open file: ${uri}`);\n\t\treturn false;\n\t}\n\n\tconst doc = displayedView.state.doc;\n\tdisplayedView.dispatch({\n\t\tchanges: lspChanges.map((change) => ({\n\t\t\tfrom: lspPositionToOffset(doc, change.range.start),\n\t\t\tto: lspPositionToOffset(doc, change.range.end),\n\t\t\tinsert: change.newText,\n\t\t})),\n\t\tuserEvent: \"rename\",\n\t});\n\n\treturn true;\n}\n\nasync function doRename(\n\tview: EditorView,\n\tnewName: string,\n\tposition: number,\n): Promise<void> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) return;\n\n\tplugin.client.sync();\n\n\tconst response = await plugin.client.withMapping((mapping) =>\n\t\tgetRename(plugin, position, newName).then((response) => {\n\t\t\tif (!response) return null;\n\t\t\treturn { response, mapping };\n\t\t}),\n\t);\n\n\tif (!response) {\n\t\tconsole.info(\"[LSP:Rename] No changes returned from server\");\n\t\treturn;\n\t}\n\n\tconst { response: workspaceEdit, mapping } = response;\n\tconst workspace = plugin.client.workspace as AcodeWorkspace;\n\tlet filesChanged = 0;\n\n\tif (workspaceEdit.changes) {\n\t\tfor (const uri in workspaceEdit.changes) {\n\t\t\tconst lspChanges = workspaceEdit.changes[uri] as TextDocumentEdit[];\n\t\t\tif (!lspChanges.length) continue;\n\n\t\t\tconst success = await applyChangesToFile(\n\t\t\t\tworkspace,\n\t\t\t\turi,\n\t\t\t\tlspChanges,\n\t\t\t\tmapping,\n\t\t\t);\n\t\t\tif (success) filesChanged++;\n\t\t}\n\t}\n\n\tif (workspaceEdit.documentChanges) {\n\t\tfor (const docChange of workspaceEdit.documentChanges) {\n\t\t\tif (\"textDocument\" in docChange && \"edits\" in docChange) {\n\t\t\t\tconst uri = docChange.textDocument.uri;\n\t\t\t\tconst edits = docChange.edits as TextDocumentEdit[];\n\t\t\t\tif (!edits.length) continue;\n\n\t\t\t\tconst success = await applyChangesToFile(\n\t\t\t\t\tworkspace,\n\t\t\t\t\turi,\n\t\t\t\t\tedits,\n\t\t\t\t\tmapping,\n\t\t\t\t);\n\t\t\t\tif (success) filesChanged++;\n\t\t\t}\n\t\t}\n\t}\n\n\tconsole.info(\n\t\t`[LSP:Rename] Renamed to \"${newName}\" in ${filesChanged} file(s)`,\n\t);\n}\n\nexport const renameSymbol: Command = (view) => {\n\tperformRename(view).catch((error) => {\n\t\tconsole.error(\"[LSP:Rename] Rename command failed:\", error);\n\t});\n\treturn true;\n};\n\nexport const acodeRenameKeymap: readonly KeyBinding[] = [\n\t{ key: \"F2\", run: renameSymbol, preventDefault: true },\n];\n\nexport const acodeRenameExtension = () => keymap.of([...acodeRenameKeymap]);\n"
  },
  {
    "path": "src/cm/lsp/serverCatalog.ts",
    "content": "import { builtinServerBundles } from \"./servers\";\nimport type { LspServerBundle, LspServerManifest } from \"./types\";\n\nfunction toKey(id: string | undefined | null): string {\n\treturn String(id ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\ninterface RegistryAdapter {\n\tregisterServer: (\n\t\tdefinition: LspServerManifest,\n\t\toptions?: { replace?: boolean },\n\t) => unknown;\n\tunregisterServer: (id: string) => boolean;\n}\n\nconst bundles = new Map<string, LspServerBundle>();\nconst bundleServers = new Map<string, Set<string>>();\nconst serverOwners = new Map<string, string>();\n\nlet registryAdapter: RegistryAdapter | null = null;\nlet builtinsRegistered = false;\n\nexport function bindServerRegistry(adapter: RegistryAdapter): void {\n\tregistryAdapter = adapter;\n}\n\nfunction requireRegistry(): RegistryAdapter {\n\tif (!registryAdapter) {\n\t\tthrow new Error(\"LSP server catalog is not bound to the registry\");\n\t}\n\treturn registryAdapter;\n}\n\nfunction resolveBundleServers(bundle: LspServerBundle): LspServerManifest[] {\n\tconst servers = bundle.getServers();\n\treturn Array.isArray(servers) ? servers : [];\n}\n\nexport function registerServerBundle(\n\tbundle: LspServerBundle,\n\toptions: { replace?: boolean } = {},\n): LspServerBundle {\n\tconst { replace = false } = options;\n\tconst key = toKey(bundle.id);\n\tif (!key) {\n\t\tthrow new Error(\"LSP server bundle requires a non-empty id\");\n\t}\n\n\tif (bundles.has(key) && !replace) {\n\t\tconst existing = bundles.get(key);\n\t\tif (existing) return existing;\n\t}\n\n\tconst registry = requireRegistry();\n\tconst definitions = resolveBundleServers(bundle);\n\tconst previousIds = bundleServers.get(key) || new Set<string>();\n\tconst nextIds = new Set<string>();\n\n\tfor (const definition of definitions) {\n\t\tconst serverId = toKey(definition.id);\n\t\tif (!serverId) {\n\t\t\tthrow new Error(`LSP server bundle ${key} returned a server without id`);\n\t\t}\n\n\t\tconst owner = serverOwners.get(serverId);\n\t\tif (owner && owner !== key && !replace) {\n\t\t\tthrow new Error(\n\t\t\t\t`LSP server ${serverId} is already provided by ${owner}; ${key} must replace explicitly`,\n\t\t\t);\n\t\t}\n\n\t\tregistry.registerServer(definition, { replace: true });\n\t\tserverOwners.set(serverId, key);\n\t\tnextIds.add(serverId);\n\t}\n\n\tfor (const previousId of previousIds) {\n\t\tif (!nextIds.has(previousId) && serverOwners.get(previousId) === key) {\n\t\t\tregistry.unregisterServer(previousId);\n\t\t\tserverOwners.delete(previousId);\n\t\t}\n\t}\n\n\tconst normalizedBundle = {\n\t\t...bundle,\n\t\tid: key,\n\t};\n\tbundles.set(key, normalizedBundle);\n\tbundleServers.set(key, nextIds);\n\treturn normalizedBundle;\n}\n\nexport function unregisterServerBundle(id: string): boolean {\n\tconst key = toKey(id);\n\tif (!key || !bundles.has(key)) return false;\n\n\tconst registry = requireRegistry();\n\tfor (const serverId of bundleServers.get(key) || []) {\n\t\tif (serverOwners.get(serverId) === key) {\n\t\t\tregistry.unregisterServer(serverId);\n\t\t\tserverOwners.delete(serverId);\n\t\t}\n\t}\n\n\tbundleServers.delete(key);\n\treturn bundles.delete(key);\n}\n\nexport function listServerBundles(): LspServerBundle[] {\n\treturn Array.from(bundles.values());\n}\n\nexport function getServerBundle(id: string): LspServerBundle | null {\n\tconst owner = serverOwners.get(toKey(id));\n\tif (!owner) return null;\n\treturn bundles.get(owner) || null;\n}\n\nexport function ensureBuiltinBundlesRegistered(): void {\n\tif (builtinsRegistered) return;\n\tbuiltinServerBundles.forEach((bundle) => {\n\t\tregisterServerBundle(bundle, { replace: false });\n\t});\n\tbuiltinsRegistered = true;\n}\n"
  },
  {
    "path": "src/cm/lsp/serverLauncher.ts",
    "content": "import lspStatusBar from \"components/lspStatusBar\";\nimport toast from \"components/toast\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport { buildShellArchCase } from \"./installerUtils\";\nimport {\n\tformatCommand,\n\tquoteArg,\n\trunForegroundCommand,\n\trunQuickCommand,\n} from \"./installRuntime\";\nimport { getServerBundle } from \"./serverCatalog\";\nimport type {\n\tBridgeConfig,\n\tInstallCheckResult,\n\tInstallStatus,\n\tLauncherConfig,\n\tLspServerDefinition,\n\tLspServerStats,\n\tLspServerStatsFormatted,\n\tManagedServerEntry,\n\tPortInfo,\n\tWaitOptions,\n} from \"./types\";\n\nconst managedServers = new Map<string, ManagedServerEntry>();\nconst checkedCommands = new Map<string, InstallStatus>();\nconst pendingInstallChecks = new Map<string, Promise<boolean>>();\nconst announcedServers = new Set<string>();\n\nconst STATUS_PRESENT: InstallStatus = \"present\";\nconst STATUS_DECLINED: InstallStatus = \"declined\";\nconst STATUS_FAILED: InstallStatus = \"failed\";\n\nconst AXS_BINARY = \"$PREFIX/axs\";\n\nfunction getTerminalRequiredMessage(): string {\n\treturn (\n\t\tstrings?.terminal_required_message_for_lsp ??\n\t\t\"Terminal not installed. Please install Terminal first to use LSP servers.\"\n\t);\n}\n\ninterface LspError extends Error {\n\tcode?: string;\n}\n\nfunction getExecutor(): Executor {\n\tconst executor = (globalThis as unknown as { Executor?: Executor }).Executor;\n\tif (!executor) {\n\t\tthrow new Error(\"Executor plugin is not available\");\n\t}\n\treturn executor;\n}\n\n/**\n * Get the background executor\n */\nfunction getBackgroundExecutor(): Executor {\n\tconst executor = getExecutor();\n\treturn executor.BackgroundExecutor ?? executor;\n}\n\nfunction joinCommand(command: string, args: string[] = []): string {\n\tif (!Array.isArray(args) || !args.length) return quoteArg(command);\n\treturn [quoteArg(command), ...args.map((arg) => quoteArg(arg))].join(\" \");\n}\n\nexport { formatCommand } from \"./installRuntime\";\n\n// ============================================================================\n// Auto-Port Discovery\n// ============================================================================\n\n// Cache for the filesDir path\nlet cachedFilesDir: string | null = null;\n\n/**\n * Get the terminal home directory from system.getFilesDir().\n * This is where axs stores port files.\n */\nasync function getTerminalHomeDir(): Promise<string> {\n\tif (cachedFilesDir) {\n\t\treturn `${cachedFilesDir}/alpine/home`;\n\t}\n\n\tconst system = (\n\t\tglobalThis as unknown as {\n\t\t\tsystem?: {\n\t\t\t\tgetFilesDir: (\n\t\t\t\t\tsuccess: (filesDir: string) => void,\n\t\t\t\t\terror: (error: string) => void,\n\t\t\t\t) => void;\n\t\t\t};\n\t\t}\n\t).system;\n\n\tif (!system?.getFilesDir) {\n\t\tthrow new Error(\"System plugin is not available\");\n\t}\n\n\treturn new Promise((resolve, reject) => {\n\t\tsystem.getFilesDir(\n\t\t\t(filesDir: string) => {\n\t\t\t\tcachedFilesDir = filesDir;\n\t\t\t\tresolve(`${filesDir}/alpine/home`);\n\t\t\t},\n\t\t\t(error: string) => reject(new Error(error)),\n\t\t);\n\t});\n}\n\n/**\n * Get the port file path for a given server and session.\n * Port file format: ~/.axs/lsp_ports/{serverName}_{session}\n */\nasync function getPortFilePath(\n\tserverName: string,\n\tsession: string,\n): Promise<string> {\n\tconst homeDir = await getTerminalHomeDir();\n\t// Use just the binary name (not full path), mirroring axs behavior\n\tconst baseName = serverName.split(\"/\").pop() || serverName;\n\treturn `file://${homeDir}/.axs/lsp_ports/${baseName}_${session}`;\n}\n\n/**\n * Read the port from a port file using the filesystem API.\n * Returns null if the file doesn't exist or contains invalid data.\n */\nasync function readPortFromFile(filePath: string): Promise<number | null> {\n\ttry {\n\t\t// Dynamic import to get fsOperation\n\t\tconst { default: fsOperation } = await import(\"fileSystem\");\n\t\tconst fs = fsOperation(filePath);\n\n\t\t// Check if file exists first\n\t\tconst exists = await fs.exists();\n\t\tif (!exists) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Read the file content as text\n\t\tconst content = (await fs.readFile(\"utf-8\")) as string;\n\t\tconst port = Number.parseInt(content.trim(), 10);\n\n\t\tif (!Number.isFinite(port) || port <= 0 || port > 65535) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn port;\n\t} catch {\n\t\t// File doesn't exist or couldn't be read\n\t\treturn null;\n\t}\n}\n\n/**\n * Get the port for a running LSP server from the axs port file.\n * @param serverName - The LSP server binary name (e.g., \"typescript-language-server\")\n * @param session - Session ID for port file naming\n */\nexport async function getLspPort(\n\tserverName: string,\n\tsession: string,\n): Promise<PortInfo | null> {\n\ttry {\n\t\tconst filePath = await getPortFilePath(serverName, session);\n\t\tconst port = await readPortFromFile(filePath);\n\n\t\tif (port === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn { port, filePath, session };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Wait for the server ready signal (when axs prints \"listening on\").\n * The axs proxy writes the port file immediately after binding, then prints the message.\n * So once the signal is received, the port file should be available.\n */\nasync function waitForServerReady(\n\tserverId: string,\n\ttimeout = 10000,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeout;\n\tconst pollInterval = 50;\n\n\twhile (Date.now() < deadline) {\n\t\tif (serverReadySignals.has(serverId)) {\n\t\t\tserverReadySignals.delete(serverId);\n\t\t\treturn true;\n\t\t}\n\t\tawait sleep(pollInterval);\n\t}\n\n\treturn false;\n}\n\n/**\n * Wait for the port file to be available after server signals ready.\n * This is the most efficient approach: wait for ready signal, then read port.\n */\nasync function waitForPort(\n\tserverId: string,\n\tserverName: string,\n\tsession: string,\n\ttimeout = 10000,\n): Promise<PortInfo | null> {\n\t// First, wait for the server to signal it's ready\n\tconst ready = await waitForServerReady(serverId, timeout);\n\n\tif (!ready) {\n\t\tconsole.warn(\n\t\t\t`[LSP:${serverId}] Server did not signal ready within timeout`,\n\t\t);\n\t}\n\n\t// The port file should be available now (axs writes it before printing \"listening on\")\n\t// Read it directly\n\tconst portInfo = await getLspPort(serverName, session);\n\n\tif (!portInfo && ready) {\n\t\t// Server signaled ready but port file not found - retry a few times\n\t\tfor (let i = 0; i < 5; i++) {\n\t\t\tawait sleep(100);\n\t\t\tconst retryPortInfo = await getLspPort(serverName, session);\n\t\t\tif (retryPortInfo) {\n\t\t\t\treturn retryPortInfo;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn portInfo;\n}\n\n/**\n * Quick check if a server is running and connectable.\n * Attempts a fast WebSocket connection test.\n */\nasync function checkServerAlive(url: string, timeout = 1000): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\ttry {\n\t\t\tconst ws = new WebSocket(url);\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\ttry {\n\t\t\t\t\tws.close();\n\t\t\t\t} catch {}\n\t\t\t\tresolve(false);\n\t\t\t}, timeout);\n\n\t\t\tws.onopen = () => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\ttry {\n\t\t\t\t\tws.close();\n\t\t\t\t} catch {}\n\t\t\t\tresolve(true);\n\t\t\t};\n\n\t\t\tws.onerror = () => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(false);\n\t\t\t};\n\n\t\t\tws.onclose = () => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(false);\n\t\t\t};\n\t\t} catch {\n\t\t\tresolve(false);\n\t\t}\n\t});\n}\n\n/**\n * Check if we can reuse an existing server by testing the port.\n * Returns the port number if the server is alive, null otherwise.\n */\nexport async function canReuseExistingServer(\n\tserver: LspServerDefinition,\n\tsession: string,\n): Promise<number | null> {\n\tconst bridge = server.launcher?.bridge;\n\tconst serverName =\n\t\tresolveServerExecutable(server) ||\n\t\tbridge?.command ||\n\t\tserver.launcher?.command ||\n\t\tserver.id;\n\n\tconst portInfo = await getLspPort(serverName, session);\n\tif (!portInfo) {\n\t\treturn null;\n\t}\n\n\tconst url = `ws://127.0.0.1:${portInfo.port}/`;\n\tconst alive = await checkServerAlive(url, 1000);\n\n\tif (alive) {\n\t\tconsole.info(\n\t\t\t`[LSP:${server.id}] Reusing existing server on port ${portInfo.port}`,\n\t\t);\n\t\treturn portInfo.port;\n\t}\n\n\tconsole.info(\n\t\t`[LSP:${server.id}] Found stale port file, will start new server`,\n\t);\n\treturn null;\n}\n\nfunction buildAxsBridgeCommand(\n\tbridge: BridgeConfig | undefined,\n\tcommandOverride?: string | null,\n\tsession?: string,\n): string | null {\n\tif (!bridge || bridge.kind !== \"axs\") return null;\n\n\tconst binary =\n\t\tcommandOverride || bridge.command\n\t\t\t? String(commandOverride || bridge.command)\n\t\t\t: (() => {\n\t\t\t\t\tthrow new Error(\"Bridge requires a command to execute\");\n\t\t\t\t})();\n\tconst args: string[] = Array.isArray(bridge.args)\n\t\t? bridge.args.map((arg) => String(arg))\n\t\t: [];\n\n\t// Use session ID or bridge session or server command as fallback session\n\tconst effectiveSession = session || bridge.session || binary;\n\n\tconst parts = [AXS_BINARY, \"lsp\"];\n\n\t// Add --session flag for port file naming\n\tparts.push(\"--session\", quoteArg(effectiveSession));\n\n\t// Only add --port if explicitly specified\n\tif (\n\t\ttypeof bridge.port === \"number\" &&\n\t\tbridge.port > 0 &&\n\t\tbridge.port <= 65535\n\t) {\n\t\tparts.push(\"--port\", String(bridge.port));\n\t}\n\n\tparts.push(quoteArg(binary));\n\n\tif (args.length) {\n\t\tparts.push(\"--\");\n\t\targs.forEach((arg) => parts.push(quoteArg(arg)));\n\t}\n\treturn parts.join(\" \");\n}\n\nfunction resolveStartCommand(\n\tserver: LspServerDefinition,\n\tsession?: string,\n): string | null {\n\tconst launcher = server.launcher;\n\tif (!launcher) return null;\n\tconst executable = resolveServerExecutable(server);\n\n\tif (launcher.startCommand) {\n\t\treturn formatCommand(launcher.startCommand);\n\t}\n\tif (launcher.command) {\n\t\treturn joinCommand(executable || launcher.command, launcher.args);\n\t}\n\tif (launcher.bridge) {\n\t\treturn buildAxsBridgeCommand(launcher.bridge, executable, session);\n\t}\n\treturn null;\n}\n\nexport function getStartCommand(server: LspServerDefinition): string | null {\n\treturn resolveStartCommand(server);\n}\n\nfunction getInstallCacheKey(server: LspServerDefinition): string | null {\n\tconst checkCommand =\n\t\tserver.launcher?.checkCommand || buildDerivedCheckCommand(server);\n\tif (!checkCommand) return null;\n\treturn `${server.id}:${checkCommand}`;\n}\n\nfunction normalizeInstallSpec(server: LspServerDefinition) {\n\tconst install = server.launcher?.install;\n\tif (!install) return null;\n\n\tconst packages = Array.isArray(install.packages)\n\t\t? install.packages\n\t\t\t\t.map((entry) => String(entry || \"\").trim())\n\t\t\t\t.filter(Boolean)\n\t\t: [];\n\tconst kind =\n\t\tinstall.kind ||\n\t\t(install.binaryPath ? \"manual\" : null) ||\n\t\t(install.source === \"apk\" ? \"apk\" : null) ||\n\t\t(install.source === \"npm\" ? \"npm\" : null) ||\n\t\t(install.source === \"pip\" ? \"pip\" : null) ||\n\t\t(install.source === \"cargo\" ? \"cargo\" : null) ||\n\t\t(install.command ? \"shell\" : null) ||\n\t\t\"shell\";\n\n\treturn {\n\t\t...install,\n\t\tkind,\n\t\tpackages,\n\t\tcommand:\n\t\t\ttypeof install.command === \"string\" && install.command.trim()\n\t\t\t\t? install.command.trim()\n\t\t\t\t: undefined,\n\t\tupdateCommand:\n\t\t\ttypeof install.updateCommand === \"string\" && install.updateCommand.trim()\n\t\t\t\t? install.updateCommand.trim()\n\t\t\t\t: undefined,\n\t\tsource:\n\t\t\tinstall.source ||\n\t\t\t(kind === \"shell\" ? \"custom\" : kind === \"manual\" ? \"manual\" : kind),\n\t\texecutable:\n\t\t\ttypeof install.executable === \"string\" && install.executable.trim()\n\t\t\t\t? install.executable.trim()\n\t\t\t\t: undefined,\n\t\tbinaryPath:\n\t\t\ttypeof install.binaryPath === \"string\" && install.binaryPath.trim()\n\t\t\t\t? install.binaryPath.trim()\n\t\t\t\t: undefined,\n\t\trepo:\n\t\t\ttypeof install.repo === \"string\" && install.repo.trim()\n\t\t\t\t? install.repo.trim()\n\t\t\t\t: undefined,\n\t\tassetNames:\n\t\t\tinstall.assetNames && typeof install.assetNames === \"object\"\n\t\t\t\t? Object.fromEntries(\n\t\t\t\t\t\tObject.entries(install.assetNames)\n\t\t\t\t\t\t\t.map(([key, value]) => [String(key), String(value || \"\").trim()])\n\t\t\t\t\t\t\t.filter(([, value]) => Boolean(value)),\n\t\t\t\t\t)\n\t\t\t\t: {},\n\t\tarchiveType: install.archiveType === \"binary\" ? \"binary\" : \"zip\",\n\t\textractFile:\n\t\t\ttypeof install.extractFile === \"string\" && install.extractFile.trim()\n\t\t\t\t? install.extractFile.trim()\n\t\t\t\t: undefined,\n\t\tnpmCommand:\n\t\t\ttypeof install.npmCommand === \"string\" && install.npmCommand.trim()\n\t\t\t\t? install.npmCommand.trim()\n\t\t\t\t: \"npm\",\n\t\tpipCommand:\n\t\t\ttypeof install.pipCommand === \"string\" && install.pipCommand.trim()\n\t\t\t\t? install.pipCommand.trim()\n\t\t\t\t: \"pip\",\n\t\tpythonCommand:\n\t\t\ttypeof install.pythonCommand === \"string\" && install.pythonCommand.trim()\n\t\t\t\t? install.pythonCommand.trim()\n\t\t\t\t: \"python3\",\n\t\tglobal: install.global !== false,\n\t\tbreakSystemPackages: install.breakSystemPackages !== false,\n\t};\n}\n\nfunction getInstallerExecutable(server: LspServerDefinition): string | null {\n\tconst install = normalizeInstallSpec(server);\n\tif (!install) return null;\n\treturn install.binaryPath || install.executable || null;\n}\n\nfunction getProviderExecutable(server: LspServerDefinition): string | null {\n\tconst bundle = getServerBundle(server.id);\n\tif (!bundle?.getExecutable) return null;\n\ttry {\n\t\treturn bundle.getExecutable(server.id, server) || null;\n\t} catch (error) {\n\t\tconsole.warn(`Failed to resolve bundle executable for ${server.id}`, error);\n\t\treturn null;\n\t}\n}\n\nfunction resolveServerExecutable(server: LspServerDefinition): string | null {\n\treturn (\n\t\tgetProviderExecutable(server) ||\n\t\tgetInstallerExecutable(server) ||\n\t\tserver.launcher?.bridge?.command ||\n\t\tserver.launcher?.command ||\n\t\tnull\n\t);\n}\n\nfunction getInstallLabel(server: LspServerDefinition): string {\n\treturn (\n\t\tnormalizeInstallSpec(server)?.label ||\n\t\tserver.launcher?.install?.label ||\n\t\tserver.label ||\n\t\tserver.id\n\t).trim();\n}\n\nfunction buildUninstallCommand(server: LspServerDefinition): string | null {\n\tconst spec = normalizeInstallSpec(server);\n\tif (!spec) return null;\n\n\tif (spec.uninstallCommand) {\n\t\treturn spec.uninstallCommand;\n\t}\n\tif (server.launcher?.uninstallCommand) {\n\t\treturn server.launcher.uninstallCommand;\n\t}\n\n\tswitch (spec.kind) {\n\t\tcase \"apk\":\n\t\t\treturn spec.packages.length\n\t\t\t\t? `apk del ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`\n\t\t\t\t: null;\n\t\tcase \"npm\": {\n\t\t\tif (!spec.packages.length) return null;\n\t\t\tconst npmCommand = spec.npmCommand || \"npm\";\n\t\t\tconst uninstallFlags =\n\t\t\t\tspec.global !== false ? \"uninstall -g\" : \"uninstall\";\n\t\t\treturn `${npmCommand} ${uninstallFlags} ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`;\n\t\t}\n\t\tcase \"pip\":\n\t\t\treturn spec.packages.length\n\t\t\t\t? `${spec.pipCommand || \"pip\"} uninstall -y ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`\n\t\t\t\t: null;\n\t\tcase \"cargo\":\n\t\t\treturn spec.packages.length\n\t\t\t\t? spec.packages\n\t\t\t\t\t\t.map((entry) => `cargo uninstall ${quoteArg(entry)}`)\n\t\t\t\t\t\t.join(\" && \")\n\t\t\t\t: null;\n\t\tcase \"github-release\":\n\t\tcase \"manual\":\n\t\t\treturn spec.binaryPath ? `rm -f ${quoteArg(spec.binaryPath)}` : null;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction buildInstallCommand(\n\tserver: LspServerDefinition,\n\tmode: \"install\" | \"update\" = \"install\",\n): string | null {\n\tconst spec = normalizeInstallSpec(server);\n\tif (!spec) return null;\n\n\tif (mode === \"update\" && spec.updateCommand) {\n\t\treturn spec.updateCommand;\n\t}\n\n\tswitch (spec.kind) {\n\t\tcase \"apk\":\n\t\t\treturn spec.packages.length\n\t\t\t\t? `apk add --no-cache ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`\n\t\t\t\t: null;\n\t\tcase \"npm\": {\n\t\t\tif (!spec.packages.length) return null;\n\t\t\tconst npmCommand = spec.npmCommand || \"npm\";\n\t\t\tconst installFlags = spec.global !== false ? \"install -g\" : \"install\";\n\t\t\treturn `apk add --no-cache nodejs npm && ${npmCommand} ${installFlags} ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`;\n\t\t}\n\t\tcase \"pip\": {\n\t\t\tif (!spec.packages.length) return null;\n\t\t\tconst pipCommand = spec.pipCommand || \"pip\";\n\t\t\tconst breakPackages =\n\t\t\t\tspec.breakSystemPackages !== false\n\t\t\t\t\t? \"PIP_BREAK_SYSTEM_PACKAGES=1 \"\n\t\t\t\t\t: \"\";\n\t\t\treturn `apk add --no-cache python3 py3-pip && ${breakPackages}${pipCommand} install ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`;\n\t\t}\n\t\tcase \"cargo\":\n\t\t\treturn spec.packages.length\n\t\t\t\t? `apk add --no-cache rust cargo && cargo install ${spec.packages.map((entry) => quoteArg(entry)).join(\" \")}`\n\t\t\t\t: null;\n\t\tcase \"github-release\": {\n\t\t\tif (!spec.repo || !spec.binaryPath) return null;\n\t\t\tconst caseLines = buildShellArchCase(spec.assetNames, quoteArg);\n\t\t\tif (!caseLines) return null;\n\t\t\tconst archivePath = '\"$TMP_DIR/$ASSET\"';\n\t\t\tconst extractedFile = quoteArg(spec.extractFile || \"luau-lsp\");\n\t\t\tconst installTarget = quoteArg(spec.binaryPath);\n\t\t\tconst downloadUrl = `https://github.com/${spec.repo}/releases/latest/download/$ASSET`;\n\n\t\t\tif (spec.archiveType === \"binary\") {\n\t\t\t\treturn `apk add --no-cache curl && ARCH=\"$(uname -m)\" && case \"$ARCH\" in\\n${caseLines}\\n\\t*) echo \"Unsupported architecture: $ARCH\" >&2; exit 1 ;;\\nesac && TMP_DIR=\"$(mktemp -d)\" && cleanup() { rm -rf \"$TMP_DIR\"; } && trap cleanup EXIT && curl -fsSL \"${downloadUrl}\" -o ${archivePath} && install -Dm755 ${archivePath} ${installTarget}`;\n\t\t\t}\n\n\t\t\treturn `apk add --no-cache curl unzip && ARCH=\"$(uname -m)\" && case \"$ARCH\" in\\n${caseLines}\\n\\t*) echo \"Unsupported architecture: $ARCH\" >&2; exit 1 ;;\\nesac && TMP_DIR=\"$(mktemp -d)\" && cleanup() { rm -rf \"$TMP_DIR\"; } && trap cleanup EXIT && curl -fsSL \"${downloadUrl}\" -o ${archivePath} && unzip -oq ${archivePath} -d \"$TMP_DIR\" && install -Dm755 \"$TMP_DIR\"/${extractedFile} ${installTarget}`;\n\t\t}\n\t\tcase \"manual\":\n\t\t\treturn null;\n\t\tdefault:\n\t\t\treturn spec.command || null;\n\t}\n}\n\nfunction buildDerivedCheckCommand(server: LspServerDefinition): string | null {\n\tconst binary = resolveServerExecutable(server)?.trim() || \"\";\n\tconst install = normalizeInstallSpec(server);\n\n\tif (install?.kind === \"manual\" && install.binaryPath) {\n\t\treturn `test -x ${quoteArg(install.binaryPath)}`;\n\t}\n\n\tif (binary.includes(\"/\")) {\n\t\treturn `test -x ${quoteArg(binary)}`;\n\t}\n\n\tif (binary) {\n\t\treturn `which ${quoteArg(binary)}`;\n\t}\n\n\treturn null;\n}\n\nfunction getUpdateCommand(server: LspServerDefinition): string | null {\n\tconst launcher = server.launcher;\n\tif (!launcher) return null;\n\tif (\n\t\ttypeof launcher.updateCommand === \"string\" &&\n\t\tlauncher.updateCommand.trim()\n\t) {\n\t\treturn launcher.updateCommand.trim();\n\t}\n\treturn buildInstallCommand(server, \"update\");\n}\n\nasync function readServerVersion(\n\tserver: LspServerDefinition,\n): Promise<string | null> {\n\tconst command = server.launcher?.versionCommand;\n\tif (!command) return null;\n\n\ttry {\n\t\tconst output = await runQuickCommand(command);\n\t\tconst version = String(output || \"\")\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trim())\n\t\t\t.find(Boolean);\n\t\treturn version || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function getInstallCommand(\n\tserver: LspServerDefinition,\n\tmode: \"install\" | \"update\" = \"install\",\n): string | null {\n\tif (mode === \"update\") {\n\t\treturn getUpdateCommand(server);\n\t}\n\treturn buildInstallCommand(server, \"install\");\n}\n\nexport function getInstallSource(server: LspServerDefinition): string | null {\n\treturn normalizeInstallSpec(server)?.source || null;\n}\n\nexport function getUninstallCommand(\n\tserver: LspServerDefinition,\n): string | null {\n\treturn buildUninstallCommand(server);\n}\n\nexport async function checkServerInstallation(\n\tserver: LspServerDefinition,\n): Promise<InstallCheckResult> {\n\tconst bundle = getServerBundle(server.id);\n\tif (bundle?.checkInstallation) {\n\t\ttry {\n\t\t\tconst result = await bundle.checkInstallation(server.id, server);\n\t\t\tif (result) return result;\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tversion: null,\n\t\t\t\tcanInstall: Boolean(getInstallCommand(server, \"install\")),\n\t\t\t\tcanUpdate: Boolean(getInstallCommand(server, \"update\")),\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t}\n\n\tconst launcher = server.launcher;\n\tconst installCommand = getInstallCommand(server, \"install\");\n\tconst updateCommand = getInstallCommand(server, \"update\");\n\tconst checkCommand =\n\t\tlauncher?.checkCommand || buildDerivedCheckCommand(server);\n\n\tif (!checkCommand) {\n\t\treturn {\n\t\t\tstatus: \"unknown\",\n\t\t\tversion: await readServerVersion(server),\n\t\t\tcanInstall: Boolean(installCommand),\n\t\t\tcanUpdate: Boolean(updateCommand),\n\t\t\tmessage: \"No install check configured for this server.\",\n\t\t};\n\t}\n\n\ttry {\n\t\tawait runQuickCommand(checkCommand);\n\t\treturn {\n\t\t\tstatus: \"present\",\n\t\t\tversion: await readServerVersion(server),\n\t\t\tcanInstall: Boolean(installCommand),\n\t\t\tcanUpdate: Boolean(updateCommand),\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tstatus: installCommand ? \"missing\" : \"failed\",\n\t\t\tversion: null,\n\t\t\tcanInstall: Boolean(installCommand),\n\t\t\tcanUpdate: Boolean(updateCommand),\n\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nexport function resetInstallState(serverId?: string): void {\n\tif (!serverId) {\n\t\tcheckedCommands.clear();\n\t\treturn;\n\t}\n\n\tconst prefix = `${serverId}:`;\n\tfor (const key of Array.from(checkedCommands.keys())) {\n\t\tif (key.startsWith(prefix)) {\n\t\t\tcheckedCommands.delete(key);\n\t\t}\n\t}\n}\n\nasync function ensureInstalled(server: LspServerDefinition): Promise<boolean> {\n\tconst launcher = server.launcher;\n\tconst checkCommand =\n\t\tlauncher?.checkCommand || buildDerivedCheckCommand(server);\n\tif (!checkCommand) return true;\n\n\tconst cacheKey = getInstallCacheKey(server);\n\tif (!cacheKey) return true;\n\n\t// Return cached result if already checked\n\tif (checkedCommands.has(cacheKey)) {\n\t\tconst status = checkedCommands.get(cacheKey);\n\t\tif (status === STATUS_PRESENT) {\n\t\t\treturn true;\n\t\t}\n\t\tif (status === STATUS_DECLINED) {\n\t\t\treturn false;\n\t\t}\n\t\tcheckedCommands.delete(cacheKey);\n\t}\n\n\t// If there's already a pending check for this server, wait for it\n\tif (pendingInstallChecks.has(cacheKey)) {\n\t\tconst pending = pendingInstallChecks.get(cacheKey);\n\t\tif (pending) return pending;\n\t}\n\n\t// Create and track the pending promise\n\tconst checkPromise = performInstallCheck(server, launcher, cacheKey);\n\tpendingInstallChecks.set(cacheKey, checkPromise);\n\n\ttry {\n\t\treturn await checkPromise;\n\t} finally {\n\t\tpendingInstallChecks.delete(cacheKey);\n\t}\n}\n\ninterface LoaderDialog {\n\tshow: () => void;\n\tdestroy: () => void;\n}\n\ntype InstallActionMode = \"install\" | \"update\" | \"reinstall\";\n\nexport async function installServer(\n\tserver: LspServerDefinition,\n\tmode: InstallActionMode = \"install\",\n\toptions: { promptConfirm?: boolean } = {},\n): Promise<boolean> {\n\tconst bundle = getServerBundle(server.id);\n\tif (bundle?.installServer) {\n\t\treturn bundle.installServer(server.id, server, mode, options);\n\t}\n\n\tconst { promptConfirm = true } = options;\n\tconst cacheKey = getInstallCacheKey(server);\n\tconst displayLabel = getInstallLabel(server);\n\tconst isUpdate = mode === \"update\";\n\tconst actionLabel = isUpdate ? \"Update\" : \"Install\";\n\tconst command =\n\t\tmode === \"install\"\n\t\t\t? getInstallCommand(server, \"install\")\n\t\t\t: getUpdateCommand(server);\n\n\tif (!command) {\n\t\tthrow new Error(\n\t\t\t`${displayLabel} has no ${actionLabel.toLowerCase()} command.`,\n\t\t);\n\t}\n\n\tif (promptConfirm) {\n\t\tconst shouldContinue = await confirm(\n\t\t\tdisplayLabel,\n\t\t\t`${actionLabel} ${displayLabel} language server?`,\n\t\t);\n\t\tif (!shouldContinue) {\n\t\t\tif (cacheKey) {\n\t\t\t\tcheckedCommands.set(cacheKey, STATUS_DECLINED);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tlet loadingDialog: LoaderDialog | null = null;\n\ttry {\n\t\tloadingDialog = loader.create(\n\t\t\tdisplayLabel,\n\t\t\t`${actionLabel}ing ${displayLabel}...`,\n\t\t);\n\t\tloadingDialog.show();\n\t\tawait runForegroundCommand(command);\n\t\tresetInstallState(server.id);\n\n\t\tconst result = await checkServerInstallation(server);\n\t\tif (cacheKey && result.status === \"present\") {\n\t\t\tcheckedCommands.set(cacheKey, STATUS_PRESENT);\n\t\t}\n\n\t\ttoast(\n\t\t\tresult.status === \"present\"\n\t\t\t\t? `${displayLabel} ${isUpdate ? \"updated\" : \"installed\"}`\n\t\t\t\t: `${displayLabel} ${actionLabel.toLowerCase()} finished`,\n\t\t);\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(`Failed to ${actionLabel.toLowerCase()} ${server.id}`, error);\n\t\tif (cacheKey) {\n\t\t\tcheckedCommands.set(cacheKey, STATUS_FAILED);\n\t\t}\n\t\ttoast(strings?.error ?? \"Error\");\n\t\tthrow error;\n\t} finally {\n\t\tloadingDialog?.destroy?.();\n\t}\n}\n\nexport async function uninstallServer(\n\tserver: LspServerDefinition,\n\toptions: { promptConfirm?: boolean } = {},\n): Promise<boolean> {\n\tconst bundle = getServerBundle(server.id);\n\tif (bundle?.uninstallServer) {\n\t\treturn bundle.uninstallServer(server.id, server, options);\n\t}\n\n\tconst { promptConfirm = true } = options;\n\tconst cacheKey = getInstallCacheKey(server);\n\tconst displayLabel = getInstallLabel(server);\n\tconst command = getUninstallCommand(server);\n\n\tif (!command) {\n\t\tthrow new Error(`${displayLabel} has no uninstall command.`);\n\t}\n\n\tif (promptConfirm) {\n\t\tconst shouldContinue = await confirm(\n\t\t\tdisplayLabel,\n\t\t\t`Uninstall ${displayLabel} language server?`,\n\t\t);\n\t\tif (!shouldContinue) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tlet loadingDialog: LoaderDialog | null = null;\n\ttry {\n\t\tloadingDialog = loader.create(\n\t\t\tdisplayLabel,\n\t\t\t`Uninstalling ${displayLabel}...`,\n\t\t);\n\t\tloadingDialog.show();\n\t\tawait runForegroundCommand(command);\n\t\tif (cacheKey) {\n\t\t\tcheckedCommands.delete(cacheKey);\n\t\t}\n\t\tresetInstallState(server.id);\n\t\tstopManagedServer(server.id);\n\t\treturn true;\n\t} catch (error) {\n\t\tconsole.error(`Failed to uninstall ${server.id}`, error);\n\t\ttoast(strings?.error ?? \"Error\");\n\t\tthrow error;\n\t} finally {\n\t\tloadingDialog?.destroy();\n\t}\n}\n\nasync function performInstallCheck(\n\tserver: LspServerDefinition,\n\tlauncher: LauncherConfig | undefined,\n\tcacheKey: string,\n): Promise<boolean> {\n\ttry {\n\t\tconst checkCommand =\n\t\t\tlauncher?.checkCommand || buildDerivedCheckCommand(server);\n\t\tif (checkCommand) {\n\t\t\tawait runQuickCommand(checkCommand);\n\t\t}\n\t\tcheckedCommands.set(cacheKey, STATUS_PRESENT);\n\t\treturn true;\n\t} catch (error) {\n\t\tif (!getInstallCommand(server, \"install\")) {\n\t\t\tcheckedCommands.set(cacheKey, STATUS_FAILED);\n\t\t\tconsole.warn(\n\t\t\t\t`LSP server ${server.id} is missing check command result and has no installer.`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst installed = await installServer(server, \"install\", {\n\t\t\tpromptConfirm: true,\n\t\t});\n\t\tif (!installed) {\n\t\t\tcheckedCommands.set(cacheKey, STATUS_DECLINED);\n\t\t\treturn false;\n\t\t}\n\t\tcheckedCommands.set(cacheKey, STATUS_PRESENT);\n\t\treturn true;\n\t}\n}\n\nasync function startInteractiveServer(\n\tcommand: string,\n\tserverId: string,\n): Promise<string> {\n\tconst executor = getExecutor();\n\tconst callback: ExecutorCallback = (type, data) => {\n\t\tif (type === \"stderr\") {\n\t\t\tif (/proot warning/i.test(data)) return;\n\t\t\tconsole.warn(`[LSP:${serverId}] ${data}`);\n\t\t} else if (type === \"stdout\" && data && data.trim()) {\n\t\t\tconsole.info(`[LSP:${serverId}] ${data}`);\n\t\t\t// Detect when the axs proxy signals it's listening\n\t\t\tif (/listening on/i.test(data)) {\n\t\t\t\tsignalServerReady(serverId);\n\t\t\t}\n\t\t}\n\t};\n\tconst uuid = await executor.start(command, callback, true);\n\tmanagedServers.set(serverId, {\n\t\tuuid,\n\t\tcommand,\n\t\tstartedAt: Date.now(),\n\t});\n\treturn uuid;\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Tracks servers that have signaled they're ready (listening)\n * Key: serverId, Value: timestamp when ready\n */\nconst serverReadySignals = new Map<string, number>();\n\n/**\n * Called when stdout contains a \"listening\" message from the axs proxy.\n * This signals that the server is ready to accept connections.\n */\nexport function signalServerReady(serverId: string): void {\n\tserverReadySignals.set(serverId, Date.now());\n}\n\n/**\n * Wait for the LSP server to be ready.\n *\n * This function polls for a ready signal (set when stdout contains \"listening\")\n */\nasync function waitForWebSocket(\n\turl: string,\n\toptions: WaitOptions = {},\n): Promise<void> {\n\tconst {\n\t\tdelay = 100, // Poll interval\n\t\tprobeTimeout = 5000, // Max wait time\n\t} = options;\n\n\t// Extract server ID from URL (e.g., \"ws://127.0.0.1:2090\" -> check by port)\n\tconst portMatch = url.match(/:(\\d+)/);\n\tconst port = portMatch ? portMatch[1] : null;\n\n\t// Find the server ID that's starting on this port\n\tlet targetServerId: string | null = null;\n\tconst entries = Array.from(managedServers.entries());\n\tfor (const [serverId, entry] of entries) {\n\t\tif (\n\t\t\tentry.command.includes(`--port ${port}`) ||\n\t\t\tentry.command.includes(`:${port}`)\n\t\t) {\n\t\t\ttargetServerId = serverId;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst deadline = Date.now() + probeTimeout;\n\n\twhile (Date.now() < deadline) {\n\t\t// Check if we got a ready signal\n\t\tif (targetServerId && serverReadySignals.has(targetServerId)) {\n\t\t\t// Server is ready, clear the signal and return\n\t\t\tserverReadySignals.delete(targetServerId);\n\t\t\treturn;\n\t\t}\n\n\t\tawait sleep(delay);\n\t}\n\n\t// Timeout reached, proceed anyway (transport will retry if needed)\n\tconsole.debug(\n\t\t`[LSP] waitForWebSocket timed out for ${url}, proceeding anyway`,\n\t);\n}\n\nexport interface EnsureServerResult {\n\tuuid: string | null;\n\t/** Port discovered from port file (for auto-port discovery) */\n\tdiscoveredPort?: number;\n}\n\nexport async function ensureServerRunning(\n\tserver: LspServerDefinition,\n\tsession?: string,\n): Promise<EnsureServerResult> {\n\tconst launcher = server.launcher;\n\tif (!launcher) return { uuid: null };\n\n\t// Derive session from server ID if not provided\n\tconst effectiveSession = session || server.id;\n\n\t// Check if server is already running via port file (dead client detection)\n\tconst bridge = launcher.bridge;\n\tconst serverName =\n\t\tresolveServerExecutable(server) ||\n\t\tbridge?.command ||\n\t\tlauncher.command ||\n\t\tserver.id;\n\n\ttry {\n\t\tconst existingPort = await canReuseExistingServer(server, effectiveSession);\n\t\tif (existingPort !== null) {\n\t\t\t// Server is already running and responsive, no need to start\n\t\t\treturn { uuid: null, discoveredPort: existingPort };\n\t\t}\n\t} catch {\n\t\t// Failed to check, proceed with normal startup\n\t}\n\n\tconst terminal = (\n\t\tglobalThis as unknown as {\n\t\t\tTerminal?: { isInstalled?: () => Promise<boolean> | boolean };\n\t\t}\n\t).Terminal;\n\tlet isTerminalInstalled = false;\n\ttry {\n\t\tisTerminalInstalled = Boolean(await terminal?.isInstalled?.());\n\t} catch {}\n\tif (!isTerminalInstalled) {\n\t\tconst message = getTerminalRequiredMessage();\n\t\talert(strings?.error, message);\n\t\tconst unavailable: LspError = new Error(message);\n\t\tunavailable.code = \"LSP_SERVER_UNAVAILABLE\";\n\t\tthrow unavailable;\n\t}\n\n\tconst installed = await ensureInstalled(server);\n\tif (!installed) {\n\t\tconst unavailable: LspError = new Error(\n\t\t\t`Language server ${server.id} is not available.`,\n\t\t);\n\t\tunavailable.code = \"LSP_SERVER_UNAVAILABLE\";\n\t\tthrow unavailable;\n\t}\n\n\tconst key = server.id;\n\tif (managedServers.has(key)) {\n\t\tconst existing = managedServers.get(key);\n\t\treturn { uuid: existing?.uuid ?? null };\n\t}\n\n\tconst command = resolveStartCommand(server, effectiveSession);\n\tif (!command) {\n\t\treturn { uuid: null };\n\t}\n\n\ttry {\n\t\tconst uuid = await startInteractiveServer(command, key);\n\n\t\t// For auto-port discovery, wait for server ready signal then read port\n\t\tlet discoveredPort: number | undefined;\n\t\tif (bridge && !bridge.port) {\n\t\t\t// Auto-port mode - wait for server ready signal and then read port file\n\t\t\tconst portInfo = await waitForPort(\n\t\t\t\tkey,\n\t\t\t\tserverName,\n\t\t\t\teffectiveSession,\n\t\t\t\t10000,\n\t\t\t);\n\t\t\tif (portInfo) {\n\t\t\t\tdiscoveredPort = portInfo.port;\n\t\t\t\tconsole.info(\n\t\t\t\t\t`[LSP:${server.id}] Auto-discovered port ${discoveredPort}`,\n\t\t\t\t);\n\t\t\t\t// Update managed server entry with the port\n\t\t\t\tconst entry = managedServers.get(key);\n\t\t\t\tif (entry) {\n\t\t\t\t\tentry.port = discoveredPort;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (\n\t\t\tserver.transport?.url &&\n\t\t\t(server.transport.kind === \"websocket\" ||\n\t\t\t\tserver.transport.kind === \"stdio\")\n\t\t) {\n\t\t\t// Fixed port mode - wait for the server to signal ready\n\t\t\tawait waitForWebSocket(server.transport.url);\n\t\t}\n\n\t\tif (!announcedServers.has(key)) {\n\t\t\tconsole.info(`[LSP:${server.id}] ${server.label} connected`);\n\t\t\tannouncedServers.add(key);\n\t\t}\n\t\treturn { uuid, discoveredPort };\n\t} catch (error) {\n\t\tconsole.error(`Failed to start language server ${server.id}`, error);\n\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\tlspStatusBar.show({\n\t\t\tmessage: errorMessage || \"Connection failed\",\n\t\t\ttitle: `${server.label} failed`,\n\t\t\ttype: \"error\",\n\t\t\ticon: \"error\",\n\t\t\tduration: false,\n\t\t});\n\t\tconst entry = managedServers.get(key);\n\t\tif (entry) {\n\t\t\tgetExecutor()\n\t\t\t\t.stop(entry.uuid)\n\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Failed to stop language server shell ${server.id}`,\n\t\t\t\t\t\terr,\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\tmanagedServers.delete(key);\n\t\t}\n\t\tconst unavailable: LspError = new Error(\n\t\t\t`Language server ${server.id} failed to start (${errorMessage})`,\n\t\t);\n\t\tunavailable.code = \"LSP_SERVER_UNAVAILABLE\";\n\t\tthrow unavailable;\n\t}\n}\n\nexport function stopManagedServer(serverId: string): void {\n\tconst entry = managedServers.get(serverId);\n\tif (!entry) return;\n\tconst executor = getExecutor();\n\texecutor.stop(entry.uuid).catch((error: Error) => {\n\t\tconsole.warn(`Failed to stop language server ${serverId}`, error);\n\t});\n\tmanagedServers.delete(serverId);\n\tannouncedServers.delete(serverId);\n\n\t// Stop foreground service when all servers are stopped\n\tif (managedServers.size === 0) {\n\t\texecutor.stopService().catch(() => {});\n\t}\n}\n\nexport function resetManagedServers(): void {\n\tfor (const id of Array.from(managedServers.keys())) {\n\t\tstopManagedServer(id);\n\t}\n\tmanagedServers.clear();\n\t// Ensure foreground service is stopped\n\tgetExecutor()\n\t\t.stopService()\n\t\t.catch(() => {});\n}\n\n/**\n * Get managed server info by server ID\n */\nexport function getManagedServerInfo(\n\tserverId: string,\n): ManagedServerEntry | null {\n\treturn managedServers.get(serverId) ?? null;\n}\n\n/**\n * Get all managed servers\n */\nexport function getAllManagedServers(): Map<string, ManagedServerEntry> {\n\treturn new Map(managedServers);\n}\n\nfunction formatMemory(bytes: number): string {\n\tif (!bytes || bytes <= 0) return \"—\";\n\tconst mb = bytes / (1024 * 1024);\n\tif (mb >= 1) return `${mb.toFixed(1)} MB`;\n\tconst kb = bytes / 1024;\n\treturn `${kb.toFixed(0)} KB`;\n}\n\nfunction formatUptime(seconds: number): string {\n\tif (!seconds || seconds <= 0) return \"—\";\n\tif (seconds < 60) return `${seconds}s`;\n\tconst mins = Math.floor(seconds / 60);\n\tconst secs = seconds % 60;\n\tif (mins < 60) return `${mins}m ${secs}s`;\n\tconst hours = Math.floor(mins / 60);\n\tconst remainingMins = mins % 60;\n\treturn `${hours}h ${remainingMins}m`;\n}\n\n/**\n * Fetch server stats from the axs proxy /status endpoint\n * @param serverId - The server ID to fetch stats for\n * @param timeout - Timeout in milliseconds (default: 2000)\n */\nexport async function getServerStats(\n\tserverId: string,\n\ttimeout = 2000,\n): Promise<LspServerStatsFormatted | null> {\n\tconst entry = managedServers.get(serverId);\n\tif (!entry?.port) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst controller = new AbortController();\n\t\tconst timeoutId = setTimeout(() => controller.abort(), timeout);\n\n\t\tconst response = await fetch(`http://127.0.0.1:${entry.port}/status`, {\n\t\t\tsignal: controller.signal,\n\t\t});\n\n\t\tclearTimeout(timeoutId);\n\n\t\tif (!response.ok) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = (await response.json()) as LspServerStats;\n\n\t\t// Aggregate stats from all processes\n\t\tlet totalMemory = 0;\n\t\tlet maxUptime = 0;\n\t\tlet firstPid: number | null = null;\n\n\t\tfor (const proc of data.processes || []) {\n\t\t\ttotalMemory += proc.memory_bytes || 0;\n\t\t\tif (proc.uptime_secs > maxUptime) {\n\t\t\t\tmaxUptime = proc.uptime_secs;\n\t\t\t}\n\t\t\tif (firstPid === null && proc.pid) {\n\t\t\t\tfirstPid = proc.pid;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tmemoryBytes: totalMemory,\n\t\t\tmemoryFormatted: formatMemory(totalMemory),\n\t\t\tuptimeSeconds: maxUptime,\n\t\t\tuptimeFormatted: formatUptime(maxUptime),\n\t\t\tpid: firstPid,\n\t\t\tprocessCount: data.processes?.length ?? 0,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/cm/lsp/serverRegistry.ts",
    "content": "import {\n\tbindServerRegistry,\n\tensureBuiltinBundlesRegistered,\n} from \"./serverCatalog\";\nimport type {\n\tAcodeClientConfig,\n\tBridgeConfig,\n\tLanguageResolverContext,\n\tLauncherConfig,\n\tLspServerDefinition,\n\tLspServerManifest,\n\tRegistryEventListener,\n\tRegistryEventType,\n\tRootUriContext,\n\tTransportDescriptor,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nconst registry = new Map<string, LspServerDefinition>();\nconst listeners = new Set<RegistryEventListener>();\n\nfunction toKey(id: string | undefined | null): string {\n\treturn String(id ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\nfunction clone<T>(value: T): T | undefined {\n\tif (!value || typeof value !== \"object\") return undefined;\n\ttry {\n\t\treturn JSON.parse(JSON.stringify(value)) as T;\n\t} catch (_) {\n\t\treturn value;\n\t}\n}\n\nfunction sanitizeLanguages(languages: string[] = []): string[] {\n\tif (!Array.isArray(languages)) return [];\n\treturn languages\n\t\t.map((lang) =>\n\t\t\tString(lang ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase(),\n\t\t)\n\t\t.filter(Boolean);\n}\n\nfunction parsePort(value: unknown): number | null {\n\tconst num = Number(value);\n\tif (!Number.isFinite(num)) return null;\n\tconst int = Math.floor(num);\n\tif (int !== num || int <= 0 || int > 65535) return null;\n\treturn int;\n}\n\ninterface RawBridgeConfig {\n\tkind?: string;\n\tport?: unknown;\n\tcommand?: string;\n\targs?: unknown[];\n\tsession?: string;\n}\n\nfunction sanitizeInstallKind(\n\tvalue: unknown,\n):\n\t| \"apk\"\n\t| \"npm\"\n\t| \"pip\"\n\t| \"cargo\"\n\t| \"github-release\"\n\t| \"manual\"\n\t| \"shell\"\n\t| undefined {\n\tswitch (value) {\n\t\tcase \"apk\":\n\t\tcase \"npm\":\n\t\tcase \"pip\":\n\t\tcase \"cargo\":\n\t\tcase \"github-release\":\n\t\tcase \"manual\":\n\t\tcase \"shell\":\n\t\t\treturn value;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction sanitizeBridge(\n\tserverId: string,\n\tbridge: RawBridgeConfig | undefined | null,\n): BridgeConfig | undefined {\n\tif (!bridge || typeof bridge !== \"object\") return undefined;\n\tconst kind = bridge.kind ?? \"axs\";\n\tif (kind !== \"axs\") {\n\t\tthrow new Error(\n\t\t\t`LSP server ${serverId} declares unsupported bridge kind ${kind}`,\n\t\t);\n\t}\n\t// Port is now optional - if not provided, auto-port discovery will be used\n\tconst port = bridge.port ? (parsePort(bridge.port) ?? undefined) : undefined;\n\tconst command = bridge.command ? String(bridge.command) : null;\n\tif (!command) {\n\t\tthrow new Error(`LSP server ${serverId} bridge must supply a command`);\n\t}\n\tconst args = Array.isArray(bridge.args)\n\t\t? bridge.args.map((arg) => String(arg))\n\t\t: undefined;\n\treturn {\n\t\tkind: \"axs\",\n\t\tport,\n\t\tcommand,\n\t\targs,\n\t\tsession: bridge.session ? String(bridge.session) : undefined,\n\t};\n}\n\ninterface RawTransportDescriptor {\n\tkind?: string;\n\tcommand?: string;\n\targs?: unknown[];\n\toptions?: Record<string, unknown> | WebSocketTransportOptions;\n\turl?: string;\n}\n\ninterface RawLauncherConfig {\n\tcommand?: string;\n\targs?: unknown[];\n\tstartCommand?: string | string[];\n\tcheckCommand?: string;\n\tversionCommand?: string;\n\tupdateCommand?: string;\n\tinstall?: {\n\t\tkind?: string;\n\t\tcommand?: string;\n\t\tupdateCommand?: string;\n\t\tuninstallCommand?: string;\n\t\tlabel?: string;\n\t\tsource?: string;\n\t\texecutable?: string;\n\t\tpackages?: unknown[];\n\t\tpipCommand?: string;\n\t\tnpmCommand?: string;\n\t\tpythonCommand?: string;\n\t\tglobal?: boolean;\n\t\tbreakSystemPackages?: boolean;\n\t\trepo?: string;\n\t\tassetNames?: Record<string, unknown>;\n\t\tarchiveType?: string;\n\t\textractFile?: string;\n\t\tbinaryPath?: string;\n\t};\n\tbridge?: RawBridgeConfig;\n}\n\nexport type RawServerDefinition = LspServerManifest;\n\nfunction sanitizeDefinition(\n\tdefinition: RawServerDefinition,\n): LspServerDefinition {\n\tif (!definition || typeof definition !== \"object\") {\n\t\tthrow new TypeError(\"LSP server definition must be an object\");\n\t}\n\n\tconst id = toKey(definition.id);\n\tif (!id) throw new Error(\"LSP server definition requires a non-empty id\");\n\n\tconst transport: RawTransportDescriptor = definition.transport ?? {};\n\tconst kind = (transport.kind ?? \"stdio\") as\n\t\t| \"stdio\"\n\t\t| \"websocket\"\n\t\t| \"external\";\n\n\tif (!transport || typeof transport !== \"object\") {\n\t\tthrow new Error(`LSP server ${id} is missing a transport descriptor`);\n\t}\n\n\tif (\n\t\t!(\"languages\" in definition) ||\n\t\t!sanitizeLanguages(definition.languages).length\n\t) {\n\t\tthrow new Error(`LSP server ${id} must declare supported languages`);\n\t}\n\n\tif (kind === \"stdio\" && !transport.command) {\n\t\tthrow new Error(`LSP server ${id} (stdio) requires a command`);\n\t}\n\n\t// Websocket transport requires a URL unless a bridge is configured for auto-port discovery\n\tconst hasBridge = definition.launcher?.bridge?.command;\n\tif (kind === \"websocket\" && !transport.url && !hasBridge) {\n\t\tthrow new Error(\n\t\t\t`LSP server ${id} (websocket) requires a url or a launcher bridge`,\n\t\t);\n\t}\n\n\tconst transportOptions: Record<string, unknown> =\n\t\ttransport.options && typeof transport.options === \"object\"\n\t\t\t? { ...transport.options }\n\t\t\t: {};\n\n\tconst sanitizedTransport: TransportDescriptor = {\n\t\tkind,\n\t\tcommand: transport.command,\n\t\targs: Array.isArray(transport.args)\n\t\t\t? transport.args.map((arg) => String(arg))\n\t\t\t: undefined,\n\t\toptions: transportOptions,\n\t\turl: transport.url,\n\t\tprotocols: undefined,\n\t};\n\n\tlet launcher: LauncherConfig | undefined;\n\tif (definition.launcher && typeof definition.launcher === \"object\") {\n\t\tconst rawLauncher = definition.launcher;\n\t\tconst installExecutable =\n\t\t\ttypeof rawLauncher.install?.executable === \"string\"\n\t\t\t\t? rawLauncher.install.executable.trim()\n\t\t\t\t: \"\";\n\t\tlauncher = {\n\t\t\tcommand: rawLauncher.command,\n\t\t\targs: Array.isArray(rawLauncher.args)\n\t\t\t\t? rawLauncher.args.map((arg) => String(arg))\n\t\t\t\t: undefined,\n\t\t\tstartCommand: Array.isArray(rawLauncher.startCommand)\n\t\t\t\t? rawLauncher.startCommand.map((arg) => String(arg))\n\t\t\t\t: rawLauncher.startCommand,\n\t\t\tcheckCommand: rawLauncher.checkCommand,\n\t\t\tversionCommand: rawLauncher.versionCommand,\n\t\t\tupdateCommand: rawLauncher.updateCommand,\n\t\t\tuninstallCommand: rawLauncher.uninstallCommand,\n\t\t\tinstall:\n\t\t\t\trawLauncher.install && typeof rawLauncher.install === \"object\"\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tkind: sanitizeInstallKind(rawLauncher.install.kind),\n\t\t\t\t\t\t\tcommand: rawLauncher.install.command ?? \"\",\n\t\t\t\t\t\t\tupdateCommand: rawLauncher.install.updateCommand,\n\t\t\t\t\t\t\tuninstallCommand: rawLauncher.install.uninstallCommand,\n\t\t\t\t\t\t\tlabel: rawLauncher.install.label,\n\t\t\t\t\t\t\tsource: rawLauncher.install.source,\n\t\t\t\t\t\t\texecutable: installExecutable || undefined,\n\t\t\t\t\t\t\tpackages: Array.isArray(rawLauncher.install.packages)\n\t\t\t\t\t\t\t\t? rawLauncher.install.packages.map((entry) => String(entry))\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tpipCommand: rawLauncher.install.pipCommand,\n\t\t\t\t\t\t\tnpmCommand: rawLauncher.install.npmCommand,\n\t\t\t\t\t\t\tpythonCommand: rawLauncher.install.pythonCommand,\n\t\t\t\t\t\t\tglobal: rawLauncher.install.global,\n\t\t\t\t\t\t\tbreakSystemPackages: rawLauncher.install.breakSystemPackages,\n\t\t\t\t\t\t\trepo: rawLauncher.install.repo,\n\t\t\t\t\t\t\tassetNames:\n\t\t\t\t\t\t\t\trawLauncher.install.assetNames &&\n\t\t\t\t\t\t\t\ttypeof rawLauncher.install.assetNames === \"object\"\n\t\t\t\t\t\t\t\t\t? Object.fromEntries(\n\t\t\t\t\t\t\t\t\t\t\tObject.entries(rawLauncher.install.assetNames).map(\n\t\t\t\t\t\t\t\t\t\t\t\t([key, value]) => [String(key), String(value)],\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\tarchiveType:\n\t\t\t\t\t\t\t\trawLauncher.install.archiveType === \"binary\" ? \"binary\" : \"zip\",\n\t\t\t\t\t\t\textractFile: rawLauncher.install.extractFile,\n\t\t\t\t\t\t\tbinaryPath: rawLauncher.install.binaryPath,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\tbridge: sanitizeBridge(id, rawLauncher.bridge),\n\t\t};\n\n\t\tconst installKind = launcher.install?.kind;\n\t\tconst isManagedInstall = installKind && installKind !== \"shell\";\n\t\tif (isManagedInstall) {\n\t\t\tconst providedExecutable =\n\t\t\t\tlauncher.install?.binaryPath || launcher.install?.executable;\n\t\t\tif (!providedExecutable) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`LSP server ${id} managed installers must declare install.binaryPath or install.executable`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst sanitized: LspServerDefinition = {\n\t\tid,\n\t\tlabel: definition.label ?? id,\n\t\tenabled: definition.enabled !== false,\n\t\tlanguages: sanitizeLanguages(definition.languages),\n\t\ttransport: sanitizedTransport,\n\t\tinitializationOptions: clone(definition.initializationOptions),\n\t\tclientConfig: clone(definition.clientConfig),\n\t\tstartupTimeout:\n\t\t\ttypeof definition.startupTimeout === \"number\"\n\t\t\t\t? definition.startupTimeout\n\t\t\t\t: undefined,\n\t\tcapabilityOverrides: clone(definition.capabilityOverrides),\n\t\trootUri:\n\t\t\ttypeof definition.rootUri === \"function\" ? definition.rootUri : null,\n\t\tdocumentUri:\n\t\t\ttypeof definition.documentUri === \"function\"\n\t\t\t\t? definition.documentUri\n\t\t\t\t: null,\n\t\tresolveLanguageId:\n\t\t\ttypeof definition.resolveLanguageId === \"function\"\n\t\t\t\t? definition.resolveLanguageId\n\t\t\t\t: null,\n\t\tlauncher,\n\t\tuseWorkspaceFolders: definition.useWorkspaceFolders === true,\n\t};\n\n\tif (!Object.keys(transportOptions).length) {\n\t\tsanitized.transport.options = undefined;\n\t}\n\n\treturn sanitized;\n}\n\nfunction notify(event: RegistryEventType, payload: LspServerDefinition): void {\n\tlisteners.forEach((fn) => {\n\t\ttry {\n\t\t\tfn(event, payload);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"LSP server registry listener failed\", error);\n\t\t}\n\t});\n}\n\nexport interface RegisterServerOptions {\n\treplace?: boolean;\n}\n\nexport function registerServer(\n\tdefinition: RawServerDefinition,\n\toptions: RegisterServerOptions = {},\n): LspServerDefinition {\n\tconst { replace = false } = options;\n\tconst normalized = sanitizeDefinition(definition);\n\tconst exists = registry.has(normalized.id);\n\tif (exists && !replace) {\n\t\tconst existing = registry.get(normalized.id);\n\t\tif (existing) return existing;\n\t}\n\n\tregistry.set(normalized.id, normalized);\n\tnotify(\"register\", normalized);\n\treturn normalized;\n}\n\nexport function unregisterServer(id: string): boolean {\n\tconst key = toKey(id);\n\tif (!key || !registry.has(key)) return false;\n\tconst existing = registry.get(key);\n\tregistry.delete(key);\n\tif (existing) {\n\t\tnotify(\"unregister\", existing);\n\t}\n\treturn true;\n}\n\nexport type ServerUpdater = (\n\tcurrent: LspServerDefinition,\n) => Partial<LspServerDefinition> | null;\n\nexport function updateServer(\n\tid: string,\n\tupdater: ServerUpdater,\n): LspServerDefinition | null {\n\tconst key = toKey(id);\n\tif (!key || !registry.has(key)) return null;\n\tconst current = registry.get(key);\n\tif (!current) return null;\n\tconst next = updater({ ...current });\n\tif (!next) return current;\n\tconst normalized = sanitizeDefinition({\n\t\t...current,\n\t\t...next,\n\t\tid: current.id,\n\t});\n\tregistry.set(key, normalized);\n\tnotify(\"update\", normalized);\n\treturn normalized;\n}\n\nexport function getServer(id: string): LspServerDefinition | null {\n\treturn registry.get(toKey(id)) ?? null;\n}\n\nexport function listServers(): LspServerDefinition[] {\n\treturn Array.from(registry.values());\n}\n\nexport interface GetServersOptions {\n\tincludeDisabled?: boolean;\n}\n\nexport function getServersForLanguage(\n\tlanguageId: string,\n\toptions: GetServersOptions = {},\n): LspServerDefinition[] {\n\tconst { includeDisabled = false } = options;\n\tconst langKey = toKey(languageId);\n\tif (!langKey) return [];\n\n\treturn listServers().filter((server) => {\n\t\tif (!includeDisabled && !server.enabled) return false;\n\t\treturn server.languages.includes(langKey);\n\t});\n}\n\nexport function onRegistryChange(listener: RegistryEventListener): () => void {\n\tif (typeof listener !== \"function\") return () => {};\n\tlisteners.add(listener);\n\treturn () => listeners.delete(listener);\n}\n\nbindServerRegistry({\n\tregisterServer,\n\tunregisterServer,\n});\nensureBuiltinBundlesRegistered();\n\nexport default {\n\tregisterServer,\n\tunregisterServer,\n\tupdateServer,\n\tgetServer,\n\tgetServersForLanguage,\n\tlistServers,\n\tonRegistryChange,\n};\n"
  },
  {
    "path": "src/cm/lsp/servers/index.ts",
    "content": "import type { LspServerBundle, LspServerManifest } from \"../types\";\nimport { javascriptBundle, javascriptServers } from \"./javascript\";\nimport { luauBundle, luauServers } from \"./luau\";\nimport { pythonBundle, pythonServers } from \"./python\";\nimport { systemsBundle, systemsServers } from \"./systems\";\nimport { webBundle, webServers } from \"./web\";\n\nexport const builtinServers: LspServerManifest[] = [\n\t...javascriptServers,\n\t...pythonServers,\n\t...luauServers,\n\t...webServers,\n\t...systemsServers,\n];\n\nexport const builtinServerBundles: LspServerBundle[] = [\n\tjavascriptBundle,\n\tpythonBundle,\n\tluauBundle,\n\twebBundle,\n\tsystemsBundle,\n];\n"
  },
  {
    "path": "src/cm/lsp/servers/javascript.ts",
    "content": "import { defineBundle, defineServer, installers } from \"../providerUtils\";\nimport type { LspServerBundle, LspServerManifest } from \"../types\";\nimport { resolveJsTsLanguageId } from \"./shared\";\n\nexport const javascriptServers: LspServerManifest[] = [\n\tdefineServer({\n\t\tid: \"typescript\",\n\t\tlabel: \"TypeScript / JavaScript\",\n\t\tuseWorkspaceFolders: true,\n\t\tlanguages: [\n\t\t\t\"javascript\",\n\t\t\t\"javascriptreact\",\n\t\t\t\"typescript\",\n\t\t\t\"typescriptreact\",\n\t\t\t\"tsx\",\n\t\t\t\"jsx\",\n\t\t],\n\t\ttransport: {\n\t\t\tkind: \"websocket\",\n\t\t},\n\t\tcommand: \"typescript-language-server\",\n\t\targs: [\"--stdio\"],\n\t\tcheckCommand: \"which typescript-language-server\",\n\t\tinstaller: installers.npm({\n\t\t\texecutable: \"typescript-language-server\",\n\t\t\tpackages: [\"typescript-language-server\", \"typescript\"],\n\t\t}),\n\t\tenabled: true,\n\t\tinitializationOptions: {\n\t\t\tprovideFormatter: true,\n\t\t\thostInfo: \"acode\",\n\t\t\ttsserver: {\n\t\t\t\tmaxTsServerMemory: 4096,\n\t\t\t\tuseSeparateSyntaxServer: true,\n\t\t\t},\n\t\t\tpreferences: {\n\t\t\t\tincludeInlayParameterNameHints: \"all\",\n\t\t\t\tincludeInlayParameterNameHintsWhenArgumentMatchesName: true,\n\t\t\t\tincludeInlayFunctionParameterTypeHints: true,\n\t\t\t\tincludeInlayVariableTypeHints: true,\n\t\t\t\tincludeInlayVariableTypeHintsWhenTypeMatchesName: false,\n\t\t\t\tincludeInlayPropertyDeclarationTypeHints: true,\n\t\t\t\tincludeInlayFunctionLikeReturnTypeHints: true,\n\t\t\t\tincludeInlayEnumMemberValueHints: true,\n\t\t\t\timportModuleSpecifierPreference: \"shortest\",\n\t\t\t\timportModuleSpecifierEnding: \"auto\",\n\t\t\t\tincludePackageJsonAutoImports: \"auto\",\n\t\t\t\tprovideRefactorNotApplicableReason: true,\n\t\t\t\tallowIncompleteCompletions: true,\n\t\t\t\tallowRenameOfImportPath: true,\n\t\t\t\tgenerateReturnInDocTemplate: true,\n\t\t\t\torganizeImportsIgnoreCase: \"auto\",\n\t\t\t\torganizeImportsCollation: \"ordinal\",\n\t\t\t\torganizeImportsCollationConfig: \"default\",\n\t\t\t\tautoImportFileExcludePatterns: [],\n\t\t\t\tpreferTypeOnlyAutoImports: false,\n\t\t\t},\n\t\t\tcompletions: {\n\t\t\t\tcompleteFunctionCalls: true,\n\t\t\t},\n\t\t\tdiagnostics: {\n\t\t\t\treportStyleChecksAsWarnings: true,\n\t\t\t},\n\t\t},\n\t\tresolveLanguageId: ({ languageId, languageName }) =>\n\t\t\tresolveJsTsLanguageId(languageId, languageName),\n\t}),\n\tdefineServer({\n\t\tid: \"vtsls\",\n\t\tlabel: \"TypeScript / JavaScript (vtsls)\",\n\t\tuseWorkspaceFolders: true,\n\t\tlanguages: [\n\t\t\t\"javascript\",\n\t\t\t\"javascriptreact\",\n\t\t\t\"typescript\",\n\t\t\t\"typescriptreact\",\n\t\t\t\"tsx\",\n\t\t\t\"jsx\",\n\t\t],\n\t\ttransport: {\n\t\t\tkind: \"websocket\",\n\t\t},\n\t\tcommand: \"vtsls\",\n\t\targs: [\"--stdio\"],\n\t\tcheckCommand: \"which vtsls\",\n\t\tinstaller: installers.npm({\n\t\t\texecutable: \"vtsls\",\n\t\t\tpackages: [\"@vtsls/language-server\"],\n\t\t}),\n\t\tenabled: false,\n\t\tinitializationOptions: {\n\t\t\thostInfo: \"acode\",\n\t\t\ttypescript: {\n\t\t\t\tenablePromptUseWorkspaceTsdk: true,\n\t\t\t\tinlayHints: {\n\t\t\t\t\tparameterNames: {\n\t\t\t\t\t\tenabled: \"all\",\n\t\t\t\t\t\tsuppressWhenArgumentMatchesName: false,\n\t\t\t\t\t},\n\t\t\t\t\tparameterTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tvariableTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t\tsuppressWhenTypeMatchesName: false,\n\t\t\t\t\t},\n\t\t\t\t\tpropertyDeclarationTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tfunctionLikeReturnTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tenumMemberValues: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tsuggest: {\n\t\t\t\t\tcompleteFunctionCalls: true,\n\t\t\t\t\tincludeCompletionsForModuleExports: true,\n\t\t\t\t\tincludeCompletionsWithInsertText: true,\n\t\t\t\t\tincludeAutomaticOptionalChainCompletions: true,\n\t\t\t\t\tincludeCompletionsWithSnippetText: true,\n\t\t\t\t\tincludeCompletionsWithClassMemberSnippets: true,\n\t\t\t\t\tincludeCompletionsWithObjectLiteralMethodSnippets: true,\n\t\t\t\t\tautoImports: true,\n\t\t\t\t\tclassMemberSnippets: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tobjectLiteralMethodSnippets: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpreferences: {\n\t\t\t\t\timportModuleSpecifier: \"shortest\",\n\t\t\t\t\timportModuleSpecifierEnding: \"auto\",\n\t\t\t\t\tincludePackageJsonAutoImports: \"auto\",\n\t\t\t\t\tpreferTypeOnlyAutoImports: false,\n\t\t\t\t\tquoteStyle: \"auto\",\n\t\t\t\t\tjsxAttributeCompletionStyle: \"auto\",\n\t\t\t\t},\n\t\t\t\tformat: {\n\t\t\t\t\tenable: true,\n\t\t\t\t\tinsertSpaceAfterCommaDelimiter: true,\n\t\t\t\t\tinsertSpaceAfterSemicolonInForStatements: true,\n\t\t\t\t\tinsertSpaceBeforeAndAfterBinaryOperators: true,\n\t\t\t\t\tinsertSpaceAfterKeywordsInControlFlowStatements: true,\n\t\t\t\t\tinsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,\n\t\t\t\t\tinsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,\n\t\t\t\t\tinsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,\n\t\t\t\t\tinsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,\n\t\t\t\t\tinsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,\n\t\t\t\t\tinsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,\n\t\t\t\t\tplaceOpenBraceOnNewLineForFunctions: false,\n\t\t\t\t\tplaceOpenBraceOnNewLineForControlBlocks: false,\n\t\t\t\t\tsemicolons: \"ignore\",\n\t\t\t\t},\n\t\t\t\tupdateImportsOnFileMove: {\n\t\t\t\t\tenabled: \"always\",\n\t\t\t\t},\n\t\t\t\tcodeActionsOnSave: {\n\t\t\t\t\torganizeImports: false,\n\t\t\t\t\taddMissingImports: false,\n\t\t\t\t},\n\t\t\t\tworkspaceSymbols: {\n\t\t\t\t\tscope: \"allOpenProjects\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tjavascript: {\n\t\t\t\tinlayHints: {\n\t\t\t\t\tparameterNames: {\n\t\t\t\t\t\tenabled: \"all\",\n\t\t\t\t\t\tsuppressWhenArgumentMatchesName: false,\n\t\t\t\t\t},\n\t\t\t\t\tparameterTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tvariableTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t\tsuppressWhenTypeMatchesName: false,\n\t\t\t\t\t},\n\t\t\t\t\tpropertyDeclarationTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tfunctionLikeReturnTypes: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t\tenumMemberValues: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tsuggest: {\n\t\t\t\t\tcompleteFunctionCalls: true,\n\t\t\t\t\tincludeCompletionsForModuleExports: true,\n\t\t\t\t\tautoImports: true,\n\t\t\t\t\tclassMemberSnippets: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpreferences: {\n\t\t\t\t\timportModuleSpecifier: \"shortest\",\n\t\t\t\t\tquoteStyle: \"auto\",\n\t\t\t\t},\n\t\t\t\tformat: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tupdateImportsOnFileMove: {\n\t\t\t\t\tenabled: \"always\",\n\t\t\t\t},\n\t\t\t},\n\t\t\ttsserver: {\n\t\t\t\tmaxTsServerMemory: 8092,\n\t\t\t},\n\t\t\tvtsls: {\n\t\t\t\texperimental: {\n\t\t\t\t\tcompletion: {\n\t\t\t\t\t\tenableServerSideFuzzyMatch: true,\n\t\t\t\t\t\tentriesLimit: 5000,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tautoUseWorkspaceTsdk: true,\n\t\t\t},\n\t\t},\n\t\tresolveLanguageId: ({ languageId, languageName }) =>\n\t\t\tresolveJsTsLanguageId(languageId, languageName),\n\t}),\n\tdefineServer({\n\t\tid: \"eslint\",\n\t\tlabel: \"ESLint\",\n\t\tlanguages: [\n\t\t\t\"javascript\",\n\t\t\t\"javascriptreact\",\n\t\t\t\"typescript\",\n\t\t\t\"typescriptreact\",\n\t\t\t\"tsx\",\n\t\t\t\"jsx\",\n\t\t\t\"vue\",\n\t\t\t\"svelte\",\n\t\t\t\"html\",\n\t\t\t\"markdown\",\n\t\t\t\"json\",\n\t\t\t\"jsonc\",\n\t\t],\n\t\ttransport: {\n\t\t\tkind: \"websocket\",\n\t\t},\n\t\tcommand: \"vscode-eslint-language-server\",\n\t\targs: [\"--stdio\"],\n\t\tcheckCommand: \"which vscode-eslint-language-server\",\n\t\tinstaller: installers.npm({\n\t\t\texecutable: \"vscode-eslint-language-server\",\n\t\t\tpackages: [\"vscode-langservers-extracted\"],\n\t\t}),\n\t\tenabled: false,\n\t\tinitializationOptions: {\n\t\t\tvalidate: \"on\",\n\t\t\trulesCustomizations: [],\n\t\t\trun: \"onType\",\n\t\t\tnodePath: null,\n\t\t\tworkingDirectory: {\n\t\t\t\tmode: \"auto\",\n\t\t\t},\n\t\t\tproblems: {\n\t\t\t\tshortenToSingleLine: false,\n\t\t\t},\n\t\t\tcodeActionOnSave: {\n\t\t\t\tenable: true,\n\t\t\t\trules: [],\n\t\t\t\tmode: \"all\",\n\t\t\t},\n\t\t\tcodeAction: {\n\t\t\t\tdisableRuleComment: {\n\t\t\t\t\tenable: true,\n\t\t\t\t\tlocation: \"separateLine\",\n\t\t\t\t\tcommentStyle: \"line\",\n\t\t\t\t},\n\t\t\t\tshowDocumentation: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\texperimental: {\n\t\t\t\tuseFlatConfig: false,\n\t\t\t},\n\t\t\tformat: {\n\t\t\t\tenable: true,\n\t\t\t},\n\t\t\tquiet: false,\n\t\t\tonIgnoredFiles: \"off\",\n\t\t\tuseESLintClass: false,\n\t\t},\n\t\tclientConfig: {\n\t\t\tbuiltinExtensions: {\n\t\t\t\thover: false,\n\t\t\t\tcompletion: false,\n\t\t\t\tsignature: false,\n\t\t\t\tkeymaps: false,\n\t\t\t\tdiagnostics: true,\n\t\t\t},\n\t\t},\n\t\tresolveLanguageId: ({ languageId, languageName }) =>\n\t\t\tresolveJsTsLanguageId(languageId, languageName),\n\t}),\n];\n\nexport const javascriptBundle: LspServerBundle = defineBundle({\n\tid: \"builtin-javascript\",\n\tlabel: \"JavaScript / TypeScript\",\n\tservers: javascriptServers,\n});\n"
  },
  {
    "path": "src/cm/lsp/servers/luau.ts",
    "content": "import toast from \"components/toast\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport { buildShellArchCase } from \"../installerUtils\";\nimport {\n\tquoteArg,\n\trunForegroundCommand,\n\trunQuickCommand,\n} from \"../installRuntime\";\nimport { defineBundle, defineServer, installers } from \"../providerUtils\";\nimport type {\n\tInstallCheckResult,\n\tLspServerBundle,\n\tLspServerManifest,\n} from \"../types\";\n\nfunction isGlibcRuntimeError(output: string): boolean {\n\treturn (\n\t\toutput.includes(\"ld-linux-aarch64.so.1\") ||\n\t\toutput.includes(\"ld-linux-x86-64.so.2\") ||\n\t\toutput.includes(\"Error loading shared library\") ||\n\t\toutput.includes(\"__fprintf_chk\") ||\n\t\toutput.includes(\"__snprintf_chk\") ||\n\t\toutput.includes(\"__vsnprintf_chk\") ||\n\t\toutput.includes(\"__libc_single_threaded\") ||\n\t\toutput.includes(\"GLIBC_\")\n\t);\n}\n\nfunction getLuauRuntimeFailureMessage(output: string): string {\n\tif (isGlibcRuntimeError(output)) {\n\t\treturn \"Luau release binary requires glibc and is not runnable in this Alpine/musl environment.\";\n\t}\n\n\tconst firstLine = String(output || \"\")\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.find(Boolean);\n\treturn firstLine || \"Luau binary is installed but not runnable.\";\n}\n\nasync function readLuauRuntimeFailure(binaryPath: string): Promise<string> {\n\tconst command = `${quoteArg(binaryPath)} --help >/dev/null 2>&1 || ${quoteArg(binaryPath)} lsp --help >/dev/null 2>&1`;\n\ttry {\n\t\tawait runQuickCommand(command);\n\t\treturn \"\";\n\t} catch (error) {\n\t\tconst primaryMessage =\n\t\t\terror instanceof Error ? error.message : String(error);\n\t\ttry {\n\t\t\tconst lddOutput = await runQuickCommand(\n\t\t\t\t`command -v ldd >/dev/null 2>&1 && ldd ${quoteArg(binaryPath)} 2>&1 || true`,\n\t\t\t);\n\t\t\treturn [primaryMessage, lddOutput].filter(Boolean).join(\"\\n\");\n\t\t} catch {\n\t\t\treturn primaryMessage;\n\t\t}\n\t}\n}\n\nexport const luauServers: LspServerManifest[] = [\n\tdefineServer({\n\t\tid: \"luau\",\n\t\tlabel: \"Luau\",\n\t\tuseWorkspaceFolders: true,\n\t\tlanguages: [\"luau\"],\n\t\tcommand: \"/usr/local/bin/luau-lsp\",\n\t\targs: [\"lsp\"],\n\t\tinstaller: installers.githubRelease({\n\t\t\trepo: \"JohnnyMorganz/luau-lsp\",\n\t\t\tbinaryPath: \"/usr/local/bin/luau-lsp\",\n\t\t\tassetNames: {\n\t\t\t\taarch64: \"luau-lsp-linux-arm64.zip\",\n\t\t\t\tarm64: \"luau-lsp-linux-arm64.zip\",\n\t\t\t\t\"arm64-v8a\": \"luau-lsp-linux-arm64.zip\",\n\t\t\t\tx86_64: \"luau-lsp-linux-x86_64.zip\",\n\t\t\t\tamd64: \"luau-lsp-linux-x86_64.zip\",\n\t\t\t},\n\t\t\textractFile: \"luau-lsp\",\n\t\t}),\n\t\tenabled: false,\n\t}),\n];\n\nexport const luauBundle: LspServerBundle = defineBundle({\n\tid: \"builtin-luau\",\n\tlabel: \"Luau\",\n\tservers: luauServers,\n\thooks: {\n\t\tgetExecutable: (_, manifest) =>\n\t\t\tmanifest.launcher?.install?.binaryPath ||\n\t\t\tmanifest.launcher?.install?.executable ||\n\t\t\tnull,\n\t\tasync checkInstallation(_, manifest): Promise<InstallCheckResult> {\n\t\t\tconst binary =\n\t\t\t\tmanifest.launcher?.install?.binaryPath ||\n\t\t\t\tmanifest.launcher?.install?.executable;\n\t\t\tif (!binary) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\tversion: null,\n\t\t\t\t\tcanInstall: true,\n\t\t\t\t\tcanUpdate: true,\n\t\t\t\t\tmessage: \"Luau bundle is missing a binary path\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait runQuickCommand(`test -x ${quoteArg(binary)}`);\n\t\t\t\tconst runtimeFailure = await readLuauRuntimeFailure(binary);\n\t\t\t\tif (runtimeFailure) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstatus: \"failed\",\n\t\t\t\t\t\tversion: null,\n\t\t\t\t\t\tcanInstall: true,\n\t\t\t\t\t\tcanUpdate: true,\n\t\t\t\t\t\tmessage: getLuauRuntimeFailureMessage(runtimeFailure),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"present\",\n\t\t\t\t\tversion: null,\n\t\t\t\t\tcanInstall: true,\n\t\t\t\t\tcanUpdate: true,\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: \"missing\",\n\t\t\t\t\tversion: null,\n\t\t\t\t\tcanInstall: true,\n\t\t\t\t\tcanUpdate: true,\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t};\n\t\t\t}\n\t\t},\n\t\tasync installServer(_, manifest, mode, options = {}): Promise<boolean> {\n\t\t\tconst { promptConfirm = true } = options;\n\t\t\tconst install = manifest.launcher?.install;\n\t\t\tconst assetCases = buildShellArchCase(install?.assetNames, quoteArg);\n\t\t\tconst binaryPath = install?.binaryPath;\n\t\t\tconst repo = install?.repo;\n\t\t\tif (!assetCases || !binaryPath || !repo) {\n\t\t\t\tthrow new Error(\"Luau bundle is missing release metadata\");\n\t\t\t}\n\n\t\t\tconst label = manifest.label || \"Luau\";\n\t\t\tconst actionLabel = mode === \"update\" ? \"Update\" : \"Install\";\n\n\t\t\tif (promptConfirm) {\n\t\t\t\tconst shouldContinue = await confirm(\n\t\t\t\t\tlabel,\n\t\t\t\t\t`${actionLabel} ${label} language server?`,\n\t\t\t\t);\n\t\t\t\tif (!shouldContinue) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst downloadUrl = `https://github.com/${repo}/releases/latest/download/$ASSET`;\n\t\t\tconst command = `apk add --no-cache curl unzip && ARCH=\"$(uname -m)\" && case \"$ARCH\" in\n${assetCases}\n\\t*) echo \"Unsupported architecture: $ARCH\" >&2; exit 1 ;;\nesac && apk add --no-cache gcompat libstdc++ && TMP_DIR=\"$(mktemp -d)\" && cleanup() { rm -rf \"$TMP_DIR\"; } && trap cleanup EXIT && curl -fsSL \"${downloadUrl}\" -o \"$TMP_DIR/$ASSET\" && unzip -oq \"$TMP_DIR/$ASSET\" -d \"$TMP_DIR\" && chmod +x \"$TMP_DIR/luau-lsp\" && if ! \"$TMP_DIR/luau-lsp\" --help >/dev/null 2>&1 && ! \"$TMP_DIR/luau-lsp\" lsp --help >/dev/null 2>&1; then command -v ldd >/dev/null 2>&1 && ldd \"$TMP_DIR/luau-lsp\" >&2 || true; echo \"Luau release binary is not runnable in this environment.\" >&2; exit 1; fi && install -Dm755 \"$TMP_DIR/luau-lsp\" ${quoteArg(binaryPath)}`;\n\n\t\t\tconst loadingDialog = loader.create(\n\t\t\t\tlabel,\n\t\t\t\t`${actionLabel}ing ${label}...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tloadingDialog.show();\n\t\t\t\tawait runForegroundCommand(command);\n\t\t\t\tconst runtimeFailure = await readLuauRuntimeFailure(binaryPath);\n\t\t\t\tif (runtimeFailure) {\n\t\t\t\t\tawait runQuickCommand(`rm -f ${quoteArg(binaryPath)}`);\n\t\t\t\t\tthrow new Error(getLuauRuntimeFailureMessage(runtimeFailure));\n\t\t\t\t}\n\t\t\t\ttoast(`${label} ${mode === \"update\" ? \"updated\" : \"installed\"}`);\n\t\t\t\treturn true;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Failed to ${actionLabel.toLowerCase()} ${label}`, error);\n\t\t\t\ttoast(strings?.error ?? \"Error\");\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tloadingDialog.destroy();\n\t\t\t}\n\t\t},\n\t},\n});\n"
  },
  {
    "path": "src/cm/lsp/servers/python.ts",
    "content": "import { defineBundle, defineServer, installers } from \"../providerUtils\";\nimport type { LspServerBundle, LspServerManifest } from \"../types\";\n\nexport const pythonServers: LspServerManifest[] = [\n\tdefineServer({\n\t\tid: \"ty\",\n\t\tlabel: \"Python (ty)\",\n\t\tlanguages: [\"python\"],\n\t\tcommand: \"ty\",\n\t\targs: [\"server\"],\n\t\tcheckCommand: \"which ty\",\n\t\tinstaller: installers.pip({\n\t\t\texecutable: \"ty\",\n\t\t\tpackages: [\"ty\"],\n\t\t}),\n\t\tenabled: true,\n\t}),\n\tdefineServer({\n\t\tid: \"python\",\n\t\tlabel: \"Python (pylsp)\",\n\t\tlanguages: [\"python\"],\n\t\tcommand: \"pylsp\",\n\t\tcheckCommand: \"which pylsp\",\n\t\tinstaller: installers.pip({\n\t\t\texecutable: \"pylsp\",\n\t\t\tpackages: [\"python-lsp-server[all]\"],\n\t\t}),\n\t\tinitializationOptions: {\n\t\t\tpylsp: {\n\t\t\t\tplugins: {\n\t\t\t\t\tpyflakes: { enabled: true },\n\t\t\t\t\tpycodestyle: { enabled: true },\n\t\t\t\t\tmccabe: { enabled: true },\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tenabled: false,\n\t}),\n];\n\nexport const pythonBundle: LspServerBundle = defineBundle({\n\tid: \"builtin-python\",\n\tlabel: \"Python\",\n\tservers: pythonServers,\n});\n"
  },
  {
    "path": "src/cm/lsp/servers/shared.ts",
    "content": "export function normalizeServerLanguageKey(\n\tvalue: string | undefined | null,\n): string {\n\treturn String(value ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\nexport function resolveJsTsLanguageId(\n\tlanguageId: string | undefined,\n\tlanguageName: string | undefined,\n): string | null {\n\tconst lang = normalizeServerLanguageKey(languageId ?? languageName);\n\tswitch (lang) {\n\t\tcase \"tsx\":\n\t\tcase \"typescriptreact\":\n\t\t\treturn \"typescriptreact\";\n\t\tcase \"jsx\":\n\t\tcase \"javascriptreact\":\n\t\t\treturn \"javascriptreact\";\n\t\tcase \"ts\":\n\t\t\treturn \"typescript\";\n\t\tcase \"js\":\n\t\t\treturn \"javascript\";\n\t\tdefault:\n\t\t\treturn lang || null;\n\t}\n}\n"
  },
  {
    "path": "src/cm/lsp/servers/systems.ts",
    "content": "import { defineBundle, defineServer, installers } from \"../providerUtils\";\nimport type { LspServerBundle, LspServerManifest } from \"../types\";\n\nexport const systemsServers: LspServerManifest[] = [\n\tdefineServer({\n\t\tid: \"clangd\",\n\t\tlabel: \"C / C++ (clangd)\",\n\t\tlanguages: [\"c\", \"cpp\"],\n\t\tcommand: \"clangd\",\n\t\targs: [\n\t\t\t\"--background-index=0\",\n\t\t\t\"--clang-tidy=0\",\n\t\t\t\"--header-insertion=never\",\n\t\t],\n\t\tcheckCommand: \"which clangd\",\n\t\tinstaller: installers.apk({\n\t\t\texecutable: \"clangd\",\n\t\t\tpackages: [\"clang-extra-tools\"],\n\t\t}),\n\t\tenabled: false,\n\t}),\n\tdefineServer({\n\t\tid: \"gopls\",\n\t\tlabel: \"Go (gopls)\",\n\t\tlanguages: [\"go\", \"go.mod\", \"go.sum\", \"gotmpl\"],\n\t\tcommand: \"gopls\",\n\t\targs: [\"serve\"],\n\t\tcheckCommand: \"which gopls\",\n\t\tinstaller: installers.apk({\n\t\t\texecutable: \"gopls\",\n\t\t\tpackages: [\"go\", \"gopls\"],\n\t\t}),\n\t\tinitializationOptions: {\n\t\t\tusePlaceholders: false,\n\t\t\tcompleteUnimported: true,\n\t\t\tdeepCompletion: true,\n\t\t\tcompletionBudget: \"100ms\",\n\t\t\tmatcher: \"Fuzzy\",\n\t\t\tstaticcheck: true,\n\t\t\tgofumpt: true,\n\t\t\thints: {\n\t\t\t\tassignVariableTypes: true,\n\t\t\t\tcompositeLiteralFields: true,\n\t\t\t\tcompositeLiteralTypes: true,\n\t\t\t\tconstantValues: true,\n\t\t\t\tfunctionTypeParameters: true,\n\t\t\t\tparameterNames: true,\n\t\t\t\trangeVariableTypes: true,\n\t\t\t},\n\t\t\tdiagnosticsDelay: \"250ms\",\n\t\t\tdiagnosticsTrigger: \"Edit\",\n\t\t\tannotations: {\n\t\t\t\tbounds: true,\n\t\t\t\tescape: true,\n\t\t\t\tinline: true,\n\t\t\t\tnil: true,\n\t\t\t},\n\t\t\tsemanticTokens: true,\n\t\t\tanalyses: {\n\t\t\t\tnilness: true,\n\t\t\t\tunusedparams: true,\n\t\t\t\tunusedvariable: true,\n\t\t\t\tunusedwrite: true,\n\t\t\t\tshadow: true,\n\t\t\t\tfieldalignment: false,\n\t\t\t\tstringintconv: true,\n\t\t\t},\n\t\t\timportShortcut: \"Both\",\n\t\t\tsymbolMatcher: \"FastFuzzy\",\n\t\t\tsymbolStyle: \"Dynamic\",\n\t\t\tsymbolScope: \"all\",\n\t\t\tlocal: \"\",\n\t\t\tlinksInHover: true,\n\t\t\thoverKind: \"FullDocumentation\",\n\t\t\tverboseOutput: false,\n\t\t},\n\t\tenabled: true,\n\t}),\n\tdefineServer({\n\t\tid: \"rust-analyzer\",\n\t\tlabel: \"Rust (rust-analyzer)\",\n\t\tuseWorkspaceFolders: true,\n\t\tlanguages: [\"rust\"],\n\t\tcommand: \"rust-analyzer\",\n\t\tcheckCommand: \"which rust-analyzer\",\n\t\tinstaller: installers.apk({\n\t\t\texecutable: \"rust-analyzer\",\n\t\t\tpackages: [\"rust\", \"cargo\", \"rust-analyzer\"],\n\t\t}),\n\t\tinitializationOptions: {\n\t\t\tcargo: {\n\t\t\t\tallFeatures: true,\n\t\t\t\tbuildScripts: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tloadOutDirsFromCheck: true,\n\t\t\t},\n\t\t\tprocMacro: {\n\t\t\t\tenable: true,\n\t\t\t\tattributes: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tcheckOnSave: {\n\t\t\t\tenable: true,\n\t\t\t\tcommand: \"clippy\",\n\t\t\t\textraArgs: [\"--no-deps\"],\n\t\t\t},\n\t\t\tdiagnostics: {\n\t\t\t\tenable: true,\n\t\t\t\texperimental: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinlayHints: {\n\t\t\t\tbindingModeHints: {\n\t\t\t\t\tenable: false,\n\t\t\t\t},\n\t\t\t\tchainingHints: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tclosingBraceHints: {\n\t\t\t\t\tenable: true,\n\t\t\t\t\tminLines: 25,\n\t\t\t\t},\n\t\t\t\tclosureReturnTypeHints: {\n\t\t\t\t\tenable: \"with_block\",\n\t\t\t\t},\n\t\t\t\tlifetimeElisionHints: {\n\t\t\t\t\tenable: \"skip_trivial\",\n\t\t\t\t\tuseParameterNames: true,\n\t\t\t\t},\n\t\t\t\tmaxLength: 25,\n\t\t\t\tparameterHints: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\treborrowHints: {\n\t\t\t\t\tenable: \"mutable\",\n\t\t\t\t},\n\t\t\t\ttypeHints: {\n\t\t\t\t\tenable: true,\n\t\t\t\t\thideClosureInitialization: false,\n\t\t\t\t\thideNamedConstructor: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlens: {\n\t\t\t\tenable: true,\n\t\t\t\tdebug: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\timplementations: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\treferences: {\n\t\t\t\t\tadt: { enable: false },\n\t\t\t\t\tenumVariant: { enable: false },\n\t\t\t\t\tmethod: { enable: false },\n\t\t\t\t\ttrait: { enable: false },\n\t\t\t\t},\n\t\t\t\trun: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tcompletion: {\n\t\t\t\tautoimport: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tautoself: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tcallable: {\n\t\t\t\t\tsnippets: \"fill_arguments\",\n\t\t\t\t},\n\t\t\t\tpostfix: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tprivateEditable: {\n\t\t\t\t\tenable: false,\n\t\t\t\t},\n\t\t\t},\n\t\t\tsemanticHighlighting: {\n\t\t\t\tdoc: {\n\t\t\t\t\tcomment: {\n\t\t\t\t\t\tinject: {\n\t\t\t\t\t\t\tenable: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\toperator: {\n\t\t\t\t\tenable: true,\n\t\t\t\t\tspecialization: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tpunctuation: {\n\t\t\t\t\tenable: false,\n\t\t\t\t\tseparate: {\n\t\t\t\t\t\tmacro: {\n\t\t\t\t\t\t\tbang: true,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tspecialization: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstrings: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\thover: {\n\t\t\t\tactions: {\n\t\t\t\t\tdebug: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t\tenable: true,\n\t\t\t\t\tgotoTypeDef: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t\timplementations: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t\treferences: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t\trun: {\n\t\t\t\t\t\tenable: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdocumentation: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t\tlinks: {\n\t\t\t\t\tenable: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\tworkspace: {\n\t\t\t\tsymbol: {\n\t\t\t\t\tsearch: {\n\t\t\t\t\t\tkind: \"all_symbols\",\n\t\t\t\t\t\tscope: \"workspace\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\trustfmt: {\n\t\t\t\textraArgs: [],\n\t\t\t\toverrideCommand: null,\n\t\t\t\trangeFormatting: {\n\t\t\t\t\tenable: false,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tenabled: true,\n\t}),\n];\n\nexport const systemsBundle: LspServerBundle = defineBundle({\n\tid: \"builtin-systems\",\n\tlabel: \"Systems\",\n\tservers: systemsServers,\n});\n"
  },
  {
    "path": "src/cm/lsp/servers/web.ts",
    "content": "import { defineBundle, defineServer, installers } from \"../providerUtils\";\nimport type { LspServerBundle, LspServerManifest } from \"../types\";\n\nexport const webServers: LspServerManifest[] = [\n\tdefineServer({\n\t\tid: \"html\",\n\t\tlabel: \"HTML\",\n\t\tlanguages: [\"html\", \"vue\", \"svelte\"],\n\t\tcommand: \"vscode-html-language-server\",\n\t\targs: [\"--stdio\"],\n\t\tcheckCommand: \"which vscode-html-language-server\",\n\t\tinstaller: installers.npm({\n\t\t\texecutable: \"vscode-html-language-server\",\n\t\t\tpackages: [\"vscode-langservers-extracted\"],\n\t\t}),\n\t\tclientConfig: {\n\t\t\tbuiltinExtensions: {\n\t\t\t\tkeymaps: false,\n\t\t\t},\n\t\t},\n\t\tenabled: true,\n\t}),\n\tdefineServer({\n\t\tid: \"css\",\n\t\tlabel: \"CSS\",\n\t\tlanguages: [\"css\", \"scss\", \"less\"],\n\t\tcommand: \"vscode-css-language-server\",\n\t\targs: [\"--stdio\"],\n\t\tcheckCommand: \"which vscode-css-language-server\",\n\t\tinstaller: installers.npm({\n\t\t\texecutable: \"vscode-css-language-server\",\n\t\t\tpackages: [\"vscode-langservers-extracted\"],\n\t\t}),\n\t\tclientConfig: {\n\t\t\tbuiltinExtensions: {\n\t\t\t\tkeymaps: false,\n\t\t\t},\n\t\t},\n\t\tenabled: true,\n\t}),\n\tdefineServer({\n\t\tid: \"json\",\n\t\tlabel: \"JSON\",\n\t\tlanguages: [\"json\", \"jsonc\"],\n\t\tcommand: \"vscode-json-language-server\",\n\t\targs: [\"--stdio\"],\n\t\tcheckCommand: \"which vscode-json-language-server\",\n\t\tinstaller: installers.npm({\n\t\t\texecutable: \"vscode-json-language-server\",\n\t\t\tpackages: [\"vscode-langservers-extracted\"],\n\t\t}),\n\t\tclientConfig: {\n\t\t\tbuiltinExtensions: {\n\t\t\t\tkeymaps: false,\n\t\t\t},\n\t\t},\n\t\tenabled: true,\n\t}),\n];\n\nexport const webBundle: LspServerBundle = defineBundle({\n\tid: \"builtin-web\",\n\tlabel: \"Web\",\n\tservers: webServers,\n});\n"
  },
  {
    "path": "src/cm/lsp/tooltipExtensions.ts",
    "content": "import {\n\thighlightingFor,\n\ttype Language,\n\tlanguage as languageFacet,\n} from \"@codemirror/language\";\nimport { LSPPlugin } from \"@codemirror/lsp-client\";\nimport {\n\ttype Extension,\n\tPrec,\n\tStateEffect,\n\tStateField,\n} from \"@codemirror/state\";\nimport {\n\ttype Command,\n\tcloseHoverTooltips,\n\tEditorView,\n\thasHoverTooltips,\n\thoverTooltip,\n\ttype KeyBinding,\n\tkeymap,\n\tshowTooltip,\n\ttype Tooltip,\n\tViewPlugin,\n\ttype ViewUpdate,\n} from \"@codemirror/view\";\nimport { highlightCode } from \"@lezer/highlight\";\nimport type {\n\tHoverParams,\n\tSignatureHelpContext,\n\tSignatureHelpParams,\n} from \"vscode-languageserver-protocol\";\nimport type {\n\tHover,\n\tSignatureHelp as LspSignatureHelp,\n\tMarkedString,\n\tMarkupContent,\n} from \"vscode-languageserver-types\";\n\ninterface LspClientInternals {\n\tconfig?: {\n\t\thighlightLanguage?: (language: string) => Language | null | undefined;\n\t};\n\thasCapability?: (name: string) => boolean;\n}\n\nconst SIGNATURE_TRIGGER_DELAY = 120;\nconst SIGNATURE_RETRIGGER_DELAY = 250;\n\nfunction fromPosition(\n\tdoc: EditorView[\"state\"][\"doc\"],\n\tposition: { line: number; character: number },\n): number {\n\tconst line = doc.line(position.line + 1);\n\treturn Math.min(line.to, line.from + position.character);\n}\n\nfunction escapeHtml(value: string): string {\n\treturn value.replace(/[&<>\"']/g, (match) => {\n\t\tswitch (match) {\n\t\t\tcase \"&\":\n\t\t\t\treturn \"&amp;\";\n\t\t\tcase \"<\":\n\t\t\t\treturn \"&lt;\";\n\t\t\tcase \">\":\n\t\t\t\treturn \"&gt;\";\n\t\t\tcase '\"':\n\t\t\t\treturn \"&quot;\";\n\t\t\tdefault:\n\t\t\t\treturn \"&#39;\";\n\t\t}\n\t});\n}\n\nfunction renderCode(plugin: LSPPlugin, code: MarkedString): string {\n\tconst client = plugin.client as typeof plugin.client & LspClientInternals;\n\n\tif (typeof code === \"string\") {\n\t\treturn plugin.docToHTML(code, \"markdown\");\n\t}\n\n\tconst { language, value } = code;\n\tlet lang = client.config?.highlightLanguage?.(language || \"\") ?? undefined;\n\n\tif (!lang) {\n\t\tconst viewLang = plugin.view.state.facet(languageFacet);\n\t\tif (viewLang && (!language || viewLang.name === language)) {\n\t\t\tlang = viewLang;\n\t\t}\n\t}\n\n\tif (!lang) return escapeHtml(value);\n\n\tlet result = \"\";\n\thighlightCode(\n\t\tvalue,\n\t\tlang.parser.parse(value),\n\t\t{ style: (tags) => highlightingFor(plugin.view.state, tags) },\n\t\t(text, cls) => {\n\t\t\tresult += cls\n\t\t\t\t? `<span class=\"${cls}\">${escapeHtml(text)}</span>`\n\t\t\t\t: escapeHtml(text);\n\t\t},\n\t\t() => {\n\t\t\tresult += \"<br>\";\n\t\t},\n\t);\n\treturn result;\n}\n\nfunction renderTooltipContent(\n\tplugin: LSPPlugin,\n\tvalue: string | MarkupContent | MarkedString | MarkedString[],\n): string {\n\tif (Array.isArray(value)) {\n\t\treturn value.map((item) => renderCode(plugin, item)).join(\"<br>\");\n\t}\n\n\tif (\n\t\ttypeof value === \"string\" ||\n\t\t(typeof value === \"object\" && value != null && \"language\" in value)\n\t) {\n\t\treturn renderCode(plugin, value);\n\t}\n\n\treturn plugin.docToHTML(value);\n}\n\nfunction isPointerOrTouchSelection(update: ViewUpdate): boolean {\n\treturn (\n\t\tupdate.selectionSet &&\n\t\tupdate.transactions.some(\n\t\t\t(tr) =>\n\t\t\t\ttr.isUserEvent(\"pointer\") ||\n\t\t\t\ttr.isUserEvent(\"select.pointer\") ||\n\t\t\t\ttr.isUserEvent(\"touch\") ||\n\t\t\t\ttr.isUserEvent(\"select.touch\"),\n\t\t)\n\t);\n}\n\nfunction closeHoverIfNeeded(view: EditorView): void {\n\tif (hasHoverTooltips(view.state)) {\n\t\tview.dispatch({ effects: closeHoverTooltips });\n\t}\n}\n\nfunction hoverRequest(plugin: LSPPlugin, pos: number) {\n\tconst client = plugin.client as typeof plugin.client & LspClientInternals;\n\tif (client.hasCapability?.(\"hoverProvider\") === false) {\n\t\treturn Promise.resolve(null);\n\t}\n\n\tplugin.client.sync();\n\treturn plugin.client.request<HoverParams, Hover | null>(\n\t\t\"textDocument/hover\",\n\t\t{\n\t\t\tposition: plugin.toPosition(pos),\n\t\t\ttextDocument: { uri: plugin.uri },\n\t\t},\n\t);\n}\n\nfunction lspTooltipSource(\n\tview: EditorView,\n\tpos: number,\n): Promise<Tooltip | null> {\n\tconst plugin = LSPPlugin.get(view);\n\tif (!plugin) return Promise.resolve(null);\n\n\treturn hoverRequest(plugin, pos).then((result) => {\n\t\tif (!result) return null;\n\n\t\treturn {\n\t\t\tpos: result.range\n\t\t\t\t? fromPosition(view.state.doc, result.range.start)\n\t\t\t\t: pos,\n\t\t\tend: result.range ? fromPosition(view.state.doc, result.range.end) : pos,\n\t\t\tcreate() {\n\t\t\t\tconst dom = document.createElement(\"div\");\n\t\t\t\tdom.className = \"cm-lsp-hover-tooltip cm-lsp-documentation\";\n\t\t\t\tdom.innerHTML = renderTooltipContent(plugin, result.contents);\n\t\t\t\treturn { dom };\n\t\t\t},\n\t\t\tabove: true,\n\t\t};\n\t});\n}\n\nconst closeHoverOnInteraction = ViewPlugin.fromClass(\n\tclass {\n\t\tconstructor(readonly view: EditorView) {}\n\t},\n\t{\n\t\teventObservers: {\n\t\t\tpointerdown() {\n\t\t\t\tcloseHoverIfNeeded(this.view);\n\t\t\t},\n\t\t\ttouchstart() {\n\t\t\t\tcloseHoverIfNeeded(this.view);\n\t\t\t},\n\t\t\twheel() {\n\t\t\t\tcloseHoverIfNeeded(this.view);\n\t\t\t},\n\t\t\tscroll() {\n\t\t\t\tcloseHoverIfNeeded(this.view);\n\t\t\t},\n\t\t},\n\t},\n);\n\nfunction getSignatureHelp(\n\tplugin: LSPPlugin,\n\tpos: number,\n\tcontext: SignatureHelpContext,\n) {\n\tconst client = plugin.client as typeof plugin.client & LspClientInternals;\n\tif (client.hasCapability?.(\"signatureHelpProvider\") === false) {\n\t\treturn Promise.resolve(null);\n\t}\n\n\tplugin.client.sync();\n\treturn plugin.client.request<SignatureHelpParams, LspSignatureHelp | null>(\n\t\t\"textDocument/signatureHelp\",\n\t\t{\n\t\t\tcontext,\n\t\t\tposition: plugin.toPosition(pos),\n\t\t\ttextDocument: { uri: plugin.uri },\n\t\t},\n\t);\n}\n\nfunction sameSignatures(a: LspSignatureHelp, b: LspSignatureHelp): boolean {\n\tif (a.signatures.length !== b.signatures.length) return false;\n\treturn a.signatures.every((signature, index) => {\n\t\treturn signature.label === b.signatures[index]?.label;\n\t});\n}\n\nfunction sameActiveParam(\n\ta: LspSignatureHelp,\n\tb: LspSignatureHelp,\n\tactive: number,\n): boolean {\n\tconst current = a.signatures[active];\n\tconst next = b.signatures[active];\n\tif (!current || !next) return false;\n\n\treturn (\n\t\t(current.activeParameter ?? a.activeParameter) ===\n\t\t(next.activeParameter ?? b.activeParameter)\n\t);\n}\n\nclass SignatureState {\n\tconstructor(\n\t\treadonly data: LspSignatureHelp,\n\t\treadonly active: number,\n\t\treadonly tooltip: Tooltip,\n\t) {}\n}\n\nconst signatureEffect = StateEffect.define<{\n\tdata: LspSignatureHelp;\n\tactive: number;\n\tpos: number;\n} | null>();\n\nfunction signatureTooltip(\n\tdata: LspSignatureHelp,\n\tactive: number,\n\tpos: number,\n): Tooltip {\n\treturn {\n\t\tpos,\n\t\tabove: true,\n\t\tcreate: (view) => drawSignatureTooltip(view, data, active),\n\t};\n}\n\nconst signatureState = StateField.define<SignatureState | null>({\n\tcreate() {\n\t\treturn null;\n\t},\n\tupdate(value, tr) {\n\t\tfor (const effect of tr.effects) {\n\t\t\tif (effect.is(signatureEffect)) {\n\t\t\t\tif (effect.value) {\n\t\t\t\t\treturn new SignatureState(\n\t\t\t\t\t\teffect.value.data,\n\t\t\t\t\t\teffect.value.active,\n\t\t\t\t\t\tsignatureTooltip(\n\t\t\t\t\t\t\teffect.value.data,\n\t\t\t\t\t\t\teffect.value.active,\n\t\t\t\t\t\t\teffect.value.pos,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tif (value && tr.docChanged) {\n\t\t\treturn new SignatureState(value.data, value.active, {\n\t\t\t\t...value.tooltip,\n\t\t\t\tpos: tr.changes.mapPos(value.tooltip.pos),\n\t\t\t});\n\t\t}\n\n\t\treturn value;\n\t},\n\tprovide: (field) =>\n\t\tshowTooltip.from(field, (value) => value?.tooltip ?? null),\n});\n\nfunction drawSignatureTooltip(\n\tview: EditorView,\n\tdata: LspSignatureHelp,\n\tactive: number,\n) {\n\tconst dom = document.createElement(\"div\");\n\tdom.className = \"cm-lsp-signature-tooltip\";\n\n\tif (data.signatures.length > 1) {\n\t\tdom.classList.add(\"cm-lsp-signature-multiple\");\n\t\tconst num = dom.appendChild(document.createElement(\"div\"));\n\t\tnum.className = \"cm-lsp-signature-num\";\n\t\tnum.textContent = `${active + 1}/${data.signatures.length}`;\n\t}\n\n\tconst signature = data.signatures[active];\n\tif (!signature) {\n\t\treturn { dom };\n\t}\n\n\tconst sig = dom.appendChild(document.createElement(\"div\"));\n\tsig.className = \"cm-lsp-signature\";\n\tlet activeFrom = 0;\n\tlet activeTo = 0;\n\tconst activeParamIndex = signature.activeParameter ?? data.activeParameter;\n\tconst activeParam =\n\t\tactiveParamIndex != null && signature.parameters\n\t\t\t? signature.parameters[activeParamIndex]\n\t\t\t: null;\n\n\tif (activeParam && Array.isArray(activeParam.label)) {\n\t\t[activeFrom, activeTo] = activeParam.label;\n\t} else if (activeParam) {\n\t\tconst found = signature.label.indexOf(activeParam.label as string);\n\t\tif (found > -1) {\n\t\t\tactiveFrom = found;\n\t\t\tactiveTo = found + activeParam.label.length;\n\t\t}\n\t}\n\n\tif (activeTo) {\n\t\tsig.appendChild(\n\t\t\tdocument.createTextNode(signature.label.slice(0, activeFrom)),\n\t\t);\n\t\tconst activeElt = sig.appendChild(document.createElement(\"span\"));\n\t\tactiveElt.className = \"cm-lsp-active-parameter\";\n\t\tactiveElt.textContent = signature.label.slice(activeFrom, activeTo);\n\t\tsig.appendChild(document.createTextNode(signature.label.slice(activeTo)));\n\t} else {\n\t\tsig.textContent = signature.label;\n\t}\n\n\tif (signature.documentation) {\n\t\tconst plugin = LSPPlugin.get(view);\n\t\tif (plugin) {\n\t\t\tconst docs = dom.appendChild(document.createElement(\"div\"));\n\t\t\tdocs.className = \"cm-lsp-signature-documentation cm-lsp-documentation\";\n\t\t\tdocs.innerHTML = plugin.docToHTML(signature.documentation);\n\t\t}\n\t}\n\n\treturn { dom };\n}\n\nconst signaturePlugin = ViewPlugin.fromClass(\n\tclass {\n\t\tactiveRequest: { pos: number; drop: boolean } | null = null;\n\t\tdelayedRequest = 0;\n\n\t\tconstructor(readonly view: EditorView) {}\n\n\t\tupdate(update: ViewUpdate) {\n\t\t\tconst pointerOrTouchSelection = isPointerOrTouchSelection(update);\n\n\t\t\tif (this.activeRequest) {\n\t\t\t\tif (update.selectionSet) {\n\t\t\t\t\tthis.activeRequest.drop = true;\n\t\t\t\t\tthis.activeRequest = null;\n\t\t\t\t} else if (update.docChanged) {\n\t\t\t\t\tthis.activeRequest.pos = update.changes.mapPos(\n\t\t\t\t\t\tthis.activeRequest.pos,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst plugin = LSPPlugin.get(update.view);\n\t\t\tif (!plugin) return;\n\n\t\t\tconst sigState = update.view.state.field(signatureState);\n\t\t\tlet triggerCharacter = \"\";\n\n\t\t\tif (\n\t\t\t\tupdate.docChanged &&\n\t\t\t\tupdate.transactions.some((tr) => tr.isUserEvent(\"input.type\"))\n\t\t\t) {\n\t\t\t\tconst serverConf =\n\t\t\t\t\tplugin.client.serverCapabilities?.signatureHelpProvider;\n\t\t\t\tconst triggers = (serverConf?.triggerCharacters || []).concat(\n\t\t\t\t\t(sigState && serverConf?.retriggerCharacters) || [],\n\t\t\t\t);\n\n\t\t\t\tif (triggers.length) {\n\t\t\t\t\tupdate.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {\n\t\t\t\t\t\tconst insertedText = inserted.toString();\n\t\t\t\t\t\tif (!insertedText) return;\n\t\t\t\t\t\tfor (const trigger of triggers) {\n\t\t\t\t\t\t\tif (insertedText.includes(trigger)) {\n\t\t\t\t\t\t\t\ttriggerCharacter = trigger;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (triggerCharacter) {\n\t\t\t\tthis.scheduleRequest(\n\t\t\t\t\tplugin,\n\t\t\t\t\t{\n\t\t\t\t\t\ttriggerKind: 2,\n\t\t\t\t\t\tisRetrigger: !!sigState,\n\t\t\t\t\t\ttriggerCharacter,\n\t\t\t\t\t\tactiveSignatureHelp: sigState?.data,\n\t\t\t\t\t},\n\t\t\t\t\tSIGNATURE_TRIGGER_DELAY,\n\t\t\t\t);\n\t\t\t} else if (sigState && update.selectionSet && !pointerOrTouchSelection) {\n\t\t\t\tthis.scheduleRequest(\n\t\t\t\t\tplugin,\n\t\t\t\t\t{\n\t\t\t\t\t\ttriggerKind: 3,\n\t\t\t\t\t\tisRetrigger: true,\n\t\t\t\t\t\tactiveSignatureHelp: sigState.data,\n\t\t\t\t\t},\n\t\t\t\t\tSIGNATURE_RETRIGGER_DELAY,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tscheduleRequest(\n\t\t\tplugin: LSPPlugin,\n\t\t\tcontext: SignatureHelpContext,\n\t\t\tdelay: number,\n\t\t) {\n\t\t\tif (this.delayedRequest) {\n\t\t\t\tclearTimeout(this.delayedRequest);\n\t\t\t}\n\t\t\tthis.delayedRequest = window.setTimeout(() => {\n\t\t\t\tthis.delayedRequest = 0;\n\t\t\t\tthis.startRequest(plugin, context);\n\t\t\t}, delay);\n\t\t}\n\n\t\tstartRequest(plugin: LSPPlugin, context: SignatureHelpContext) {\n\t\t\tif (this.delayedRequest) {\n\t\t\t\tclearTimeout(this.delayedRequest);\n\t\t\t\tthis.delayedRequest = 0;\n\t\t\t}\n\n\t\t\tconst { view } = plugin;\n\t\t\tconst pos = view.state.selection.main.head;\n\n\t\t\tif (this.activeRequest) this.activeRequest.drop = true;\n\t\t\tconst request = (this.activeRequest = { pos, drop: false });\n\n\t\t\tgetSignatureHelp(plugin, pos, context).then(\n\t\t\t\t(result) => {\n\t\t\t\t\tif (request.drop) return;\n\n\t\t\t\t\tif (result && result.signatures.length) {\n\t\t\t\t\t\tconst current = view.state.field(signatureState);\n\t\t\t\t\t\tconst same = current && sameSignatures(current.data, result);\n\t\t\t\t\t\tconst active =\n\t\t\t\t\t\t\tsame && context.triggerKind === 3\n\t\t\t\t\t\t\t\t? current!.active\n\t\t\t\t\t\t\t\t: (result.activeSignature ?? 0);\n\n\t\t\t\t\t\tif (same && sameActiveParam(current!.data, result, active)) return;\n\n\t\t\t\t\t\tview.dispatch({\n\t\t\t\t\t\t\teffects: signatureEffect.of({\n\t\t\t\t\t\t\t\tdata: result,\n\t\t\t\t\t\t\t\tactive,\n\t\t\t\t\t\t\t\tpos: same ? current!.tooltip.pos : request.pos,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t});\n\t\t\t\t\t} else if (view.state.field(signatureState, false)) {\n\t\t\t\t\t\tview.dispatch({ effects: signatureEffect.of(null) });\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tcontext.triggerKind === 1\n\t\t\t\t\t? (error) => plugin.reportError(\"Signature request failed\", error)\n\t\t\t\t\t: undefined,\n\t\t\t);\n\t\t}\n\n\t\tclose() {\n\t\t\tif (this.delayedRequest) {\n\t\t\t\tclearTimeout(this.delayedRequest);\n\t\t\t\tthis.delayedRequest = 0;\n\t\t\t}\n\t\t\tif (this.activeRequest) {\n\t\t\t\tthis.activeRequest.drop = true;\n\t\t\t\tthis.activeRequest = null;\n\t\t\t}\n\t\t\tif (this.view.state.field(signatureState, false)) {\n\t\t\t\tthis.view.dispatch({ effects: signatureEffect.of(null) });\n\t\t\t}\n\t\t}\n\n\t\tdestroy() {\n\t\t\tthis.close();\n\t\t}\n\t},\n\t{\n\t\teventObservers: {\n\t\t\tpointerdown() {\n\t\t\t\tthis.close();\n\t\t\t},\n\t\t\ttouchstart() {\n\t\t\t\tthis.close();\n\t\t\t},\n\t\t\twheel() {\n\t\t\t\tthis.close();\n\t\t\t},\n\t\t\tscroll() {\n\t\t\t\tthis.close();\n\t\t\t},\n\t\t},\n\t},\n);\n\nexport const showSignatureHelp: Command = (view) => {\n\tlet plugin = view.plugin(signaturePlugin);\n\tif (!plugin) {\n\t\tview.dispatch({\n\t\t\teffects: StateEffect.appendConfig.of([signatureState, signaturePlugin]),\n\t\t});\n\t\tplugin = view.plugin(signaturePlugin);\n\t}\n\n\tconst field = view.state.field(signatureState);\n\tif (!plugin || field === undefined) return false;\n\n\tconst lspPlugin = LSPPlugin.get(view);\n\tif (!lspPlugin) return false;\n\n\tplugin.startRequest(lspPlugin, {\n\t\ttriggerKind: 1,\n\t\tactiveSignatureHelp: field ? field.data : undefined,\n\t\tisRetrigger: !!field,\n\t});\n\treturn true;\n};\n\nexport const nextSignature: Command = (view) => {\n\tconst field = view.state.field(signatureState, false);\n\tif (!field) return false;\n\tif (field.active < field.data.signatures.length - 1) {\n\t\tview.dispatch({\n\t\t\teffects: signatureEffect.of({\n\t\t\t\tdata: field.data,\n\t\t\t\tactive: field.active + 1,\n\t\t\t\tpos: field.tooltip.pos,\n\t\t\t}),\n\t\t});\n\t}\n\treturn true;\n};\n\nexport const prevSignature: Command = (view) => {\n\tconst field = view.state.field(signatureState, false);\n\tif (!field) return false;\n\tif (field.active > 0) {\n\t\tview.dispatch({\n\t\t\teffects: signatureEffect.of({\n\t\t\t\tdata: field.data,\n\t\t\t\tactive: field.active - 1,\n\t\t\t\tpos: field.tooltip.pos,\n\t\t\t}),\n\t\t});\n\t}\n\treturn true;\n};\n\nexport const signatureKeymap: readonly KeyBinding[] = [\n\t{ key: \"Mod-Shift-Space\", run: showSignatureHelp },\n\t{ key: \"Mod-Shift-ArrowUp\", run: prevSignature },\n\t{ key: \"Mod-Shift-ArrowDown\", run: nextSignature },\n];\n\nexport function hoverTooltips(config: { hoverTime?: number } = {}): Extension {\n\treturn [\n\t\thoverTooltip(lspTooltipSource, {\n\t\t\thideOnChange: true,\n\t\t\thoverTime: config.hoverTime,\n\t\t}),\n\t\tcloseHoverOnInteraction,\n\t];\n}\n\nexport function signatureHelp(config: { keymap?: boolean } = {}): Extension {\n\treturn [\n\t\tsignatureState,\n\t\tsignaturePlugin,\n\t\tconfig.keymap === false ? [] : Prec.high(keymap.of(signatureKeymap)),\n\t];\n}\n"
  },
  {
    "path": "src/cm/lsp/transport.ts",
    "content": "/*\n\tLanguage servers that expose stdio are proxied through a lightweight\n\tWebSocket bridge so the CodeMirror client can continue to speak WebSocket.\n*/\n\nimport type { Transport } from \"@codemirror/lsp-client\";\nimport type {\n\tLspServerDefinition,\n\tTransportContext,\n\tTransportHandle,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nconst DEFAULT_TIMEOUT = 5000;\nconst RECONNECT_BASE_DELAY = 500;\nconst RECONNECT_MAX_DELAY = 10000;\nconst RECONNECT_MAX_ATTEMPTS = 5;\n\ntype MessageListener = (data: string) => void;\n\ninterface TransportInterface extends Transport {\n\tsend(message: string): void;\n\tsubscribe(handler: MessageListener): void;\n\tunsubscribe(handler: MessageListener): void;\n}\n\nfunction createWebSocketTransport(\n\tserver: LspServerDefinition,\n\tcontext: TransportContext,\n): TransportHandle {\n\tconst transport = server.transport;\n\tif (!transport) {\n\t\tthrow new Error(\n\t\t\t`LSP server ${server.id} is missing transport configuration`,\n\t\t);\n\t}\n\n\tlet url = transport.url;\n\tconst options: WebSocketTransportOptions = transport.options ?? {};\n\n\t// Use dynamic port from auto-port discovery if available\n\tif (context.dynamicPort && context.dynamicPort > 0) {\n\t\turl = `ws://127.0.0.1:${context.dynamicPort}/`;\n\t\tconsole.info(\n\t\t\t`[LSP:${server.id}] Using auto-discovered port ${context.dynamicPort}`,\n\t\t);\n\t}\n\n\t// URL is only required when not using dynamic port\n\tif (!url) {\n\t\tthrow new Error(\n\t\t\t`WebSocket transport for ${server.id} has no URL (and no dynamic port available)`,\n\t\t);\n\t}\n\n\t// Store validated URL in a const for TypeScript narrowing in nested functions\n\tconst wsUrl: string = url;\n\n\tconst listeners = new Set<MessageListener>();\n\tconst binaryMode = !!options.binary;\n\tconst timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\tconst enableReconnect = options.reconnect !== false;\n\tconst maxReconnectAttempts =\n\t\toptions.maxReconnectAttempts ?? RECONNECT_MAX_ATTEMPTS;\n\n\tlet socket: WebSocket | null = null;\n\tlet disposed = false;\n\tlet reconnectAttempts = 0;\n\tlet reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet connected = false;\n\n\tconst encoder = binaryMode ? new TextEncoder() : null;\n\n\tfunction createSocket(): WebSocket {\n\t\ttry {\n\t\t\t// pylsp's websocket endpoint does not require subprotocol negotiation.\n\t\t\t// Avoid passing protocols to keep the handshake simple.\n\t\t\tconst ws = new WebSocket(wsUrl);\n\t\t\tif (binaryMode) {\n\t\t\t\tws.binaryType = \"arraybuffer\";\n\t\t\t}\n\t\t\treturn ws;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to construct WebSocket for ${server.id} (${wsUrl}): ${message}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction handleMessage(event: MessageEvent): void {\n\t\tlet data: string;\n\t\tif (typeof event.data === \"string\") {\n\t\t\tdata = event.data;\n\t\t} else if (event.data instanceof Blob) {\n\t\t\t// Handle Blob synchronously by queuing - avoids async ordering issues\n\t\t\tevent.data\n\t\t\t\t.text()\n\t\t\t\t.then((text: string) => {\n\t\t\t\t\tdispatchToListeners(text);\n\t\t\t\t})\n\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\tconsole.error(\"Failed to read Blob message\", err);\n\t\t\t\t});\n\t\t\treturn;\n\t\t} else if (event.data instanceof ArrayBuffer) {\n\t\t\tdata = new TextDecoder().decode(event.data);\n\t\t} else {\n\t\t\tconsole.warn(\n\t\t\t\t\"Unknown WebSocket message type\",\n\t\t\t\ttypeof event.data,\n\t\t\t\tevent.data,\n\t\t\t);\n\t\t\tdata = String(event.data);\n\t\t}\n\t\tdispatchToListeners(data);\n\t}\n\n\tfunction dispatchToListeners(data: string): void {\n\t\t// Debugging aid while stabilising websocket transport\n\t\tif (context?.debugWebSocket) {\n\t\t\tconsole.debug(`[LSP:${server.id}] <=`, data);\n\t\t}\n\n\t\t// Temporary fix\n\t\t// Intercept server requests that the CodeMirror LSP client doesn't handle\n\t\t// The client only handles notifications, but some servers (e.g., TypeScript)\n\t\t// send requests like window/workDoneProgress/create that need a response\n\t\ttry {\n\t\t\tconst msg = JSON.parse(data);\n\t\t\tif (\n\t\t\t\tmsg &&\n\t\t\t\ttypeof msg.id !== \"undefined\" &&\n\t\t\t\tmsg.method === \"window/workDoneProgress/create\"\n\t\t\t) {\n\t\t\t\t// This is a request, respond with success\n\t\t\t\tconst response = JSON.stringify({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tid: msg.id,\n\t\t\t\t\tresult: null,\n\t\t\t\t});\n\t\t\t\tif (context?.debugWebSocket) {\n\t\t\t\t\tconsole.debug(`[LSP:${server.id}] => (auto-response)`, response);\n\t\t\t\t}\n\t\t\t\tif (socket && socket.readyState === WebSocket.OPEN) {\n\t\t\t\t\tif (binaryMode && encoder) {\n\t\t\t\t\t\tsocket.send(encoder.encode(response));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsocket.send(response);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Don't pass this request to listeners since we handled it\n\t\t\t\tconsole.info(\n\t\t\t\t\t`[LSP:${server.id}] Auto-responded to window/workDoneProgress/create`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (_) {\n\t\t\t// Not valid JSON or missing fields, pass through normally\n\t\t}\n\n\t\tlisteners.forEach((listener) => {\n\t\t\ttry {\n\t\t\t\tlistener(data);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"LSP transport listener failed\", error);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction handleClose(event: CloseEvent): void {\n\t\tconnected = false;\n\t\tif (disposed) return;\n\n\t\tconst wasClean = event.wasClean || event.code === 1000;\n\t\tif (wasClean) {\n\t\t\tconsole.info(`[LSP:${server.id}] WebSocket closed cleanly`);\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.warn(\n\t\t\t`[LSP:${server.id}] WebSocket closed unexpectedly (code: ${event.code})`,\n\t\t);\n\n\t\tif (enableReconnect && reconnectAttempts < maxReconnectAttempts) {\n\t\t\tscheduleReconnect();\n\t\t} else if (reconnectAttempts >= maxReconnectAttempts) {\n\t\t\tconsole.error(`[LSP:${server.id}] Max reconnection attempts reached`);\n\t\t}\n\t}\n\n\tfunction handleError(event: Event): void {\n\t\tif (disposed) return;\n\t\tconst errorEvent = event as ErrorEvent;\n\t\tconst reason =\n\t\t\terrorEvent?.message || errorEvent?.type || \"connection error\";\n\t\tconsole.error(`[LSP:${server.id}] WebSocket error: ${reason}`);\n\t}\n\n\tfunction scheduleReconnect(): void {\n\t\tif (disposed || reconnectTimer) return;\n\n\t\tconst delay = Math.min(\n\t\t\tRECONNECT_BASE_DELAY * Math.pow(2, reconnectAttempts),\n\t\t\tRECONNECT_MAX_DELAY,\n\t\t);\n\t\treconnectAttempts++;\n\n\t\tconsole.info(\n\t\t\t`[LSP:${server.id}] Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})`,\n\t\t);\n\n\t\treconnectTimer = setTimeout(() => {\n\t\t\treconnectTimer = null;\n\t\t\tif (disposed) return;\n\t\t\tattemptReconnect();\n\t\t}, delay);\n\t}\n\n\tfunction attemptReconnect(): void {\n\t\tif (disposed) return;\n\n\t\ttry {\n\t\t\tsocket = createSocket();\n\t\t\tsetupSocketHandlers(socket);\n\n\t\t\tsocket.onopen = () => {\n\t\t\t\tconnected = true;\n\t\t\t\treconnectAttempts = 0;\n\t\t\t\tconsole.info(`[LSP:${server.id}] Reconnected successfully`);\n\t\t\t\tif (socket) {\n\t\t\t\t\tsocket.onopen = null;\n\t\t\t\t}\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`[LSP:${server.id}] Reconnection failed`, error);\n\t\t\tif (reconnectAttempts < maxReconnectAttempts) {\n\t\t\t\tscheduleReconnect();\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction setupSocketHandlers(ws: WebSocket): void {\n\t\tws.onmessage = handleMessage;\n\t\tws.onclose = handleClose;\n\t\tws.onerror = handleError;\n\t}\n\n\t// Initial socket creation\n\tsocket = createSocket();\n\n\tconst ready = new Promise<void>((resolve, reject) => {\n\t\tconst timeoutId = setTimeout(() => {\n\t\t\tif (socket) {\n\t\t\t\tsocket.onopen = null;\n\t\t\t\tsocket.onerror = null;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket?.close();\n\t\t\t} catch (_) {\n\t\t\t\t// Ignore close errors\n\t\t\t}\n\t\t\treject(new Error(`Timed out opening WebSocket for ${server.id}`));\n\t\t}, timeout);\n\n\t\tif (socket) {\n\t\t\tsocket.onopen = () => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tconnected = true;\n\t\t\t\tif (socket) {\n\t\t\t\t\tsetupSocketHandlers(socket);\n\t\t\t\t}\n\t\t\t\tresolve();\n\t\t\t};\n\n\t\t\tsocket.onerror = (event: Event) => {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tif (socket) {\n\t\t\t\t\tsocket.onopen = null;\n\t\t\t\t\tsocket.onerror = null;\n\t\t\t\t}\n\t\t\t\tconst errorEvent = event as ErrorEvent;\n\t\t\t\tconst reason =\n\t\t\t\t\terrorEvent?.message || errorEvent?.type || \"connection error\";\n\t\t\t\treject(new Error(`WebSocket error for ${server.id}: ${reason}`));\n\t\t\t};\n\t\t}\n\t});\n\n\tconst transportInterface: TransportInterface = {\n\t\tsend(message: string): void {\n\t\t\tif (!connected || !socket || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\tthrow new Error(\"WebSocket transport is not open\");\n\t\t\t}\n\t\t\tif (binaryMode && encoder) {\n\t\t\t\tsocket.send(encoder.encode(message));\n\t\t\t} else {\n\t\t\t\tsocket.send(message);\n\t\t\t}\n\t\t},\n\t\tsubscribe(handler: MessageListener): void {\n\t\t\tlisteners.add(handler);\n\t\t},\n\t\tunsubscribe(handler: MessageListener): void {\n\t\t\tlisteners.delete(handler);\n\t\t},\n\t};\n\n\tconst dispose = (): void => {\n\t\tdisposed = true;\n\t\tconnected = false;\n\n\t\tif (reconnectTimer) {\n\t\t\tclearTimeout(reconnectTimer);\n\t\t\treconnectTimer = null;\n\t\t}\n\n\t\tlisteners.clear();\n\n\t\tif (socket) {\n\t\t\tif (\n\t\t\t\tsocket.readyState === WebSocket.CLOSED ||\n\t\t\t\tsocket.readyState === WebSocket.CLOSING\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsocket.close(1000, \"Client disposed\");\n\t\t\t} catch (_) {\n\t\t\t\t// Ignore close errors\n\t\t\t}\n\t\t}\n\t};\n\n\treturn { transport: transportInterface, dispose, ready };\n}\n\nfunction createStdioTransport(\n\tserver: LspServerDefinition,\n\tcontext: TransportContext,\n): TransportHandle {\n\tif (!server.transport) {\n\t\tthrow new Error(\n\t\t\t`LSP server ${server.id} is missing transport configuration`,\n\t\t);\n\t}\n\tif (\n\t\t!server.transport.url &&\n\t\t!(context.dynamicPort && context.dynamicPort > 0)\n\t) {\n\t\tthrow new Error(\n\t\t\t`STDIO transport for ${server.id} is missing a websocket bridge url`,\n\t\t);\n\t}\n\tif (!server.transport.options?.binary) {\n\t\tconsole.info(\n\t\t\t`LSP server ${server.id} is using stdio bridge without binary mode. Falling back to text frames.`,\n\t\t);\n\t}\n\treturn createWebSocketTransport(server, context);\n}\n\nexport function createTransport(\n\tserver: LspServerDefinition,\n\tcontext: TransportContext = {},\n): TransportHandle {\n\tif (!server) {\n\t\tthrow new Error(\"createTransport requires a server configuration\");\n\t}\n\tif (!server.transport) {\n\t\tthrow new Error(\n\t\t\t`LSP server ${server.id || \"unknown\"} is missing transport configuration`,\n\t\t);\n\t}\n\n\tconst kind = server.transport.kind;\n\tif (!kind) {\n\t\tthrow new Error(\n\t\t\t`LSP server ${server.id} transport is missing 'kind' property`,\n\t\t);\n\t}\n\n\tswitch (kind) {\n\t\tcase \"websocket\":\n\t\t\treturn createWebSocketTransport(server, context);\n\t\tcase \"stdio\":\n\t\t\treturn createStdioTransport(server, context);\n\t\tcase \"external\":\n\t\t\tif (typeof server.transport.create === \"function\") {\n\t\t\t\treturn server.transport.create(server, context);\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t`LSP server ${server.id} declares an external transport without a create() factory`,\n\t\t\t);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported transport kind: ${kind}`);\n\t}\n}\n\nexport default { createTransport };\n"
  },
  {
    "path": "src/cm/lsp/types.ts",
    "content": "import type {\n\tLSPClient,\n\tLSPClientConfig,\n\tLSPClientExtension,\n\tTransport,\n\tWorkspace,\n\tWorkspaceFile,\n} from \"@codemirror/lsp-client\";\nimport type { ChangeSet, Extension, MapMode, Text } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\n\nimport type {\n\tDiagnostic as LSPDiagnostic,\n\tFormattingOptions as LSPFormattingOptions,\n\tPosition,\n\tRange,\n\tTextEdit,\n} from \"vscode-languageserver-types\";\n\nexport type {\n\tLSPClient,\n\tLSPClientConfig,\n\tLSPClientExtension,\n\tLSPDiagnostic,\n\tLSPFormattingOptions,\n\tPosition,\n\tRange,\n\tTextEdit,\n\tTransport,\n\tWorkspace,\n\tWorkspaceFile,\n};\n\nexport interface WorkspaceFileUpdate {\n\tfile: WorkspaceFile;\n\tprevDoc: Text;\n\tchanges: ChangeSet;\n}\n\n// ============================================================================\n// Transport Types\n// ============================================================================\n\nexport type TransportKind = \"websocket\" | \"stdio\" | \"external\";\ntype MaybePromise<T> = T | Promise<T>;\n\nexport interface WebSocketTransportOptions {\n\tbinary?: boolean;\n\ttimeout?: number;\n\treconnect?: boolean;\n\tmaxReconnectAttempts?: number;\n}\n\nexport interface TransportDescriptor {\n\tkind: TransportKind;\n\turl?: string;\n\tcommand?: string;\n\targs?: string[];\n\toptions?: WebSocketTransportOptions;\n\tprotocols?: string[];\n\tcreate?: (\n\t\tserver: LspServerDefinition,\n\t\tcontext: TransportContext,\n\t) => TransportHandle;\n}\n\nexport interface TransportHandle {\n\ttransport: Transport;\n\tdispose: () => Promise<void> | void;\n\tready: Promise<void>;\n}\n\nexport interface TransportContext {\n\turi?: string;\n\tfile?: AcodeFile;\n\tview?: EditorView;\n\tlanguageId?: string;\n\trootUri?: string | null;\n\toriginalRootUri?: string;\n\tdebugWebSocket?: boolean;\n\t/** Dynamically discovered port from auto-port discovery */\n\tdynamicPort?: number;\n}\n\n// ============================================================================\n// Server Registry Types\n// ============================================================================\n\nexport interface BridgeConfig {\n\tkind: \"axs\";\n\t/** Optional port - if not provided, auto-port discovery will be used */\n\tport?: number;\n\tcommand: string;\n\targs?: string[];\n\t/** Session ID for port file naming (defaults to command name) */\n\tsession?: string;\n}\n\nexport type InstallerKind =\n\t| \"apk\"\n\t| \"npm\"\n\t| \"pip\"\n\t| \"cargo\"\n\t| \"github-release\"\n\t| \"manual\"\n\t| \"shell\";\n\nexport interface LauncherInstallConfig {\n\tkind?: InstallerKind;\n\tcommand?: string;\n\tupdateCommand?: string;\n\tuninstallCommand?: string;\n\tlabel?: string;\n\tsource?: string;\n\texecutable?: string;\n\tpackages?: string[];\n\tpipCommand?: string;\n\tnpmCommand?: string;\n\tpythonCommand?: string;\n\tglobal?: boolean;\n\tbreakSystemPackages?: boolean;\n\trepo?: string;\n\tassetNames?: Record<string, string>;\n\tarchiveType?: \"zip\" | \"binary\";\n\textractFile?: string;\n\tbinaryPath?: string;\n}\n\nexport interface LauncherConfig {\n\tcommand?: string;\n\targs?: string[];\n\tstartCommand?: string | string[];\n\tcheckCommand?: string;\n\tversionCommand?: string;\n\tupdateCommand?: string;\n\tuninstallCommand?: string;\n\tinstall?: LauncherInstallConfig;\n\tbridge?: BridgeConfig;\n}\n\nexport interface BuiltinExtensionsConfig {\n\thover?: boolean;\n\tcompletion?: boolean;\n\tsignature?: boolean;\n\tkeymaps?: boolean;\n\tdiagnostics?: boolean;\n\tinlayHints?: boolean;\n\tformatting?: boolean;\n}\n\nexport interface AcodeClientConfig {\n\tuseDefaultExtensions?: boolean;\n\tbuiltinExtensions?: BuiltinExtensionsConfig;\n\textensions?: (Extension | LSPClientExtension)[];\n\tnotificationHandlers?: Record<\n\t\tstring,\n\t\t(client: LSPClient, params: unknown) => boolean\n\t>;\n\tworkspace?: (client: LSPClient) => Workspace;\n\trootUri?: string;\n\ttimeout?: number;\n}\n\nexport interface LanguageResolverContext {\n\tlanguageId: string;\n\tlanguageName?: string;\n\turi?: string;\n\tfile?: AcodeFile;\n}\n\nexport interface DocumentUriContext extends RootUriContext {\n\tnormalizedUri?: string | null;\n}\n\nexport interface LspServerManifest {\n\tid?: string;\n\tlabel?: string;\n\tenabled?: boolean;\n\tlanguages?: string[];\n\ttransport?: TransportDescriptor;\n\tinitializationOptions?: Record<string, unknown>;\n\tclientConfig?: Record<string, unknown> | AcodeClientConfig;\n\tstartupTimeout?: number;\n\tcapabilityOverrides?: Record<string, unknown>;\n\trootUri?:\n\t\t| ((uri: string, context: unknown) => MaybePromise<string | null>)\n\t\t| ((uri: string, context: RootUriContext) => MaybePromise<string | null>)\n\t\t| null;\n\tdocumentUri?:\n\t\t| ((\n\t\t\t\turi: string,\n\t\t\t\tcontext: DocumentUriContext,\n\t\t  ) => MaybePromise<string | null | undefined>)\n\t\t| null;\n\tresolveLanguageId?:\n\t\t| ((context: LanguageResolverContext) => string | null)\n\t\t| null;\n\tlauncher?: LauncherConfig;\n\tuseWorkspaceFolders?: boolean;\n}\n\nexport interface LspServerBundle {\n\tid: string;\n\tlabel?: string;\n\tgetServers: () => LspServerManifest[];\n\tgetExecutable?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t) => string | null | undefined;\n\tcheckInstallation?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t) => Promise<InstallCheckResult | null | undefined>;\n\tinstallServer?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t\tmode: \"install\" | \"update\" | \"reinstall\",\n\t\toptions?: { promptConfirm?: boolean },\n\t) => Promise<boolean>;\n\tuninstallServer?: (\n\t\tserverId: string,\n\t\tmanifest: LspServerManifest,\n\t\toptions?: { promptConfirm?: boolean },\n\t) => Promise<boolean>;\n}\n\nexport type LspServerProvider = LspServerBundle;\n\nexport interface LspServerDefinition {\n\tid: string;\n\tlabel: string;\n\tenabled: boolean;\n\tlanguages: string[];\n\ttransport: TransportDescriptor;\n\tinitializationOptions?: Record<string, unknown>;\n\tclientConfig?: AcodeClientConfig;\n\tstartupTimeout?: number;\n\tcapabilityOverrides?: Record<string, unknown>;\n\trootUri?:\n\t\t| ((uri: string, context: RootUriContext) => MaybePromise<string | null>)\n\t\t| null;\n\tdocumentUri?:\n\t\t| ((\n\t\t\t\turi: string,\n\t\t\t\tcontext: DocumentUriContext,\n\t\t  ) => MaybePromise<string | null | undefined>)\n\t\t| null;\n\tresolveLanguageId?:\n\t\t| ((context: LanguageResolverContext) => string | null)\n\t\t| null;\n\tlauncher?: LauncherConfig;\n\t/**\n\t * When true, uses a single server instance with workspace folders\n\t * instead of starting separate servers per project root.\n\t * Heavy LSP servers like TypeScript and rust-analyzer should use this.\n\t */\n\tuseWorkspaceFolders?: boolean;\n}\n\nexport interface RootUriContext {\n\turi?: string;\n\tfile?: AcodeFile;\n\tview?: EditorView;\n\tlanguageId?: string;\n\trootUri?: string;\n}\n\nexport type RegistryEventType = \"register\" | \"unregister\" | \"update\";\n\nexport type RegistryEventListener = (\n\tevent: RegistryEventType,\n\tserver: LspServerDefinition,\n) => void;\n\n// ============================================================================\n// Client Manager Types\n// ============================================================================\n\nexport interface FileMetadata {\n\turi: string;\n\tlanguageId?: string;\n\tlanguageName?: string;\n\tview?: EditorView;\n\tfile?: AcodeFile;\n\trootUri?: string;\n}\n\nexport interface FormattingOptions {\n\ttabSize?: number;\n\tinsertSpaces?: boolean;\n\t[key: string]: unknown;\n}\n\nexport interface ClientManagerOptions {\n\tdiagnosticsUiExtension?: Extension | Extension[];\n\tclientExtensions?: Extension | Extension[];\n\tresolveRoot?: (context: RootUriContext) => Promise<string | null>;\n\tdisplayFile?: (uri: string) => Promise<EditorView | null>;\n\topenFile?: (uri: string) => Promise<EditorView | null>;\n\tresolveLanguageId?: (uri: string) => string | null;\n\tonClientIdle?: (info: ClientIdleInfo) => void;\n}\n\nexport interface ClientIdleInfo {\n\tserver: LspServerDefinition;\n\tclient: LSPClient;\n\trootUri: string | null;\n}\n\nexport interface ClientState {\n\tserver: LspServerDefinition;\n\tclient: LSPClient;\n\ttransport: TransportHandle;\n\trootUri: string | null;\n\tattach: (uri: string, view: EditorView, aliases?: string[]) => void;\n\tdetach: (uri: string, view?: EditorView) => void;\n\tdispose: () => Promise<void>;\n}\n\nexport interface NormalizedRootUri {\n\tnormalizedRootUri: string | null;\n\toriginalRootUri: string | null;\n}\n\n// ============================================================================\n// Server Launcher Types\n// ============================================================================\n\nexport interface ManagedServerEntry {\n\tuuid: string;\n\tcommand: string;\n\tstartedAt: number;\n\t/** Port number for the axs proxy (for stats endpoint) */\n\tport?: number;\n}\n\nexport type InstallStatus = \"present\" | \"declined\" | \"failed\";\n\nexport interface InstallCheckResult {\n\tstatus: \"present\" | \"missing\" | \"failed\" | \"unknown\";\n\tversion?: string | null;\n\tcanInstall: boolean;\n\tcanUpdate: boolean;\n\tmessage?: string;\n}\n\n/**\n * Port information from auto-port discovery\n */\nexport interface PortInfo {\n\t/** The discovered port number */\n\tport: number;\n\t/** Path to the port file */\n\tfilePath: string;\n\t/** Session ID used for the port file */\n\tsession: string;\n}\n\nexport interface WaitOptions {\n\tattempts?: number;\n\tdelay?: number;\n\tprobeTimeout?: number;\n}\n\n/**\n * Result from ensureServerRunning\n */\nexport interface EnsureServerResult {\n\tuuid: string | null;\n\t/** Port discovered from port file (for auto-port discovery) */\n\tdiscoveredPort?: number;\n}\n\n/**\n * Stats returned from the axs proxy /status endpoint\n */\nexport interface LspServerStats {\n\tprogram: string;\n\tprocesses: Array<{\n\t\tpid: number;\n\t\tuptime_secs: number;\n\t\tmemory_bytes: number;\n\t}>;\n}\n\n/**\n * Formatted stats for UI display\n */\nexport interface LspServerStatsFormatted {\n\tmemoryBytes: number;\n\tmemoryFormatted: string;\n\tuptimeSeconds: number;\n\tuptimeFormatted: string;\n\tpid: number | null;\n\tprocessCount: number;\n}\n\n// ============================================================================\n// Workspace Types\n// ============================================================================\n\nexport interface WorkspaceOptions {\n\tdisplayFile?: (uri: string) => Promise<EditorView | null>;\n\topenFile?: (uri: string) => Promise<EditorView | null>;\n\tresolveLanguageId?: (uri: string) => string | null;\n}\n\n// ============================================================================\n// Diagnostics Types\n// ============================================================================\n\nexport interface LspDiagnostic {\n\tfrom: number;\n\tto: number;\n\tseverity: \"error\" | \"warning\" | \"info\" | \"hint\";\n\tmessage: string;\n\tsource?: string;\n\t/** Related diagnostic information (e.g., location of declaration for 'unused' errors) */\n\trelatedInformation?: DiagnosticRelatedInformation[];\n}\n\n/** Related information for a diagnostic (mapped to editor positions) */\nexport interface DiagnosticRelatedInformation {\n\t/** Document URI */\n\turi: string;\n\t/** Start position (offset in document) */\n\tfrom: number;\n\t/** End position (offset in document) */\n\tto: number;\n\t/** Message describing the relationship */\n\tmessage: string;\n}\n\nexport interface PublishDiagnosticsParams {\n\turi: string;\n\tversion?: number;\n\tdiagnostics: RawDiagnostic[];\n}\n\nexport interface RawDiagnostic {\n\trange: Range;\n\tseverity?: number;\n\tcode?: number | string;\n\tsource?: string;\n\tmessage: string;\n\t/** Related diagnostic locations from LSP (raw positions) */\n\trelatedInformation?: RawDiagnosticRelatedInformation[];\n}\n\n/** Raw related information from LSP (before position mapping) */\nexport interface RawDiagnosticRelatedInformation {\n\tlocation: {\n\t\turi: string;\n\t\trange: Range;\n\t};\n\tmessage: string;\n}\n\n// ============================================================================\n// Formatter Types\n// ============================================================================\n\nexport interface AcodeApi {\n\tregisterFormatter: (\n\t\tid: string,\n\t\textensions: string[],\n\t\tformatter: () => Promise<boolean>,\n\t\tlabel: string,\n\t) => void;\n}\n\n/**\n * Uri utility interface\n */\nexport interface ParsedUri {\n\tdocId?: string;\n\trootUri?: string;\n\tisFileUri?: boolean;\n}\n\n/**\n * Interface representing the LSPPlugin instance API.\n */\nexport interface LSPPluginAPI {\n\t/** The document URI this plugin is attached to */\n\turi: string;\n\t/** The LSP client instance */\n\tclient: LSPClient & { sync: () => void; connected?: boolean };\n\t/** Convert a document offset to an LSP Position */\n\ttoPosition: (offset: number) => { line: number; character: number };\n\t/** Convert an LSP Position to a document offset */\n\tfromPosition: (\n\t\tpos: { line: number; character: number },\n\t\tdoc?: unknown,\n\t) => number;\n\t/** The currently synced document state */\n\tsyncedDoc: { length: number };\n\t/** Pending changes that haven't been synced yet */\n\tunsyncedChanges: {\n\t\tmapPos: (pos: number, assoc?: number, mode?: MapMode) => number | null;\n\t\tempty: boolean;\n\t};\n\t/** Clear pending changes */\n\tclear: () => void;\n}\n\n/**\n * Interface for workspace file with view access\n */\nexport interface WorkspaceFileWithView {\n\tversion: number;\n\tgetView: () => EditorView | null;\n}\n\n/**\n * Interface for workspace with file access\n */\nexport interface WorkspaceWithFileAccess {\n\tgetFile: (uri: string) => WorkspaceFileWithView | null;\n}\n\n/**\n * LSPClient with workspace access (for type casting in notification handlers)\n */\nexport interface LSPClientWithWorkspace {\n\tworkspace: WorkspaceWithFileAccess;\n}\n\n// Extend the LSPClient with Acode-specific properties\ndeclare module \"@codemirror/lsp-client\" {\n\tinterface LSPClient {\n\t\t__acodeLoggedInfo?: boolean;\n\t}\n}\n"
  },
  {
    "path": "src/cm/lsp/workspace.ts",
    "content": "import type { WorkspaceFile } from \"@codemirror/lsp-client\";\nimport { LSPPlugin, Workspace } from \"@codemirror/lsp-client\";\nimport type { Text, TransactionSpec } from \"@codemirror/state\";\nimport type { EditorView } from \"@codemirror/view\";\nimport { getModeForPath } from \"cm/modelist\";\nimport type { WorkspaceFileUpdate, WorkspaceOptions } from \"./types\";\n\nclass AcodeWorkspaceFile implements WorkspaceFile {\n\turi: string;\n\tlanguageId: string;\n\tversion: number;\n\tdoc: Text;\n\tviews: Set<EditorView>;\n\n\tconstructor(\n\t\turi: string,\n\t\tlanguageId: string,\n\t\tversion: number,\n\t\tdoc: Text,\n\t\tview?: EditorView,\n\t) {\n\t\tthis.uri = uri;\n\t\tthis.languageId = languageId;\n\t\tthis.version = version;\n\t\tthis.doc = doc;\n\t\tthis.views = new Set();\n\t\tif (view) this.views.add(view);\n\t}\n\n\tgetView(preferred?: EditorView): EditorView | null {\n\t\tif (preferred && this.views.has(preferred)) return preferred;\n\t\tconst iterator = this.views.values();\n\t\tconst next = iterator.next();\n\t\treturn next.done ? null : next.value;\n\t}\n}\n\nexport default class AcodeWorkspace extends Workspace {\n\tfiles: AcodeWorkspaceFile[];\n\toptions: WorkspaceOptions;\n\n\t#fileMap: Map<string, AcodeWorkspaceFile>;\n\t#versions: Record<string, number>;\n\t#workspaceFolders: Set<string>;\n\n\tconstructor(\n\t\tclient: ConstructorParameters<typeof Workspace>[0],\n\t\toptions: WorkspaceOptions = {},\n\t) {\n\t\tsuper(client);\n\t\tthis.files = [];\n\t\tthis.#fileMap = new Map();\n\t\tthis.#versions = Object.create(null) as Record<string, number>;\n\t\tthis.#workspaceFolders = new Set();\n\t\tthis.options = options;\n\t}\n\n\t#getOrCreateFile(\n\t\turi: string,\n\t\tlanguageId: string,\n\t\tview: EditorView,\n\t): AcodeWorkspaceFile {\n\t\tlet file = this.#fileMap.get(uri);\n\t\tif (!file) {\n\t\t\tconst doc = view.state?.doc;\n\t\t\tif (!doc) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Cannot create workspace file without document: ${uri}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tfile = new AcodeWorkspaceFile(\n\t\t\t\turi,\n\t\t\t\tlanguageId,\n\t\t\t\tthis.#nextFileVersion(uri),\n\t\t\t\tdoc,\n\t\t\t\tview,\n\t\t\t);\n\t\t\tthis.#fileMap.set(uri, file);\n\t\t\tthis.files.push(file);\n\t\t\tthis.client.didOpen(file);\n\t\t}\n\t\tfile.views.add(view);\n\t\treturn file;\n\t}\n\n\t#getFileEntry(uri: string): AcodeWorkspaceFile | null {\n\t\treturn this.#fileMap.get(uri) ?? null;\n\t}\n\n\t#removeFileEntry(file: AcodeWorkspaceFile): void {\n\t\tthis.#fileMap.delete(file.uri);\n\t\tthis.files = this.files.filter((candidate) => candidate !== file);\n\t}\n\n\t#nextFileVersion(uri: string): number {\n\t\tconst current = this.#versions[uri] ?? -1;\n\t\tconst next = current + 1;\n\t\tthis.#versions[uri] = next;\n\t\treturn next;\n\t}\n\n\t#resolveLanguageIdForUri(uri: string): string {\n\t\tif (typeof this.options.resolveLanguageId === \"function\") {\n\t\t\tconst resolved = this.options.resolveLanguageId(uri);\n\t\t\tif (resolved) return resolved;\n\t\t}\n\t\ttry {\n\t\t\tconst mode = getModeForPath(uri);\n\t\t\tif (mode?.name) {\n\t\t\t\treturn String(mode.name).toLowerCase();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[LSP:Workspace] Failed to resolve language id for ${uri}`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t\treturn \"plaintext\";\n\t}\n\n\tsyncFiles(): readonly WorkspaceFileUpdate[] {\n\t\tconst updates: WorkspaceFileUpdate[] = [];\n\t\tfor (const file of this.files) {\n\t\t\tconst view = file.getView();\n\t\t\tif (!view) continue;\n\t\t\tconst plugin = LSPPlugin.get(view);\n\t\t\tif (!plugin) continue;\n\t\t\tconst { unsyncedChanges } = plugin;\n\t\t\tif (unsyncedChanges.empty) continue;\n\n\t\t\tupdates.push({ file, prevDoc: file.doc, changes: unsyncedChanges });\n\t\t\tfile.doc = view.state.doc;\n\t\t\tfile.version = this.#nextFileVersion(file.uri);\n\t\t\tplugin.clear();\n\t\t}\n\t\treturn updates;\n\t}\n\n\topenFile(uri: string, languageId: string, view: EditorView): void {\n\t\tif (!view) return;\n\t\tthis.#getOrCreateFile(uri, languageId, view);\n\t}\n\n\tcloseFile(uri: string, view?: EditorView): void {\n\t\tconst file = this.#getFileEntry(uri);\n\t\tif (!file) return;\n\n\t\tif (view && file.views.has(view)) {\n\t\t\tfile.views.delete(view);\n\t\t}\n\n\t\tif (!file.views.size) {\n\t\t\tthis.client.didClose(uri);\n\t\t\tthis.#removeFileEntry(file);\n\t\t}\n\t}\n\n\tgetFile(uri: string): AcodeWorkspaceFile | null {\n\t\treturn this.#getFileEntry(uri);\n\t}\n\n\trequestFile(uri: string): Promise<AcodeWorkspaceFile | null> {\n\t\treturn Promise.resolve(this.#getFileEntry(uri));\n\t}\n\n\tconnected(): void {\n\t\tfor (const file of this.files) {\n\t\t\tthis.client.didOpen(file);\n\t\t}\n\t}\n\n\tupdateFile(uri: string, update: TransactionSpec): void {\n\t\tconst file = this.#getFileEntry(uri);\n\n\t\tif (file) {\n\t\t\tconst view = file.getView();\n\t\t\tif (view) {\n\t\t\t\tview.dispatch(update);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// File is not open - try to open it and apply the update\n\t\tthis.#applyUpdateToClosedFile(uri, update).catch((error) => {\n\t\t\tconsole.warn(`[LSP:Workspace] Failed to apply update: ${uri}`, error);\n\t\t});\n\t}\n\n\tasync #applyUpdateToClosedFile(\n\t\turi: string,\n\t\tupdate: TransactionSpec,\n\t): Promise<void> {\n\t\tif (typeof this.options.displayFile !== \"function\") return;\n\n\t\ttry {\n\t\t\tconst view = await this.options.displayFile(uri);\n\t\t\tif (!view?.state?.doc) return;\n\t\t\tconst languageId = this.#resolveLanguageIdForUri(uri);\n\t\t\tconst file = this.#getOrCreateFile(uri, languageId, view);\n\t\t\tconst fileView = file.getView();\n\t\t\tif (fileView) {\n\t\t\t\tfileView.dispatch(update);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`[LSP:Workspace] Failed to apply update: ${uri}`, error);\n\t\t}\n\t}\n\n\tasync displayFile(uri: string): Promise<EditorView | null> {\n\t\tif (typeof this.options.displayFile === \"function\") {\n\t\t\ttry {\n\t\t\t\treturn await this.options.displayFile(uri);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[LSP:Workspace] Failed to display file\", error);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t// ========================================================================\n\t// Workspace Folders Support\n\t// ========================================================================\n\n\t#getFolderName(uri: string): string {\n\t\tconst parts = uri.replace(/\\/$/, \"\").split(\"/\");\n\t\treturn parts[parts.length - 1] || uri;\n\t}\n\n\t#sendNotification(method: string, params: unknown): void {\n\t\t// Access the client's transport to send raw JSON-RPC notification\n\t\tconst client = this.client as unknown as {\n\t\t\tconnected: boolean;\n\t\t\ttransport?: { send: (message: string) => void };\n\t\t};\n\n\t\tif (!client.connected || !client.transport) {\n\t\t\tconsole.warn(`[LSP:Workspace] Cannot send notification: not connected`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst message = JSON.stringify({\n\t\t\tjsonrpc: \"2.0\",\n\t\t\tmethod,\n\t\t\tparams,\n\t\t});\n\n\t\tclient.transport.send(message);\n\t}\n\n\thasWorkspaceFolder(uri: string): boolean {\n\t\treturn this.#workspaceFolders.has(uri);\n\t}\n\n\tgetWorkspaceFolders(): string[] {\n\t\treturn Array.from(this.#workspaceFolders);\n\t}\n\n\taddWorkspaceFolder(uri: string): boolean {\n\t\tif (this.#workspaceFolders.has(uri)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.#workspaceFolders.add(uri);\n\t\tthis.#sendNotification(\"workspace/didChangeWorkspaceFolders\", {\n\t\t\tevent: {\n\t\t\t\tadded: [{ uri, name: this.#getFolderName(uri) }],\n\t\t\t\tremoved: [],\n\t\t\t},\n\t\t});\n\t\tconsole.info(`[LSP:Workspace] Added workspace folder: ${uri}`);\n\t\treturn true;\n\t}\n\n\tremoveWorkspaceFolder(uri: string): boolean {\n\t\tif (!this.#workspaceFolders.has(uri)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.#workspaceFolders.delete(uri);\n\t\tthis.#sendNotification(\"workspace/didChangeWorkspaceFolders\", {\n\t\t\tevent: {\n\t\t\t\tadded: [],\n\t\t\t\tremoved: [{ uri, name: this.#getFolderName(uri) }],\n\t\t\t},\n\t\t});\n\t\tconsole.info(`[LSP:Workspace] Removed workspace folder: ${uri}`);\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "src/cm/mainEditorExtensions.ts",
    "content": "import type { Extension } from \"@codemirror/state\";\nimport { EditorView, scrollPastEnd } from \"@codemirror/view\";\n\ninterface MainEditorExtensionOptions {\n\temmetExtensions?: Extension[];\n\tbaseExtensions?: Extension[];\n\tcommandKeymapExtension?: Extension;\n\tthemeExtension?: Extension;\n\tpointerCursorVisibilityExtension?: Extension;\n\tshiftClickSelectionExtension?: Extension;\n\ttouchSelectionUpdateExtension?: Extension;\n\tsearchExtension?: Extension;\n\treadOnlyExtension?: Extension;\n\toptionExtensions?: Extension[];\n}\n\nfunction pushExtension(target: Extension[], extension?: Extension): void {\n\tif (extension == null) return;\n\ttarget.push(extension);\n}\n\nexport const fixedHeightTheme = EditorView.theme({\n\t\"&\": { height: \"100%\" },\n\t\".cm-scroller\": { height: \"100%\", overflow: \"auto\" },\n});\n\nexport function createMainEditorExtensions(\n\toptions: MainEditorExtensionOptions = {},\n): Extension[] {\n\tconst extensions: Extension[] = [];\n\n\tif (options.emmetExtensions?.length) {\n\t\textensions.push(...options.emmetExtensions);\n\t}\n\tif (options.baseExtensions?.length) {\n\t\textensions.push(...options.baseExtensions);\n\t}\n\n\tpushExtension(extensions, options.commandKeymapExtension);\n\tpushExtension(extensions, options.themeExtension);\n\textensions.push(fixedHeightTheme);\n\textensions.push(scrollPastEnd());\n\tpushExtension(extensions, options.pointerCursorVisibilityExtension);\n\tpushExtension(extensions, options.shiftClickSelectionExtension);\n\tpushExtension(extensions, options.touchSelectionUpdateExtension);\n\tpushExtension(extensions, options.searchExtension);\n\tpushExtension(extensions, options.readOnlyExtension);\n\n\tif (options.optionExtensions?.length) {\n\t\textensions.push(...options.optionExtensions);\n\t}\n\n\treturn extensions;\n}\n\nexport default createMainEditorExtensions;\n"
  },
  {
    "path": "src/cm/modelist.ts",
    "content": "import type { Extension } from \"@codemirror/state\";\n\nexport type LanguageExtensionProvider = () => Extension | Promise<Extension>;\n\nexport interface AddModeOptions {\n\taliases?: string[];\n\tfilenameMatchers?: RegExp[];\n}\n\nexport interface ModesByName {\n\t[name: string]: Mode;\n}\n\nconst modesByName: ModesByName = {};\nconst modes: Mode[] = [];\n\nfunction normalizeModeKey(value: string): string {\n\treturn String(value ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\nfunction normalizeAliases(aliases: string[] = [], name: string): string[] {\n\tconst normalized = new Set<string>();\n\tfor (const alias of aliases) {\n\t\tconst key = normalizeModeKey(alias);\n\t\tif (!key || key === name) continue;\n\t\tnormalized.add(key);\n\t}\n\treturn [...normalized];\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn String(value ?? \"\").replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Initialize CodeMirror mode list functionality\n */\nexport function initModes(): void {\n\t// CodeMirror modes don't need the same ace.define wrapper\n\t// but we maintain the same API structure for compatibility\n}\n\n/**\n * Add language mode to CodeMirror editor\n */\nexport function addMode(\n\tname: string,\n\textensions: string | string[],\n\tcaption?: string,\n\tlanguageExtension: LanguageExtensionProvider | null = null,\n\toptions: AddModeOptions = {},\n): void {\n\tconst filename = normalizeModeKey(name);\n\tconst mode = new Mode(\n\t\tfilename,\n\t\tcaption,\n\t\textensions,\n\t\tlanguageExtension,\n\t\toptions,\n\t);\n\tmodesByName[filename] = mode;\n\tmode.aliases.forEach((alias) => {\n\t\tif (!modesByName[alias]) {\n\t\t\tmodesByName[alias] = mode;\n\t\t}\n\t});\n\tmodes.push(mode);\n}\n\n/**\n * Remove language mode from CodeMirror editor\n */\nexport function removeMode(name: string): void {\n\tconst filename = normalizeModeKey(name);\n\tconst mode = modesByName[filename];\n\tif (!mode) return;\n\n\tdelete modesByName[mode.name];\n\tmode.aliases.forEach((alias) => {\n\t\tif (modesByName[alias] === mode) {\n\t\t\tdelete modesByName[alias];\n\t\t}\n\t});\n\n\tconst modeIndex = modes.findIndex(\n\t\t(registeredMode) => registeredMode === mode,\n\t);\n\tif (modeIndex >= 0) {\n\t\tmodes.splice(modeIndex, 1);\n\t}\n}\n\n/**\n * Get mode for file path\n */\nexport function getModeForPath(path: string): Mode {\n\tlet mode = modesByName.text;\n\tconst fileName = path.split(/[/\\\\]/).pop() || \"\";\n\n\t// Sort modes by specificity (descending) to check most specific first\n\tconst sortedModes = [...modes].sort((a, b) => {\n\t\treturn getModeSpecificityScore(b) - getModeSpecificityScore(a);\n\t});\n\n\tfor (const iMode of sortedModes) {\n\t\tif (iMode.supportsFile?.(fileName)) {\n\t\t\tmode = iMode;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn mode;\n}\n\n/**\n * Calculates a specificity score for a mode.\n * Higher score means more specific.\n * - Anchored patterns (e.g., \"^Dockerfile\") get a base score of 1000.\n * - Non-anchored patterns (extensions) are scored by length.\n */\nfunction getModeSpecificityScore(modeInstance: Mode): number {\n\tconst extensionsStr = modeInstance.extensions;\n\tlet maxScore = 0;\n\n\tif (extensionsStr) {\n\t\tconst patterns = extensionsStr.split(\"|\");\n\t\tfor (const pattern of patterns) {\n\t\t\tlet currentScore = 0;\n\t\t\tif (pattern.startsWith(\"^\")) {\n\t\t\t\t// Exact filename match or anchored pattern\n\t\t\t\tcurrentScore = 1000 + (pattern.length - 1); // Subtract 1 for '^'\n\t\t\t} else {\n\t\t\t\t// Extension match\n\t\t\t\tcurrentScore = pattern.length;\n\t\t\t}\n\t\t\tif (currentScore > maxScore) {\n\t\t\t\tmaxScore = currentScore;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const matcher of modeInstance.filenameMatchers) {\n\t\tconst score = 1000 + matcher.source.length;\n\t\tif (score > maxScore) {\n\t\t\tmaxScore = score;\n\t\t}\n\t}\n\n\treturn maxScore;\n}\n\n/**\n * Get all modes by name\n */\nexport function getModesByName(): ModesByName {\n\treturn modesByName;\n}\n\n/**\n * Get all modes array\n */\nexport function getModes(): Mode[] {\n\treturn modes;\n}\n\nexport function getMode(name: string): Mode | null {\n\treturn modesByName[normalizeModeKey(name)] || null;\n}\n\nexport class Mode {\n\textensions: string;\n\tcaption: string;\n\tname: string;\n\tmode: string;\n\taliases: string[];\n\textRe: RegExp | null;\n\tfilenameMatchers: RegExp[];\n\tlanguageExtension: LanguageExtensionProvider | null;\n\n\tconstructor(\n\t\tname: string,\n\t\tcaption: string | undefined,\n\t\textensions: string | string[],\n\t\tlanguageExtension: LanguageExtensionProvider | null = null,\n\t\toptions: AddModeOptions = {},\n\t) {\n\t\tif (Array.isArray(extensions)) {\n\t\t\textensions = extensions.join(\"|\");\n\t\t}\n\n\t\tthis.name = name;\n\t\tthis.mode = name; // CodeMirror uses different mode naming\n\t\tthis.extensions = extensions;\n\t\tthis.caption = caption || this.name.replace(/_/g, \" \");\n\t\tthis.aliases = normalizeAliases(options.aliases, this.name);\n\t\tthis.filenameMatchers = Array.isArray(options.filenameMatchers)\n\t\t\t? options.filenameMatchers.filter((matcher) => matcher instanceof RegExp)\n\t\t\t: [];\n\t\tthis.languageExtension = languageExtension;\n\t\tlet re = \"\";\n\n\t\tif (!extensions) {\n\t\t\tthis.extRe = null;\n\t\t\treturn;\n\t\t}\n\n\t\tconst patterns = extensions\n\t\t\t.split(\"|\")\n\t\t\t.map((pattern) => pattern.trim())\n\t\t\t.filter(Boolean);\n\t\tconst filenamePatterns = patterns\n\t\t\t.filter((pattern) => pattern.startsWith(\"^\"))\n\t\t\t.map((pattern) => `^${escapeRegExp(pattern.slice(1))}$`);\n\t\tconst extensionPatterns = patterns\n\t\t\t.filter((pattern) => !pattern.startsWith(\"^\"))\n\t\t\t.map((pattern) => escapeRegExp(pattern));\n\t\tconst regexParts: string[] = [];\n\n\t\tif (extensionPatterns.length) {\n\t\t\tregexParts.push(`^.*\\\\.(${extensionPatterns.join(\"|\")})$`);\n\t\t}\n\n\t\tregexParts.push(...filenamePatterns);\n\n\t\tif (!regexParts.length) {\n\t\t\tthis.extRe = null;\n\t\t\treturn;\n\t\t}\n\n\t\tre =\n\t\t\tregexParts.length === 1 ? regexParts[0] : `(?:${regexParts.join(\"|\")})`;\n\t\tthis.extRe = new RegExp(re, \"i\");\n\t}\n\n\tsupportsFile(filename: string): boolean {\n\t\tif (this.extRe?.test(filename)) return true;\n\n\t\treturn this.filenameMatchers.some((matcher) => {\n\t\t\tmatcher.lastIndex = 0;\n\t\t\treturn matcher.test(filename);\n\t\t});\n\t}\n\n\t/**\n\t * Get the CodeMirror language extension\n\t */\n\tgetExtension(): LanguageExtensionProvider | null {\n\t\treturn this.languageExtension;\n\t}\n\n\t/**\n\t * Check if the language extension is available (loaded)\n\t */\n\tisAvailable(): boolean {\n\t\treturn this.languageExtension !== null;\n\t}\n}\n"
  },
  {
    "path": "src/cm/modes/luau/index.ts",
    "content": "import {\n\tIndentContext,\n\tLanguageSupport,\n\tStreamLanguage,\n\tStringStream,\n} from \"@codemirror/language\";\n\ntype Tokenizer = (stream: StringStream, state: LuauState) => string | null;\n\ninterface LuauState {\n\tbasecol: number;\n\tindentDepth: number;\n\tcur: Tokenizer;\n\tstack: Tokenizer[];\n\texpectFunctionName: boolean;\n\tafterFunctionName: boolean;\n\texpectTypeName: boolean;\n\tafterTypeName: boolean;\n\tafterTypeIdentifier: boolean;\n\tinType: boolean;\n\ttypeDepth: number;\n\tgenericDepth: number;\n\tinterpolationBraceDepth: number;\n\tafterPropertyAccess: boolean;\n\tlastIdentifierWasStandard: boolean;\n\tdocCommentExpectParamName: boolean;\n\tdocCommentExpectType: boolean;\n}\n\nconst controlKeywords = new Set([\n\t\"break\",\n\t\"continue\",\n\t\"do\",\n\t\"else\",\n\t\"elseif\",\n\t\"end\",\n\t\"for\",\n\t\"function\",\n\t\"if\",\n\t\"in\",\n\t\"repeat\",\n\t\"return\",\n\t\"then\",\n\t\"type\",\n\t\"until\",\n\t\"while\",\n]);\n\nconst modifierKeywords = new Set([\"export\", \"local\"]);\nconst logicalKeywords = new Set([\"and\", \"not\", \"or\"]);\nconst typePrimitives = new Set([\n\t\"any\",\n\t\"boolean\",\n\t\"buffer\",\n\t\"never\",\n\t\"nil\",\n\t\"number\",\n\t\"string\",\n\t\"symbol\",\n\t\"thread\",\n\t\"unknown\",\n\t\"userdata\",\n\t\"vector\",\n]);\n\nconst standardFunctions = new Set([\n\t\"assert\",\n\t\"collectgarbage\",\n\t\"delay\",\n\t\"error\",\n\t\"gcinfo\",\n\t\"getfenv\",\n\t\"getmetatable\",\n\t\"ipairs\",\n\t\"loadstring\",\n\t\"newproxy\",\n\t\"next\",\n\t\"pairs\",\n\t\"pcall\",\n\t\"print\",\n\t\"printidentity\",\n\t\"rawequal\",\n\t\"rawset\",\n\t\"require\",\n\t\"select\",\n\t\"setfenv\",\n\t\"setmetatable\",\n\t\"settings\",\n\t\"spawn\",\n\t\"stats\",\n\t\"tick\",\n\t\"time\",\n\t\"tonumber\",\n\t\"tostring\",\n\t\"type\",\n\t\"typeof\",\n\t\"unpack\",\n\t\"UserSettings\",\n\t\"version\",\n\t\"wait\",\n\t\"warn\",\n]);\n\nconst standardNamespaces = new Set([\n\t\"bit32\",\n\t\"buffer\",\n\t\"coroutine\",\n\t\"debug\",\n\t\"math\",\n\t\"os\",\n\t\"string\",\n\t\"table\",\n\t\"task\",\n\t\"utf8\",\n\t\"vector\",\n\t\"Enum\",\n]);\n\nconst standardVariables = new Set([\n\t\"_G\",\n\t\"_VERSION\",\n\t\"DebuggerManager\",\n\t\"PluginManager\",\n\t\"game\",\n\t\"plugin\",\n\t\"script\",\n\t\"shared\",\n\t\"workspace\",\n]);\n\nconst metamethods = new Set([\n\t\"__add\",\n\t\"__call\",\n\t\"__concat\",\n\t\"__div\",\n\t\"__eq\",\n\t\"__idiv\",\n\t\"__index\",\n\t\"__iter\",\n\t\"__le\",\n\t\"__len\",\n\t\"__lt\",\n\t\"__metatable\",\n\t\"__mod\",\n\t\"__mode\",\n\t\"__mul\",\n\t\"__newindex\",\n\t\"__pow\",\n\t\"__sub\",\n\t\"__tostring\",\n\t\"__unm\",\n]);\n\nconst typeTerminators = new Set([\n\t\"break\",\n\t\"continue\",\n\t\"do\",\n\t\"else\",\n\t\"elseif\",\n\t\"end\",\n\t\"for\",\n\t\"if\",\n\t\"in\",\n\t\"local\",\n\t\"repeat\",\n\t\"return\",\n\t\"then\",\n\t\"until\",\n\t\"while\",\n]);\n\nconst indentTokens = new Set([\"do\", \"function\", \"if\", \"repeat\", \"(\", \"{\"]);\nconst dedentTokens = new Set([\"end\", \"until\", \")\", \"}\"]);\nconst dedentPartial = /^(?:end|until|\\)|}|else|elseif)\\b/;\n\nfunction pushTokenizer(state: LuauState, tokenizer: Tokenizer) {\n\tstate.stack.push(state.cur);\n\tstate.cur = tokenizer;\n}\n\nfunction popTokenizer(state: LuauState) {\n\tstate.cur = state.stack.pop() || normal;\n}\n\nfunction enterTypeContext(state: LuauState, depth = 0) {\n\tstate.inType = true;\n\tstate.typeDepth = depth;\n}\n\nfunction exitTypeContext(state: LuauState) {\n\tstate.inType = false;\n\tstate.typeDepth = 0;\n\tstate.genericDepth = 0;\n\tstate.afterTypeIdentifier = false;\n}\n\nfunction isWordStart(char: string) {\n\treturn /[A-Za-z_]/.test(char);\n}\n\nfunction isWord(char: string) {\n\treturn /[A-Za-z0-9_]/.test(char);\n}\n\nfunction isUpperConstant(word: string) {\n\treturn /^[A-Z_][A-Z0-9_]*$/.test(word);\n}\n\nfunction isStandardWord(word: string) {\n\treturn (\n\t\tstandardFunctions.has(word) ||\n\t\tstandardNamespaces.has(word) ||\n\t\tstandardVariables.has(word)\n\t);\n}\n\nfunction looksLikeMethodSeparator(stream: StringStream) {\n\treturn /^\\s*[A-Za-z_][A-Za-z0-9_]*\\s*\\(/.test(\n\t\tstream.string.slice(stream.pos),\n\t);\n}\n\nfunction readLongBracket(stream: StringStream) {\n\tlet level = 0;\n\twhile (stream.eat(\"=\")) level++;\n\treturn stream.eat(\"[\") ? level : -1;\n}\n\nfunction bracketed(level: number, style: string): Tokenizer {\n\treturn (stream, state) => {\n\t\tlet seenEquals: number | null = null;\n\t\twhile (true) {\n\t\t\tconst char = stream.next();\n\t\t\tif (char == null) break;\n\t\t\tif (seenEquals == null) {\n\t\t\t\tif (char === \"]\") seenEquals = 0;\n\t\t\t} else if (char === \"=\") {\n\t\t\t\tseenEquals++;\n\t\t\t} else if (char === \"]\" && seenEquals === level) {\n\t\t\t\tpopTokenizer(state);\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tseenEquals = null;\n\t\t\t}\n\t\t}\n\n\t\treturn style;\n\t};\n}\n\nfunction quotedString(quote: string): Tokenizer {\n\treturn (stream, state) => {\n\t\tlet escaped = false;\n\t\twhile (true) {\n\t\t\tconst char = stream.next();\n\t\t\tif (char == null) break;\n\t\t\tif (char === quote && !escaped) {\n\t\t\t\tpopTokenizer(state);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tescaped = !escaped && char === \"\\\\\";\n\t\t}\n\n\t\treturn \"string\";\n\t};\n}\n\nconst interpolatedString: Tokenizer = (stream, state) => {\n\twhile (true) {\n\t\tconst char = stream.next();\n\t\tif (char == null) break;\n\t\tif (char === \"\\\\\") {\n\t\t\tstream.next();\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === \"{\") {\n\t\t\tif (stream.pos - stream.start > 1) {\n\t\t\t\tstream.backUp(1);\n\t\t\t\treturn \"string\";\n\t\t\t}\n\n\t\t\tstate.interpolationBraceDepth = 0;\n\t\t\tpushTokenizer(state, interpolatedExpression);\n\t\t\treturn \"punctuation\";\n\t\t}\n\n\t\tif (char === \"`\") {\n\t\t\tpopTokenizer(state);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn \"string\";\n};\n\nconst interpolatedExpression: Tokenizer = (stream, state) => {\n\tif (stream.eatSpace()) return null;\n\n\tif (stream.peek() === \"}\" && state.interpolationBraceDepth === 0) {\n\t\tstream.next();\n\t\tpopTokenizer(state);\n\t\treturn \"punctuation\";\n\t}\n\n\tconst style = normal(stream, state);\n\tconst token = stream.current();\n\n\tif (state.cur === interpolatedExpression) {\n\t\tif (token === \"{\") {\n\t\t\tstate.interpolationBraceDepth++;\n\t\t} else if (token === \"}\" && state.interpolationBraceDepth > 0) {\n\t\t\tstate.interpolationBraceDepth--;\n\t\t}\n\t}\n\n\treturn style;\n};\n\nconst docCommentLine: Tokenizer = (stream, state) => {\n\tif (stream.sol()) {\n\t\tpopTokenizer(state);\n\t\treturn normal(stream, state);\n\t}\n\n\tif (stream.eatSpace()) return null;\n\n\tconst peek = stream.peek();\n\tif (!peek) {\n\t\tstate.docCommentExpectParamName = false;\n\t\tstate.docCommentExpectType = false;\n\t\treturn null;\n\t}\n\n\tif (stream.match(/(?:\\\\|@)[A-Za-z_][A-Za-z0-9_]*/)) {\n\t\tconst tag = stream.current();\n\t\tstate.docCommentExpectParamName = /(?:\\\\|@)param$/.test(tag);\n\t\tstate.docCommentExpectType = false;\n\t\treturn \"attributeName\";\n\t}\n\n\tif (state.docCommentExpectParamName && isWordStart(peek)) {\n\t\tstream.next();\n\t\tstream.eatWhile(isWord);\n\t\tstate.docCommentExpectParamName = false;\n\t\tstate.docCommentExpectType = true;\n\t\treturn \"variableName\";\n\t}\n\n\tif (\n\t\tstate.docCommentExpectType &&\n\t\t(isWordStart(peek) ||\n\t\t\tpeek === \"{\" ||\n\t\t\tpeek === \"(\" ||\n\t\t\tpeek === \"[\" ||\n\t\t\tpeek === \"?\" ||\n\t\t\tpeek === \".\" ||\n\t\t\tpeek === \"|\")\n\t) {\n\t\tstream.next();\n\t\tstream.eatWhile(/[^\\s,;]+/);\n\t\tstate.docCommentExpectType = false;\n\t\treturn \"typeName\";\n\t}\n\n\tstream.next();\n\tstream.eatWhile((char) => !/\\s/.test(char));\n\treturn \"comment\";\n};\n\nfunction readNumber(stream: StringStream, firstChar: string) {\n\tconst next = stream.peek();\n\tif (firstChar === \"0\" && next && /[xX]/.test(next)) {\n\t\tstream.next();\n\t\tstream.eatWhile(/[0-9a-fA-F_]/);\n\t\treturn;\n\t}\n\n\tstream.eatWhile(/[\\d_]/);\n\n\tif (stream.peek() === \".\" && stream.string.charAt(stream.pos + 1) !== \".\") {\n\t\tstream.next();\n\t\tstream.eatWhile(/[\\d_]/);\n\t}\n\n\tconst exponent = stream.peek();\n\tif (exponent && /[eE]/.test(exponent)) {\n\t\tstream.next();\n\t\tstream.eat(/[+-]/);\n\t\tstream.eatWhile(/[\\d_]/);\n\t}\n}\n\nfunction classifyIdentifier(word: string, state: LuauState) {\n\tif (state.expectFunctionName && isWordStart(word)) {\n\t\tstate.expectFunctionName = false;\n\t\tstate.afterFunctionName = true;\n\t\tstate.afterPropertyAccess = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn metamethods.has(word)\n\t\t\t? \"variableName.function.definition.special\"\n\t\t\t: \"variableName.function.definition\";\n\t}\n\n\tif (state.expectTypeName && word !== \"function\") {\n\t\tstate.expectTypeName = false;\n\t\tstate.afterTypeName = true;\n\t\tstate.afterTypeIdentifier = true;\n\t\tstate.afterFunctionName = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"typeName.definition\";\n\t}\n\n\tif (state.afterPropertyAccess) {\n\t\tstate.afterPropertyAccess = false;\n\t\tconst isStandardProperty = state.lastIdentifierWasStandard;\n\t\tconst isStandardMember = isStandardProperty || isStandardWord(word);\n\t\tstate.lastIdentifierWasStandard = isStandardMember;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tif (metamethods.has(word)) return \"propertyName.special\";\n\t\treturn isStandardMember ? \"propertyName.standard\" : \"propertyName\";\n\t}\n\n\tif (logicalKeywords.has(word)) {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"operatorKeyword\";\n\t}\n\n\tif (modifierKeywords.has(word)) {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"modifier\";\n\t}\n\n\tif (word === \"type\") {\n\t\tstate.expectTypeName = true;\n\t\tstate.afterTypeName = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"definitionKeyword\";\n\t}\n\n\tif (word === \"function\") {\n\t\tif (!state.expectTypeName) state.expectFunctionName = true;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"controlKeyword\";\n\t}\n\n\tif (word === \"self\") {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"variableName.special\";\n\t}\n\n\tif (word === \"true\" || word === \"false\") {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"bool\";\n\t}\n\n\tif (word === \"nil\") {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"null\";\n\t}\n\n\tif (controlKeywords.has(word)) {\n\t\tif (state.inType && state.typeDepth === 0 && typeTerminators.has(word)) {\n\t\t\texitTypeContext(state);\n\t\t}\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"controlKeyword\";\n\t}\n\n\tif (state.inType) {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = true;\n\t\tif (word === \"typeof\") return \"variableName.function.standard\";\n\t\tif (typePrimitives.has(word) || isUpperConstant(word)) return \"typeName\";\n\t\treturn \"typeName\";\n\t}\n\n\tif (standardNamespaces.has(word)) {\n\t\tstate.lastIdentifierWasStandard = true;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"namespace\";\n\t}\n\n\tif (standardVariables.has(word)) {\n\t\tstate.lastIdentifierWasStandard = true;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"variableName.standard\";\n\t}\n\n\tif (standardFunctions.has(word)) {\n\t\tstate.lastIdentifierWasStandard = true;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"variableName.function.standard\";\n\t}\n\n\tif (isUpperConstant(word)) {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"variableName.constant\";\n\t}\n\n\tstate.lastIdentifierWasStandard = isStandardWord(word);\n\tstate.afterFunctionName = false;\n\tstate.afterTypeIdentifier = false;\n\treturn \"variableName\";\n}\n\nconst normal: Tokenizer = (stream, state) => {\n\tconst char = stream.next();\n\tif (!char) return null;\n\n\tif (char === \"-\" && stream.eat(\"-\")) {\n\t\tif (stream.eat(\"-\")) {\n\t\t\tstate.docCommentExpectParamName = false;\n\t\t\tstate.docCommentExpectType = false;\n\t\t\tpushTokenizer(state, docCommentLine);\n\t\t\treturn \"comment\";\n\t\t}\n\t\tif (stream.eat(\"[\")) {\n\t\t\tconst longBracketStart = stream.pos;\n\t\t\tconst level = readLongBracket(stream);\n\t\t\tif (level >= 0) {\n\t\t\t\tpushTokenizer(state, bracketed(level, \"comment\"));\n\t\t\t\treturn state.cur(stream, state);\n\t\t\t}\n\t\t\tstream.backUp(stream.pos - longBracketStart);\n\t\t}\n\t\tstream.skipToEnd();\n\t\treturn \"comment\";\n\t}\n\n\tif (char === '\"' || char === \"'\") {\n\t\tpushTokenizer(state, quotedString(char));\n\t\treturn state.cur(stream, state);\n\t}\n\n\tif (char === \"`\") {\n\t\tpushTokenizer(state, interpolatedString);\n\t\treturn state.cur(stream, state);\n\t}\n\n\tif (char === \"[\") {\n\t\tconst longBracketStart = stream.pos;\n\t\tconst level = readLongBracket(stream);\n\t\tif (level >= 0) {\n\t\t\tpushTokenizer(state, bracketed(level, \"string\"));\n\t\t\treturn state.cur(stream, state);\n\t\t}\n\t\tstream.backUp(stream.pos - longBracketStart);\n\t}\n\n\tif (char === \"@\" && isWordStart(stream.peek() || \"\")) {\n\t\tstream.eatWhile(isWord);\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"attributeName\";\n\t}\n\n\tif (/\\d/.test(char) || (char === \".\" && /\\d/.test(stream.peek() || \"\"))) {\n\t\treadNumber(stream, char);\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"number\";\n\t}\n\n\tif (isWordStart(char)) {\n\t\tstream.eatWhile(isWord);\n\t\treturn classifyIdentifier(stream.current(), state);\n\t}\n\n\tif (char === \".\" || char === \":\") {\n\t\tif (char === \".\" && stream.eat(\".\")) {\n\t\t\tstate.afterFunctionName = false;\n\t\t\tstate.afterTypeIdentifier = false;\n\t\t\tif (stream.eat(\".\")) {\n\t\t\t\tstate.lastIdentifierWasStandard = false;\n\t\t\t\treturn \"keyword\";\n\t\t\t}\n\t\t\tstream.eat(\"=\");\n\t\t\tstate.lastIdentifierWasStandard = false;\n\t\t\treturn \"operator\";\n\t\t}\n\n\t\tif (char === \":\" && stream.eat(\":\")) {\n\t\t\tenterTypeContext(state);\n\t\t\tstate.lastIdentifierWasStandard = false;\n\t\t\treturn \"operator\";\n\t\t}\n\n\t\tif (\n\t\t\tchar === \":\" &&\n\t\t\t!state.expectFunctionName &&\n\t\t\t!looksLikeMethodSeparator(stream)\n\t\t) {\n\t\t\tenterTypeContext(state);\n\t\t\tstate.lastIdentifierWasStandard = false;\n\t\t\treturn \"operator\";\n\t\t}\n\n\t\tstate.afterPropertyAccess = true;\n\t\treturn \"punctuation\";\n\t}\n\n\tif (char === \"-\" && stream.eat(\">\")) {\n\t\tenterTypeContext(state);\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"operator\";\n\t}\n\n\tif (\n\t\tchar === \"<\" &&\n\t\t(state.afterTypeName ||\n\t\t\tstate.afterFunctionName ||\n\t\t\tstate.afterTypeIdentifier)\n\t) {\n\t\tenterTypeContext(state);\n\t\tstate.genericDepth++;\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"operator\";\n\t}\n\n\tif (\n\t\t(char === \"|\" || char === \"&\" || char === \"?\") &&\n\t\t(state.inType || char === \"?\")\n\t) {\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"operator\";\n\t}\n\n\tif (\n\t\tchar === \"+\" ||\n\t\tchar === \"-\" ||\n\t\tchar === \"*\" ||\n\t\tchar === \"/\" ||\n\t\tchar === \"%\" ||\n\t\tchar === \"^\" ||\n\t\tchar === \"#\" ||\n\t\tchar === \"=\" ||\n\t\tchar === \"<\" ||\n\t\tchar === \">\" ||\n\t\tchar === \"~\" ||\n\t\tchar === \"!\"\n\t) {\n\t\tstream.eat(\"=\");\n\t\tif (char === \">\" && state.genericDepth > 0) {\n\t\t\tstate.genericDepth--;\n\t\t\tif (state.genericDepth === 0 && state.typeDepth === 0) {\n\t\t\t\tstate.inType = false;\n\t\t\t}\n\t\t\tstate.afterTypeIdentifier = true;\n\t\t\tstate.lastIdentifierWasStandard = false;\n\t\t\treturn \"operator\";\n\t\t}\n\t\tif (char === \"/\" && stream.eat(\"/\")) stream.eat(\"=\");\n\t\tif (char === \"=\" && state.afterTypeName && state.genericDepth === 0) {\n\t\t\tstate.afterTypeName = false;\n\t\t\tenterTypeContext(state);\n\t\t}\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"operator\";\n\t}\n\n\tif (char === \"(\" || char === \"{\" || char === \"[\") {\n\t\tif (char === \"(\" && state.expectFunctionName) {\n\t\t\tstate.expectFunctionName = false;\n\t\t}\n\t\tif (char === \"(\") {\n\t\t\tstate.expectTypeName = false;\n\t\t}\n\t\tif (state.inType) state.typeDepth++;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\tif (state.afterTypeName && char === \"(\") {\n\t\t\tstate.afterTypeName = false;\n\t\t\tenterTypeContext(state, 1);\n\t\t}\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\treturn \"punctuation\";\n\t}\n\n\tif (char === \")\" || char === \"}\" || char === \"]\") {\n\t\tif (state.inType) {\n\t\t\tif (state.typeDepth > 0) {\n\t\t\t\tstate.typeDepth--;\n\t\t\t} else if (\n\t\t\t\tchar === \")\" &&\n\t\t\t\t/^\\s*->/.test(stream.string.slice(stream.pos))\n\t\t\t) {\n\t\t\t\tenterTypeContext(state);\n\t\t\t} else {\n\t\t\t\texitTypeContext(state);\n\t\t\t}\n\t\t}\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"punctuation\";\n\t}\n\n\tif (char === \",\" || char === \";\") {\n\t\tif (state.inType && state.typeDepth === 0) exitTypeContext(state);\n\t\tstate.afterFunctionName = false;\n\t\tstate.afterTypeIdentifier = false;\n\t\tstate.lastIdentifierWasStandard = false;\n\t\treturn \"punctuation\";\n\t}\n\n\tstate.afterFunctionName = false;\n\tstate.afterTypeIdentifier = false;\n\tstate.lastIdentifierWasStandard = false;\n\treturn null;\n};\n\nconst luauLanguage = StreamLanguage.define<LuauState>({\n\tname: \"luau\",\n\tstartState() {\n\t\treturn {\n\t\t\tbasecol: 0,\n\t\t\tindentDepth: 0,\n\t\t\tcur: normal,\n\t\t\tstack: [],\n\t\t\texpectFunctionName: false,\n\t\t\tafterFunctionName: false,\n\t\t\texpectTypeName: false,\n\t\t\tafterTypeName: false,\n\t\t\tafterTypeIdentifier: false,\n\t\t\tinType: false,\n\t\t\ttypeDepth: 0,\n\t\t\tgenericDepth: 0,\n\t\t\tinterpolationBraceDepth: 0,\n\t\t\tafterPropertyAccess: false,\n\t\t\tlastIdentifierWasStandard: false,\n\t\t\tdocCommentExpectParamName: false,\n\t\t\tdocCommentExpectType: false,\n\t\t};\n\t},\n\tcopyState(state) {\n\t\treturn {\n\t\t\t...state,\n\t\t\tstack: state.stack.slice(),\n\t\t};\n\t},\n\ttoken(stream, state) {\n\t\tif (stream.sol()) state.basecol = stream.indentation();\n\t\tif (stream.eatSpace()) return null;\n\n\t\tconst style = state.cur(stream, state);\n\t\tconst word = stream.current();\n\n\t\tif (style !== \"comment\" && style !== \"string\") {\n\t\t\tif (indentTokens.has(word)) state.indentDepth++;\n\t\t\tif (dedentTokens.has(word)) state.indentDepth--;\n\t\t}\n\n\t\treturn style;\n\t},\n\tindent(state, textAfter, context: IndentContext) {\n\t\tconst closing = dedentPartial.test(textAfter);\n\t\treturn (\n\t\t\tstate.basecol + context.unit * (state.indentDepth - (closing ? 1 : 0))\n\t\t);\n\t},\n\tlanguageData: {\n\t\tcommentTokens: { line: \"--\", block: { open: \"--[[\", close: \"]]\" } },\n\t\tcloseBrackets: { brackets: [\"(\", \"[\", \"{\", '\"', \"'\", \"`\"] },\n\t\tindentOnInput: /^\\s*(?:end|until|else|elseif|\\)|\\})$/,\n\t},\n});\n\nexport function luau() {\n\treturn new LanguageSupport(luauLanguage);\n}\n\nexport { luauLanguage };\n"
  },
  {
    "path": "src/cm/rainbowBrackets.ts",
    "content": "import { syntaxTree } from \"@codemirror/language\";\nimport { RangeSetBuilder } from \"@codemirror/state\";\nimport type { DecorationSet, ViewUpdate } from \"@codemirror/view\";\nimport { Decoration, EditorView, ViewPlugin } from \"@codemirror/view\";\nimport type { SyntaxNode } from \"@lezer/common\";\n\nconst DEFAULT_DARK_COLORS = [\n\t\"#e5c07b\",\n\t\"#c678dd\",\n\t\"#56b6c2\",\n\t\"#61afef\",\n\t\"#98c379\",\n\t\"#d19a66\",\n];\n\nconst DEFAULT_LIGHT_COLORS = [\n\t\"#795e26\",\n\t\"#af00db\",\n\t\"#005cc5\",\n\t\"#008000\",\n\t\"#b15c00\",\n\t\"#267f99\",\n];\n\nconst BLOCK_SIZE = 2048;\nconst MAX_BLOCK_CACHE_ENTRIES = 192;\nconst CONTEXT_SIGNATURE_DEPTH = 4;\nconst MIN_LOOK_BEHIND = 4000;\nconst MAX_LOOK_BEHIND = 24000;\nconst DEFAULT_EXACT_SCAN_LIMIT = 24000;\n\nconst SKIP_CONTEXTS = new Set([\n\t\"String\",\n\t\"TemplateString\",\n\t\"Comment\",\n\t\"LineComment\",\n\t\"BlockComment\",\n\t\"RegExp\",\n]);\n\nconst CLOSING_TO_OPENING = {\n\t\")\": \"(\",\n\t\"]\": \"[\",\n\t\"}\": \"{\",\n} as const;\n\ntype ClosingBracket = keyof typeof CLOSING_TO_OPENING;\n\nexport interface RainbowBracketThemeConfig {\n\tdark?: boolean;\n\tkeyword?: string;\n\ttype?: string;\n\tclass?: string;\n\tfunction?: string;\n\tstring?: string;\n\tnumber?: string;\n\tconstant?: string;\n\tvariable?: string;\n\tforeground?: string;\n}\n\nexport interface RainbowBracketsOptions {\n\tcolors?: readonly string[];\n\texactScanLimit?: number;\n\tlookBehind?: number;\n}\n\ninterface BracketInfo {\n\tchar: string;\n\tcolorIndex: number;\n}\n\ninterface BracketToken {\n\toffset: number;\n\tchar: string;\n}\n\ninterface BlockCacheEntry {\n\tcarrySkipChars: number;\n\ttokens: readonly BracketToken[];\n}\n\nfunction normalizeHexColor(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst color = value.trim().toLowerCase();\n\tif (/^#([\\da-f]{3}|[\\da-f]{6})$/.test(color)) return color;\n\treturn null;\n}\n\nfunction alignToBlockStart(pos: number): number {\n\treturn pos - (pos % BLOCK_SIZE);\n}\n\nfunction clampLookBehind(value: number | undefined): number {\n\tif (!Number.isFinite(value)) return MAX_LOOK_BEHIND;\n\treturn Math.max(\n\t\tMIN_LOOK_BEHIND,\n\t\tMath.min(MAX_LOOK_BEHIND, Math.floor(value || 0)),\n\t);\n}\n\nfunction getScanStart(\n\tview: EditorView,\n\tlookBehind: number,\n\texactScanLimit: number,\n): number {\n\tconst ranges = view.visibleRanges;\n\tif (!ranges.length) return 0;\n\n\tconst firstVisibleFrom = ranges[0].from;\n\tconst lastVisibleTo = ranges[ranges.length - 1].to;\n\tconst docLength = view.state.doc.length;\n\n\tif (docLength <= exactScanLimit || firstVisibleFrom <= exactScanLimit) {\n\t\treturn 0;\n\t}\n\n\tconst visibleSpan = Math.max(1, lastVisibleTo - firstVisibleFrom);\n\tconst dynamicLookBehind = Math.max(\n\t\tMIN_LOOK_BEHIND,\n\t\tMath.min(MAX_LOOK_BEHIND, visibleSpan * 3),\n\t);\n\n\treturn Math.max(\n\t\t0,\n\t\tfirstVisibleFrom - Math.max(lookBehind, dynamicLookBehind),\n\t);\n}\n\nfunction isBracketCode(code: number): boolean {\n\treturn (\n\t\tcode === 40 ||\n\t\tcode === 41 ||\n\t\tcode === 91 ||\n\t\tcode === 93 ||\n\t\tcode === 123 ||\n\t\tcode === 125\n\t);\n}\n\nfunction isOpeningBracket(char: string): boolean {\n\treturn char === \"(\" || char === \"[\" || char === \"{\";\n}\n\nfunction getSkipContextEnd(\n\ttree: ReturnType<typeof syntaxTree>,\n\tpos: number,\n): number {\n\tlet node: SyntaxNode | null = tree.resolveInner(pos, 1);\n\n\twhile (node) {\n\t\tif (SKIP_CONTEXTS.has(node.name)) return node.to;\n\t\tnode = node.parent;\n\t}\n\n\treturn -1;\n}\n\nfunction getContextChainSignature(\n\ttree: ReturnType<typeof syntaxTree>,\n\tpos: number,\n): string {\n\tif (tree.length <= 0) return \"\";\n\n\tconst clampedPos = Math.max(0, Math.min(tree.length - 1, pos));\n\tlet node: SyntaxNode | null = tree.resolveInner(clampedPos, 1);\n\tconst parts: string[] = [];\n\n\tfor (let depth = 0; node && depth < CONTEXT_SIGNATURE_DEPTH; depth++) {\n\t\tparts.push(node.name);\n\t\tnode = node.parent;\n\t}\n\n\treturn parts.join(\">\");\n}\n\nfunction getBlockContextSignature(\n\ttree: ReturnType<typeof syntaxTree>,\n\tblockStart: number,\n\tblockEnd: number,\n): string {\n\tif (blockEnd <= blockStart) return \"\";\n\tconst endPos = Math.max(blockStart, blockEnd - 1);\n\treturn `${getContextChainSignature(tree, blockStart)}|${getContextChainSignature(tree, endPos)}`;\n}\n\nfunction getBlockCacheKey(\n\tblockText: string,\n\tinitialSkipChars: number,\n\tcontextSignature: string,\n): string {\n\treturn `${initialSkipChars}\\u0000${contextSignature}\\u0000${blockText}`;\n}\n\nfunction tokenizeBlock(\n\ttree: ReturnType<typeof syntaxTree>,\n\tblockText: string,\n\tblockStart: number,\n\tinitialSkipChars: number,\n): BlockCacheEntry {\n\tconst tokens: BracketToken[] = [];\n\tlet skipUntilOffset = Math.max(0, initialSkipChars);\n\n\tif (!blockText.length) {\n\t\treturn { carrySkipChars: skipUntilOffset, tokens };\n\t}\n\n\tif (skipUntilOffset >= blockText.length) {\n\t\treturn { carrySkipChars: skipUntilOffset - blockText.length, tokens };\n\t}\n\n\tfor (let offset = 0; offset < blockText.length; offset++) {\n\t\tif (offset < skipUntilOffset) continue;\n\n\t\tconst code = blockText.charCodeAt(offset);\n\t\tif (!isBracketCode(code)) continue;\n\n\t\tconst pos = blockStart + offset;\n\t\tconst skipContextEnd = getSkipContextEnd(tree, pos);\n\t\tif (skipContextEnd > pos) {\n\t\t\tskipUntilOffset = Math.max(skipUntilOffset, skipContextEnd - blockStart);\n\t\t\tcontinue;\n\t\t}\n\n\t\ttokens.push({ offset, char: blockText[offset] });\n\t}\n\n\treturn {\n\t\tcarrySkipChars: Math.max(0, skipUntilOffset - blockText.length),\n\t\ttokens,\n\t};\n}\n\nfunction isVisiblePosition(\n\tpos: number,\n\tranges: readonly { from: number; to: number }[],\n\tcursor: { index: number },\n): boolean {\n\twhile (cursor.index < ranges.length && pos >= ranges[cursor.index].to) {\n\t\tcursor.index++;\n\t}\n\n\tconst range = ranges[cursor.index];\n\treturn !!range && pos >= range.from && pos < range.to;\n}\n\nfunction buildTheme(colors: readonly string[]) {\n\tconst themeSpec: Record<string, { color: string }> = {};\n\n\tcolors.forEach((color, index) => {\n\t\tconst selector = `.cm-rainbowBracket-${index}`;\n\t\tthemeSpec[selector] = { color: `${color} !important` };\n\t\tthemeSpec[`${selector} span`] = { color: `${color} !important` };\n\t});\n\n\treturn EditorView.baseTheme(themeSpec);\n}\n\nexport function getRainbowBracketColors(\n\tthemeConfig: RainbowBracketThemeConfig = {},\n): string[] {\n\tconst fallback = themeConfig.dark\n\t\t? DEFAULT_DARK_COLORS\n\t\t: DEFAULT_LIGHT_COLORS;\n\tconst colors: string[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const candidate of [\n\t\tthemeConfig.keyword,\n\t\tthemeConfig.type,\n\t\tthemeConfig.class,\n\t\tthemeConfig.function,\n\t\tthemeConfig.string,\n\t\tthemeConfig.number,\n\t\tthemeConfig.constant,\n\t\tthemeConfig.variable,\n\t\tthemeConfig.foreground,\n\t]) {\n\t\tconst color = normalizeHexColor(candidate);\n\t\tif (!color || seen.has(color)) continue;\n\t\tseen.add(color);\n\t\tcolors.push(color);\n\t\tif (colors.length === fallback.length) break;\n\t}\n\n\tif (colors.length < 4) {\n\t\treturn [...fallback];\n\t}\n\n\tfor (const fallbackColor of fallback) {\n\t\tif (colors.length === fallback.length) break;\n\t\tif (seen.has(fallbackColor)) continue;\n\t\tcolors.push(fallbackColor);\n\t}\n\n\treturn colors;\n}\n\nexport function rainbowBrackets(options: RainbowBracketsOptions = {}) {\n\tconst colors =\n\t\toptions.colors != null && options.colors.length > 0\n\t\t\t? [...options.colors]\n\t\t\t: getRainbowBracketColors();\n\tconst exactScanLimit = Math.max(\n\t\tMIN_LOOK_BEHIND,\n\t\tMath.floor(options.exactScanLimit || DEFAULT_EXACT_SCAN_LIMIT),\n\t);\n\tconst lookBehind = clampLookBehind(options.lookBehind);\n\tconst theme = buildTheme(colors);\n\tconst marks = colors.map((_, index) =>\n\t\tDecoration.mark({ class: `cm-rainbowBracket-${index}` }),\n\t);\n\n\tconst rainbowBracketsPlugin = ViewPlugin.fromClass(\n\t\tclass {\n\t\t\tdecorations: DecorationSet;\n\t\t\tblockCache = new Map<string, BlockCacheEntry>();\n\t\t\traf = 0;\n\t\t\tpendingView: EditorView | null = null;\n\n\t\t\tconstructor(view: EditorView) {\n\t\t\t\tthis.decorations = this.buildDecorations(view);\n\t\t\t}\n\n\t\t\tupdate(update: ViewUpdate) {\n\t\t\t\tif (!update.docChanged && !update.viewportChanged) return;\n\t\t\t\tthis.scheduleBuild(update.view);\n\t\t\t}\n\n\t\t\tscheduleBuild(view: EditorView): void {\n\t\t\t\tthis.pendingView = view;\n\t\t\t\tif (this.raf) return;\n\t\t\t\t// Bracket recoloring is cosmetic. Collapse bursts of edits/scroll\n\t\t\t\t// events into a single frame so large pastes don't block repeatedly.\n\t\t\t\tthis.raf = requestAnimationFrame(() => {\n\t\t\t\t\tthis.raf = 0;\n\t\t\t\t\tconst pendingView = this.pendingView;\n\t\t\t\t\tthis.pendingView = null;\n\t\t\t\t\tif (!pendingView) return;\n\t\t\t\t\tthis.decorations = this.buildDecorations(pendingView);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tbuildDecorations(view: EditorView): DecorationSet {\n\t\t\t\tconst visibleRanges = view.visibleRanges;\n\t\t\t\tif (!visibleRanges.length || !marks.length) return Decoration.none;\n\n\t\t\t\tconst tree = syntaxTree(view.state);\n\t\t\t\tconst scanStart = alignToBlockStart(\n\t\t\t\t\tgetScanStart(view, lookBehind, exactScanLimit),\n\t\t\t\t);\n\t\t\t\tconst scanEnd = visibleRanges[visibleRanges.length - 1].to;\n\t\t\t\tconst visibleCursor = { index: 0 };\n\t\t\t\tconst openBrackets: BracketInfo[] = [];\n\t\t\t\tlet carrySkipChars = 0;\n\t\t\t\tconst builder = new RangeSetBuilder<Decoration>();\n\n\t\t\t\tfor (\n\t\t\t\t\tlet blockStart = scanStart;\n\t\t\t\t\tblockStart < scanEnd;\n\t\t\t\t\tblockStart += BLOCK_SIZE\n\t\t\t\t) {\n\t\t\t\t\tconst blockEnd = Math.min(scanEnd, blockStart + BLOCK_SIZE);\n\t\t\t\t\tconst blockText = view.state.doc.sliceString(blockStart, blockEnd);\n\t\t\t\t\tconst cacheKey = getBlockCacheKey(\n\t\t\t\t\t\tblockText,\n\t\t\t\t\t\tcarrySkipChars,\n\t\t\t\t\t\tgetBlockContextSignature(tree, blockStart, blockEnd),\n\t\t\t\t\t);\n\t\t\t\t\tlet cachedBlock = this.getCachedBlock(cacheKey);\n\n\t\t\t\t\tif (!cachedBlock) {\n\t\t\t\t\t\tcachedBlock = tokenizeBlock(\n\t\t\t\t\t\t\ttree,\n\t\t\t\t\t\t\tblockText,\n\t\t\t\t\t\t\tblockStart,\n\t\t\t\t\t\t\tcarrySkipChars,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tthis.setCachedBlock(cacheKey, cachedBlock);\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const token of cachedBlock.tokens) {\n\t\t\t\t\t\tconst pos = blockStart + token.offset;\n\n\t\t\t\t\t\tif (isOpeningBracket(token.char)) {\n\t\t\t\t\t\t\tconst colorIndex = openBrackets.length % marks.length;\n\t\t\t\t\t\t\tif (isVisiblePosition(pos, visibleRanges, visibleCursor)) {\n\t\t\t\t\t\t\t\tbuilder.add(pos, pos + 1, marks[colorIndex]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\topenBrackets.push({ char: token.char, colorIndex });\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst matchingOpen =\n\t\t\t\t\t\t\tCLOSING_TO_OPENING[token.char as ClosingBracket];\n\t\t\t\t\t\tif (!matchingOpen) continue;\n\n\t\t\t\t\t\tfor (let index = openBrackets.length - 1; index >= 0; index--) {\n\t\t\t\t\t\t\tif (openBrackets[index].char !== matchingOpen) continue;\n\n\t\t\t\t\t\t\tif (isVisiblePosition(pos, visibleRanges, visibleCursor)) {\n\t\t\t\t\t\t\t\tbuilder.add(\n\t\t\t\t\t\t\t\t\tpos,\n\t\t\t\t\t\t\t\t\tpos + 1,\n\t\t\t\t\t\t\t\t\tmarks[openBrackets[index].colorIndex],\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\topenBrackets.length = index;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tcarrySkipChars = cachedBlock.carrySkipChars;\n\t\t\t\t}\n\n\t\t\t\treturn builder.finish();\n\t\t\t}\n\n\t\t\tgetCachedBlock(key: string): BlockCacheEntry | null {\n\t\t\t\tconst cached = this.blockCache.get(key);\n\t\t\t\tif (!cached) return null;\n\t\t\t\tthis.blockCache.delete(key);\n\t\t\t\tthis.blockCache.set(key, cached);\n\t\t\t\treturn cached;\n\t\t\t}\n\n\t\t\tsetCachedBlock(key: string, value: BlockCacheEntry): void {\n\t\t\t\tif (this.blockCache.has(key)) {\n\t\t\t\t\tthis.blockCache.delete(key);\n\t\t\t\t}\n\n\t\t\t\tthis.blockCache.set(key, value);\n\t\t\t\tif (this.blockCache.size <= MAX_BLOCK_CACHE_ENTRIES) return;\n\n\t\t\t\tconst oldestKey = this.blockCache.keys().next().value;\n\t\t\t\tif (oldestKey !== undefined) {\n\t\t\t\t\tthis.blockCache.delete(oldestKey);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdestroy(): void {\n\t\t\t\tif (this.raf) {\n\t\t\t\t\tcancelAnimationFrame(this.raf);\n\t\t\t\t\tthis.raf = 0;\n\t\t\t\t}\n\t\t\t\tthis.pendingView = null;\n\t\t\t\tthis.blockCache.clear();\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tdecorations: (value) => value.decorations,\n\t\t},\n\t);\n\n\treturn [rainbowBracketsPlugin, theme];\n}\n\nexport default rainbowBrackets;\n"
  },
  {
    "path": "src/cm/supportedModes.ts",
    "content": "import { languages } from \"@codemirror/language-data\";\nimport type { Extension } from \"@codemirror/state\";\nimport { addMode } from \"./modelist\";\n\ntype FilenameMatcher = string | RegExp;\n\ninterface LanguageDescription {\n\tname?: string;\n\talias?: readonly string[];\n\textensions?: readonly string[];\n\tfilenames?: readonly FilenameMatcher[];\n\tfilename?: FilenameMatcher;\n\tload?: () => Promise<Extension>;\n}\n\nfunction normalizeModeKey(value: string): string {\n\treturn String(value ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\nfunction isSafeModeId(value: string): boolean {\n\treturn /^[a-z0-9][a-z0-9._-]*$/.test(value);\n}\n\nfunction slugifyModeId(value: string): string {\n\treturn normalizeModeKey(value)\n\t\t.replace(/\\+\\+/g, \"pp\")\n\t\t.replace(/#/g, \"sharp\")\n\t\t.replace(/&/g, \"and\")\n\t\t.replace(/[^a-z0-9._-]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n}\n\nfunction collectAliases(\n\tname: string,\n\taliases: readonly string[] | undefined,\n): string[] {\n\treturn [\n\t\t...new Set(\n\t\t\t[name, ...(aliases || [])].map(normalizeModeKey).filter(Boolean),\n\t\t),\n\t];\n}\n\nfunction getModeId(name: string, aliases: string[]): string {\n\tconst normalizedName = normalizeModeKey(name);\n\tif (isSafeModeId(normalizedName)) return normalizedName;\n\n\tconst safeAlias = aliases.find(\n\t\t(alias) => alias !== normalizedName && isSafeModeId(alias),\n\t);\n\treturn safeAlias || slugifyModeId(name) || normalizedName || \"text\";\n}\n\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n// 1) Always register a plain text fallback\naddMode(\"Text\", \"txt|text|log|plain\", \"Plain Text\", () => []);\n\n// 2) Register all languages provided by @codemirror/language-data\n//    We convert extensions like [\".js\", \".mjs\"] into a modelist pattern: \"js|mjs\"\n//    and preserve aliases and filename regexes for languages like C++ and Dockerfile.\nfor (const lang of languages as readonly LanguageDescription[]) {\n\ttry {\n\t\tconst name = String(lang?.name || \"\").trim();\n\t\tif (!name) continue;\n\n\t\tconst aliases = collectAliases(name, lang.alias);\n\t\tconst modeId = getModeId(name, aliases);\n\t\tconst parts: string[] = [];\n\t\tconst filenameMatchers: RegExp[] = [];\n\n\t\t// File extensions\n\t\tif (Array.isArray(lang.extensions)) {\n\t\t\tfor (const e of lang.extensions) {\n\t\t\t\tif (typeof e !== \"string\") continue;\n\t\t\t\tconst cleaned = e.replace(/^\\./, \"\").trim();\n\t\t\t\tif (cleaned) parts.push(cleaned);\n\t\t\t}\n\t\t}\n\n\t\t// Exact filenames / filename regexes (Dockerfile, PKGBUILD, nginx*.conf, etc.)\n\t\tconst filenames = Array.isArray(lang.filenames)\n\t\t\t? lang.filenames\n\t\t\t: lang.filename\n\t\t\t\t? [lang.filename]\n\t\t\t\t: [];\n\t\tfor (const fn of filenames) {\n\t\t\tif (typeof fn === \"string\") {\n\t\t\t\tconst cleaned = fn.trim();\n\t\t\t\tif (cleaned) {\n\t\t\t\t\tfilenameMatchers.push(new RegExp(`^${escapeRegExp(cleaned)}$`, \"i\"));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (fn instanceof RegExp) {\n\t\t\t\tfilenameMatchers.push(new RegExp(fn.source, fn.flags));\n\t\t\t}\n\t\t}\n\n\t\tconst pattern = parts.join(\"|\");\n\n\t\t// Wrap language-data loader as our modelist language provider\n\t\t// lang.load() returns a Promise<Extension>; we let the editor handle async loading\n\t\tconst loader = typeof lang.load === \"function\" ? () => lang.load!() : null;\n\n\t\taddMode(modeId, pattern, name, loader, {\n\t\t\taliases,\n\t\t\tfilenameMatchers,\n\t\t});\n\t} catch (_) {\n\t\t// Ignore faulty entries to avoid breaking the whole registration\n\t}\n}\n\n// Luau isn't bundled in @codemirror/language-data, so register it explicitly.\naddMode(\"Luau\", \"luau\", \"Luau\", async () => {\n\tconst { luau } = await import(\"./modes/luau\");\n\treturn luau();\n});\n"
  },
  {
    "path": "src/cm/themes/aura.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\n// Aura theme configuration and extensions\nexport const config = {\n\tname: \"aura\",\n\tdark: true,\n\tbackground: \"#21202e\",\n\tforeground: \"#edecee\",\n\tselection: \"#3d375e7f\",\n\tcursor: \"#a277ff\",\n\tdropdownBackground: \"#21202e\",\n\tdropdownBorder: \"#3b334b\",\n\tactiveLine: \"#4d4b6622\",\n\tlineNumber: \"#a394f033\",\n\tlineNumberActive: \"#cdccce\",\n\tmatchingBracket: \"#a394f033\",\n\tkeyword: \"#a277ff\",\n\tstorage: \"#a277ff\",\n\tvariable: \"#edecee\",\n\tparameter: \"#edecee\",\n\tfunction: \"#ffca85\",\n\tstring: \"#61ffca\",\n\tconstant: \"#61ffca\",\n\ttype: \"#82e2ff\",\n\tclass: \"#82e2ff\",\n\tnumber: \"#61ffca\",\n\tcomment: \"#6d6d6d\",\n\theading: \"#a277ff\",\n\tinvalid: \"#ff6767\",\n\tregexp: \"#61ffca\",\n};\n\nexport const auraTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{\n\t\t\t\tbackgroundColor: config.selection,\n\t\t\t},\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const auraHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function aura() {\n\treturn [auraTheme, syntaxHighlighting(auraHighlightStyle)];\n}\n\nexport default aura;\n"
  },
  {
    "path": "src/cm/themes/dracula.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"dracula\",\n\tdark: true,\n\tbackground: \"#282A36\",\n\tforeground: \"#F8F8F2\",\n\tselection: \"#44475A\",\n\tcursor: \"#F8F8F2\",\n\tdropdownBackground: \"#282A36\",\n\tdropdownBorder: \"#191A21\",\n\tactiveLine: \"#53576c22\",\n\tlineNumber: \"#6272A4\",\n\tlineNumberActive: \"#F8F8F2\",\n\tmatchingBracket: \"#44475A\",\n\tkeyword: \"#FF79C6\",\n\tstorage: \"#FF79C6\",\n\tvariable: \"#F8F8F2\",\n\tparameter: \"#F8F8F2\",\n\tfunction: \"#50FA7B\",\n\tstring: \"#F1FA8C\",\n\tconstant: \"#BD93F9\",\n\ttype: \"#8BE9FD\",\n\tclass: \"#8BE9FD\",\n\tnumber: \"#BD93F9\",\n\tcomment: \"#6272A4\",\n\theading: \"#BD93F9\",\n\tinvalid: \"#FF5555\",\n\tregexp: \"#F1FA8C\",\n};\n\nexport const draculaTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const draculaHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function dracula() {\n\treturn [draculaTheme, syntaxHighlighting(draculaHighlightStyle)];\n}\n\nexport default dracula;\n"
  },
  {
    "path": "src/cm/themes/githubDark.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"githubDark\",\n\tdark: true,\n\tbackground: \"#24292e\",\n\tforeground: \"#d1d5da\",\n\tselection: \"#3392FF44\",\n\tcursor: \"#c8e1ff\",\n\tdropdownBackground: \"#24292e\",\n\tdropdownBorder: \"#1b1f23\",\n\tactiveLine: \"#4d566022\",\n\tlineNumber: \"#444d56\",\n\tlineNumberActive: \"#e1e4e8\",\n\tmatchingBracket: \"#17E5E650\",\n\tkeyword: \"#f97583\",\n\tstorage: \"#f97583\",\n\tvariable: \"#ffab70\",\n\tparameter: \"#e1e4e8\",\n\tfunction: \"#79b8ff\",\n\tstring: \"#9ecbff\",\n\tconstant: \"#79b8ff\",\n\ttype: \"#79b8ff\",\n\tclass: \"#b392f0\",\n\tnumber: \"#79b8ff\",\n\tcomment: \"#6a737d\",\n\theading: \"#79b8ff\",\n\tinvalid: \"#f97583\",\n\tregexp: \"#9ecbff\",\n};\n\nexport const githubDarkTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const githubDarkHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function githubDark() {\n\treturn [githubDarkTheme, syntaxHighlighting(githubDarkHighlightStyle)];\n}\n\nexport default githubDark;\n"
  },
  {
    "path": "src/cm/themes/githubLight.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"githubLight\",\n\tdark: false,\n\tbackground: \"#fff\",\n\tforeground: \"#444d56\",\n\tselection: \"#0366d625\",\n\tcursor: \"#044289\",\n\tdropdownBackground: \"#fff\",\n\tdropdownBorder: \"#e1e4e8\",\n\tactiveLine: \"#c6c6c622\",\n\tlineNumber: \"#1b1f234d\",\n\tlineNumberActive: \"#24292e\",\n\tmatchingBracket: \"#34d05840\",\n\tkeyword: \"#d73a49\",\n\tstorage: \"#d73a49\",\n\tvariable: \"#e36209\",\n\tparameter: \"#24292e\",\n\tfunction: \"#005cc5\",\n\tstring: \"#032f62\",\n\tconstant: \"#005cc5\",\n\ttype: \"#005cc5\",\n\tclass: \"#6f42c1\",\n\tnumber: \"#005cc5\",\n\tcomment: \"#6a737d\",\n\theading: \"#005cc5\",\n\tinvalid: \"#cb2431\",\n\tregexp: \"#032f62\",\n};\n\nexport const githubLightTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const githubLightHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function githubLight() {\n\treturn [githubLightTheme, syntaxHighlighting(githubLightHighlightStyle)];\n}\n\nexport default githubLight;\n"
  },
  {
    "path": "src/cm/themes/index.js",
    "content": "import { EditorState } from \"@codemirror/state\";\nimport { oneDark } from \"@codemirror/theme-one-dark\";\nimport aura, { config as auraConfig } from \"./aura\";\nimport dracula, { config as draculaConfig } from \"./dracula\";\nimport githubDark, { config as githubDarkConfig } from \"./githubDark\";\nimport githubLight, { config as githubLightConfig } from \"./githubLight\";\nimport monokai, { config as monokaiConfig } from \"./monokai\";\nimport noctisLilac, { config as noctisLilacConfig } from \"./noctisLilac\";\nimport solarizedDark, { config as solarizedDarkConfig } from \"./solarizedDark\";\nimport solarizedLight, {\n\tconfig as solarizedLightConfig,\n} from \"./solarizedLight\";\nimport tokyoNight, { config as tokyoNightConfig } from \"./tokyoNight\";\nimport tokyoNightDay, { config as tokyoNightDayConfig } from \"./tokyoNightDay\";\nimport tomorrowNight, { config as tomorrowNightConfig } from \"./tomorrowNight\";\nimport tomorrowNightBright, {\n\tconfig as tomorrowNightBrightConfig,\n} from \"./tomorrowNightBright\";\nimport vscodeDark, { config as vscodeDarkConfig } from \"./vscodeDark\";\n\nconst oneDarkConfig = {\n\tname: \"one_dark\",\n\tdark: true,\n\tbackground: \"#282c34\",\n\tforeground: \"#abb2bf\",\n\tkeyword: \"#c678dd\",\n\tstring: \"#98c379\",\n\tnumber: \"#d19a66\",\n\tcomment: \"#5c6370\",\n\tfunction: \"#61afef\",\n\tvariable: \"#e06c75\",\n\ttype: \"#e5c07b\",\n\tclass: \"#e5c07b\",\n\tconstant: \"#d19a66\",\n\toperator: \"#56b6c2\",\n\tinvalid: \"#ff6b6b\",\n};\n\nconst themes = new Map();\nconst warnedInvalidThemes = new Set();\n\nfunction normalizeExtensions(value, target = []) {\n\tif (Array.isArray(value)) {\n\t\tvalue.forEach((item) => normalizeExtensions(item, target));\n\t\treturn target;\n\t}\n\n\tif (value !== null && value !== undefined) {\n\t\ttarget.push(value);\n\t}\n\n\treturn target;\n}\n\nfunction toExtensionGetter(getExtension) {\n\tif (typeof getExtension === \"function\") {\n\t\treturn () => normalizeExtensions(getExtension());\n\t}\n\n\treturn () => normalizeExtensions(getExtension);\n}\n\nfunction logInvalidThemeOnce(themeId, error, reason = \"\") {\n\tif (warnedInvalidThemes.has(themeId)) return;\n\twarnedInvalidThemes.add(themeId);\n\tconst message = reason\n\t\t? `[editorThemes] Theme '${themeId}' is invalid: ${reason}`\n\t\t: `[editorThemes] Theme '${themeId}' is invalid.`;\n\tconsole.error(message, error);\n}\n\nfunction validateThemeExtensions(themeId, extensions) {\n\tif (!extensions.length) {\n\t\tlogInvalidThemeOnce(themeId, null, \"no extensions were returned\");\n\t\treturn false;\n\t}\n\n\ttry {\n\t\t// Validate against Acode's own CodeMirror instance.\n\t\tEditorState.create({ doc: \"\", extensions });\n\t\treturn true;\n\t} catch (error) {\n\t\tlogInvalidThemeOnce(themeId, error);\n\t\treturn false;\n\t}\n}\n\nfunction resolveThemeEntryExtensions(theme, fallbackExtensions) {\n\tconst fallback = fallbackExtensions.length\n\t\t? [...fallbackExtensions]\n\t\t: [oneDark];\n\n\tif (!theme) return fallback;\n\n\ttry {\n\t\tconst resolved = normalizeExtensions(theme.getExtension?.());\n\t\tif (!validateThemeExtensions(theme.id, resolved)) {\n\t\t\treturn fallback;\n\t\t}\n\t\treturn resolved;\n\t} catch (error) {\n\t\tlogInvalidThemeOnce(theme.id, error);\n\t\treturn fallback;\n\t}\n}\n\nexport function addTheme(id, caption, isDark, getExtension, config = null) {\n\tconst key = String(id || \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (!key || themes.has(key)) return false;\n\n\tconst theme = {\n\t\tid: key,\n\t\tcaption: caption || id,\n\t\tisDark: !!isDark,\n\t\tgetExtension: toExtensionGetter(getExtension),\n\t\tconfig: config || null,\n\t};\n\n\tif (!validateThemeExtensions(key, theme.getExtension())) {\n\t\treturn false;\n\t}\n\n\tthemes.set(key, theme);\n\treturn true;\n}\n\nexport function getThemes() {\n\treturn Array.from(themes.values());\n}\n\nexport function getThemeById(id) {\n\tif (!id) return null;\n\treturn themes.get(String(id).toLowerCase()) || null;\n}\n\nexport function getThemeConfig(id) {\n\tif (!id) return oneDarkConfig;\n\tconst theme = themes.get(String(id).toLowerCase());\n\treturn theme?.config || oneDarkConfig;\n}\n\nexport function getThemeExtensions(id, fallback = [oneDark]) {\n\tconst fallbackExtensions = normalizeExtensions(fallback);\n\tconst theme =\n\t\tgetThemeById(id) || getThemeById(String(id || \"\").replace(/-/g, \"_\"));\n\treturn resolveThemeEntryExtensions(theme, fallbackExtensions);\n}\n\nexport function removeTheme(id) {\n\tif (!id) return;\n\tthemes.delete(String(id).toLowerCase());\n}\n\naddTheme(\"one_dark\", \"One Dark\", true, () => [oneDark], oneDarkConfig);\naddTheme(auraConfig.name, \"Aura\", !!auraConfig.dark, () => aura(), auraConfig);\naddTheme(\n\tnoctisLilacConfig.name,\n\tnoctisLilacConfig.caption || \"Noctis Lilac\",\n\t!!noctisLilacConfig.dark,\n\t() => noctisLilac(),\n\tnoctisLilacConfig,\n);\naddTheme(\n\tdraculaConfig.name,\n\t\"Dracula\",\n\t!!draculaConfig.dark,\n\t() => dracula(),\n\tdraculaConfig,\n);\naddTheme(\n\tgithubDarkConfig.name,\n\t\"GitHub Dark\",\n\t!!githubDarkConfig.dark,\n\t() => githubDark(),\n\tgithubDarkConfig,\n);\naddTheme(\n\tgithubLightConfig.name,\n\t\"GitHub Light\",\n\t!!githubLightConfig.dark,\n\t() => githubLight(),\n\tgithubLightConfig,\n);\naddTheme(\n\tsolarizedDarkConfig.name,\n\t\"Solarized Dark\",\n\t!!solarizedDarkConfig.dark,\n\t() => solarizedDark(),\n\tsolarizedDarkConfig,\n);\naddTheme(\n\tsolarizedLightConfig.name,\n\t\"Solarized Light\",\n\t!!solarizedLightConfig.dark,\n\t() => solarizedLight(),\n\tsolarizedLightConfig,\n);\naddTheme(\n\ttokyoNightDayConfig.name,\n\t\"Tokyo Night Day\",\n\t!!tokyoNightDayConfig.dark,\n\t() => tokyoNightDay(),\n\ttokyoNightDayConfig,\n);\naddTheme(\n\ttokyoNightConfig.name,\n\t\"Tokyo Night\",\n\t!!tokyoNightConfig.dark,\n\t() => tokyoNight(),\n\ttokyoNightConfig,\n);\naddTheme(\n\ttomorrowNightConfig.name,\n\t\"Tomorrow Night\",\n\t!!tomorrowNightConfig.dark,\n\t() => tomorrowNight(),\n\ttomorrowNightConfig,\n);\naddTheme(\n\ttomorrowNightBrightConfig.name,\n\t\"Tomorrow Night Bright\",\n\t!!tomorrowNightBrightConfig.dark,\n\t() => tomorrowNightBright(),\n\ttomorrowNightBrightConfig,\n);\naddTheme(\n\tmonokaiConfig.name,\n\t\"Monokai\",\n\t!!monokaiConfig.dark,\n\t() => monokai(),\n\tmonokaiConfig,\n);\naddTheme(\n\tvscodeDarkConfig.name,\n\t\"VS Code Dark\",\n\t!!vscodeDarkConfig.dark,\n\t() => vscodeDark(),\n\tvscodeDarkConfig,\n);\n\nexport default {\n\tgetThemes,\n\tgetThemeById,\n\tgetThemeConfig,\n\tgetThemeExtensions,\n\taddTheme,\n\tremoveTheme,\n};\n"
  },
  {
    "path": "src/cm/themes/monokai.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"monokai\",\n\tdark: true,\n\tbackground: \"#272822\",\n\tforeground: \"#f8f8f2\",\n\tselection: \"#4a4a76\",\n\tcursor: \"#f8f8f0\",\n\tdropdownBackground: \"#414339\",\n\tdropdownBorder: \"#3e3d32\",\n\tactiveLine: \"#3e3d3257\",\n\tlineNumber: \"#f8f8f270\",\n\tlineNumberActive: \"#f8f8f2\",\n\tmatchingBracket: \"#3e3d32\",\n\tkeyword: \"#F92672\",\n\tstorage: \"#F92672\",\n\tvariable: \"#FD971F\",\n\tparameter: \"#FD971F\",\n\tfunction: \"#66D9EF\",\n\tstring: \"#E6DB74\",\n\tconstant: \"#AE81FF\",\n\ttype: \"#66D9EF\",\n\tclass: \"#A6E22E\",\n\tnumber: \"#AE81FF\",\n\tcomment: \"#88846f\",\n\theading: \"#A6E22E\",\n\tinvalid: \"#F44747\",\n\tregexp: \"#E6DB74\",\n\ttag: \"#F92672\",\n};\n\nexport const monokaiTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{\n\t\t\t\tbackgroundColor: config.selection,\n\t\t\t},\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": {\n\t\t\tborderBottom: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-panels.cm-panels-bottom\": {\n\t\t\tborderTop: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const monokaiHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.tagName, color: config.tag },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function monokai() {\n\treturn [monokaiTheme, syntaxHighlighting(monokaiHighlightStyle)];\n}\n\nexport default monokai;\n"
  },
  {
    "path": "src/cm/themes/noctisLilac.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"noctisLilac\",\n\tdark: false,\n\tbackground: \"#f2f1f8\",\n\tforeground: \"#0c006b\",\n\tselection: \"#d5d1f2\",\n\tcursor: \"#5c49e9\",\n\tdropdownBackground: \"#f2f1f8\",\n\tdropdownBorder: \"#e1def3\",\n\tactiveLine: \"#e1def3\",\n\tlineNumber: \"#0c006b70\",\n\tlineNumberActive: \"#0c006b\",\n\tmatchingBracket: \"#d5d1f2\",\n\tkeyword: \"#ff5792\",\n\tstorage: \"#ff5792\",\n\tvariable: \"#0c006b\",\n\tparameter: \"#0c006b\",\n\tfunction: \"#0095a8\",\n\tstring: \"#00b368\",\n\tconstant: \"#5842ff\",\n\ttype: \"#b3694d\",\n\tclass: \"#0094f0\",\n\tnumber: \"#5842ff\",\n\tcomment: \"#9995b7\",\n\theading: \"#0094f0\",\n\tinvalid: \"#ff5792\",\n\tregexp: \"#00b368\",\n};\n\nexport const noctisLilacTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{\n\t\t\t\tbackgroundColor: config.selection,\n\t\t\t},\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const noctisLilacHighlightStyle = HighlightStyle.define([\n\t{ tag: t.comment, color: config.comment },\n\t{ tag: t.keyword, color: config.keyword, fontWeight: \"bold\" },\n\t{ tag: [t.definitionKeyword, t.modifier], color: config.keyword },\n\t{\n\t\ttag: [t.className, t.tagName, t.definition(t.typeName)],\n\t\tcolor: config.class,\n\t},\n\t{ tag: [t.number, t.bool, t.null, t.special(t.brace)], color: config.number },\n\t{\n\t\ttag: [t.definition(t.propertyName), t.function(t.variableName)],\n\t\tcolor: config.function,\n\t},\n\t{ tag: t.typeName, color: config.type },\n\t{ tag: [t.propertyName, t.variableName], color: \"#fa8900\" },\n\t{ tag: t.operator, color: config.keyword },\n\t{ tag: t.self, color: \"#e64100\" },\n\t{ tag: [t.string, t.regexp], color: config.string },\n\t{ tag: [t.paren, t.bracket], color: \"#0431fa\" },\n\t{ tag: t.labelName, color: \"#00bdd6\" },\n\t{ tag: t.attributeName, color: \"#e64100\" },\n\t{ tag: t.angleBracket, color: config.comment },\n]);\n\nexport function noctisLilac() {\n\treturn [noctisLilacTheme, syntaxHighlighting(noctisLilacHighlightStyle)];\n}\n\nexport default noctisLilac;\n"
  },
  {
    "path": "src/cm/themes/solarizedDark.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"solarizedDark\",\n\tdark: true,\n\tbackground: \"#002B36\",\n\tforeground: \"#93A1A1\",\n\tselection: \"#274642\",\n\tcursor: \"#D30102\",\n\tdropdownBackground: \"#002B36\",\n\tdropdownBorder: \"#2AA19899\",\n\tactiveLine: \"#005b7022\",\n\tlineNumber: \"#93A1A1\",\n\tlineNumberActive: \"#949494\",\n\tmatchingBracket: \"#073642\",\n\tkeyword: \"#859900\",\n\tstorage: \"#93A1A1\",\n\tvariable: \"#268BD2\",\n\tparameter: \"#268BD2\",\n\tfunction: \"#268BD2\",\n\tstring: \"#2AA198\",\n\tconstant: \"#CB4B16\",\n\ttype: \"#CB4B16\",\n\tclass: \"#CB4B16\",\n\tnumber: \"#D33682\",\n\tcomment: \"#586E75\",\n\theading: \"#268BD2\",\n\tinvalid: \"#DC322F\",\n\tregexp: \"#DC322F\",\n};\n\nexport const solarizedDarkTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const solarizedDarkHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function solarizedDark() {\n\treturn [solarizedDarkTheme, syntaxHighlighting(solarizedDarkHighlightStyle)];\n}\n\nexport default solarizedDark;\n"
  },
  {
    "path": "src/cm/themes/solarizedLight.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"solarizedLight\",\n\tdark: false,\n\tbackground: \"#FDF6E3\",\n\tforeground: \"#586E75\",\n\tselection: \"#EEE8D5\",\n\tcursor: \"#657B83\",\n\tdropdownBackground: \"#FDF6E3\",\n\tdropdownBorder: \"#D3AF86\",\n\tactiveLine: \"#d5bd5c22\",\n\tlineNumber: \"#586E75\",\n\tlineNumberActive: \"#567983\",\n\tmatchingBracket: \"#EEE8D5\",\n\tkeyword: \"#859900\",\n\tstorage: \"#586E75\",\n\tvariable: \"#268BD2\",\n\tparameter: \"#268BD2\",\n\tfunction: \"#268BD2\",\n\tstring: \"#2AA198\",\n\tconstant: \"#CB4B16\",\n\ttype: \"#CB4B16\",\n\tclass: \"#CB4B16\",\n\tnumber: \"#D33682\",\n\tcomment: \"#93A1A1\",\n\theading: \"#268BD2\",\n\tinvalid: \"#DC322F\",\n\tregexp: \"#DC322F\",\n};\n\nexport const solarizedLightTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const solarizedLightHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function solarizedLight() {\n\treturn [\n\t\tsolarizedLightTheme,\n\t\tsyntaxHighlighting(solarizedLightHighlightStyle),\n\t];\n}\n\nexport default solarizedLight;\n"
  },
  {
    "path": "src/cm/themes/tokyoNight.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"tokyoNight\",\n\tdark: true,\n\tbackground: \"#1a1b26\",\n\tforeground: \"#787c99\",\n\tselection: \"#515c7e40\",\n\tcursor: \"#c0caf5\",\n\tdropdownBackground: \"#1a1b26\",\n\tdropdownBorder: \"#787c99\",\n\tactiveLine: \"#43455c22\",\n\tlineNumber: \"#363b54\",\n\tlineNumberActive: \"#737aa2\",\n\tmatchingBracket: \"#16161e\",\n\tkeyword: \"#bb9af7\",\n\tstorage: \"#bb9af7\",\n\tvariable: \"#c0caf5\",\n\tparameter: \"#c0caf5\",\n\tfunction: \"#7aa2f7\",\n\tstring: \"#9ece6a\",\n\tconstant: \"#bb9af7\",\n\ttype: \"#0db9d7\",\n\tclass: \"#c0caf5\",\n\tnumber: \"#ff9e64\",\n\tcomment: \"#444b6a\",\n\theading: \"#89ddff\",\n\tinvalid: \"#ff5370\",\n\tregexp: \"#b4f9f8\",\n};\n\nexport const tokyoNightTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const tokyoNightHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function tokyoNight() {\n\treturn [tokyoNightTheme, syntaxHighlighting(tokyoNightHighlightStyle)];\n}\n\nexport default tokyoNight;\n"
  },
  {
    "path": "src/cm/themes/tokyoNightDay.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView, lineNumbers } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"tokyoNightDay\",\n\tdark: false,\n\tbackground: \"#e1e2e7\",\n\tforeground: \"#6a6f8e\",\n\tselection: \"#8591b840\",\n\tcursor: \"#3760bf\",\n\tdropdownBackground: \"#e1e2e7\",\n\tdropdownBorder: \"#6a6f8e\",\n\tactiveLine: \"#a7aaba22\",\n\tlineNumber: \"#b3b6cd\",\n\tlineNumberActive: \"#68709a\",\n\tmatchingBracket: \"#e9e9ec\",\n\tkeyword: \"#9854f1\",\n\tstorage: \"#9854f1\",\n\tvariable: \"#3760bf\",\n\tparameter: \"#3760bf\",\n\tfunction: \"#2e7de9\",\n\tstring: \"#587539\",\n\tconstant: \"#9854f1\",\n\ttype: \"#07879d\",\n\tclass: \"#3760bf\",\n\tnumber: \"#b15c00\",\n\tcomment: \"#9da3c2\",\n\theading: \"#006a83\",\n\tinvalid: \"#ff3e64\",\n\tregexp: \"#2e5857\",\n};\n\nexport const tokyoNightDayTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": { borderBottom: \"2px solid black\" },\n\t\t\".cm-panels.cm-panels-bottom\": { borderTop: \"2px solid black\" },\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const tokyoNightDayHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type, fontStyle: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.keyword },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function tokyoNightDay() {\n\treturn [tokyoNightDayTheme, syntaxHighlighting(tokyoNightDayHighlightStyle)];\n}\n\nexport default tokyoNightDay;\n"
  },
  {
    "path": "src/cm/themes/tomorrowNight.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\n// Palette adapted from Tomorrow Night (Chris Kempson)\nexport const config = {\n\tname: \"tomorrowNight\",\n\tdark: true,\n\tbackground: \"#1D1F21\",\n\tforeground: \"#C5C8C6\",\n\tselection: \"#373B41\",\n\tcursor: \"#AEAFAD\",\n\tdropdownBackground: \"#1D1F21\",\n\tdropdownBorder: \"#4B4E55\",\n\tactiveLine: \"#282A2E\",\n\tlineNumber: \"#4B4E55\",\n\tlineNumberActive: \"#C5C8C6\",\n\tmatchingBracket: \"#282A2E\",\n\tkeyword: \"#B294BB\",\n\tstorage: \"#B294BB\",\n\tvariable: \"#CC6666\",\n\tparameter: \"#DE935F\",\n\tfunction: \"#81A2BE\",\n\tstring: \"#B5BD68\",\n\tconstant: \"#DE935F\",\n\ttype: \"#F0C674\",\n\tclass: \"#F0C674\",\n\tnumber: \"#DE935F\",\n\tcomment: \"#969896\",\n\theading: \"#81A2BE\",\n\tinvalid: \"#DF5F5F\",\n\tregexp: \"#CC6666\",\n\toperator: \"#8ABEB7\",\n\ttag: \"#CC6666\",\n};\n\nexport const tomorrowNightTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": {\n\t\t\tborderBottom: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-panels.cm-panels-bottom\": {\n\t\t\tborderTop: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const tomorrowNightHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.operator },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.tagName, color: config.tag },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function tomorrowNight() {\n\treturn [tomorrowNightTheme, syntaxHighlighting(tomorrowNightHighlightStyle)];\n}\n\nexport default tomorrowNight;\n"
  },
  {
    "path": "src/cm/themes/tomorrowNightBright.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\n// Palette adapted from Tomorrow Night Bright (Chris Kempson)\nexport const config = {\n\tname: \"tomorrowNightBright\",\n\tdark: true,\n\tbackground: \"#000000\",\n\tforeground: \"#DEDEDE\",\n\tselection: \"#424242\",\n\tcursor: \"#9F9F9F\",\n\tdropdownBackground: \"#000000\",\n\tdropdownBorder: \"#343434\",\n\tactiveLine: \"#2A2A2A\",\n\tlineNumber: \"#343434\",\n\tlineNumberActive: \"#DEDEDE\",\n\tmatchingBracket: \"#2A2A2A\",\n\tkeyword: \"#C397D8\",\n\tstorage: \"#C397D8\",\n\tvariable: \"#D54E53\",\n\tparameter: \"#E78C45\",\n\tfunction: \"#7AA6DA\",\n\tstring: \"#B9CA4A\",\n\tconstant: \"#E78C45\",\n\ttype: \"#E7C547\",\n\tclass: \"#E7C547\",\n\tnumber: \"#E78C45\",\n\tcomment: \"#969896\",\n\theading: \"#7AA6DA\",\n\tinvalid: \"#DF5F5F\",\n\tregexp: \"#D54E53\",\n\toperator: \"#70C0B1\",\n\ttag: \"#D54E53\",\n};\n\nexport const tomorrowNightBrightTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{ backgroundColor: config.selection },\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": {\n\t\t\tborderBottom: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-panels.cm-panels-bottom\": {\n\t\t\tborderTop: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selection,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selection },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.foreground,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selection,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const tomorrowNightBrightHighlightStyle = HighlightStyle.define([\n\t{ tag: t.keyword, color: config.keyword },\n\t{\n\t\ttag: [t.name, t.deleted, t.character, t.macroName],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: [t.propertyName], color: config.function },\n\t{\n\t\ttag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.function(t.variableName), t.labelName], color: config.function },\n\t{\n\t\ttag: [t.color, t.constant(t.name), t.standard(t.name)],\n\t\tcolor: config.constant,\n\t},\n\t{ tag: [t.definition(t.name), t.separator], color: config.variable },\n\t{ tag: [t.className], color: config.class },\n\t{\n\t\ttag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],\n\t\tcolor: config.number,\n\t},\n\t{ tag: [t.typeName], color: config.type },\n\t{ tag: [t.operator, t.operatorKeyword], color: config.operator },\n\t{ tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.tagName, color: config.tag },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.link, textDecoration: \"underline\" },\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{ tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable },\n\t{ tag: t.invalid, color: config.invalid },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n]);\n\nexport function tomorrowNightBright() {\n\treturn [\n\t\ttomorrowNightBrightTheme,\n\t\tsyntaxHighlighting(tomorrowNightBrightHighlightStyle),\n\t];\n}\n\nexport default tomorrowNightBright;\n"
  },
  {
    "path": "src/cm/themes/vscodeDark.js",
    "content": "import { HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { EditorView } from \"@codemirror/view\";\nimport { tags as t } from \"@lezer/highlight\";\n\nexport const config = {\n\tname: \"vscodeDark\",\n\tdark: true,\n\tbackground: \"#1e1e1e\",\n\tforeground: \"#9cdcfe\",\n\tselection: \"#6199ff2f\",\n\tselectionMatch: \"#72a1ff59\",\n\tcursor: \"#c6c6c6\",\n\tdropdownBackground: \"#1e1e1e\",\n\tdropdownBorder: \"#3c3c3c\",\n\tactiveLine: \"#ffffff0f\",\n\tlineNumber: \"#838383\",\n\tlineNumberActive: \"#ffffff\",\n\tmatchingBracket: \"#515c6a\",\n\tkeyword: \"#569cd6\",\n\tvariable: \"#9cdcfe\",\n\tparameter: \"#9cdcfe\",\n\tfunction: \"#dcdcaa\",\n\tstring: \"#ce9178\",\n\tconstant: \"#569cd6\",\n\ttype: \"#4ec9b0\",\n\tclass: \"#4ec9b0\",\n\tnumber: \"#b5cea8\",\n\tcomment: \"#6a9955\",\n\theading: \"#9cdcfe\",\n\tinvalid: \"#ff0000\",\n\tregexp: \"#d16969\",\n\ttag: \"#4ec9b0\",\n\toperator: \"#d4d4d4\",\n\tangleBracket: \"#808080\",\n};\n\nexport const vscodeDarkTheme = EditorView.theme(\n\t{\n\t\t\"&\": {\n\t\t\tcolor: config.foreground,\n\t\t\tbackgroundColor: config.background,\n\t\t},\n\n\t\t\".cm-content\": { caretColor: config.cursor },\n\n\t\t\".cm-cursor, .cm-dropCursor\": { borderLeftColor: config.cursor },\n\t\t\"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection\":\n\t\t\t{\n\t\t\t\tbackgroundColor: config.selection,\n\t\t\t},\n\n\t\t\".cm-panels\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-panels.cm-panels-top\": {\n\t\t\tborderBottom: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-panels.cm-panels-bottom\": {\n\t\t\tborderTop: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\n\t\t\".cm-searchMatch\": {\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\toutline: `1px solid ${config.dropdownBorder}`,\n\t\t},\n\t\t\".cm-searchMatch.cm-searchMatch-selected\": {\n\t\t\tbackgroundColor: config.selectionMatch,\n\t\t},\n\n\t\t\".cm-activeLine\": { backgroundColor: config.activeLine },\n\t\t\".cm-selectionMatch\": { backgroundColor: config.selectionMatch },\n\n\t\t\"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket\": {\n\t\t\tbackgroundColor: config.matchingBracket,\n\t\t\toutline: \"none\",\n\t\t},\n\n\t\t\".cm-gutters\": {\n\t\t\tbackgroundColor: config.background,\n\t\t\tcolor: config.lineNumber,\n\t\t\tborder: \"none\",\n\t\t},\n\t\t\".cm-activeLineGutter\": { backgroundColor: config.background },\n\n\t\t\".cm-lineNumbers .cm-gutterElement\": { color: config.lineNumber },\n\t\t\".cm-lineNumbers .cm-activeLineGutter\": { color: config.lineNumberActive },\n\n\t\t\".cm-foldPlaceholder\": {\n\t\t\tbackgroundColor: \"transparent\",\n\t\t\tborder: \"none\",\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip\": {\n\t\t\tborder: `1px solid ${config.dropdownBorder}`,\n\t\t\tbackgroundColor: config.dropdownBackground,\n\t\t\tcolor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:before\": {\n\t\t\tborderTopColor: \"transparent\",\n\t\t\tborderBottomColor: \"transparent\",\n\t\t},\n\t\t\".cm-tooltip .cm-tooltip-arrow:after\": {\n\t\t\tborderTopColor: config.foreground,\n\t\t\tborderBottomColor: config.foreground,\n\t\t},\n\t\t\".cm-tooltip-autocomplete\": {\n\t\t\t\"& > ul > li[aria-selected]\": {\n\t\t\t\tbackground: config.selectionMatch,\n\t\t\t\tcolor: config.foreground,\n\t\t\t},\n\t\t},\n\t},\n\t{ dark: config.dark },\n);\n\nexport const vscodeDarkHighlightStyle = HighlightStyle.define([\n\t{\n\t\ttag: [\n\t\t\tt.keyword,\n\t\t\tt.operatorKeyword,\n\t\t\tt.modifier,\n\t\t\tt.color,\n\t\t\tt.constant(t.name),\n\t\t\tt.standard(t.name),\n\t\t\tt.standard(t.tagName),\n\t\t\tt.special(t.brace),\n\t\t\tt.atom,\n\t\t\tt.bool,\n\t\t\tt.special(t.variableName),\n\t\t],\n\t\tcolor: config.keyword,\n\t},\n\t{ tag: [t.controlKeyword, t.moduleKeyword], color: \"#c586c0\" },\n\t{\n\t\ttag: [\n\t\t\tt.name,\n\t\t\tt.deleted,\n\t\t\tt.character,\n\t\t\tt.macroName,\n\t\t\tt.propertyName,\n\t\t\tt.variableName,\n\t\t\tt.labelName,\n\t\t\tt.definition(t.name),\n\t\t],\n\t\tcolor: config.variable,\n\t},\n\t{ tag: t.heading, fontWeight: \"bold\", color: config.heading },\n\t{\n\t\ttag: [\n\t\t\tt.typeName,\n\t\t\tt.className,\n\t\t\tt.tagName,\n\t\t\tt.number,\n\t\t\tt.changed,\n\t\t\tt.annotation,\n\t\t\tt.self,\n\t\t\tt.namespace,\n\t\t],\n\t\tcolor: config.type,\n\t},\n\t{\n\t\ttag: [t.function(t.variableName), t.function(t.propertyName)],\n\t\tcolor: config.function,\n\t},\n\t{ tag: [t.number], color: config.number },\n\t{\n\t\ttag: [t.operator, t.punctuation, t.separator, t.url, t.escape, t.regexp],\n\t\tcolor: config.operator,\n\t},\n\t{ tag: [t.regexp], color: config.regexp },\n\t{\n\t\ttag: [t.special(t.string), t.processingInstruction, t.string, t.inserted],\n\t\tcolor: config.string,\n\t},\n\t{ tag: [t.angleBracket], color: config.angleBracket },\n\t{ tag: t.strong, fontWeight: \"bold\" },\n\t{ tag: t.emphasis, fontStyle: \"italic\" },\n\t{ tag: t.strikethrough, textDecoration: \"line-through\" },\n\t{ tag: [t.meta, t.comment], color: config.comment },\n\t{ tag: t.link, color: config.comment, textDecoration: \"underline\" },\n\t{ tag: t.invalid, color: config.invalid },\n]);\n\nexport function vscodeDark() {\n\treturn [vscodeDarkTheme, syntaxHighlighting(vscodeDarkHighlightStyle)];\n}\n\nexport default vscodeDark;\n"
  },
  {
    "path": "src/cm/touchSelectionMenu.js",
    "content": "import { EditorSelection } from \"@codemirror/state\";\nimport selectionMenu from \"lib/selectionMenu\";\n\nconst TAP_MAX_DELAY = 500;\nconst TAP_MAX_DISTANCE = 20;\nconst EDGE_SCROLL_GAP = 40;\nconst MENU_MARGIN = 10;\nconst MENU_SHOW_DELAY = 120;\nconst MENU_CARET_GAP = 10;\nconst MENU_SELECTION_GAP = 12;\nconst MENU_HANDLE_CLEARANCE = 28;\nconst TAP_MAX_COLUMN_DELTA = 2;\nconst TAP_MAX_POS_DELTA = 2;\n\n/**\n * Classify taps into single/double/triple tap buckets.\n * @param {{x:number,y:number,time:number,count:number}|null} previousTap\n * @param {{x:number,y:number,time:number}} tap\n * @returns {{x:number,y:number,time:number,count:number}}\n */\nexport function classifyTap(previousTap, tap) {\n\tif (!previousTap) {\n\t\treturn { ...tap, count: 1 };\n\t}\n\n\tconst dt = tap.time - previousTap.time;\n\tconst dx = tap.x - previousTap.x;\n\tconst dy = tap.y - previousTap.y;\n\tconst distance = Math.hypot(dx, dy);\n\tconst sameTextZone =\n\t\ttap.line != null &&\n\t\tpreviousTap.line != null &&\n\t\ttap.line === previousTap.line &&\n\t\tMath.abs((tap.column ?? 0) - (previousTap.column ?? 0)) <=\n\t\t\tTAP_MAX_COLUMN_DELTA;\n\tconst nearSamePos =\n\t\ttap.pos != null &&\n\t\tpreviousTap.pos != null &&\n\t\tMath.abs(tap.pos - previousTap.pos) <= TAP_MAX_POS_DELTA;\n\n\tif (\n\t\tdt <= TAP_MAX_DELAY &&\n\t\t(distance <= TAP_MAX_DISTANCE || sameTextZone || nearSamePos)\n\t) {\n\t\treturn {\n\t\t\t...tap,\n\t\t\tcount: Math.min(previousTap.count + 1, 3),\n\t\t};\n\t}\n\n\treturn { ...tap, count: 1 };\n}\n\n/**\n * Clamp menu coordinates so it stays within the container bounds.\n * @param {{left:number, top:number, width:number, height:number}} menuRect\n * @param {{left:number, top:number, width:number, height:number}} containerRect\n * @returns {{left:number, top:number}}\n */\nexport function clampMenuPosition(menuRect, containerRect) {\n\tconst maxLeft = Math.max(\n\t\tcontainerRect.left + MENU_MARGIN,\n\t\tcontainerRect.left + containerRect.width - menuRect.width - MENU_MARGIN,\n\t);\n\tconst maxTop = Math.max(\n\t\tcontainerRect.top + MENU_MARGIN,\n\t\tcontainerRect.top + containerRect.height - menuRect.height - MENU_MARGIN,\n\t);\n\n\treturn {\n\t\tleft: clamp(menuRect.left, containerRect.left + MENU_MARGIN, maxLeft),\n\t\ttop: clamp(menuRect.top, containerRect.top + MENU_MARGIN, maxTop),\n\t};\n}\n\n/**\n * Filter menu items using Ace-compatible rules.\n * @param {ReturnType<typeof selectionMenu>} items\n * @param {{readOnly:boolean,hasSelection:boolean}} options\n */\nexport function filterSelectionMenuItems(items, options) {\n\tconst { readOnly, hasSelection } = options;\n\treturn items.filter((item) => {\n\t\tif (readOnly && !item.readOnly) return false;\n\t\tif (hasSelection && ![\"selected\", \"all\"].includes(item.mode)) return false;\n\t\tif (!hasSelection && item.mode === \"selected\") return false;\n\t\treturn true;\n\t});\n}\n\n/**\n * Detect which edge(s) should trigger drag auto-scroll.\n * @param {{\n *   x:number,\n *   y:number,\n *   rect:{left:number,right:number,top:number,bottom:number},\n *   allowHorizontal?:boolean,\n *   gap?:number,\n * }} options\n * @returns {{horizontal:number, vertical:number}}\n */\nexport function getEdgeScrollDirections(options) {\n\tconst { x, y, rect, allowHorizontal = true, gap = EDGE_SCROLL_GAP } = options;\n\tlet horizontal = 0;\n\tlet vertical = 0;\n\n\tif (allowHorizontal) {\n\t\tif (x < rect.left + gap) horizontal = -1;\n\t\telse if (x > rect.right - gap) horizontal = 1;\n\t}\n\n\tif (y < rect.top + gap) vertical = -1;\n\telse if (y > rect.bottom - gap) vertical = 1;\n\n\treturn { horizontal, vertical };\n}\n\nfunction clamp(value, min, max) {\n\treturn Math.max(min, Math.min(max, value));\n}\n\nexport default function createTouchSelectionMenu(view, options = {}) {\n\treturn new TouchSelectionMenuController(view, options);\n}\n\nclass TouchSelectionMenuController {\n\t#view;\n\t#container;\n\t#getActiveFile;\n\t#isShiftSelectionActive;\n\t#stateSyncRaf = 0;\n\t#isScrolling = false;\n\t#isPointerInteracting = false;\n\t#shiftSelectionSession = null;\n\t#pendingShiftSelectionClick = null;\n\t#menuActive = false;\n\t#menuRequested = false;\n\t#enabled = true;\n\t#handlingMenuAction = false;\n\t#menuShowTimer = null;\n\n\tconstructor(view, options = {}) {\n\t\tthis.#view = view;\n\t\tthis.#container =\n\t\t\toptions.container || view.dom.closest(\".editor-container\") || view.dom;\n\t\tthis.#getActiveFile = options.getActiveFile || (() => null);\n\t\tthis.#isShiftSelectionActive =\n\t\t\toptions.isShiftSelectionActive || (() => false);\n\t\tthis.$menu = document.createElement(\"menu\");\n\t\tthis.$menu.className = \"cursor-menu\";\n\t\tthis.#bindEvents();\n\t}\n\n\t#bindEvents() {\n\t\tconst root = this.#view.dom;\n\t\troot.addEventListener(\"contextmenu\", this.#onContextMenu, true);\n\t\tdocument.addEventListener(\"pointerdown\", this.#onGlobalPointerDown, true);\n\t\tdocument.addEventListener(\"pointerup\", this.#onGlobalPointerUp, true);\n\t\tdocument.addEventListener(\"pointercancel\", this.#onGlobalPointerUp, true);\n\t}\n\n\tdestroy() {\n\t\tconst root = this.#view.dom;\n\t\troot.removeEventListener(\"contextmenu\", this.#onContextMenu, true);\n\t\tdocument.removeEventListener(\n\t\t\t\"pointerdown\",\n\t\t\tthis.#onGlobalPointerDown,\n\t\t\ttrue,\n\t\t);\n\t\tdocument.removeEventListener(\"pointerup\", this.#onGlobalPointerUp, true);\n\t\tdocument.removeEventListener(\n\t\t\t\"pointercancel\",\n\t\t\tthis.#onGlobalPointerUp,\n\t\t\ttrue,\n\t\t);\n\t\tthis.#clearMenuShowTimer();\n\t\tcancelAnimationFrame(this.#stateSyncRaf);\n\t\tthis.#stateSyncRaf = 0;\n\t\tthis.#shiftSelectionSession = null;\n\t\tthis.#pendingShiftSelectionClick = null;\n\t\tthis.#hideMenu(true);\n\t}\n\n\tsetEnabled(enabled) {\n\t\tthis.#enabled = !!enabled;\n\t\tif (this.#enabled) return;\n\t\tthis.#shiftSelectionSession = null;\n\t\tthis.#pendingShiftSelectionClick = null;\n\t\tthis.#menuRequested = false;\n\t\tthis.#isPointerInteracting = false;\n\t\tthis.#isScrolling = false;\n\t\tthis.#clearMenuShowTimer();\n\t\tcancelAnimationFrame(this.#stateSyncRaf);\n\t\tthis.#stateSyncRaf = 0;\n\t\tthis.#hideMenu(true);\n\t}\n\n\tsetSelection(value) {\n\t\tif (!this.#enabled) return;\n\t\tif (value) {\n\t\t\tthis.#menuRequested = true;\n\t\t}\n\t\tthis.onStateChanged({\n\t\t\tpointerTriggered: !!value,\n\t\t\tselectionChanged: true,\n\t\t});\n\t}\n\n\tsetMenu(value) {\n\t\tthis.#menuRequested = !!value;\n\t\tif (!this.#enabled) return;\n\t\tif (!value) {\n\t\t\tthis.#clearMenuShowTimer();\n\t\t\tthis.#hideMenu();\n\t\t\treturn;\n\t\t}\n\t\tthis.#scheduleMenuShow(MENU_SHOW_DELAY);\n\t}\n\n\tisMenuVisible() {\n\t\treturn this.#menuActive && this.$menu.isConnected;\n\t}\n\n\tonScrollStart() {\n\t\tif (!this.#enabled) return;\n\t\tif (this.#isScrolling) return;\n\t\tthis.#clearMenuShowTimer();\n\t\tthis.#isScrolling = true;\n\t\tthis.#hideMenu();\n\t}\n\n\tonScrollEnd() {\n\t\tif (!this.#enabled || !this.#isScrolling) return;\n\t\tthis.#isScrolling = false;\n\t\tif (this.#shouldShowMenu()) this.#scheduleMenuShow(MENU_SHOW_DELAY);\n\t}\n\n\tonStateChanged(meta = {}) {\n\t\tif (!this.#enabled) return;\n\t\tif (this.#handlingMenuAction) return;\n\t\tif (meta.selectionChanged && this.#menuActive) {\n\t\t\tthis.#hideMenu();\n\t\t}\n\t\tif (!this.#shouldShowMenu()) {\n\t\t\tif (!this.#hasSelection()) {\n\t\t\t\tthis.#menuRequested = false;\n\t\t\t}\n\t\t\tthis.#clearMenuShowTimer();\n\t\t\tthis.#hideMenu();\n\t\t\treturn;\n\t\t}\n\t\tconst delay =\n\t\t\tmeta.pointerTriggered || meta.selectionChanged ? MENU_SHOW_DELAY : 0;\n\t\tthis.#scheduleMenuShow(delay);\n\t}\n\n\tonSessionChanged() {\n\t\tif (!this.#enabled) return;\n\t\tthis.#shiftSelectionSession = null;\n\t\tthis.#pendingShiftSelectionClick = null;\n\t\tthis.#menuRequested = false;\n\t\tthis.#isPointerInteracting = false;\n\t\tthis.#isScrolling = false;\n\t\tthis.#clearMenuShowTimer();\n\t\tthis.#hideMenu(true);\n\t}\n\n\t#onContextMenu = (event) => {\n\t\tif (!this.#enabled) return;\n\t\tif (this.#isIgnoredPointerTarget(event.target)) return;\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\t\tthis.#menuRequested = true;\n\t\tthis.#scheduleMenuShow(MENU_SHOW_DELAY);\n\t};\n\n\t#onGlobalPointerDown = (event) => {\n\t\tconst target = event.target;\n\t\tif (this.$menu.contains(target)) return;\n\t\tif (this.#isIgnoredPointerTarget(target)) {\n\t\t\tthis.#shiftSelectionSession = null;\n\t\t\treturn;\n\t\t}\n\t\tif (target instanceof Node && this.#view.dom.contains(target)) {\n\t\t\tthis.#captureShiftSelection(event);\n\t\t\tthis.#isPointerInteracting = true;\n\t\t\tthis.#clearMenuShowTimer();\n\t\t\tthis.#hideMenu();\n\t\t\treturn;\n\t\t}\n\t\tthis.#shiftSelectionSession = null;\n\t\tthis.#isPointerInteracting = false;\n\t\tthis.#menuRequested = false;\n\t\tthis.#hideMenu();\n\t};\n\n\t#onGlobalPointerUp = (event) => {\n\t\tif (event.type === \"pointerup\") {\n\t\t\tthis.#commitShiftSelection(event);\n\t\t} else {\n\t\t\tthis.#shiftSelectionSession = null;\n\t\t}\n\t\tif (!this.#isPointerInteracting) return;\n\t\tthis.#isPointerInteracting = false;\n\t\tif (!this.#enabled) return;\n\t\tif (this.#shouldShowMenu()) {\n\t\t\tthis.#scheduleMenuShow(MENU_SHOW_DELAY);\n\t\t\treturn;\n\t\t}\n\t\tif (!this.#hasSelection()) {\n\t\t\tthis.#menuRequested = false;\n\t\t}\n\t\tthis.#hideMenu();\n\t};\n\n\t#captureShiftSelection(event) {\n\t\tif (!this.#canExtendSelection(event)) {\n\t\t\tthis.#shiftSelectionSession = null;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#shiftSelectionSession = {\n\t\t\tpointerId: event.pointerId,\n\t\t\tanchor: this.#view.state.selection.main.anchor,\n\t\t\tx: event.clientX,\n\t\t\ty: event.clientY,\n\t\t};\n\t}\n\n\t#commitShiftSelection(event) {\n\t\tconst session = this.#shiftSelectionSession;\n\t\tthis.#shiftSelectionSession = null;\n\t\tif (!session) return;\n\t\tif (!this.#canExtendSelection(event)) return;\n\t\tif (event.pointerId !== session.pointerId) return;\n\t\tif (\n\t\t\tMath.hypot(event.clientX - session.x, event.clientY - session.y) >\n\t\t\tTAP_MAX_DISTANCE\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst target = event.target;\n\t\tif (!(target instanceof Node) || !this.#view.dom.contains(target)) return;\n\t\tif (this.#isIgnoredPointerTarget(target)) return;\n\n\t\t// Rely on pointer coordinates instead of click events so touch selection\n\t\t// keeps working when the browser/native path owns the actual tap.\n\t\tconst head = this.#view.posAtCoords(\n\t\t\t{ x: event.clientX, y: event.clientY },\n\t\t\tfalse,\n\t\t);\n\t\tthis.#view.dispatch({\n\t\t\tselection: EditorSelection.range(session.anchor, head),\n\t\t\tuserEvent: \"select.extend\",\n\t\t});\n\t\tthis.#pendingShiftSelectionClick = {\n\t\t\tx: event.clientX,\n\t\t\ty: event.clientY,\n\t\t\ttimeStamp: event.timeStamp,\n\t\t};\n\t\tevent.preventDefault();\n\t}\n\n\t#canExtendSelection(event) {\n\t\tif (!this.#enabled) return false;\n\t\tif (!(event.isTrusted && event.isPrimary)) return false;\n\t\tif (typeof event.button === \"number\" && event.button !== 0) return false;\n\t\treturn !!this.#isShiftSelectionActive(event);\n\t}\n\n\tconsumePendingShiftSelectionClick(event) {\n\t\tconst pending = this.#pendingShiftSelectionClick;\n\t\tthis.#pendingShiftSelectionClick = null;\n\t\tif (!pending || !this.#enabled) return false;\n\t\tif (event.timeStamp - pending.timeStamp > TAP_MAX_DELAY) return false;\n\t\tif (\n\t\t\tMath.hypot(event.clientX - pending.x, event.clientY - pending.y) >\n\t\t\tTAP_MAX_DISTANCE\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tconst target = event.target;\n\t\tif (!(target instanceof Node) || !this.#view.dom.contains(target))\n\t\t\treturn false;\n\t\tif (this.#isIgnoredPointerTarget(target)) return false;\n\t\treturn true;\n\t}\n\n\t#shouldShowMenu() {\n\t\tif (this.#isScrolling || this.#isPointerInteracting || !this.#view.hasFocus)\n\t\t\treturn false;\n\t\treturn this.#hasSelection() || this.#menuRequested;\n\t}\n\n\t#scheduleMenuShow(delay = 0) {\n\t\tthis.#clearMenuShowTimer();\n\t\tif (!this.#enabled || this.#isScrolling) return;\n\t\tthis.#menuShowTimer = setTimeout(() => {\n\t\t\tthis.#menuShowTimer = null;\n\t\t\tif (!this.#enabled || this.#isScrolling) return;\n\t\t\tif (!this.#shouldShowMenu()) {\n\t\t\t\tif (!this.#hasSelection()) {\n\t\t\t\t\tthis.#menuRequested = false;\n\t\t\t\t}\n\t\t\t\tthis.#hideMenu();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcancelAnimationFrame(this.#stateSyncRaf);\n\t\t\tthis.#stateSyncRaf = requestAnimationFrame(() => {\n\t\t\t\tthis.#stateSyncRaf = 0;\n\t\t\t\tthis.#showMenuDeferred();\n\t\t\t});\n\t\t}, delay);\n\t}\n\n\t#safeCoordsAtPos(view, pos) {\n\t\ttry {\n\t\t\treturn view.coordsAtPos(pos);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t#getMenuAnchor(selection = this.#hasSelection()) {\n\t\tconst range = this.#view.state.selection.main;\n\t\tif (!selection) {\n\t\t\tconst caret = this.#safeCoordsAtPos(this.#view, range.head);\n\t\t\tif (!caret) return null;\n\t\t\treturn {\n\t\t\t\tx: (caret.left + caret.right) / 2,\n\t\t\t\ttop: caret.top,\n\t\t\t\tbottom: caret.bottom,\n\t\t\t\thasSelection: false,\n\t\t\t};\n\t\t}\n\n\t\tconst start = this.#safeCoordsAtPos(this.#view, range.from);\n\t\tconst end = this.#safeCoordsAtPos(this.#view, range.to);\n\t\tconst primary = start || end;\n\t\tif (!primary) return null;\n\t\tconst secondary = end || start || primary;\n\t\treturn {\n\t\t\tx: ((start?.left ?? primary.left) + (end?.left ?? secondary.left)) / 2,\n\t\t\ttop: Math.min(primary.top, secondary.top),\n\t\t\tbottom: Math.max(primary.bottom, secondary.bottom),\n\t\t\thasSelection: true,\n\t\t};\n\t}\n\n\t#showMenu(anchor) {\n\t\tconst hasSelection = this.#hasSelection();\n\t\tconst items = filterSelectionMenuItems(selectionMenu(), {\n\t\t\treadOnly: this.#isReadOnly(),\n\t\t\thasSelection,\n\t\t});\n\n\t\tthis.$menu.innerHTML = \"\";\n\t\tif (!items.length) {\n\t\t\tthis.#menuRequested = false;\n\t\t\tthis.#hideMenu(true);\n\t\t\treturn;\n\t\t}\n\n\t\titems.forEach(({ onclick, text }) => {\n\t\t\tconst $item = document.createElement(\"div\");\n\t\t\tif (typeof text === \"string\") {\n\t\t\t\t$item.textContent = text;\n\t\t\t} else if (text instanceof Node) {\n\t\t\t\t$item.append(text.cloneNode(true));\n\t\t\t}\n\t\t\tlet handled = false;\n\t\t\tconst runAction = (event) => {\n\t\t\t\tif (handled) return;\n\t\t\t\thandled = true;\n\t\t\t\tevent.preventDefault();\n\t\t\t\tevent.stopPropagation();\n\t\t\t\tthis.#handlingMenuAction = true;\n\t\t\t\ttry {\n\t\t\t\t\tonclick?.();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.#handlingMenuAction = false;\n\t\t\t\t\tthis.#menuRequested = false;\n\t\t\t\t\tthis.#hideMenu();\n\t\t\t\t\tthis.#view.focus();\n\t\t\t\t}\n\t\t\t};\n\t\t\t$item.addEventListener(\"pointerdown\", runAction);\n\t\t\t$item.addEventListener(\"click\", runAction);\n\t\t\tthis.$menu.append($item);\n\t\t});\n\n\t\tif (!this.$menu.isConnected) {\n\t\t\tthis.#container.append(this.$menu);\n\t\t}\n\n\t\tconst containerRect = this.#container.getBoundingClientRect();\n\t\tthis.$menu.style.left = \"0px\";\n\t\tthis.$menu.style.top = \"0px\";\n\t\tthis.$menu.style.visibility = \"hidden\";\n\n\t\tconst menuRect = this.$menu.getBoundingClientRect();\n\t\tconst preferredLeft = anchor.x - menuRect.width / 2;\n\t\tconst aboveGap = anchor.hasSelection ? MENU_SELECTION_GAP : MENU_CARET_GAP;\n\t\tconst belowGap = anchor.hasSelection\n\t\t\t? MENU_HANDLE_CLEARANCE\n\t\t\t: MENU_CARET_GAP;\n\t\tconst topAbove = anchor.top - menuRect.height - aboveGap;\n\t\tconst topBelow = anchor.bottom + belowGap;\n\t\tconst minTop = containerRect.top + MENU_MARGIN;\n\t\tconst maxTop =\n\t\t\tcontainerRect.top + containerRect.height - menuRect.height - MENU_MARGIN;\n\t\tconst fitsAbove = topAbove >= minTop;\n\t\tconst fitsBelow = topBelow <= maxTop;\n\t\tconst clamped = clampMenuPosition(\n\t\t\t{\n\t\t\t\tleft: preferredLeft,\n\t\t\t\ttop: fitsAbove || !fitsBelow ? topAbove : topBelow,\n\t\t\t\twidth: menuRect.width,\n\t\t\t\theight: menuRect.height,\n\t\t\t},\n\t\t\t{\n\t\t\t\tleft: containerRect.left,\n\t\t\t\ttop: containerRect.top,\n\t\t\t\twidth: containerRect.width,\n\t\t\t\theight: containerRect.height,\n\t\t\t},\n\t\t);\n\n\t\tthis.$menu.style.left = `${clamped.left - containerRect.left}px`;\n\t\tthis.$menu.style.top = `${clamped.top - containerRect.top}px`;\n\t\tthis.$menu.style.visibility = \"\";\n\t\tthis.#menuActive = true;\n\t\tthis.#menuRequested = false;\n\t}\n\n\t#showMenuDeferred() {\n\t\tif (!this.#enabled || this.#isScrolling || !this.#shouldShowMenu()) return;\n\t\tconst useSelectionAnchor = this.#hasSelection();\n\t\tthis.#view.requestMeasure({\n\t\t\tread: () => this.#getMenuAnchor(useSelectionAnchor),\n\t\t\twrite: (anchor) => {\n\t\t\t\tif (!this.#enabled || this.#isScrolling || !this.#shouldShowMenu()) {\n\t\t\t\t\tthis.#hideMenu();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!anchor) {\n\t\t\t\t\tthis.#hideMenu(true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#showMenu(anchor);\n\t\t\t},\n\t\t});\n\t}\n\n\t#hideMenu(force = false) {\n\t\tif (!force && !this.#menuActive && !this.$menu.isConnected) return;\n\t\tif (this.$menu.isConnected) {\n\t\t\tthis.$menu.remove();\n\t\t}\n\t\tthis.#menuActive = false;\n\t}\n\n\t#clearMenuShowTimer() {\n\t\tclearTimeout(this.#menuShowTimer);\n\t\tthis.#menuShowTimer = null;\n\t}\n\n\t#isReadOnly() {\n\t\tconst activeFile = this.#getActiveFile();\n\t\tif (activeFile?.type === \"editor\") {\n\t\t\treturn !activeFile.editable || !!activeFile.loading;\n\t\t}\n\t\treturn !!this.#view.state?.readOnly;\n\t}\n\n\t#isIgnoredPointerTarget(target) {\n\t\tlet element = null;\n\t\tif (target instanceof Element) {\n\t\t\telement = target;\n\t\t} else if (target instanceof Node) {\n\t\t\telement = target.parentElement;\n\t\t}\n\t\tif (!element) return false;\n\t\tif (element.closest(\".cm-tooltip, .cm-panel\")) return true;\n\t\tconst editorContent = element.closest(\".cm-content\");\n\t\tif (editorContent && this.#view.dom.contains(editorContent)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\n\t\t\telement.closest(\n\t\t\t\t'input, textarea, select, button, a, [contenteditable], [role=\"button\"]',\n\t\t\t)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t#hasSelection() {\n\t\tconst selection = this.#view.state.selection.main;\n\t\treturn selection.from !== selection.to;\n\t}\n}\n"
  },
  {
    "path": "src/components/WebComponents/index.js",
    "content": "import \"@ungap/custom-elements\";\nimport WcPage from \"./wcPage\";\n\ncustomElements.define(\"wc-page\", WcPage);\n"
  },
  {
    "path": "src/components/WebComponents/wcPage.js",
    "content": "import tile from \"../tile\";\n\nexport default class WCPage extends HTMLElement {\n\t#leadBtn;\n\t#header;\n\t#on = {\n\t\thide: [],\n\t\tshow: [],\n\t\twillconnect: [],\n\t\twilldisconnect: [],\n\t};\n\t#append;\n\thandler;\n\tonhide;\n\tonconnect;\n\tondisconnect;\n\tonwillconnect;\n\tonwilldisconnect;\n\n\tconstructor() {\n\t\tsuper();\n\t\tconst title = this.getAttribute(\"data-title\");\n\n\t\tthis.handler = new PageHandler(this);\n\t\tthis.#append = super.append.bind(this);\n\t\tthis.append = this.appendBody.bind(this);\n\t\tthis.hide = this.hide.bind(this);\n\t\tthis.settitle = this.settitle.bind(this);\n\t\tthis.on = this.on.bind(this);\n\t\tthis.off = this.off.bind(this);\n\n\t\tthis.handler.onReplace = () => {\n\t\t\tif (typeof this.onwilldisconnect === \"function\") {\n\t\t\t\tthis.onwilldisconnect();\n\t\t\t}\n\n\t\t\tthis.#on.willdisconnect.forEach((cb) => cb.call(this));\n\t\t};\n\n\t\tthis.handler.onRestore = () => {\n\t\t\tif (typeof this.onwillconnect === \"function\") {\n\t\t\t\tthis.onwillconnect();\n\t\t\t}\n\n\t\t\tthis.#on.willconnect.forEach((cb) => cb.call(this));\n\t\t};\n\n\t\tthis.#leadBtn = (\n\t\t\t<span\n\t\t\t\tclassName=\"icon arrow_back\"\n\t\t\t\tonclick={() => this.hide.call(this)}\n\t\t\t\tattr-action=\"go-back\"\n\t\t\t></span>\n\t\t);\n\n\t\tthis.#header = tile({\n\t\t\ttype: \"header\",\n\t\t\ttext: title || \"Page\",\n\t\t\tlead: this.#leadBtn,\n\t\t});\n\t}\n\n\tappendBody(...$els) {\n\t\tlet $main = this.body;\n\t\tif (!$main) return;\n\t\tfor (const $el of $els) {\n\t\t\t$main.append($el);\n\t\t}\n\t}\n\n\tappendOuter(...$els) {\n\t\tthis.#append(...$els);\n\t}\n\n\tattributeChangedCallback(name, oldValue, newValue) {\n\t\tif (name === \"data-title\") {\n\t\t\tthis.settitle = newValue;\n\t\t}\n\t}\n\n\tconnectedCallback() {\n\t\tthis.classList.remove(\"hide\");\n\t\tif (typeof this.onconnect === \"function\") this.onconnect();\n\t\tthis.#on.show.forEach((cb) => cb.call(this));\n\t}\n\n\tdisconnectedCallback() {\n\t\tif (typeof this.ondisconnect === \"function\") this.ondisconnect();\n\t\tthis.#on.hide.forEach((cb) => cb.call(this));\n\t}\n\n\t/**\n\t * Adds event listener to the page\n\t * @param {'hide' | 'show'} event\n\t * @param {function(this: WCPage):void} cb\n\t */\n\ton(event, cb) {\n\t\tif (event in this.#on) {\n\t\t\tthis.#on[event].push(cb);\n\t\t}\n\t}\n\n\t/**\n\t * Removes event listener from the page\n\t * @param {'hide' | 'show'} event\n\t * @param {function(this: WCPage):void} cb\n\t */\n\toff(event, cb) {\n\t\tif (event in this.#on) {\n\t\t\tthis.#on[event] = this.#on[event].filter((fn) => fn !== cb);\n\t\t}\n\t}\n\n\t/**\n\t * Sets the title of the page\n\t * @param {string} title\n\t */\n\tsettitle(title) {\n\t\tthis.header.text = title;\n\t}\n\n\thide() {\n\t\tthis.classList.add(\"hide\");\n\t\tif (typeof this.onhide === \"function\") this.onhide();\n\t\tsetTimeout(() => {\n\t\t\tthis.remove();\n\t\t\tthis.handler.remove();\n\t\t}, 150);\n\t}\n\n\tget body() {\n\t\treturn this.get(\".main\") || this.get(\"main\");\n\t}\n\n\tset body($el) {\n\t\tif (this.body) this.replaceChild($el, this.body);\n\n\t\tconst headerAdjacent = this.header.nextElementSibling;\n\t\tif (headerAdjacent) {\n\t\t\tthis.insertBefore($el, headerAdjacent);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.appendChild($el);\n\t}\n\n\tget innerHTML() {\n\t\treturn this.body?.innerHTML;\n\t}\n\n\tset innerHTML(html) {\n\t\tif (this.body) this.body.innerHTML = html;\n\t}\n\n\tget textContent() {\n\t\treturn this.body?.textContent;\n\t}\n\n\tset textContent(text) {\n\t\tif (this.body) this.body.textContent = text;\n\t}\n\n\tget lead() {\n\t\treturn this.#leadBtn;\n\t}\n\n\tset lead($el) {\n\t\tthis.header.replaceChild($el, this.#leadBtn);\n\t\tthis.#leadBtn = $el;\n\t}\n\n\tget header() {\n\t\treturn this.#header;\n\t}\n\n\tset header($el) {\n\t\tthis.#header.replaceChild($el, this.#header);\n\t\tthis.#header = $el;\n\t}\n\n\tinitializeIfNotAlreadyInitialized() {\n\t\tif (!this.#header.isConnected) {\n\t\t\tthis.#addHeaderOrAssignHeader();\n\t\t}\n\t}\n\n\t#addHeaderOrAssignHeader() {\n\t\tif (!this.classList.contains(\"primary\")) {\n\t\t\tthis.#append(this.#header);\n\t\t\tthis.#append(<div className=\"main\"></div>);\n\t\t} else {\n\t\t\tthis.#header = this.get(\"header\");\n\t\t\tif (this.#header) {\n\t\t\t\tthis.#leadBtn = this.#header.firstChild;\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass PageHandler {\n\t$el;\n\t$replacement;\n\tonRestore;\n\tonReplace;\n\n\t/**\n\t *\n\t * @param {HTMLElement} $el\n\t */\n\tconstructor($el) {\n\t\tthis.$el = $el;\n\n\t\tthis.onhide = this.onhide.bind(this);\n\t\tthis.onshow = this.onshow.bind(this);\n\n\t\tthis.$replacement = <span className=\"page-replacement\"></span>;\n\t\tthis.$replacement.handler = this;\n\n\t\tthis.$el.on(\"hide\", this.onhide);\n\t\tthis.$el.on(\"show\", this.onshow);\n\t}\n\n\t/**\n\t * Replace current element with a replacement element\n\t */\n\treplaceEl() {\n\t\tthis.$el.off(\"hide\", this.onhide);\n\t\tif (!this.$el.isConnected || this.$replacement.isConnected) return;\n\t\tif (typeof this.onReplace === \"function\") this.onReplace();\n\t\tthis.$el.parentElement.replaceChild(this.$replacement, this.$el);\n\t\tthis.$el.classList.add(\"no-transition\");\n\t}\n\n\t/**\n\t * Restore current element from a replacement element\n\t */\n\trestoreEl() {\n\t\tif (this.$el.isConnected || !this.$replacement.isConnected) return;\n\t\tif (typeof this.onRestore === \"function\") this.onRestore();\n\t\tthis.$el.off(\"hide\", this.onhide);\n\t\tthis.$replacement.parentElement.replaceChild(this.$el, this.$replacement);\n\t\tthis.$el.on(\"hide\", this.onhide);\n\t}\n\n\tonhide() {\n\t\tthis.$el.off(\"hide\", this.onhide);\n\t\thandlePagesForSmoothExperienceBack();\n\t}\n\n\tonshow() {\n\t\tthis.$el.off(\"show\", this.onshow);\n\t\thandlePagesForSmoothExperience();\n\t}\n\n\tremove() {\n\t\tthis.$replacement.remove();\n\t}\n}\n\n/**\n * Remove invisible pages from DOM and add them to the stack\n */\nfunction handlePagesForSmoothExperience() {\n\tconst $pages = [...tag.getAll(\"wc-page\")];\n\tfor (let $page of $pages.slice(0, -1)) {\n\t\t$page.handler.replaceEl();\n\t}\n}\n\nfunction handlePagesForSmoothExperienceBack() {\n\t[...tag.getAll(\".page-replacement\")].pop()?.handler.restoreEl();\n}\n"
  },
  {
    "path": "src/components/audioPlayer/index.js",
    "content": "import \"./style.scss\";\nimport Ref from \"html-tag-js/ref\";\n\nexport default class AudioPlayer {\n\tconstructor(container) {\n\t\tthis.container = container;\n\t\tthis.audio = new Audio();\n\t\tthis.isPlaying = false;\n\t\tthis.elements = {\n\t\t\tplayBtn: Ref(),\n\t\t\tplayIcon: Ref(),\n\t\t\ttimeline: Ref(),\n\t\t\tprogress: Ref(),\n\t\t\tprogressHandle: Ref(),\n\t\t\ttimeDisplay: Ref(),\n\t\t\tduration: Ref(),\n\t\t\tvolumeBtn: Ref(),\n\t\t};\n\t\tthis.initializeUI();\n\t\tthis.initializeEvents();\n\t\tthis.cleanup = this.cleanup.bind(this);\n\t}\n\n\tinitializeUI() {\n\t\tconst audioPlayer = (\n\t\t\t<div className=\"audio-player\">\n\t\t\t\t<button\n\t\t\t\t\tref={this.elements.playBtn}\n\t\t\t\t\tclassName=\"play-btn\"\n\t\t\t\t\tariaLabel=\"Play/Pause\"\n\t\t\t\t>\n\t\t\t\t\t<span ref={this.elements.playIcon} className=\"icon play_arrow\"></span>\n\t\t\t\t</button>\n\n\t\t\t\t<div ref={this.elements.timeline} className=\"timeline\">\n\t\t\t\t\t<div ref={this.elements.progress} className=\"progress\"></div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tref={this.elements.progressHandle}\n\t\t\t\t\t\tclassName=\"progress-handle\"\n\t\t\t\t\t></div>\n\t\t\t\t</div>\n\n\t\t\t\t<div ref={this.elements.timeDisplay} className=\"time\">\n\t\t\t\t\t0:00\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"volume-control\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tref={this.elements.volumeBtn}\n\t\t\t\t\t\tclassName=\"volume-btn\"\n\t\t\t\t\t\tariaLabel=\"Volume\"\n\t\t\t\t\t></button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\n\t\tthis.container.appendChild(audioPlayer);\n\n\t\tthis.elements.volumeBtn.el.innerHTML = `<svg viewBox=\"0 0 24 24\">\n      <path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/>\n    </svg>`;\n\t}\n\n\tinitializeEvents() {\n\t\t// Play/Pause\n\t\tthis.elements.playBtn.el.addEventListener(\"click\", () => this.togglePlay());\n\n\t\t// Timeline\n\t\tthis.elements.timeline.el.addEventListener(\"click\", (e) => this.seek(e));\n\t\tthis.elements.timeline.el.addEventListener(\"touchstart\", (e) =>\n\t\t\tthis.seek(e),\n\t\t);\n\n\t\t// Volume\n\t\tthis.elements.volumeBtn.el.addEventListener(\"click\", () =>\n\t\t\tthis.toggleMute(),\n\t\t);\n\n\t\t// Audio events\n\t\tthis.audio.addEventListener(\"timeupdate\", () => this.updateProgress());\n\t\tthis.audio.addEventListener(\"ended\", () => this.audioEnded());\n\t}\n\n\ttogglePlay() {\n\t\tif (this.isPlaying) {\n\t\t\tthis.audio.pause();\n\t\t\tthis.elements.playIcon.el.classList.remove(\"pause\");\n\t\t\tthis.elements.playIcon.el.classList.add(\"play_arrow\");\n\t\t} else {\n\t\t\tthis.audio.play();\n\t\t\tthis.elements.playIcon.el.classList.remove(\"play_arrow\");\n\t\t\tthis.elements.playIcon.el.classList.add(\"pause\");\n\t\t}\n\t\tthis.isPlaying = !this.isPlaying;\n\t}\n\n\tseek(e) {\n\t\tconst rect = this.elements.timeline.el.getBoundingClientRect();\n\t\tconst pos =\n\t\t\t(e.type.includes(\"touch\") ? e.touches[0].clientX : e.clientX) - rect.left;\n\t\tconst percentage = pos / rect.width;\n\t\tthis.audio.currentTime = percentage * this.audio.duration;\n\t}\n\n\tupdateProgress() {\n\t\tconst percentage = (this.audio.currentTime / this.audio.duration) * 100;\n\t\tthis.elements.progress.el.style.width = `${percentage}%`;\n\t\tthis.elements.progressHandle.el.style.left = `${percentage}%`;\n\t\tthis.elements.timeDisplay.el.textContent = this.formatTime(\n\t\t\tthis.audio.currentTime,\n\t\t);\n\t}\n\n\tformatTime(seconds) {\n\t\tconst mins = Math.floor(seconds / 60);\n\t\tconst secs = Math.floor(seconds % 60);\n\t\treturn `${mins}:${secs.toString().padStart(2, \"0\")}`;\n\t}\n\n\ttoggleMute() {\n\t\tthis.audio.muted = !this.audio.muted;\n\t\tif (this.audio.muted) {\n\t\t\tthis.elements.volumeBtn.el.innerHTML =\n\t\t\t\t'<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z\"/></svg>';\n\t\t} else {\n\t\t\tthis.elements.volumeBtn.el.innerHTML =\n\t\t\t\t'<svg viewBox=\"0 0 24 24\"><path d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"/></svg>';\n\t\t}\n\t}\n\n\taudioEnded() {\n\t\tthis.isPlaying = false;\n\t\tthis.elements.playIcon.el.classList.remove(\"pause\");\n\t\tthis.elements.playIcon.el.classList.add(\"play_arrow\");\n\t}\n\n\tloadTrack(src) {\n\t\tthis.audio.src = src;\n\t\tthis.audio.load();\n\t}\n\n\tcleanup() {\n\t\tthis.audio.pause();\n\t\tthis.audio.currentTime = 0;\n\t\tthis.isPlaying = false;\n\n\t\tthis.elements.playBtn.el.removeEventListener(\"click\", () =>\n\t\t\tthis.togglePlay(),\n\t\t);\n\t\tthis.elements.timeline.el.removeEventListener(\"click\", (e) => this.seek(e));\n\t\tthis.elements.timeline.el.removeEventListener(\"touchstart\", (e) =>\n\t\t\tthis.seek(e),\n\t\t);\n\t\tthis.elements.volumeBtn.el.removeEventListener(\"click\", () =>\n\t\t\tthis.toggleMute(),\n\t\t);\n\t\tthis.audio.removeEventListener(\"timeupdate\", () => this.updateProgress());\n\t\tthis.audio.removeEventListener(\"ended\", () => this.audioEnded());\n\n\t\tconst audioSrc = this.audio.src;\n\t\tthis.audio.src = \"\";\n\t\tthis.audio.load();\n\t\tif (audioSrc.startsWith(\"blob:\")) {\n\t\t\tURL.revokeObjectURL(audioSrc);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/components/audioPlayer/style.scss",
    "content": ".audio-player {\n  background: var(--primary-color, #1e1e1e);\n  border-radius: 10px;\n  padding: 15px;\n  display: flex;\n  align-items: center;\n  gap: 15px;\n  width: 100%;\n  max-width: 400px;\n  user-select: none;\n\n  .play-btn {\n    background: transparent;\n    border: none;\n    width: 30px;\n    height: 30px;\n    border-radius: 50%;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n    transition: all 0.2s;\n\n    span {\n      font-size: 20px;\n      color: var(--primary-text-color);\n    }\n\n    &:hover {\n      background: color-mix(in srgb, var(--secondary-color) 30%, transparent);\n    }\n  }\n\n  .timeline {\n    flex: 1;\n    height: 4px;\n    background: color-mix(in srgb, var(--secondary-color) 60%, transparent);\n    border-radius: 2px;\n    position: relative;\n    cursor: pointer;\n\n    &:hover .progress-handle {\n      opacity: 1;\n    }\n  }\n\n  .progress {\n    background: var(--primary-text-color, #fff);\n    width: 0%;\n    height: 100%;\n    border-radius: 2px;\n    transition: width 0.1s linear;\n  }\n\n  .progress-handle {\n    width: 12px;\n    height: 12px;\n    background: var(--primary-text-color, #fff);\n    border-radius: 50%;\n    position: absolute;\n    top: 50%;\n    transform: translate(-50%, -50%);\n    pointer-events: none;\n    opacity: 0;\n    transition: opacity 0.2s;\n  }\n\n  .time {\n    color: var(--primary-text-color, #fff);\n    font-family: monospace;\n    font-size: 12px;\n    min-width: 45px;\n  }\n\n  .volume-control {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n  }\n\n  .volume-btn {\n    background: transparent;\n    border: none;\n    cursor: pointer;\n\n    svg {\n      width: 20px;\n      height: 20px;\n      fill: var(--primary-text-color, #fff);\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/checkbox/index.js",
    "content": "import \"./styles.scss\";\nimport Ref from \"html-tag-js/ref\";\n\n/**\n * @typedef {Object} Checkbox\n * @property {string} text\n * @property {Ref} ref\n * @property {boolean} checked\n * @property {string} [name]\n * @property {string} [id]\n * @property {string} [size]\n * @property {\"checkbox\"|\"radio\"} [type]\n */\n\n/**\n * Create a checkbox\n * @param {string | Checkbox} text Checkbox label\n * @param {Boolean} checked Whether checkbox is checked or not\n * @param {string} [name] Name of checkbox\n * @param {string} [id] Id of checkbox\n * @param {\"checkbox\"|\"radio\"} [type] Type of checkbox\n * @param {Ref} [ref] A reference to the input element\n * @param {string} [size] Size of checkbox\n * @returns {Checkbox & HTMLLabelElement}\n */\nfunction Checkbox(text, checked, name, id, type, ref, size) {\n\tif (typeof text === \"object\") {\n\t\t({ text, checked, name, id, type, ref, size } = text);\n\t}\n\n\tsize = size || \"1rem\";\n\n\tconst $input = ref || Ref();\n\tconst $checkbox = (\n\t\t<label className=\"input-checkbox\">\n\t\t\t<input\n\t\t\t\tref={$input}\n\t\t\t\tchecked={checked}\n\t\t\t\ttype={type || \"checkbox\"}\n\t\t\t\tname={name}\n\t\t\t\tid={id}\n\t\t\t/>\n\t\t\t<span style={{ height: size, width: size }} className=\"box\"></span>\n\t\t\t<span>{text}</span>\n\t\t</label>\n\t);\n\n\tObject.defineProperties($checkbox, {\n\t\tchecked: {\n\t\t\tget() {\n\t\t\t\treturn !!$input.el.checked;\n\t\t\t},\n\t\t\tset(value) {\n\t\t\t\t$input.el.checked = value;\n\t\t\t},\n\t\t},\n\t\tonclick: {\n\t\t\tget() {\n\t\t\t\treturn $input.el.onclick;\n\t\t\t},\n\t\t\tset(onclick) {\n\t\t\t\t$input.el.onclick = onclick;\n\t\t\t},\n\t\t},\n\t\tonchange: {\n\t\t\tget() {\n\t\t\t\treturn $input.el.onchange;\n\t\t\t},\n\t\t\tset(onchange) {\n\t\t\t\t$input.el.onchange = onchange;\n\t\t\t},\n\t\t},\n\t\tvalue: {\n\t\t\tget() {\n\t\t\t\treturn this.checked;\n\t\t\t},\n\t\t\tset(value) {\n\t\t\t\tthis.checked = value;\n\t\t\t},\n\t\t},\n\t\ttoggle: {\n\t\t\tvalue() {\n\t\t\t\tthis.checked = !this.checked;\n\t\t\t},\n\t\t},\n\t});\n\n\treturn $checkbox;\n}\n\nexport default Checkbox;\n"
  },
  {
    "path": "src/components/checkbox/styles.scss",
    "content": ".input-checkbox {\n  display: inline-flex;\n  align-items: center;\n\n  input {\n    display: none;\n  }\n\n  .box {\n    display: flex;\n    height: 1.2rem;\n    width: 1.2rem;\n    border-radius: 4px;\n    border: solid 1px #252525;\n    border: solid 1px var(--secondary-text-color);\n    margin: 0 5px;\n\n    &::after {\n      content: '';\n      display: block;\n      height: 80%;\n      width: 80%;\n      background-color: #3399ff;\n      background-color: var(--button-background-color);\n      margin: auto;\n      border-radius: 2px;\n    }\n  }\n\n  input:checked {\n    &+.box::after {\n      opacity: 1;\n    }\n  }\n\n  input:not(:checked) {\n    &+.box::after {\n      opacity: 0;\n    }\n  }\n}"
  },
  {
    "path": "src/components/collapsableList.js",
    "content": "import tag from \"html-tag-js\";\nimport tile from \"./tile\";\n\n/**\n * @typedef {object} CollapsibleBase\n * @property {HTMLElement} $title\n * @property {HTMLUListElement} $ul\n * @property {function(void):void} ontoggle\n * @property {function(void):void} collapse\n * @property {function(void):void} expand\n * @property {boolean} collapsed\n * @property {boolean} unclasped\n */\n\n/**\n * @typedef {CollapsibleBase & HTMLElement} Collapsible\n */\n\n/**\n * Create a collapsible list\n * @param {string} titleText Title of the list\n * @param {boolean} hidden If true, the list will be hidden\n * @param {\"indicator\"|\"folder\"} type Type of the list toggle indicator\n * @param {object} [options] Configuration options\n * @param {HTMLElement} [options.tail] Tail element of the title\n * @param {string} [options.type] Type of the list element\n * @param {boolean} [options.allCaps] If true, the title will be in all caps\n * @param {function(this:Collapsible):void} [options.ontoggle] Called when the list is toggled\n * @returns {Collapsible}\n */\nexport default function collapsableList(\n\ttitleText,\n\ttype = \"indicator\",\n\toptions = {},\n) {\n\tlet onscroll = null;\n\tconst $ul = tag(\"ul\", {\n\t\tclassName: \"scroll\",\n\t\tonscroll: onUlScroll,\n\t});\n\tconst $collapseIndicator = tag(\"span\", {\n\t\tclassName: `icon ${type}`,\n\t});\n\tconst $title = tile({\n\t\tlead: $collapseIndicator,\n\t\ttype: \"div\",\n\t\ttext: options.allCaps ? titleText.toUpperCase() : titleText,\n\t\ttail: options.tail,\n\t});\n\tconst $mainWrapper = tag(options.type || \"div\", {\n\t\tclassName: \"list collapsible hidden\",\n\t\tchildren: [$title, $ul],\n\t});\n\n\tlet collapse = () => {\n\t\t$mainWrapper.classList.add(\"hidden\");\n\t\tif ($mainWrapper.ontoggle) $mainWrapper.ontoggle.call($mainWrapper);\n\t\tdelete $ul.dataset.scrollTop;\n\t};\n\n\tlet expand = () => {\n\t\t$mainWrapper.classList.remove(\"hidden\");\n\t\tif ($mainWrapper.ontoggle) $mainWrapper.ontoggle.call($mainWrapper);\n\t};\n\n\t$title.classList.add(\"light\");\n\t$title.addEventListener(\"click\", toggle);\n\n\t[$title, $mainWrapper].forEach(defineProperties);\n\n\t$mainWrapper.dataset.id = `${Math.random().toString(36).substring(2, 15)}`;\n\n\treturn $mainWrapper;\n\n\tfunction onUlScroll() {\n\t\tif (onscroll) onscroll.call($ul);\n\t\t$ul.dataset.scrollTop = $ul.scrollTop;\n\t}\n\n\tfunction toggle() {\n\t\tif ($title.collapsed) {\n\t\t\texpand();\n\t\t} else {\n\t\t\tcollapse();\n\t\t}\n\t}\n\n\tfunction defineProperties($el) {\n\t\tObject.defineProperties($el, {\n\t\t\t$title: {\n\t\t\t\tget() {\n\t\t\t\t\treturn $title;\n\t\t\t\t},\n\t\t\t},\n\t\t\t$ul: {\n\t\t\t\tget() {\n\t\t\t\t\treturn $ul;\n\t\t\t\t},\n\t\t\t},\n\t\t\tontoggle: {\n\t\t\t\tget() {\n\t\t\t\t\treturn options.ontoggle;\n\t\t\t\t},\n\t\t\t\tset(fun) {\n\t\t\t\t\tif (typeof fun === \"function\") options.ontoggle = fun;\n\t\t\t\t},\n\t\t\t},\n\t\t\tcollapse: {\n\t\t\t\tget() {\n\t\t\t\t\treturn collapse || (() => {});\n\t\t\t\t},\n\t\t\t\tset(fun) {\n\t\t\t\t\tif (typeof fun === \"function\") collapse = fun;\n\t\t\t\t},\n\t\t\t},\n\t\t\texpand: {\n\t\t\t\tget() {\n\t\t\t\t\treturn expand || (() => {});\n\t\t\t\t},\n\t\t\t\tset(fun) {\n\t\t\t\t\tif (typeof fun === \"function\") expand = fun;\n\t\t\t\t},\n\t\t\t},\n\t\t\tcollapsed: {\n\t\t\t\tget() {\n\t\t\t\t\treturn $mainWrapper.classList.contains(\"hidden\");\n\t\t\t\t},\n\t\t\t},\n\t\t\tunclasped: {\n\t\t\t\tget() {\n\t\t\t\t\treturn !this.collapsed;\n\t\t\t\t},\n\t\t\t},\n\t\t\tonscroll: {\n\t\t\t\tget() {\n\t\t\t\t\treturn onscroll;\n\t\t\t\t},\n\t\t\t\tset(fun) {\n\t\t\t\t\tif (typeof fun === \"function\") {\n\t\t\t\t\t\tonscroll = fun;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tscrollTop: {\n\t\t\t\tget() {\n\t\t\t\t\treturn $ul.dataset.scrollTop || 0;\n\t\t\t\t},\n\t\t\t\tset(val) {\n\t\t\t\t\t$ul.dataset.scrollTop = val;\n\t\t\t\t\t$ul.scrollTop = val;\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/components/contextmenu/index.js",
    "content": "import \"./style.scss\";\nimport actionStack from \"lib/actionStack\";\n\n/**\n * @typedef {object} ContextMenuObj\n * @extends HTMLElement\n * @property {function():void} hide hides the menu\n * @property {function():void} show shows the page\n * @property {function():void} destroy destroys the menu\n */\n\n/**\n * @typedef {object} ContextMenuOptions\n * @property {number} left\n * @property {number} top\n * @property {number} bottom\n * @property {number} right\n * @property {string} transformOrigin\n * @property {HTMLElement} toggler\n * @property {function} onshow\n * @property {function} onhide\n * @property {Array<[string, string]>} items Array of [text, action] pairs\n * @property {(this:HTMLElement, event:MouseEvent)=>void} onclick Called when an item is clicked\n * @property {(item:string) => void} onselect Called when an item is selected\n * @property {(this:HTMLElement) => string} innerHTML Called when the menu is shown\n */\n\n/**\n * Create a context menu\n * @param {string|ContextMenuOptions} content Context menu content or options\n * @param {ContextMenuOptions} [options] Options\n * @returns {ContextMenuObj}\n */\nexport default function Contextmenu(content, options) {\n\tif (!options && typeof content === \"object\") {\n\t\toptions = content;\n\t\tcontent = null;\n\t} else if (!options) {\n\t\toptions = {};\n\t}\n\n\tconst $el = tag(\"ul\", {\n\t\tclassName: \"context-menu scroll\",\n\t\tinnerHTML: content || \"\",\n\t\tonclick(e) {\n\t\t\tif (options.onclick) options.onclick.call(this, e);\n\t\t\tif (options.onselect) {\n\t\t\t\tconst $target = e.target;\n\t\t\t\tconst { action } = $target.dataset;\n\t\t\t\tif (!action) return;\n\t\t\t\thide();\n\t\t\t\toptions.onselect.call(this, action);\n\t\t\t}\n\t\t},\n\t\tstyle: {\n\t\t\ttop: options.top || \"auto\",\n\t\t\tleft: options.left || \"auto\",\n\t\t\tright: options.right || \"auto\",\n\t\t\tbottom: options.bottom || \"auto\",\n\t\t\ttransformOrigin: options.transformOrigin,\n\t\t},\n\t});\n\tconst $mask = tag(\"span\", {\n\t\tclassName: \"mask\",\n\t\tontouchstart: hide,\n\t\tonmousedown: hide,\n\t});\n\n\tif (Array.isArray(options.items)) {\n\t\toptions.items.forEach(([text, action]) => {\n\t\t\t$el.append(<li data-action={action}>{text}</li>);\n\t\t});\n\t}\n\n\tif (!options.innerHTML) addTabindex();\n\n\tfunction show() {\n\t\tactionStack.push({\n\t\t\tid: \"main-menu\",\n\t\t\taction: hide,\n\t\t});\n\t\t$el.onshow();\n\t\t$el.classList.remove(\"hide\");\n\n\t\tif (options.innerHTML) {\n\t\t\t$el.innerHTML = options.innerHTML.call($el);\n\t\t\taddTabindex();\n\t\t}\n\n\t\tif (options.toggler) {\n\t\t\tconst client = options.toggler.getBoundingClientRect();\n\t\t\tif (!options.top && !options.bottom) {\n\t\t\t\t$el.style.top = client.top + \"px\";\n\t\t\t}\n\t\t\tif (!options.left && !options.right) {\n\t\t\t\t$el.style.right = innerWidth - client.right + \"px\";\n\t\t\t}\n\t\t}\n\n\t\tapp.append($el, $mask);\n\n\t\tconst $firstChild = $el.firstChild;\n\t\tif ($firstChild && $firstChild.focus) $firstChild.focus();\n\t}\n\n\tfunction hide() {\n\t\tactionStack.remove(\"main-menu\");\n\t\t$el.onhide();\n\t\t$el.classList.add(\"hide\");\n\t\tsetTimeout(() => {\n\t\t\t$mask.remove();\n\t\t\t$el.remove();\n\t\t}, 100);\n\t}\n\n\tfunction toggle() {\n\t\tif ($el.parentElement) return hide();\n\t\tshow();\n\t}\n\n\tfunction addTabindex() {\n\t\t/**@type {Array<HTMLLIElement>} */\n\t\tconst children = [...$el.children];\n\t\tfor (let $el of children) $el.tabIndex = \"0\";\n\t}\n\n\tfunction destroy() {\n\t\t$el.remove();\n\t\t$mask.remove();\n\t\toptions.toggler?.removeEventListener(\"click\", toggle);\n\t}\n\n\tif (options.toggler) {\n\t\toptions.toggler.addEventListener(\"click\", toggle);\n\t}\n\n\t$el.hide = hide;\n\t$el.show = show;\n\t$el.destroy = destroy;\n\t$el.onshow = options.onshow || (() => {});\n\t$el.onhide = options.onhide || (() => {});\n\n\treturn $el;\n}\n"
  },
  {
    "path": "src/components/contextmenu/style.scss",
    "content": "body.no-animation {\n  .context-menu {\n    border: solid 1px rgba($color: #000000, $alpha: 0.2);\n  }\n}\n\n.context-menu {\n  z-index: 111;\n  box-sizing: border-box;\n  position: fixed;\n  width: fit-content;\n  min-width: 220px;\n  max-width: 320px;\n  min-height: 40px;\n  max-height: calc(100vh - 38px);\n  overflow-y: auto;\n  user-select: none;\n  background-color: rgb(255, 255, 255);\n  background-color: var(--popup-background-color);\n  box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 0 4px var(--box-shadow-color);\n  border-radius: var(--popup-border-radius);\n\n  transform-origin: top center;\n  animation: menu-grow 100ms ease 1;\n\n  list-style: none;\n  padding: 0;\n  border: solid 1px transparent;\n  border: solid 1px var(--popup-border-color);\n\n  &+.mask {\n    z-index: 110;\n  }\n\n  &.disabled {\n    li {\n      pointer-events: none;\n      opacity: 0.5;\n    }\n  }\n\n  hr {\n    border: none;\n    border-bottom: solid 1px rgba(122, 122, 122, 0.227);\n    border-bottom: solid 1px var(--border-color);\n  }\n\n  li {\n    display: flex;\n    height: 50px;\n    align-items: center;\n    padding: 0 10px;\n    user-select: none;\n\n    &.notice::after {\n      content: \"•\";\n      color: rgb(255, 218, 12);\n      font-size: 1.2em;\n      margin-left: 2.5px;\n      text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.5);\n    }\n\n    &:focus {\n      background-color: rgba($color: #000000, $alpha: 0.1);\n    }\n\n    * {\n      pointer-events: none;\n    }\n\n    [data-action],\n    [action] {\n      pointer-events: all !important;\n    }\n\n    .text {\n      flex: 1;\n      color: rgb(37, 37, 37);\n      color: var(--popup-text-color);\n\n      .value {\n        display: block;\n        opacity: 0.6;\n        font-size: 0.8em;\n      }\n    }\n\n    .icon {\n      color: rgb(153, 153, 255);\n      color: var(--popup-icon-color);\n    }\n\n    &.disabled {\n      pointer-events: none;\n      opacity: 0.5;\n    }\n  }\n\n  &.hide {\n    transition: all 100ms ease;\n    opacity: 0;\n  }\n}"
  },
  {
    "path": "src/components/fileTree/index.js",
    "content": "import \"./style.scss\";\nimport tile from \"components/tile\";\nimport VirtualList from \"components/virtualList\";\nimport tag from \"html-tag-js\";\nimport helpers from \"utils/helpers\";\nimport Path from \"utils/Path\";\n\nconst VIRTUALIZATION_THRESHOLD = Number.POSITIVE_INFINITY; // FIX: temporary due to some scrolling issues in VirtualList\nconst ITEM_HEIGHT = 30;\n\n/**\n * @typedef {object} FileTreeOptions\n * @property {function(string): Promise<Array>} getEntries - Function to get directory entries\n * @property {function(string, string): void} [onFileClick] - File click handler\n * @property {function(string, string, string, HTMLElement): void} [onContextMenu] - Context menu handler\n * @property {Object<string, boolean>} [expandedState] - Map of expanded folder URLs\n * @property {function(string, boolean): void} [onExpandedChange] - Called when folder expanded state changes\n */\n\n/**\n * FileTree component for rendering folder contents with virtual scrolling\n */\nexport default class FileTree {\n\t/**\n\t * @param {HTMLElement} container\n\t * @param {FileTreeOptions} options\n\t */\n\tconstructor(container, options = {}) {\n\t\tthis.container = container;\n\t\tthis.container.classList.add(\"file-tree\");\n\n\t\tthis.options = options;\n\t\tthis.virtualList = null;\n\t\tthis.entries = [];\n\t\tthis.isLoading = false;\n\t\tthis.childTrees = new Map(); // Track child trees for cleanup\n\t\tthis.depth = options._depth || 0; // Internal: nesting depth\n\t}\n\n\t/**\n\t * Load and render entries for a directory\n\t * @param {string} url - Directory URL\n\t */\n\tasync load(url) {\n\t\tif (this.isLoading) return;\n\t\tthis.isLoading = true;\n\t\tthis.currentUrl = url;\n\n\t\ttry {\n\t\t\tthis.clear();\n\n\t\t\tconst entries = await this.options.getEntries(url);\n\t\t\tthis.entries = helpers.sortDir(entries, {\n\t\t\t\tsortByName: true,\n\t\t\t\tshowHiddenFiles: true,\n\t\t\t});\n\n\t\t\tif (this.entries.length > VIRTUALIZATION_THRESHOLD) {\n\t\t\t\tthis.renderVirtualized();\n\t\t\t} else {\n\t\t\t\tthis.renderWithFragment();\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.isLoading = false;\n\t\t}\n\t}\n\n\t/**\n\t * Render using DocumentFragment for batch DOM updates (small folders)\n\t */\n\trenderWithFragment() {\n\t\tconst fragment = document.createDocumentFragment();\n\n\t\tfor (const entry of this.entries) {\n\t\t\tconst $el = this.createEntryElement(entry);\n\t\t\tfragment.appendChild($el);\n\t\t}\n\n\t\tthis.container.appendChild(fragment);\n\t}\n\n\t/**\n\t * Render using virtual scrolling (large folders)\n\t */\n\trenderVirtualized() {\n\t\tthis.container.classList.add(\"virtual-scroll\");\n\n\t\tthis.virtualList = new VirtualList(this.container, {\n\t\t\titemHeight: ITEM_HEIGHT,\n\t\t\tbuffer: 15,\n\t\t\trenderItem: (entry, recycledEl) =>\n\t\t\t\tthis.createEntryElement(entry, recycledEl),\n\t\t});\n\n\t\tthis.virtualList.setItems(this.entries);\n\t}\n\n\t/**\n\t * Create DOM element for a file/folder entry\n\t * @param {object} entry\n\t * @param {HTMLElement} [recycledEl] - Optional recycled element for reuse\n\t * @returns {HTMLElement}\n\t */\n\tcreateEntryElement(entry, recycledEl) {\n\t\tconst name = entry.name || Path.basename(entry.url);\n\n\t\tif (entry.isDirectory) {\n\t\t\treturn this.createFolderElement(name, entry.url, recycledEl);\n\t\t} else {\n\t\t\treturn this.createFileElement(name, entry.url, recycledEl);\n\t\t}\n\t}\n\n\t/**\n\t * Create folder element (collapsible)\n\t * @param {string} name\n\t * @param {string} url\n\t * @param {HTMLElement} [recycledEl] - Optional recycled element for reuse\n\t * @returns {HTMLElement}\n\t */\n\tcreateFolderElement(name, url, recycledEl) {\n\t\t// Try to recycle existing folder element\n\t\tif (recycledEl && recycledEl.classList.contains(\"collapsible\")) {\n\t\t\tconst $title = recycledEl.$title;\n\t\t\tif ($title) {\n\t\t\t\t$title.dataset.url = url;\n\t\t\t\t$title.dataset.name = name;\n\t\t\t\tconst textEl = $title.querySelector(\".text\");\n\t\t\t\tif (textEl) textEl.textContent = name;\n\n\t\t\t\t// Collapse if expanded and clear children\n\t\t\t\tif (!recycledEl.classList.contains(\"hidden\")) {\n\t\t\t\t\trecycledEl.classList.add(\"hidden\");\n\t\t\t\t\tconst childTree = this.childTrees.get(recycledEl._folderUrl);\n\t\t\t\t\tif (childTree) {\n\t\t\t\t\t\tchildTree.destroy();\n\t\t\t\t\t\tthis.childTrees.delete(recycledEl._folderUrl);\n\t\t\t\t\t}\n\t\t\t\t\trecycledEl.$ul.innerHTML = \"\";\n\t\t\t\t}\n\n\t\t\t\trecycledEl._folderUrl = url;\n\t\t\t\treturn recycledEl;\n\t\t\t}\n\t\t}\n\n\t\tconst $wrapper = tag(\"div\", {\n\t\t\tclassName: \"list collapsible hidden\",\n\t\t});\n\t\t$wrapper._folderUrl = url;\n\n\t\tconst $indicator = tag(\"span\", { className: \"icon folder\" });\n\n\t\tconst $title = tile({\n\t\t\tlead: $indicator,\n\t\t\ttype: \"div\",\n\t\t\ttext: name,\n\t\t});\n\n\t\t$title.classList.add(\"light\");\n\t\t$title.dataset.url = url;\n\t\t$title.dataset.name = name;\n\t\t$title.dataset.type = \"dir\";\n\n\t\tconst $content = tag(\"ul\", { className: \"scroll folder-content\" });\n\t\t$wrapper.append($title, $content);\n\n\t\t// Child file tree for nested folders\n\t\tlet childTree = null;\n\t\t$content._fileTree = null;\n\n\t\tconst toggle = async () => {\n\t\t\tconst isExpanded = !$wrapper.classList.contains(\"hidden\");\n\n\t\t\tif (isExpanded) {\n\t\t\t\t// Collapse\n\t\t\t\t$wrapper.classList.add(\"hidden\");\n\n\t\t\t\tif (childTree) {\n\t\t\t\t\tchildTree.destroy();\n\t\t\t\t\tthis.childTrees.delete(url);\n\t\t\t\t\tchildTree = null;\n\t\t\t\t\t$content._fileTree = null;\n\t\t\t\t}\n\t\t\t\tthis.options.onExpandedChange?.(url, false);\n\t\t\t} else {\n\t\t\t\t// Expand\n\t\t\t\t$wrapper.classList.remove(\"hidden\");\n\t\t\t\t$title.classList.add(\"loading\");\n\n\t\t\t\t// Create child tree with incremented depth\n\t\t\t\tchildTree = new FileTree($content, {\n\t\t\t\t\t...this.options,\n\t\t\t\t\t_depth: this.depth + 1,\n\t\t\t\t});\n\t\t\t\tthis.childTrees.set(url, childTree);\n\t\t\t\t$content._fileTree = childTree;\n\t\t\t\ttry {\n\t\t\t\t\tawait childTree.load(url);\n\t\t\t\t} finally {\n\t\t\t\t\t$title.classList.remove(\"loading\");\n\t\t\t\t}\n\t\t\t\tthis.options.onExpandedChange?.(url, true);\n\t\t\t}\n\t\t};\n\n\t\t$title.addEventListener(\"click\", (e) => {\n\t\t\te.stopPropagation();\n\t\t\ttoggle();\n\t\t});\n\n\t\t$title.addEventListener(\"contextmenu\", (e) => {\n\t\t\te.stopPropagation();\n\t\t\tthis.options.onContextMenu?.(\"dir\", url, name, $title);\n\t\t});\n\n\t\t// Check if folder should be expanded from saved state\n\t\tif (this.options.expandedState?.[url]) {\n\t\t\tqueueMicrotask(() => toggle());\n\t\t}\n\n\t\tconst defineCollapsibleAccessors = ($el, { includeTitle = true } = {}) => {\n\t\t\tconst properties = {\n\t\t\t\tcollapsed: { get: () => $wrapper.classList.contains(\"hidden\") },\n\t\t\t\texpanded: { get: () => !$wrapper.classList.contains(\"hidden\") },\n\t\t\t\tunclasped: { get: () => !$wrapper.classList.contains(\"hidden\") }, // Legacy compatibility\n\t\t\t\t$ul: { get: () => $content },\n\t\t\t\tfileTree: { get: () => childTree },\n\t\t\t\trefresh: {\n\t\t\t\t\tvalue: () => childTree?.refresh(),\n\t\t\t\t},\n\t\t\t\texpand: {\n\t\t\t\t\tvalue: () => !$wrapper.classList.contains(\"hidden\") || toggle(),\n\t\t\t\t},\n\t\t\t\tcollapse: {\n\t\t\t\t\tvalue: () => $wrapper.classList.contains(\"hidden\") || toggle(),\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (includeTitle) {\n\t\t\t\tproperties.$title = { get: () => $title };\n\t\t\t}\n\n\t\t\tObject.defineProperties($el, properties);\n\t\t};\n\n\t\t// Keep nested folders compatible with the legacy collapsableList API.\n\t\tdefineCollapsibleAccessors($wrapper);\n\t\tdefineCollapsibleAccessors($title, { includeTitle: false });\n\n\t\treturn $wrapper;\n\t}\n\n\t/**\n\t * Create file element (tile)\n\t * @param {string} name\n\t * @param {string} url\n\t * @param {HTMLElement} [recycledEl] - Optional recycled element for reuse\n\t * @returns {HTMLElement}\n\t */\n\tcreateFileElement(name, url, recycledEl) {\n\t\tconst iconClass = helpers.getIconForFile(name);\n\n\t\t// Try to recycle existing element\n\t\tif (recycledEl && recycledEl.dataset.type === \"file\") {\n\t\t\trecycledEl.dataset.url = url;\n\t\t\trecycledEl.dataset.name = name;\n\t\t\tconst textEl = recycledEl.querySelector(\".text\");\n\t\t\tconst iconEl = recycledEl.querySelector(\"span:first-child\");\n\t\t\tif (textEl) textEl.textContent = name;\n\t\t\tif (iconEl) iconEl.className = iconClass;\n\t\t\treturn recycledEl;\n\t\t}\n\n\t\tconst $tile = tile({\n\t\t\tlead: tag(\"span\", { className: iconClass }),\n\t\t\ttext: name,\n\t\t});\n\n\t\t$tile.dataset.url = url;\n\t\t$tile.dataset.name = name;\n\t\t$tile.dataset.type = \"file\";\n\n\t\t$tile.addEventListener(\"click\", (e) => {\n\t\t\te.stopPropagation();\n\t\t\tthis.options.onFileClick?.(url, name);\n\t\t});\n\n\t\t$tile.addEventListener(\"contextmenu\", (e) => {\n\t\t\te.stopPropagation();\n\t\t\tthis.options.onContextMenu?.(\"file\", url, name, $tile);\n\t\t});\n\n\t\treturn $tile;\n\t}\n\n\t/**\n\t * Clear all rendered content\n\t */\n\tclear() {\n\t\tthis.destroyChildTrees();\n\n\t\tif (this.virtualList) {\n\t\t\tthis.virtualList.destroy();\n\t\t\tthis.virtualList = null;\n\t\t}\n\t\tthis.container.innerHTML = \"\";\n\t\tthis.container.classList.remove(\"virtual-scroll\");\n\t\tthis.entries = [];\n\t}\n\n\t/**\n\t * Destroy the file tree and cleanup\n\t */\n\tdestroy() {\n\t\tthis.clear();\n\t\tthis.container.classList.remove(\"file-tree\");\n\t}\n\n\t/**\n\t * Find an entry element by URL\n\t * @param {string} url\n\t * @returns {HTMLElement|null}\n\t */\n\tfindElement(url) {\n\t\treturn this.container.querySelector(`[data-url=\"${CSS.escape(url)}\"]`);\n\t}\n\n\t/**\n\t * Refresh the current directory\n\t */\n\tasync refresh() {\n\t\tif (this.currentUrl) {\n\t\t\tawait this.load(this.currentUrl);\n\t\t}\n\t}\n\n\t/**\n\t * Destroy all expanded child trees and clear their references.\n\t */\n\tdestroyChildTrees() {\n\t\tfor (const childTree of this.childTrees.values()) {\n\t\t\tchildTree.destroy();\n\t\t}\n\t\tthis.childTrees.clear();\n\t}\n\n\t/**\n\t * Append a new entry to the tree\n\t * @param {string} name\n\t * @param {string} url\n\t * @param {boolean} isDirectory\n\t */\n\tappendEntry(name, url, isDirectory) {\n\t\tconst entry = { name, url, isDirectory, isFile: !isDirectory };\n\n\t\t// Insert in sorted position\n\t\tif (isDirectory) {\n\t\t\t// Find first file or end of dirs\n\t\t\tconst insertIndex = this.entries.findIndex((e) => !e.isDirectory);\n\t\t\tif (insertIndex === -1) {\n\t\t\t\tthis.entries.push(entry);\n\t\t\t} else {\n\t\t\t\tthis.entries.splice(insertIndex, 0, entry);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.entries.push(entry);\n\t\t}\n\n\t\t// Re-sort entries\n\t\tthis.entries = helpers.sortDir(this.entries, {\n\t\t\tsortByName: true,\n\t\t\tshowHiddenFiles: true,\n\t\t});\n\n\t\t// Update rendering based on mode\n\t\tif (this.virtualList) {\n\t\t\t// Virtual list mode: update items\n\t\t\tthis.virtualList.setItems(this.entries);\n\t\t} else {\n\t\t\t// Fragment mode: re-render\n\t\t\tthis.destroyChildTrees();\n\t\t\tthis.container.innerHTML = \"\";\n\t\t\tthis.renderWithFragment();\n\t\t}\n\t}\n\n\t/**\n\t * Remove an entry from the tree\n\t * @param {string} url\n\t */\n\tremoveEntry(url) {\n\t\t// Update data first\n\t\tconst index = this.entries.findIndex((e) => e.url === url);\n\t\tif (index === -1) return;\n\n\t\t// Clean up child tree if folder\n\t\tconst entry = this.entries[index];\n\t\tif (entry.isDirectory && this.childTrees.has(url)) {\n\t\t\tthis.childTrees.get(url).destroy();\n\t\t\tthis.childTrees.delete(url);\n\t\t}\n\n\t\t// Remove from entries\n\t\tthis.entries.splice(index, 1);\n\n\t\t// Update rendering based on mode\n\t\tif (this.virtualList) {\n\t\t\t// Virtual list mode: update items\n\t\t\tthis.virtualList.setItems(this.entries);\n\t\t} else {\n\t\t\t// Fragment mode: remove element directly\n\t\t\tconst $el = this.findElement(url);\n\t\t\tif ($el) {\n\t\t\t\tif ($el.dataset.type === \"dir\") {\n\t\t\t\t\t$el.closest(\".list.collapsible\")?.remove();\n\t\t\t\t} else {\n\t\t\t\t\t$el.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/components/fileTree/style.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n.file-tree {\n    --file-tree-indent: 24px;\n\n    min-width: 100%;\n    width: max-content;\n\n    &.virtual-scroll {\n        position: relative;\n        overflow-y: auto;\n        will-change: transform;\n    }\n\n    // Collapsible folder structure\n    .list.collapsible {\n        &.hidden>ul {\n            display: none;\n        }\n\n        >.tile {\n            cursor: pointer;\n\n            &.loading {\n                @include mixins.linear-loader(30%, 2px);\n            }\n        }\n    }\n\n    // Folder content with indent\n    ul.folder-content {\n        padding-left: var(--file-tree-indent);\n        position: relative;\n        margin-left: 0;\n        min-width: 100%;\n        width: max-content;\n        overflow-x: visible !important;\n        max-width: none;\n    }\n\n    // Indent guides\n    &.show-indent-guide {\n        >.list.collapsible>ul.folder-content {\n            &::before {\n                content: \"\";\n                position: absolute;\n                left: calc(var(--file-tree-indent) / 2);\n                top: 0;\n                height: 100%;\n                width: 1px;\n                background: var(--border-color);\n                z-index: 0;\n            }\n        }\n    }\n\n    .tile {\n        min-width: 100%;\n        width: max-content;\n\n        >.text {\n            white-space: nowrap !important;\n            overflow: visible !important;\n            width: max-content !important;\n            text-overflow: clip !important;\n        }\n    }\n\n    // File items\n    li,\n    [data-type=\"file\"] {\n        min-width: 100%;\n        width: max-content;\n    }\n}"
  },
  {
    "path": "src/components/inputhints/index.js",
    "content": "import \"./style.scss\";\n\n/**\n * @typedef {Object} HintObj\n * @property {string} value\n * @property {string} text\n * @property {boolean} [active]\n */\n\n/**\n * @typedef {HintObj|string} Hint\n */\n\n/**\n * @typedef {Object} HintModification\n * @property {(hint:Hint, index:number)=>void} add\n * @property {(hint:Hint)=>void} remove\n * @property {(index:number)=>void} removeIndex\n */\n\n/**\n * @typedef {(setHints:(hints:Array<Hint>)=>void, modification: HintModification) => void} HintCallback\n */\n\n/**\n * Generate a list of hints for an input field\n * @param {HTMLInputElement} $input Input field\n * @param {Array<Hint>|HintCallback} hints Hints or a callback to generate hints\n * @param {(value: string) => void} onSelect Callback to call when a hint is selected\n * @returns {{getSelected: ()=>HTMLLIElement, container: HTMLUListElement}}\n */\nexport default function inputhints($input, hints, onSelect) {\n\t/**@type {HTMLUListElement} */\n\tconst $ul = <Ul />;\n\tconst LIMIT = 100;\n\n\tlet preventUpdate = false;\n\tlet updateUlTimeout;\n\tlet pages = 0;\n\tlet currentHints = [];\n\n\t$input.addEventListener(\"focus\", onfocus);\n\n\tif (typeof hints === \"function\") {\n\t\tconst cb = hints;\n\t\thints = [];\n\t\t$ul.content = [<Hint hint={{ value: \"\", text: strings[\"loading...\"] }} />];\n\t\tcb(setHints, hintModification());\n\t} else {\n\t\tsetHints(hints);\n\t}\n\n\t/**\n\t * Retain the focus on the input field\n\t */\n\tfunction handleMouseDown() {\n\t\tpreventUpdate = true;\n\t}\n\n\tfunction handleMouseUp() {\n\t\t$input.focus();\n\t\tpreventUpdate = false;\n\t}\n\n\t/**\n\t * Handle click event\n\t * @param {MouseEvent} e Event\n\t */\n\tfunction handleClick(e) {\n\t\tconst $el = e.target;\n\t\tconst action = $el.getAttribute(\"action\");\n\t\tif (action !== \"hint\") return;\n\t\tconst value = $el.getAttribute(\"value\");\n\t\tif (!value) {\n\t\t\tonblur();\n\t\t\treturn;\n\t\t}\n\t\t$input.value = $el.textContent;\n\t\tif (onSelect) onSelect(value);\n\t\tpreventUpdate = false;\n\t\tonblur();\n\t}\n\n\t/**\n\t * Handle keypress event\n\t * @param {KeyboardEvent} e Event\n\t */\n\tfunction handleKeypress(e) {\n\t\tif (e.key !== \"Enter\") return;\n\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tconst activeHint = $ul.get(\".active\");\n\t\tif (!activeHint) return;\n\t\tconst value = activeHint.getAttribute(\"value\");\n\t\tif (onSelect) onSelect(value);\n\t\telse $input.value = value;\n\t}\n\n\t/**\n\t * Handle keydown event\n\t * @param {KeyboardEvent} e Event\n\t */\n\tfunction handleKeydown(e) {\n\t\tconst code = e.key;\n\t\tif (code === \"ArrowUp\" || code === \"ArrowDown\") {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t}\n\t\tupdateHintFocus(code);\n\t}\n\n\t/**\n\t * Moves the active hint up or down\n\t * @param {\"ArrowDown\" | \"ArrowUp\"} key Direction to move\n\t */\n\tfunction updateHintFocus(key) {\n\t\tlet nextHint;\n\t\tlet activeHint = $ul.get(\".active\");\n\t\tif (!activeHint) activeHint = $ul.firstChild;\n\n\t\tif (key === \"ArrowDown\") {\n\t\t\tnextHint = activeHint.nextElementSibling;\n\t\t\tif (!nextHint) nextHint = $ul.firstElementChild;\n\t\t} else if (key === \"ArrowUp\") {\n\t\t\tnextHint = activeHint.previousElementSibling;\n\t\t\tif (!nextHint) nextHint = $ul.lastElementChild;\n\t\t}\n\n\t\tif (nextHint) {\n\t\t\tactiveHint.classList.remove(\"active\");\n\t\t\tnextHint.classList.add(\"active\");\n\t\t\tnextHint.scrollIntoView();\n\t\t}\n\t}\n\n\t/**\n\t * @this {HTMLInputElement}\n\t */\n\tfunction oninput() {\n\t\tconst { value: toTest } = this;\n\t\tconst matched = [];\n\t\tconst regexp = new RegExp(escapeRegExp(toTest), \"i\");\n\t\thints.forEach((hint) => {\n\t\t\tconst { value, text } = hint;\n\t\t\tif (regexp.test(value) || regexp.test(text)) {\n\t\t\t\tmatched.push(hint);\n\t\t\t}\n\t\t});\n\t\tupdateUl(matched);\n\t}\n\n\tfunction onfocus() {\n\t\tif (preventUpdate) return;\n\n\t\t$input.addEventListener(\"keypress\", handleKeypress);\n\t\t$input.addEventListener(\"keydown\", handleKeydown);\n\t\t$input.addEventListener(\"blur\", onblur);\n\t\t$input.addEventListener(\"input\", oninput);\n\t\twindow.addEventListener(\"resize\", position);\n\t\tulAddEventListeners();\n\t\tapp.append($ul);\n\t\tposition();\n\t}\n\n\t/**\n\t * Event listener for blur\n\t * @returns\n\t */\n\tfunction onblur() {\n\t\tif (preventUpdate) return;\n\n\t\tclearTimeout(updateUlTimeout);\n\t\t$input.removeEventListener(\"keypress\", handleKeypress);\n\t\t$input.removeEventListener(\"keydown\", handleKeydown);\n\t\t$input.removeEventListener(\"blur\", onblur);\n\t\t$input.removeEventListener(\"input\", oninput);\n\t\twindow.removeEventListener(\"resize\", position);\n\t\tulRemoveEventListeners();\n\t\t$ul.remove();\n\t}\n\n\t/**\n\t * Update the position of the hint list\n\t * @param {boolean} append Append the list to the body or not\n\t */\n\tfunction position() {\n\t\tconst activeHint = $ul.get(\".active\");\n\t\tconst { firstElementChild } = $ul;\n\t\tif (!activeHint && firstElementChild)\n\t\t\tfirstElementChild.classList.add(\"active\");\n\t\tconst client = $input.getBoundingClientRect();\n\t\tconst inputTop = client.top - 5;\n\t\tconst inputBottom = client.bottom + 5;\n\t\tconst inputLeft = client.left;\n\t\tconst bottomHeight = window.innerHeight - inputBottom;\n\t\tconst mid = window.innerHeight / 2;\n\n\t\tif (bottomHeight >= mid) {\n\t\t\t$ul.classList.remove(\"bottom\");\n\t\t\t$ul.style.top = `${inputBottom}px`;\n\t\t\t$ul.style.bottom = \"auto\";\n\t\t} else {\n\t\t\t$ul.classList.add(\"bottom\");\n\t\t\t$ul.style.top = \"auto\";\n\t\t\t$ul.style.bottom = `${inputTop}px`;\n\t\t}\n\n\t\t$ul.style.left = `${inputLeft}px`;\n\t\t$ul.style.width = `${client.width}px`;\n\t}\n\n\t/**\n\t * Set hint items\n\t * @param {Array<Hint>} list Hint items\n\t */\n\tfunction setHints(list) {\n\t\tif (Array.isArray(list)) {\n\t\t\thints = list;\n\t\t} else {\n\t\t\thints = [];\n\t\t}\n\t\tupdateUl(hints);\n\t\t$ul.classList.remove(\"loading\");\n\t}\n\n\tfunction hintModification() {\n\t\treturn {\n\t\t\tadd(item, index) {\n\t\t\t\tif (index) {\n\t\t\t\t\thints.splice(index, 0, item);\n\t\t\t\t\tconst child = $ul.children[index];\n\t\t\t\t\tif (child) {\n\t\t\t\t\t\t$ul.insertBefore(child, $ul.children[index]);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\thints.push(item);\n\t\t\t},\n\t\t\tremove(item) {\n\t\t\t\tconst index = hints.indexOf(item);\n\t\t\t\tif (index > -1) {\n\t\t\t\t\thints.splice(index, 1);\n\t\t\t\t}\n\t\t\t},\n\t\t\tremoveIndex(index) {\n\t\t\t\thints.splice(index, 1);\n\t\t\t},\n\t\t};\n\t}\n\n\tfunction ulAddEventListeners() {\n\t\twindow.addEventListener(\"resize\", position);\n\t\t$ul.addEventListener(\"click\", handleClick);\n\t\t$ul.addEventListener(\"mousedown\", handleMouseDown);\n\t\t$ul.addEventListener(\"mouseup\", handleMouseUp);\n\t\t$ul.addEventListener(\"touchstart\", handleMouseDown);\n\t\t$ul.addEventListener(\"touchend\", handleMouseUp);\n\t\t$ul.addEventListener(\"scroll\", updatePage);\n\t}\n\n\tfunction ulRemoveEventListeners() {\n\t\twindow.removeEventListener(\"resize\", position);\n\t\t$ul.removeEventListener(\"click\", handleClick);\n\t\t$ul.removeEventListener(\"mousedown\", handleMouseDown);\n\t\t$ul.removeEventListener(\"mouseup\", handleMouseUp);\n\t\t$ul.removeEventListener(\"touchstart\", handleMouseDown);\n\t\t$ul.removeEventListener(\"touchend\", handleMouseUp);\n\t\t$ul.removeEventListener(\"scroll\", updatePage);\n\t}\n\n\tfunction updatePage() {\n\t\tconst offset = (pages + 1) * LIMIT;\n\t\tconst hasMore = offset < currentHints.length;\n\n\t\t// if the scroll is at the bottom\n\t\tif ($ul.scrollTop + $ul.clientHeight >= $ul.scrollHeight && hasMore) {\n\t\t\tpages++;\n\t\t\tupdateUlNow(currentHints, pages);\n\t\t}\n\t}\n\n\t/**\n\t * First time updates the hint instantly, then debounce\n\t * @param {Array<HintObj>} hints\n\t */\n\tfunction updateUl(hints) {\n\t\tupdateUlNow(hints);\n\t\tupdateUl = updateUlDebounce;\n\t}\n\n\t/**\n\t * Update the hint list after a delay\n\t * @param {Array<HintObj>} hints\n\t */\n\tfunction updateUlDebounce(hints) {\n\t\tclearTimeout(updateUlTimeout);\n\t\tupdateUlTimeout = setTimeout(updateUlNow, 300, hints);\n\t}\n\n\t/**\n\t * Update the hint list instantly\n\t * @param {Array<HintObj>} hints\n\t * @param {number} page\n\t */\n\tfunction updateUlNow(hints, page = 0) {\n\t\t// render only first 500 hints\n\t\tcurrentHints = hints;\n\t\tconst offset = page * LIMIT;\n\t\tconst end = offset + LIMIT;\n\t\tconst list = hints.slice(offset, end);\n\t\tlet scrollTop = $ul.scrollTop;\n\t\t//if (!list.length) return;\n\n\t\t$ul.remove();\n\n\t\tif (!hints.length) {\n\t\t\t$ul.content = [<Hint hint={{ value: \"\", text: \"No matches found\" }} />];\n\t\t} else if (!list.length) {\n\t\t\t// No more hints to load\n\t\t\treturn;\n\t\t} else {\n\t\t\tif (!page) {\n\t\t\t\tscrollTop = 0;\n\t\t\t\t$ul.content = list.map((hint) => <Hint hint={hint} />);\n\t\t\t} else {\n\t\t\t\t$ul.append(...list.map((hint) => <Hint hint={hint} />));\n\t\t\t}\n\t\t}\n\t\tapp.append($ul);\n\t\t$ul.scrollTop = scrollTop;\n\t\tposition(); // Update the position of the new list\n\t}\n\n\treturn {\n\t\tgetSelected() {\n\t\t\t$ul.get(\".active\");\n\t\t},\n\t\tget container() {\n\t\t\treturn $ul;\n\t\t},\n\t};\n}\n\n/**\n * Create a hint item\n * @param {object} param0 Hint item\n * @param {HintObj} param0.hint Hint item\n * @returns {HTMLLIElement}\n */\nfunction Hint({ hint }) {\n\tlet value = \"\";\n\tlet text = \"\";\n\tlet active = false;\n\n\tif (typeof hint === \"string\") {\n\t\tvalue = hint;\n\t\ttext = hint;\n\t} else {\n\t\tvalue = hint.value;\n\t\ttext = hint.text;\n\t\tactive = !!hint.active;\n\t}\n\n\treturn (\n\t\t<li\n\t\t\tclassName={active ? \"active\" : \"\"}\n\t\t\tattr-action=\"hint\"\n\t\t\tattr-value={value}\n\t\t\tinnerHTML={text}\n\t\t></li>\n\t);\n}\n\n/**\n * Create a hint list\n * @param {object} param0 Attributes\n * @param {Array<Hint>} param0.hints Hint items\n * @returns {HTMLUListElement}\n */\nfunction Ul({ hints = [] }) {\n\treturn (\n\t\t<ul id=\"hints\" className=\"scroll\">\n\t\t\t{hints.map((hint) => (\n\t\t\t\t<Hint hint={hint} />\n\t\t\t))}\n\t\t</ul>\n\t);\n}\n\nfunction escapeRegExp(value) {\n\treturn String(value ?? \"\").replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"
  },
  {
    "path": "src/components/inputhints/style.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n#hints {\n  position: fixed;\n  top: 0;\n  left: 0;\n  padding: 0;\n  margin: 0;\n  border-radius: 0 0 4px 4px;\n  background-color: rgb(255, 255, 255);\n  background-color: var(--secondary-color);\n  color: rgb(37, 37, 37);\n  color: var(--secondary-text-color);\n  box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 0 4px var(--box-shadow-color);\n  border: solid 1px transparent;\n  border: solid 1px var(--popup-border-color);\n  height: fit-content;\n  min-height: 30px;\n  max-height: 70vh;\n  z-index: 999;\n  overflow-y: scroll;\n\n  &.bottom {\n    border-radius: 4px 4px 0 0;\n  }\n\n  &.all {\n    border-radius: 4px;\n  }\n\n  [data-action=\"hint\"],\n  [action=\"hint\"] {\n    font-size: 0.9rem;\n    min-height: 30px;\n    height: fit-content;\n    display: flex;\n    align-items: center;\n    box-sizing: border-box;\n    padding: 5px;\n    overflow-x: hidden;\n    text-overflow: ellipsis;\n\n    * {\n      pointer-events: none;\n    }\n\n    [data-str]::after {\n      content: attr(data-str);\n      font-size: 0.6rem;\n      opacity: 0.5;\n      margin-left: 10px;\n    }\n\n    small:not(:empty) {\n      margin-left: auto;\n      color: rgb(51, 153, 255);\n      color: var(--active-color);\n      padding: 2px;\n      border-radius: 2px;\n      font-size: 0.6rem;\n    }\n\n    &.active {\n      background-color: rgb(51, 153, 255);\n      background-color: var(--active-color);\n      color: rgb(27, 26, 26);\n      color: var(--primary-text-color);\n\n      small {\n        color: rgb(255, 215, 0);\n        color: var(--active-text-color);\n      }\n    }\n\n    &:hover {\n      background-color: rgb(107, 168, 229);\n      background-color: var(--primary-color);\n      color: rgb(27, 26, 26);\n      color: var(--secondary-text-color);\n    }\n  }\n\n  &.loading {\n    @include mixins.loader(18px);\n  }\n\n  &:empty {\n    display: none;\n  }\n}"
  },
  {
    "path": "src/components/logo/index.js",
    "content": "import \"./style.scss\";\n\nexport default function Logo() {\n\treturn <div className=\"logo\" />;\n}\n"
  },
  {
    "path": "src/components/logo/style.scss",
    "content": ".logo {\n  display: block;\n  width: 150px;\n  height: 150px;\n  position: relative;\n  margin: 0 auto;\n\n  &::after {\n    content: '';\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    background-image: url(./logo.png);\n    background-size: 80px;\n    background-repeat: no-repeat;\n    background-position: center;\n  }\n\n  &::before {\n    content: '';\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    background: radial-gradient(circle, rgb(68, 153, 254, 0.5), rgb(68, 153, 254, 0.1), rgb(68, 153, 254, 0), rgb(68, 153, 254, 0));\n    overflow: visible;\n  }\n}"
  },
  {
    "path": "src/components/lspInfoDialog/index.js",
    "content": "import \"./styles.scss\";\nimport lspClientManager from \"cm/lsp/clientManager\";\nimport { getServerStats } from \"cm/lsp/serverLauncher\";\nimport serverRegistry from \"cm/lsp/serverRegistry\";\nimport toast from \"components/toast\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\nlet dialogInstance = null;\n\nconst lspLogs = new Map();\nconst MAX_LOGS = 200;\nconst logListeners = new Set();\nconst IGNORED_LOG_PATTERNS = [\n\t/\\$\\/progress\\b/i,\n\t/\\bProgress:/i,\n\t/\\bwindow\\/workDoneProgress\\/create\\b/i,\n\t/\\bAuto-responded to window\\/workDoneProgress\\/create\\b/i,\n];\n\nfunction shouldIgnoreLog(message) {\n\tif (typeof message !== \"string\") return false;\n\treturn IGNORED_LOG_PATTERNS.some((pattern) => pattern.test(message));\n}\n\nfunction addLspLog(serverId, level, message, details = null) {\n\tif (shouldIgnoreLog(message)) {\n\t\treturn;\n\t}\n\n\tif (!lspLogs.has(serverId)) {\n\t\tlspLogs.set(serverId, []);\n\t}\n\tconst logs = lspLogs.get(serverId);\n\tconst entry = {\n\t\ttimestamp: new Date(),\n\t\tlevel,\n\t\tmessage,\n\t\tdetails,\n\t};\n\tlogs.push(entry);\n\tif (logs.length > MAX_LOGS) {\n\t\tlogs.shift();\n\t}\n\tlogListeners.forEach((fn) => fn(serverId, entry));\n}\n\nfunction getLspLogs(serverId) {\n\treturn lspLogs.get(serverId) || [];\n}\n\nfunction clearLspLogs(serverId) {\n\tlspLogs.delete(serverId);\n}\n\nconst originalConsoleInfo = console.info;\nconst originalConsoleWarn = console.warn;\nconst originalConsoleError = console.error;\n\nfunction stripAnsi(str) {\n\tif (typeof str !== \"string\") return str;\n\treturn str.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\nfunction extractServerId(message) {\n\tconst cleaned = stripAnsi(message);\n\t// Match [LSP:serverId] format\n\tconst lspMatch = cleaned?.match?.(/\\[LSP:([^\\]]+)\\]/);\n\tif (lspMatch) return lspMatch[1];\n\n\t// Match [LSP-STDERR:program] format from axs proxy\n\tconst stderrMatch = cleaned?.match?.(/\\[LSP-STDERR:([^\\]]+)\\]/);\n\tif (stderrMatch) {\n\t\tconst program = stderrMatch[1];\n\t\treturn program;\n\t}\n\n\treturn null;\n}\n\nfunction extractLogMessage(message) {\n\tconst cleaned = stripAnsi(message);\n\t// Strip [LSP:...] and [LSP-STDERR:...] prefixes\n\t// Strip ISO timestamps like 2026-02-05T08:26:24.745443Z\n\t// Strip log levels like INFO, WARN, ERROR and the source like axs::lsp:\n\treturn (\n\t\tcleaned\n\t\t\t?.replace?.(/\\[LSP:[^\\]]+\\]\\s*/, \"\")\n\t\t\t?.replace?.(/\\[LSP-STDERR:[^\\]]+\\]\\s*/, \"\")\n\t\t\t?.replace?.(/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z?\\s*/g, \"\")\n\t\t\t?.replace?.(/\\s*(INFO|WARN|ERROR|DEBUG|TRACE)\\s+/gi, \"\")\n\t\t\t?.replace?.(/[a-z_]+::[a-z_]+:\\s*/gi, \"\")\n\t\t\t?.trim() || cleaned\n\t);\n}\n\nconsole.info = function (...args) {\n\toriginalConsoleInfo.apply(console, args);\n\tconst msg = args[0];\n\tif (\n\t\ttypeof msg === \"string\" &&\n\t\t(msg.includes(\"[LSP:\") || msg.includes(\"[LSP-STDERR:\"))\n\t) {\n\t\tconst serverId = extractServerId(msg);\n\t\tif (serverId) {\n\t\t\taddLspLog(serverId, \"info\", extractLogMessage(msg));\n\t\t}\n\t}\n};\n\nconsole.warn = function (...args) {\n\toriginalConsoleWarn.apply(console, args);\n\tconst msg = args[0];\n\tif (\n\t\ttypeof msg === \"string\" &&\n\t\t(msg.includes(\"[LSP:\") || msg.includes(\"[LSP-STDERR:\"))\n\t) {\n\t\tconst serverId = extractServerId(msg);\n\t\tif (serverId) {\n\t\t\t// stderr from axs is logged as warn, mark it appropriately\n\t\t\tconst isStderr = msg.includes(\"[LSP-STDERR:\");\n\t\t\taddLspLog(serverId, isStderr ? \"stderr\" : \"warn\", extractLogMessage(msg));\n\t\t}\n\t}\n};\n\nconsole.error = function (...args) {\n\toriginalConsoleError.apply(console, args);\n\tconst msg = args[0];\n\tif (\n\t\ttypeof msg === \"string\" &&\n\t\t(msg.includes(\"[LSP:\") || msg.includes(\"[LSP-STDERR:\"))\n\t) {\n\t\tconst serverId = extractServerId(msg);\n\t\tif (serverId) {\n\t\t\taddLspLog(serverId, \"error\", extractLogMessage(msg));\n\t\t}\n\t}\n};\n\nfunction getActiveClients() {\n\ttry {\n\t\treturn lspClientManager.getActiveClients();\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction getCurrentFileLanguage() {\n\ttry {\n\t\tconst file = window.editorManager?.activeFile;\n\t\tif (!file || file.type !== \"editor\") return null;\n\t\treturn file.currentMode?.toLowerCase() || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction getServersForCurrentFile() {\n\tconst language = getCurrentFileLanguage();\n\tif (!language) return [];\n\n\ttry {\n\t\treturn serverRegistry.getServersForLanguage(language);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction getServerStatus(serverId) {\n\tconst activeClients = getActiveClients();\n\tconst client = activeClients.find((c) => c.server?.id === serverId);\n\tif (!client) return \"stopped\";\n\ttry {\n\t\treturn client.client?.connected !== false ? \"active\" : \"connecting\";\n\t} catch {\n\t\treturn \"stopped\";\n\t}\n}\n\nfunction getClientState(serverId) {\n\tconst activeClients = getActiveClients();\n\treturn activeClients.find((c) => c.server?.id === serverId) || null;\n}\n\nfunction getStatusColor(status) {\n\tswitch (status) {\n\t\tcase \"active\":\n\t\t\treturn \"var(--lsp-status-active, #22c55e)\";\n\t\tcase \"connecting\":\n\t\t\treturn \"var(--lsp-status-connecting, #f59e0b)\";\n\t\tdefault:\n\t\t\treturn \"var(--lsp-status-stopped, #6b7280)\";\n\t}\n}\n\nfunction copyLogsToClipboard(serverId, serverLabel) {\n\tconst logs = getLspLogs(serverId);\n\tif (logs.length === 0) {\n\t\ttoast(\"No logs to copy\");\n\t\treturn;\n\t}\n\n\tconst text = logs\n\t\t.map((log) => {\n\t\t\tconst time = log.timestamp.toLocaleTimeString(\"en-US\", {\n\t\t\t\thour12: false,\n\t\t\t\thour: \"2-digit\",\n\t\t\t\tminute: \"2-digit\",\n\t\t\t\tsecond: \"2-digit\",\n\t\t\t});\n\t\t\treturn `[${time}] [${log.level.toUpperCase()}] ${log.message}`;\n\t\t})\n\t\t.join(\"\\n\");\n\n\tconst header = `=== ${serverLabel} LSP Logs ===\\n`;\n\n\tif (navigator.clipboard?.writeText) {\n\t\tnavigator.clipboard\n\t\t\t.writeText(header + text)\n\t\t\t.then(() => {\n\t\t\t\ttoast(\"Logs copied\");\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\ttoast(\"Failed to copy\");\n\t\t\t});\n\t} else if (cordova?.plugins?.clipboard) {\n\t\tcordova.plugins.clipboard.copy(header + text);\n\t\ttoast(\"Logs copied\");\n\t} else {\n\t\ttoast(\"Clipboard not available\");\n\t}\n}\n\nasync function restartServer(serverId) {\n\taddLspLog(serverId, \"info\", \"Restart requested by user\");\n\ttoast(\"Restarting server...\");\n\n\ttry {\n\t\tconst clientState = getClientState(serverId);\n\t\tif (clientState) {\n\t\t\tawait clientState.dispose();\n\t\t}\n\n\t\tconst { stopManagedServer } = await import(\"cm/lsp/serverLauncher\");\n\t\tstopManagedServer(serverId);\n\n\t\twindow.editorManager?.restartLsp?.();\n\n\t\taddLspLog(serverId, \"info\", \"Server restarted successfully\");\n\t\ttoast(\"Server restarted\");\n\t} catch (err) {\n\t\taddLspLog(serverId, \"error\", `Restart failed: ${err.message}`);\n\t\ttoast(\"Restart failed\");\n\t}\n}\n\nasync function stopServer(serverId) {\n\taddLspLog(serverId, \"info\", \"Stop requested by user\");\n\ttoast(\"Stopping...\");\n\n\ttry {\n\t\tconst clientState = getClientState(serverId);\n\t\tif (clientState) {\n\t\t\tawait clientState.dispose();\n\t\t}\n\n\t\tconst { stopManagedServer } = await import(\"cm/lsp/serverLauncher\");\n\t\tstopManagedServer(serverId);\n\n\t\taddLspLog(serverId, \"info\", \"Server stopped\");\n\t\ttoast(\"Server stopped\");\n\t} catch (err) {\n\t\taddLspLog(serverId, \"error\", `Stop failed: ${err.message}`);\n\t\ttoast(\"Failed to stop\");\n\t}\n}\n\nasync function startAllServers() {\n\ttoast(\"Starting LSP servers...\");\n\ttry {\n\t\twindow.editorManager?.restartLsp?.();\n\t\ttoast(\"Servers started\");\n\t} catch (err) {\n\t\ttoast(\"Failed to start servers\");\n\t}\n}\n\nasync function restartAllServers() {\n\tconst activeClients = getActiveClients();\n\tif (!activeClients.length) {\n\t\tawait startAllServers();\n\t\treturn;\n\t}\n\n\tconst count = activeClients.length;\n\ttoast(`Restarting ${count} LSP server${count > 1 ? \"s\" : \"\"}...`);\n\n\ttry {\n\t\tawait lspClientManager.dispose();\n\t\twindow.editorManager?.restartLsp?.();\n\t\ttoast(\"All servers restarted\");\n\t} catch (err) {\n\t\ttoast(\"Failed to restart servers\");\n\t}\n}\n\nasync function stopAllServers() {\n\tconst activeClients = getActiveClients();\n\tif (!activeClients.length) {\n\t\ttoast(\"No LSP servers are currently running\");\n\t\treturn;\n\t}\n\n\tconst count = activeClients.length;\n\n\ttry {\n\t\tawait lspClientManager.dispose();\n\t\ttoast(`Stopped ${count} LSP server${count > 1 ? \"s\" : \"\"}`);\n\t} catch (err) {\n\t\ttoast(\"Failed to stop servers\");\n\t}\n}\n\nfunction showLspInfoDialog() {\n\tif (dialogInstance) {\n\t\tdialogInstance.hide();\n\t\treturn;\n\t}\n\n\tconst relevantServers = getServersForCurrentFile();\n\tconst currentLanguage = getCurrentFileLanguage();\n\n\tlet currentView = \"list\";\n\tlet selectedServer = null;\n\n\tconst $mask = <span className=\"mask\" onclick={hide} />;\n\tconst $dialog = (\n\t\t<div className=\"prompt lsp-info-dialog\">\n\t\t\t<div className=\"title\">\n\t\t\t\t<span className=\"icon zap\" style={{ marginRight: \"8px\" }} />\n\t\t\t\tLanguage Servers\n\t\t\t</div>\n\t\t\t<div className=\"lsp-dialog-body\" />\n\t\t</div>\n\t);\n\n\tconst $body = $dialog.querySelector(\".lsp-dialog-body\");\n\n\tfunction renderList() {\n\t\t$body.innerHTML = \"\";\n\n\t\tif (relevantServers.length === 0) {\n\t\t\t$body.appendChild(\n\t\t\t\t<div className=\"lsp-empty-state\">\n\t\t\t\t\t<span className=\"icon code\" />\n\t\t\t\t\t<p>\n\t\t\t\t\t\tNo language servers for{\" \"}\n\t\t\t\t\t\t<strong>{currentLanguage || \"this file\"}</strong>\n\t\t\t\t\t</p>\n\t\t\t\t</div>,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst $list = <ul className=\"lsp-server-list\" />;\n\n\t\tconst runningServers = relevantServers.filter(\n\t\t\t(s) => getServerStatus(s.id) !== \"stopped\",\n\t\t);\n\t\tconst hasRunning = runningServers.length > 0;\n\n\t\tconst $actions = (\n\t\t\t<div className=\"lsp-list-actions\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName=\"lsp-action-btn\"\n\t\t\t\t\tonclick={async () => {\n\t\t\t\t\t\tawait restartAllServers();\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 500));\n\t\t\t\t\t\trenderList();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"icon autorenew\" />\n\t\t\t\t\t<span>{hasRunning ? \"Restart All\" : \"Start All\"}</span>\n\t\t\t\t</button>\n\t\t\t\t{hasRunning && (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"lsp-action-btn danger\"\n\t\t\t\t\t\tonclick={async () => {\n\t\t\t\t\t\t\tawait stopAllServers();\n\t\t\t\t\t\t\trenderList();\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"icon power_settings_new\" />\n\t\t\t\t\t\t<span>Stop All</span>\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t\t$body.appendChild($actions);\n\n\t\tfor (const server of relevantServers) {\n\t\t\tconst status = getServerStatus(server.id);\n\t\t\tconst statusColor = getStatusColor(status);\n\t\t\tconst logs = getLspLogs(server.id);\n\t\t\tconst errorCount = logs.filter((l) => l.level === \"error\").length;\n\n\t\t\tconst $item = (\n\t\t\t\t<li\n\t\t\t\t\tclassName=\"lsp-server-item\"\n\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\tselectedServer = server;\n\t\t\t\t\t\tcurrentView = \"details\";\n\t\t\t\t\t\trenderDetails();\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"lsp-status-dot\"\n\t\t\t\t\t\tstyle={{ backgroundColor: statusColor }}\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"lsp-server-info\">\n\t\t\t\t\t\t<span className=\"lsp-server-name\">{server.label}</span>\n\t\t\t\t\t\t<span className=\"lsp-server-status\">{status}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{errorCount > 0 && (\n\t\t\t\t\t\t<span className=\"lsp-error-badge\">{errorCount}</span>\n\t\t\t\t\t)}\n\t\t\t\t\t<span className=\"icon keyboard_arrow_right lsp-arrow\" />\n\t\t\t\t</li>\n\t\t\t);\n\t\t\t$list.appendChild($item);\n\t\t}\n\n\t\t$body.appendChild($list);\n\t}\n\n\tfunction renderDetails() {\n\t\tif (!selectedServer) return;\n\t\t$body.innerHTML = \"\";\n\n\t\tconst server = selectedServer;\n\t\tconst status = getServerStatus(server.id);\n\t\tconst clientState = getClientState(server.id);\n\t\tconst isRunning = status !== \"stopped\";\n\n\t\tconst capabilities = [];\n\t\tconst hasCapabilities = clientState?.client?.serverCapabilities;\n\t\tif (hasCapabilities) {\n\t\t\tconst caps = clientState.client.serverCapabilities;\n\t\t\tif (caps.completionProvider) capabilities.push(\"Completion\");\n\t\t\tif (caps.hoverProvider) capabilities.push(\"Hover\");\n\t\t\tif (caps.definitionProvider) capabilities.push(\"Go to Definition\");\n\t\t\tif (caps.referencesProvider) capabilities.push(\"Find References\");\n\t\t\tif (caps.renameProvider) capabilities.push(\"Rename\");\n\t\t\tif (caps.documentFormattingProvider) capabilities.push(\"Format\");\n\t\t\tif (caps.signatureHelpProvider) capabilities.push(\"Signature Help\");\n\t\t\tif (caps.inlayHintProvider) capabilities.push(\"Inlay Hints\");\n\t\t\tif (caps.codeActionProvider) capabilities.push(\"Code Actions\");\n\t\t\tif (caps.diagnosticProvider) capabilities.push(\"Diagnostics\");\n\t\t}\n\t\tif (isRunning && capabilities.length === 0 && hasCapabilities) {\n\t\t\tcapabilities.push(\"Diagnostics\");\n\t\t}\n\n\t\tconst logs = getLspLogs(server.id);\n\n\t\tconst $details = (\n\t\t\t<div className=\"lsp-details\">\n\t\t\t\t<div className=\"lsp-details-header\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"lsp-icon-btn\"\n\t\t\t\t\t\tonclick={() => {\n\t\t\t\t\t\t\tcurrentView = \"list\";\n\t\t\t\t\t\t\tselectedServer = null;\n\t\t\t\t\t\t\trenderList();\n\t\t\t\t\t\t}}\n\t\t\t\t\t\taria-label=\"Back\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"icon keyboard_arrow_left\" />\n\t\t\t\t\t</button>\n\t\t\t\t\t<div className=\"lsp-details-title\">\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclassName=\"lsp-status-dot\"\n\t\t\t\t\t\t\tstyle={{ backgroundColor: getStatusColor(status) }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<span>{server.label}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"lsp-header-actions\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"lsp-icon-btn\"\n\t\t\t\t\t\t\tonclick={async () => {\n\t\t\t\t\t\t\t\tawait restartServer(server.id);\n\t\t\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 500));\n\t\t\t\t\t\t\t\trenderDetails();\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\taria-label=\"Restart Server\"\n\t\t\t\t\t\t\ttitle=\"Restart Server\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"icon autorenew\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t{isRunning && (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclassName=\"lsp-icon-btn danger\"\n\t\t\t\t\t\t\t\tonclick={async () => {\n\t\t\t\t\t\t\t\t\tawait stopServer(server.id);\n\t\t\t\t\t\t\t\t\trenderDetails();\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\taria-label=\"Stop Server\"\n\t\t\t\t\t\t\t\ttitle=\"Stop Server\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"icon power_settings_new\" />\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<div className=\"lsp-section\">\n\t\t\t\t\t\t<div className=\"lsp-section-label\">Capabilities</div>\n\t\t\t\t\t\t<div className=\"lsp-chip-container\">\n\t\t\t\t\t\t\t{capabilities.length > 0\n\t\t\t\t\t\t\t\t? capabilities.map((cap) => (\n\t\t\t\t\t\t\t\t\t\t<span className=\"lsp-chip\">{cap}</span>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t: !hasCapabilities && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"lsp-chip\">Initializing...</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t<div className=\"lsp-section\">\n\t\t\t\t\t<div className=\"lsp-section-label\">Supported</div>\n\t\t\t\t\t<div className=\"lsp-chip-container\">\n\t\t\t\t\t\t{server.languages.map((lang) => (\n\t\t\t\t\t\t\t<span className=\"lsp-chip ext\">.{lang}</span>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<div className=\"lsp-section\">\n\t\t\t\t\t\t<div className=\"lsp-section-label\">Project</div>\n\t\t\t\t\t\t<div className=\"lsp-project-path\">\n\t\t\t\t\t\t\t{clientState?.rootUri || \"(workspace folders mode)\"}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<div className=\"lsp-section\">\n\t\t\t\t\t\t<div className=\"lsp-section-label\">Resources</div>\n\t\t\t\t\t\t<div className=\"lsp-stats-container\">\n\t\t\t\t\t\t\t<div className=\"lsp-stat\">\n\t\t\t\t\t\t\t\t<span className=\"lsp-stat-label\">Memory</span>\n\t\t\t\t\t\t\t\t<span className=\"lsp-stat-value\" id={`lsp-mem-${server.id}`}>\n\t\t\t\t\t\t\t\t\t—\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"lsp-stat\">\n\t\t\t\t\t\t\t\t<span className=\"lsp-stat-label\">Uptime</span>\n\t\t\t\t\t\t\t\t<span className=\"lsp-stat-value\" id={`lsp-uptime-${server.id}`}>\n\t\t\t\t\t\t\t\t\t—\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"lsp-stat\">\n\t\t\t\t\t\t\t\t<span className=\"lsp-stat-label\">PID</span>\n\t\t\t\t\t\t\t\t<span className=\"lsp-stat-value\" id={`lsp-pid-${server.id}`}>\n\t\t\t\t\t\t\t\t\t—\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\n\t\t$body.appendChild($details);\n\n\t\t// Create simple collapsible logs section\n\t\tconst $logsSection = (\n\t\t\t<div className=\"lsp-logs-section collapsed\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"lsp-logs-header\"\n\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\tconst section = e.currentTarget.closest(\".lsp-logs-section\");\n\t\t\t\t\t\tif (section) {\n\t\t\t\t\t\t\tsection.classList.toggle(\"collapsed\");\n\t\t\t\t\t\t\tif (!section.classList.contains(\"collapsed\")) {\n\t\t\t\t\t\t\t\tconst container = section.querySelector(\".lsp-logs-container\");\n\t\t\t\t\t\t\t\tif (container) container.scrollTop = container.scrollHeight;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"lsp-logs-title\">\n\t\t\t\t\t\t<span className=\"icon expand_more lsp-expand-icon\" />\n\t\t\t\t\t\t<span>LSP Logs</span>\n\t\t\t\t\t\t{logs.length > 0 && (\n\t\t\t\t\t\t\t<span className=\"lsp-log-count\">({logs.length})</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"lsp-logs-actions\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"lsp-icon-btn small\"\n\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\tcopyLogsToClipboard(server.id, server.label);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\taria-label=\"Copy Logs\"\n\t\t\t\t\t\t\ttitle=\"Copy Logs\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"icon copy\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"lsp-icon-btn small lsp-clear-btn\"\n\t\t\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\tclearLspLogs(server.id);\n\t\t\t\t\t\t\t\trenderDetails();\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\taria-label=\"Clear Logs\"\n\t\t\t\t\t\t\ttitle=\"Clear Logs\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"icon delete\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"lsp-logs-container\">\n\t\t\t\t\t{logs.length === 0 ? (\n\t\t\t\t\t\t<div className=\"lsp-logs-empty\">No logs yet</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\tlogs.slice(-50).map((log) => {\n\t\t\t\t\t\t\tconst time = log.timestamp.toLocaleTimeString(\"en-US\", {\n\t\t\t\t\t\t\t\thour12: false,\n\t\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t\t\tsecond: \"2-digit\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div className={`lsp-log ${log.level}`}>\n\t\t\t\t\t\t\t\t\t<span className=\"lsp-log-time\">{time}</span>\n\t\t\t\t\t\t\t\t\t<span className=\"lsp-log-text\">{log.message}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\n\t\t$body.appendChild($logsSection);\n\n\t\t// Fetch and update stats asynchronously\n\t\tif (isRunning) {\n\t\t\tgetServerStats(server.id).then((stats) => {\n\t\t\t\tif (!stats) return;\n\t\t\t\tconst $mem = document.getElementById(`lsp-mem-${server.id}`);\n\t\t\t\tconst $uptime = document.getElementById(`lsp-uptime-${server.id}`);\n\t\t\t\tconst $pid = document.getElementById(`lsp-pid-${server.id}`);\n\t\t\t\tif ($mem) $mem.textContent = stats.memoryFormatted;\n\t\t\t\tif ($uptime) $uptime.textContent = stats.uptimeFormatted;\n\t\t\t\tif ($pid) $pid.textContent = stats.pid ? String(stats.pid) : \"—\";\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction hide() {\n\t\t$dialog.classList.add(\"hide\");\n\t\trestoreTheme();\n\t\tactionStack.remove(\"lsp-info-dialog\");\n\t\tsetTimeout(() => {\n\t\t\t$dialog.remove();\n\t\t\t$mask.remove();\n\t\t\tdialogInstance = null;\n\t\t}, 200);\n\t}\n\n\tdialogInstance = { hide, element: $dialog };\n\n\tactionStack.push({\n\t\tid: \"lsp-info-dialog\",\n\t\taction: hide,\n\t});\n\n\trestoreTheme(true);\n\tdocument.body.appendChild($dialog);\n\tdocument.body.appendChild($mask);\n\n\tif (currentView === \"list\") {\n\t\trenderList();\n\t}\n}\n\nfunction hasConnectedServers() {\n\tconst relevantServers = getServersForCurrentFile();\n\treturn relevantServers.length > 0;\n}\n\nexport { addLspLog, getLspLogs, hasConnectedServers, showLspInfoDialog };\nexport default showLspInfoDialog;\n"
  },
  {
    "path": "src/components/lspInfoDialog/styles.scss",
    "content": ":root {\n    --lsp-status-active: #22c55e;\n    --lsp-status-connecting: #f59e0b;\n    --lsp-status-stopped: #6b7280;\n}\n\n.lsp-info-dialog {\n    max-width: 360px;\n    min-width: 300px;\n\n    .title {\n        display: flex;\n        align-items: center;\n        text-transform: none;\n        font-weight: 500;\n    }\n}\n\n.lsp-dialog-body {\n    overflow-y: auto;\n    max-height: 65vh;\n\n    &::-webkit-scrollbar {\n        width: 4px;\n    }\n\n    &::-webkit-scrollbar-track {\n        background: transparent;\n    }\n\n    &::-webkit-scrollbar-thumb {\n        background: var(--scrollbar-color);\n        border-radius: 2px;\n    }\n}\n\n.lsp-empty-state {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    padding: 48px 24px;\n    color: var(--popup-text-color);\n    text-align: center;\n\n    .icon {\n        font-size: 2.5rem;\n        margin-bottom: 16px;\n        opacity: 0.3;\n    }\n\n    p {\n        font-size: 0.9rem;\n        margin: 0;\n        opacity: 0.6;\n\n        strong {\n            opacity: 1;\n        }\n    }\n}\n\n.lsp-list-actions {\n    display: flex;\n    gap: 8px;\n    padding: 8px 12px;\n    border-bottom: 1px solid var(--border-color);\n}\n\n.lsp-action-btn {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    flex: 1;\n    justify-content: center;\n    padding: 8px 12px;\n    background-color: var(--box-shadow-color);\n    border: none;\n    color: var(--popup-text-color);\n    cursor: pointer;\n    border-radius: 6px;\n    font-size: 0.75rem;\n    font-weight: 500;\n    transition: all 0.12s ease;\n\n    .icon {\n        font-size: 0.9rem;\n    }\n\n    &:hover {\n        background-color: var(--active-icon-color);\n    }\n\n    &.danger {\n        color: var(--danger-color);\n\n        &:hover {\n            background-color: color-mix(in srgb, var(--danger-color) 15%, transparent);\n        }\n    }\n}\n\n.lsp-server-list {\n    list-style: none;\n    padding: 8px;\n    margin: 0;\n}\n\n.lsp-server-item {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    padding: 14px 12px;\n    border-radius: 6px;\n    cursor: pointer;\n    transition: background-color 0.12s ease;\n    color: var(--popup-text-color);\n\n    &:hover {\n        background-color: var(--box-shadow-color);\n    }\n\n    &:active {\n        background-color: var(--active-icon-color);\n    }\n\n    &:not(:last-child) {\n        border-bottom: 1px solid var(--border-color);\n    }\n}\n\n.lsp-arrow {\n    font-size: 1.3rem;\n    opacity: 0.35;\n    margin-left: auto;\n}\n\n.lsp-status-dot {\n    width: 8px;\n    height: 8px;\n    border-radius: 50%;\n    flex-shrink: 0;\n}\n\n.lsp-server-info {\n    flex: 1;\n    min-width: 0;\n    display: flex;\n    flex-direction: column;\n    gap: 2px;\n}\n\n.lsp-server-name {\n    font-size: 0.95rem;\n    font-weight: 500;\n    color: var(--popup-text-color);\n}\n\n.lsp-server-status {\n    font-size: 0.72rem;\n    color: var(--popup-text-color);\n    opacity: 0.5;\n    text-transform: capitalize;\n}\n\n.lsp-error-badge {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    min-width: 18px;\n    height: 18px;\n    padding: 0 5px;\n    background-color: var(--danger-color);\n    color: var(--danger-text-color);\n    font-size: 0.65rem;\n    font-weight: 600;\n    border-radius: 9px;\n}\n\n.lsp-details {\n    display: flex;\n    flex-direction: column;\n}\n\n.lsp-details-header {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 8px 12px;\n    border-bottom: 1px solid var(--border-color);\n}\n\n.lsp-header-actions {\n    display: flex;\n    gap: 4px;\n    margin-left: auto;\n}\n\n.lsp-icon-btn {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 32px;\n    height: 32px;\n    background: transparent;\n    border: none;\n    color: var(--popup-text-color);\n    cursor: pointer;\n    border-radius: 6px;\n    padding: 0;\n    transition: all 0.12s ease;\n    opacity: 0.7;\n\n    &:hover {\n        background-color: var(--box-shadow-color);\n        opacity: 1;\n    }\n\n    &:active {\n        background-color: var(--active-icon-color);\n    }\n\n    .icon {\n        font-size: 1.1rem;\n    }\n\n    &.small {\n        width: 26px;\n        height: 26px;\n\n        .icon {\n            font-size: 0.95rem;\n        }\n    }\n\n    &.danger {\n        color: var(--danger-color);\n\n        &:hover {\n            background-color: color-mix(in srgb, var(--danger-color) 15%, transparent);\n        }\n    }\n}\n\n.lsp-details-title {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    font-size: 0.95rem;\n    font-weight: 500;\n    color: var(--popup-text-color);\n}\n\n.lsp-section {\n    padding: 10px 14px;\n\n    &:last-child {\n        border-bottom: none;\n    }\n}\n\n.lsp-section-label {\n    font-size: 0.65rem;\n    font-weight: 600;\n    color: var(--popup-text-color);\n    opacity: 0.5;\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n    margin-bottom: 8px;\n}\n\n.lsp-chip-container {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 5px;\n}\n\n.lsp-chip {\n    display: inline-flex;\n    align-items: center;\n    padding: 4px 10px;\n    background: var(--box-shadow-color);\n    color: var(--popup-text-color);\n    border-radius: 4px;\n    font-size: 0.7rem;\n    font-weight: 500;\n\n    &.ext {\n        font-family: monospace;\n        font-weight: 600;\n        background: color-mix(in srgb, var(--active-color) 15%, transparent);\n        color: var(--active-color);\n    }\n}\n\n.lsp-project-path {\n    font-size: 0.75rem;\n    font-family: monospace;\n    color: var(--popup-text-color);\n    opacity: 0.75;\n    word-break: break-all;\n    padding: 8px 10px;\n    background-color: var(--box-shadow-color);\n    border-radius: 6px;\n    line-height: 1.3;\n}\n\n.lsp-logs-section {\n    margin: 8px 14px 12px;\n\n    &.collapsed {\n        .lsp-logs-container {\n            display: none;\n        }\n\n        .lsp-expand-icon {\n            transform: rotate(-90deg);\n        }\n\n        .lsp-clear-btn {\n            display: none;\n        }\n    }\n}\n\n.lsp-logs-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    cursor: pointer;\n    padding: 6px 8px;\n    margin: 0 -8px;\n}\n\n.lsp-logs-title {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n    font-size: 0.75rem;\n    font-weight: 500;\n    color: var(--popup-text-color);\n    opacity: 0.7;\n}\n\n.lsp-expand-icon {\n    font-size: 1.1rem;\n    transition: transform 0.15s ease;\n}\n\n.lsp-log-count {\n    font-size: 0.65rem;\n    opacity: 0.5;\n    margin-left: 2px;\n}\n\n.lsp-logs-actions {\n    display: flex;\n    gap: 2px;\n}\n\n.lsp-logs-container {\n    max-height: 160px;\n    overflow-y: auto;\n    background-color: var(--box-shadow-color);\n    border-radius: 6px;\n    font-family: monospace;\n    font-size: 0.65rem;\n    margin-top: 4px;\n\n    &::-webkit-scrollbar {\n        width: 3px;\n    }\n\n    &::-webkit-scrollbar-track {\n        background: transparent;\n    }\n\n    &::-webkit-scrollbar-thumb {\n        background: var(--scrollbar-color);\n        border-radius: 2px;\n    }\n}\n\n.lsp-logs-empty {\n    padding: 14px;\n    text-align: center;\n    color: var(--popup-text-color);\n    opacity: 0.4;\n    font-size: 0.7rem;\n    font-family: inherit;\n}\n\n.lsp-log {\n    display: flex;\n    gap: 6px;\n    padding: 1px 8px;\n    line-height: 1.3;\n\n    &:first-child {\n        padding-top: 4px;\n    }\n\n    &:last-child {\n        padding-bottom: 4px;\n    }\n\n    &.error .lsp-log-text {\n        color: var(--danger-color);\n    }\n\n    &.warn .lsp-log-text {\n        color: var(--error-text-color);\n    }\n\n    &.stderr .lsp-log-text {\n        color: var(--error-text-color);\n    }\n}\n\n.lsp-log-time {\n    flex-shrink: 0;\n    color: var(--popup-text-color);\n    opacity: 0.35;\n    font-size: 0.6rem;\n}\n\n.lsp-log-text {\n    flex: 1;\n    color: var(--popup-text-color);\n    opacity: 0.85;\n    word-break: break-word;\n}\n\n.lsp-stats-container {\n    display: flex;\n    gap: 16px;\n}\n\n.lsp-stat {\n    display: flex;\n    align-items: baseline;\n    gap: 6px;\n}\n\n.lsp-stat-label {\n    font-size: 0.65rem;\n    font-weight: 500;\n    color: var(--popup-text-color);\n    opacity: 0.75;\n}\n\n.lsp-stat-value {\n    font-size: 0.8rem;\n    font-weight: 600;\n    color: var(--popup-text-color);\n    font-family: monospace;\n}"
  },
  {
    "path": "src/components/lspStatusBar/index.js",
    "content": "import \"./style.scss\";\n\n/**@type {HTMLElement | null} */\nlet $statusBar = null;\n\n/**@type {number | null} */\nlet hideTimeout = null;\n\n/**\n * @typedef {Object} ProgressItem\n * @property {string} title - Task title\n * @property {string} [message] - Current message\n * @property {number} [percentage] - Progress percentage (0-100)\n */\n\n/**@type {Map<string, ProgressItem>} */\nconst activeProgress = new Map();\n\n/**@type {string | null} */\nlet currentServerId = null;\n\n/**@type {string | null} */\nlet currentServerLabel = null;\n\n/**\n * @typedef {Object} LspStatusOptions\n * @property {string} message - The status message to display\n * @property {string} [icon] - Optional icon class name\n * @property {'info' | 'success' | 'warning' | 'error'} [type='info'] - Status type\n * @property {number | false} [duration=0] - Duration in ms, 0 for default (5000ms), false for persistent\n * @property {boolean} [showProgress=false] - Whether to show a progress indicator\n * @property {number} [progress] - Progress percentage (0-100)\n * @property {string} [title] - Optional title for the status\n * @property {string} [id] - Unique identifier for progress tracking\n */\n\n/**\n * Ensure the status bar exists\n * @returns {HTMLElement}\n */\nfunction ensureStatusBar() {\n\tif ($statusBar && document.body.contains($statusBar)) {\n\t\treturn $statusBar;\n\t}\n\n\t$statusBar = (\n\t\t<div id=\"lsp-status-bar\" className=\"lsp-status info\">\n\t\t\t<div className=\"lsp-status-content\">\n\t\t\t\t<span className=\"lsp-status-icon icon autorenew\"></span>\n\t\t\t\t<div className=\"lsp-status-text\">\n\t\t\t\t\t<span className=\"lsp-status-title\"></span>\n\t\t\t\t\t<span className=\"lsp-status-message\"></span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"lsp-status-progress\">\n\t\t\t\t\t<span className=\"lsp-status-progress-text\"></span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"lsp-status-close icon clearclose\"\n\t\t\t\tonclick={hideStatusBar}\n\t\t\t\taria-label=\"Close\"\n\t\t\t></button>\n\t\t</div>\n\t);\n\n\t// Find the quicktools footer to insert before it\n\tconst $footer = document.getElementById(\"quick-tools\");\n\tif ($footer && $footer.parentNode) {\n\t\t$footer.parentNode.insertBefore($statusBar, $footer);\n\t} else {\n\t\t// Fallback: append to app\n\t\tconst $app = document.getElementById(\"app\") || document.body;\n\t\t$app.appendChild($statusBar);\n\t}\n\n\treturn $statusBar;\n}\n\n/**\n * Build aggregated message from all active progress items\n * @returns {{ message: string, avgProgress: number | null, taskCount: number }}\n */\nfunction buildAggregatedStatus() {\n\tconst items = Array.from(activeProgress.values());\n\tconst taskCount = items.length;\n\n\tif (taskCount === 0) {\n\t\treturn { message: \"\", avgProgress: null, taskCount: 0 };\n\t}\n\n\t// Calculate average progress for items that have percentage\n\tconst itemsWithProgress = items.filter(\n\t\t(item) => typeof item.percentage === \"number\",\n\t);\n\tconst avgProgress =\n\t\titemsWithProgress.length > 0\n\t\t\t? Math.round(\n\t\t\t\t\titemsWithProgress.reduce(\n\t\t\t\t\t\t(sum, item) => sum + (item.percentage || 0),\n\t\t\t\t\t\t0,\n\t\t\t\t\t) / itemsWithProgress.length,\n\t\t\t\t)\n\t\t\t: null;\n\n\t// Build message\n\tif (taskCount === 1) {\n\t\tconst item = items[0];\n\t\tconst parts = [];\n\t\tif (item.message) {\n\t\t\tparts.push(item.message);\n\t\t} else if (item.title) {\n\t\t\tparts.push(item.title);\n\t\t}\n\t\treturn { message: parts.join(\" \"), avgProgress, taskCount };\n\t}\n\n\t// Multiple tasks - show count and maybe the most recent message\n\tconst latestWithMessage = items.filter((item) => item.message).pop();\n\tconst message = latestWithMessage\n\t\t? `${taskCount} tasks: ${latestWithMessage.message}`\n\t\t: `${taskCount} tasks running`;\n\n\treturn { message, avgProgress, taskCount };\n}\n\n/**\n * Update the status bar display\n */\nfunction updateStatusBarDisplay() {\n\tconst bar = $statusBar;\n\tif (!bar) return;\n\n\tconst { message, avgProgress, taskCount } = buildAggregatedStatus();\n\n\tif (taskCount === 0) {\n\t\thideStatusBar();\n\t\treturn;\n\t}\n\n\tconst $title = bar.querySelector(\".lsp-status-title\");\n\tconst $message = bar.querySelector(\".lsp-status-message\");\n\tconst $progressText = bar.querySelector(\".lsp-status-progress-text\");\n\tconst $progressContainer = bar.querySelector(\".lsp-status-progress\");\n\tconst $icon = bar.querySelector(\".lsp-status-icon\");\n\n\tif ($title) $title.textContent = currentServerLabel || \"\";\n\tif ($message) $message.textContent = message;\n\n\tif (avgProgress !== null && $progressText && $progressContainer) {\n\t\t$progressText.textContent = `${avgProgress}%`;\n\t\t$progressContainer.style.display = \"\";\n\t} else if ($progressContainer) {\n\t\t$progressContainer.style.display = \"none\";\n\t}\n\n\t// Show spinning icon while progress is active\n\tif ($icon) {\n\t\t$icon.className = \"lsp-status-icon icon autorenew\";\n\t}\n\n\tbar.className = \"lsp-status info\";\n\tbar.classList.remove(\"hiding\");\n}\n\n/**\n * Hide the status bar\n */\nfunction hideStatusBar() {\n\tif (hideTimeout) {\n\t\tclearTimeout(hideTimeout);\n\t\thideTimeout = null;\n\t}\n\n\tif ($statusBar) {\n\t\t$statusBar.classList.add(\"hiding\");\n\t\tsetTimeout(() => {\n\t\t\tif ($statusBar) {\n\t\t\t\t$statusBar.remove();\n\t\t\t\t$statusBar = null;\n\t\t\t}\n\t\t}, 300);\n\t}\n}\n\n/**\n * Show LSP status notification\n * @param {LspStatusOptions} options - Status options\n * @returns {string | undefined} The status ID for later updates/removal\n */\nexport function showLspStatus(options) {\n\tconst {\n\t\tmessage,\n\t\ticon = \"autorenew\",\n\t\ttype = \"info\",\n\t\tduration = 0,\n\t\tshowProgress = false,\n\t\tprogress,\n\t\ttitle,\n\t\tid,\n\t} = options;\n\n\t// Clear any existing hide timeout\n\tif (hideTimeout) {\n\t\tclearTimeout(hideTimeout);\n\t\thideTimeout = null;\n\t}\n\n\t// If this is a progress item (has id), track it\n\tif (id && id.includes(\"-progress-\")) {\n\t\t// Extract server info from id (format: serverId-progress-token)\n\t\tconst serverMatch = id.match(/^(.+?)-progress-/);\n\t\tif (serverMatch) {\n\t\t\tcurrentServerId = serverMatch[1];\n\t\t\tcurrentServerLabel = title || currentServerId;\n\t\t}\n\n\t\tactiveProgress.set(id, {\n\t\t\ttitle: title || \"\",\n\t\t\tmessage: message || \"\",\n\t\t\tpercentage: progress,\n\t\t});\n\n\t\tensureStatusBar();\n\t\tupdateStatusBarDisplay();\n\t\treturn id;\n\t}\n\n\t// For non-progress messages (errors, warnings, etc.)\n\tconst bar = ensureStatusBar();\n\n\tconst $title = bar.querySelector(\".lsp-status-title\");\n\tconst $message = bar.querySelector(\".lsp-status-message\");\n\tconst $progressText = bar.querySelector(\".lsp-status-progress-text\");\n\tconst $progressContainer = bar.querySelector(\".lsp-status-progress\");\n\tconst $icon = bar.querySelector(\".lsp-status-icon\");\n\n\tif ($title) $title.textContent = title || \"\";\n\tif ($message) $message.textContent = message;\n\n\tconst hasProgress = showProgress && typeof progress === \"number\";\n\tif (hasProgress && $progressText && $progressContainer) {\n\t\t$progressText.textContent = `${Math.round(progress)}%`;\n\t\t$progressContainer.style.display = \"\";\n\t} else if ($progressContainer) {\n\t\t$progressContainer.style.display = \"none\";\n\t}\n\n\tif ($icon) {\n\t\t$icon.className = `lsp-status-icon icon ${icon}`;\n\t}\n\n\tbar.className = `lsp-status ${type}`;\n\tbar.classList.remove(\"hiding\");\n\n\t// Auto-hide after duration unless duration is false\n\tif (duration !== false) {\n\t\tconst ms = duration || 5000;\n\t\thideTimeout = window.setTimeout(() => {\n\t\t\t// Only hide if no progress is active\n\t\t\tif (activeProgress.size === 0) {\n\t\t\t\thideStatusBar();\n\t\t\t}\n\t\t}, ms);\n\t}\n\n\treturn id;\n}\n\n/**\n * Hide a specific progress item by ID\n * @param {string} id - The progress ID to hide\n */\nexport function hideStatus(id) {\n\tif (activeProgress.has(id)) {\n\t\tactiveProgress.delete(id);\n\n\t\tif (activeProgress.size === 0) {\n\t\t\t// All progress complete - hide after a brief delay\n\t\t\thideTimeout = window.setTimeout(() => {\n\t\t\t\thideStatusBar();\n\t\t\t}, 500);\n\t\t} else {\n\t\t\tupdateStatusBarDisplay();\n\t\t}\n\t}\n}\n\n/**\n * Hide the LSP status bar (legacy support - hides all)\n */\nexport function hideLspStatus() {\n\tactiveProgress.clear();\n\thideStatusBar();\n}\n\n/**\n * Update a progress item\n * @param {Partial<LspStatusOptions> & { id?: string }} options - Options to update\n * @returns {string | null} The status ID\n */\nexport function updateLspStatus(options) {\n\tconst { id, message, progress } = options;\n\n\tif (!id || !activeProgress.has(id)) {\n\t\treturn null;\n\t}\n\n\tconst item = activeProgress.get(id);\n\tif (item) {\n\t\tif (message !== undefined) item.message = message;\n\t\tif (progress !== undefined) item.percentage = progress;\n\t\tactiveProgress.set(id, item);\n\t\tupdateStatusBarDisplay();\n\t}\n\n\treturn id;\n}\n\n/**\n * Check if status bar is currently visible\n * @returns {boolean}\n */\nexport function isLspStatusVisible() {\n\treturn $statusBar !== null && document.body.contains($statusBar);\n}\n\n/**\n * Get count of active progress items\n * @returns {number}\n */\nexport function getActiveStatusCount() {\n\treturn activeProgress.size;\n}\n\n/**\n * Check if a specific progress item exists\n * @param {string} id - The progress ID to check\n * @returns {boolean}\n */\nexport function hasStatus(id) {\n\treturn activeProgress.has(id);\n}\n\nexport default {\n\tshow: showLspStatus,\n\thide: hideLspStatus,\n\thideById: hideStatus,\n\tupdate: updateLspStatus,\n\tisVisible: isLspStatusVisible,\n\tgetActiveCount: getActiveStatusCount,\n\thas: hasStatus,\n};\n"
  },
  {
    "path": "src/components/lspStatusBar/style.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n#lsp-status-bar {\n    position: fixed;\n    bottom: 50px;\n    left: 0;\n    right: 0;\n    margin: 0 auto;\n    max-width: 95vw;\n    width: fit-content;\n    min-width: 200px;\n    padding: 8px 12px;\n    padding-right: 36px;\n    background-color: var(--secondary-color);\n    color: var(--secondary-text-color);\n    border-radius: 6px;\n    box-shadow: 0 2px 12px var(--box-shadow-color);\n    border: 1px solid var(--border-color);\n    z-index: 97;\n    animation: lspStatusSlideUp 0.3s ease-out;\n    transition: all 0.3s ease;\n    box-sizing: border-box;\n\n    &.hiding {\n        animation: lspStatusSlideDown 0.3s ease-out forwards;\n    }\n\n    .lsp-status-content {\n        display: flex;\n        align-items: center;\n        gap: 10px;\n    }\n\n    .lsp-status-icon {\n        width: 20px;\n        height: 20px;\n        font-size: 1rem;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        flex-shrink: 0;\n\n        &.autorenew {\n            animation: lspSpinSlow 2s linear infinite;\n        }\n    }\n\n    .lsp-status-text {\n        display: flex;\n        flex-direction: column;\n        gap: 2px;\n        min-width: 0;\n        flex: 1;\n    }\n\n    .lsp-status-title {\n        font-size: 0.75rem;\n        font-weight: 600;\n        color: var(--primary-text-color);\n        text-transform: uppercase;\n        letter-spacing: 0.5px;\n        opacity: 0.8;\n    }\n\n    .lsp-status-message {\n        font-size: 0.85rem;\n        font-weight: 400;\n        color: var(--secondary-text-color);\n        line-height: 1.3;\n        white-space: nowrap;\n        overflow: hidden;\n        text-overflow: ellipsis;\n        max-width: 60vw;\n    }\n\n    .lsp-status-progress {\n        display: flex;\n        align-items: center;\n        gap: 8px;\n        margin-left: auto;\n        flex-shrink: 0;\n    }\n\n    .lsp-status-progress-text {\n        font-size: 0.8rem;\n        font-weight: 500;\n        color: var(--active-color);\n        min-width: 36px;\n        text-align: right;\n    }\n\n    .lsp-status-close {\n        position: absolute;\n        top: 50%;\n        right: 6px;\n        transform: translateY(-50%);\n        width: 24px;\n        height: 24px;\n        font-size: 0.9rem;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        background: transparent;\n        border: none;\n        color: var(--secondary-text-color);\n        cursor: pointer;\n        border-radius: 4px;\n        transition: all 0.2s ease;\n        padding: 0;\n\n        &:hover,\n        &:active {\n            background-color: rgba(0, 0, 0, 0.1);\n            color: var(--primary-text-color);\n        }\n    }\n\n    // Status type colors\n    &.info {\n        .lsp-status-icon {\n            color: var(--active-color);\n        }\n    }\n\n    &.success {\n        .lsp-status-icon {\n            color: #48c158;\n        }\n\n        .lsp-status-progress-text {\n            color: #48c158;\n        }\n    }\n\n    &.warning {\n        .lsp-status-icon {\n            color: #f59e0b;\n        }\n    }\n\n    &.error {\n        .lsp-status-icon {\n            color: var(--error-text-color);\n        }\n    }\n}\n\n// Adjust position when quicktools has different heights\nwc-page[footer-height=\"1\"] #lsp-status-bar {\n    bottom: 50px;\n}\n\nwc-page[footer-height=\"2\"] #lsp-status-bar {\n    bottom: 90px;\n}\n\nwc-page[footer-height=\"3\"] #lsp-status-bar {\n    bottom: 130px;\n}\n\n// When quicktools is hidden\nwc-page:not([footer-height]) #lsp-status-bar {\n    bottom: 10px;\n}\n\n@keyframes lspStatusSlideUp {\n    from {\n        transform: translateY(100%);\n        opacity: 0;\n    }\n\n    to {\n        transform: translateY(0);\n        opacity: 1;\n    }\n}\n\n@keyframes lspStatusSlideDown {\n    from {\n        transform: translateY(0);\n        opacity: 1;\n    }\n\n    to {\n        transform: translateY(100%);\n        opacity: 0;\n    }\n}\n\n@keyframes lspSpinSlow {\n    from {\n        transform: rotate(0deg);\n    }\n\n    to {\n        transform: rotate(360deg);\n    }\n}"
  },
  {
    "path": "src/components/page.js",
    "content": "import WCPage from \"./WebComponents/wcPage\";\n\n/**\n *\n * @param {string} title\n * @param {object} options\n * @param {HTMLElement} [options.lead] type of page\n * @param {HTMLElement} [options.tail] type of page\n * @returns {WCPage}\n */\nfunction Page(title, options = {}) {\n\tlet page = <wc-page />;\n\tpage.append = page.appendBody;\n\tpage.initializeIfNotAlreadyInitialized();\n\tpage.settitle(title);\n\n\tif (options.tail) {\n\t\tpage.header.append(options.tail);\n\t}\n\tif (options.lead) {\n\t\tpage.lead = options.lead;\n\t}\n\n\treturn page;\n}\n\nexport default Page;\n"
  },
  {
    "path": "src/components/palette/index.js",
    "content": "import \"./style.scss\";\nimport inputhints from \"components/inputhints\";\nimport keyboardHandler from \"handlers/keyboard\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\n/**\n * @typedef {import('./inputhints').HintCallback} HintCallback\n * @typedef {import('./inputhints').HintModification} HintModification\n */\n\n/*\nBenchmark to show keyboard\n\nWhen not using keyboardHideStart event;\n=============================================\nTime taken to remove palette: 104\nindex.js:177 Time taken to show keyboard: 198\nindex.js:178 Total time taken: 302\n\nWhen using keyboardHideStart event;\n=============================================\nindex.js:150 Time taken to remove palette: 0\nindex.js:177 Time taken to show keyboard: 187\nindex.js:178 Total time taken: 188\n\nWhen not using keyboardHideStart event;\n=============================================\nindex.js:150 Time taken to remove palette: 105\nindex.js:177 Time taken to show keyboard: 203\nindex.js:178 Total time taken: 310\n\nWhen using keyboardHideStart event;\n=============================================\nindex.js:150 Time taken to remove palette: 0\nindex.js:177 Time taken to show keyboard: 176\nindex.js:178 Total time taken: 176\n\nThis shows that using keyboardHideStart event is faster than not using it.\n*/\n\n/**\n * Opens a palette with input and hints\n * @param {(hints:HintModification)=>string[]} getList Callback to get list of hints\n * @param {()=>string} onsSelectCb Callback to call when a hint is selected\n * @param {string} placeholder Placeholder for input\n * @param {function} onremove Callback to call when palette is removed\n * @returns {void}\n */\n// Track active palette for chaining\nlet activePalette = null;\n\nexport default function palette(getList, onsSelectCb, placeholder, onremove) {\n\t// Store previous palette if exists\n\tconst previousPalette = activePalette;\n\tconst isChained = !!previousPalette;\n\t/**@type {HTMLInputElement} */\n\tconst $input = (\n\t\t<input\n\t\t\tonkeydown={onkeydown}\n\t\t\ttype=\"search\"\n\t\t\tplaceholder={placeholder}\n\t\t\tenterKeyHint=\"go\"\n\t\t/>\n\t);\n\t/**@type {HTMLElement} */\n\tconst $mask = <div className=\"mask\" onclick={remove} />;\n\t/**@type {HTMLDivElement} */\n\tconst $palette = <div id=\"palette\">{$input}</div>;\n\n\t// Create a palette with input and hints\n\tinputhints($input, generateHints, onSelect);\n\n\t// Only set the darkened theme when this is not a chained palette\n\tif (!isChained) {\n\t\t// Removes the darkened color from status bar and navigation bar\n\t\trestoreTheme(true);\n\t}\n\n\t// Remove palette when input is blurred\n\t$input.addEventListener(\"blur\", remove);\n\t// Don't wait for input to blur when keyboard hides, remove is\n\t// as soon as keyboard starts to hide\n\tkeyboardHandler.on(\"keyboardHideStart\", remove);\n\n\t// Add to DOM\n\tapp.append($palette, $mask);\n\n\t// If we're in a chained palette, ensure we don't lose focus\n\tif (isChained) {\n\t\t// Don't let any blur events from previous palette affect this one\n\t\tsetTimeout(() => {\n\t\t\t$input.focus();\n\t\t}, 0);\n\t}\n\n\t// Focus input to show options\n\t$input.focus();\n\n\t// Trigger input event to show hints immediately\n\t$input.dispatchEvent(new Event(\"input\"));\n\n\t// Add to action stack to remove on back button\n\tactionStack.push({\n\t\tid: \"palette\",\n\t\taction: remove,\n\t});\n\t// Store this palette as the active one for chaining\n\tactivePalette = { remove };\n\n\t/**\n\t * On select callback for inputhints\n\t * @param {string} value\n\t */\n\tfunction onSelect(value) {\n\t\tconst currentPalette = { remove };\n\t\tactivePalette = currentPalette;\n\n\t\tonsSelectCb(value);\n\n\t\tif (activePalette === currentPalette) {\n\t\t\tremove();\n\t\t}\n\t}\n\n\t/**\n\t * Keydown event handler for input\n\t * @param {KeyboardEvent} e\n\t */\n\tfunction onkeydown(e) {\n\t\tif (e.key !== \"Escape\") return;\n\t\tremove();\n\t}\n\n\t/**\n\t * Generates hint for inputhints\n\t * @param {HintCallback} setHints Set hints callback\n\t * @param {HintModification} hintModification Hint modification object\n\t */\n\tasync function generateHints(setHints, hintModification) {\n\t\tconst list = getList(hintModification);\n\t\tconst data = list instanceof Promise ? await list : list;\n\t\tsetHints(data);\n\t}\n\n\t/**\n\t * Removes the palette\n\t */\n\tfunction remove() {\n\t\tactionStack.remove(\"palette\");\n\t\tkeyboardHandler.off(\"keyboardHideStart\", remove);\n\t\t$input.removeEventListener(\"blur\", remove);\n\n\t\t$palette.remove();\n\t\t$mask.remove();\n\n\t\t// Restore previous palette if chained\n\t\tif (isChained && previousPalette) {\n\t\t\tactivePalette = previousPalette;\n\t\t} else {\n\t\t\tactivePalette = null;\n\t\t\trestoreTheme();\n\t\t}\n\n\t\tif (typeof onremove === \"function\") {\n\t\t\tonremove();\n\t\t\treturn;\n\t\t}\n\n\t\t// If not chained or last in chain, focus the editor\n\t\tif (!isChained) {\n\t\t\tconst { activeFile, editor } = editorManager;\n\t\t\tif (activeFile.wasFocused) {\n\t\t\t\teditor.focus();\n\t\t\t}\n\t\t}\n\n\t\tremove = () => {\n\t\t\twindow.log(\"warn\", \"Palette already removed.\");\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "src/components/palette/style.scss",
    "content": "#palette {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  margin: auto;\n  height: 40px;\n  width: 90vw;\n  max-width: 600px;\n  z-index: 99;\n  display: flex;\n  align-items: center;\n  background-color: inherit;\n  box-shadow: 0 0 4px black;\n  z-index: 999;\n\n  &~#hints {\n    width: 90vw;\n    box-sizing: border-box;\n    z-index: 999;\n  }\n\n  &~.mask {\n    opacity: 0.4;\n    z-index: 998;\n    // pointer-events: none;\n  }\n\n  input {\n    width: 100%;\n    border: none;\n    color: var(--primary-text-color);\n  }\n}"
  },
  {
    "path": "src/components/quickTools/footer.js",
    "content": "/**\n * @typedef {import('html-tag-js/ref')} Ref\n */\n\nimport settings from \"lib/settings\";\nimport items, { ref } from \"./items\";\n\n/**\n * Create a row with common buttons\n * @param {object} param0 Attributes\n * @param {number} [param0.row] Row number\n */\nexport const Row = ({ row }) => {\n\tconst startIndex =\n\t\t(row - 1) * settings.QUICKTOOLS_GROUP_CAPACITY * settings.QUICKTOOLS_GROUPS;\n\treturn (\n\t\t<div id={`row${row}`} className=\"button-container\">\n\t\t\t{(() => {\n\t\t\t\tconst sections = [];\n\t\t\t\tfor (let i = 0; i < settings.QUICKTOOLS_GROUPS; ++i) {\n\t\t\t\t\tconst section = [];\n\t\t\t\t\tfor (let j = 0; j < settings.QUICKTOOLS_GROUP_CAPACITY; ++j) {\n\t\t\t\t\t\tconst index =\n\t\t\t\t\t\t\tstartIndex + (i * settings.QUICKTOOLS_GROUP_CAPACITY + j);\n\t\t\t\t\t\tconst itemIndex = settings.value.quicktoolsItems[index]; // saved item index\n\t\t\t\t\t\tconst item = items[itemIndex]; // item object\n\t\t\t\t\t\tsection.push(<RowItem {...item} index={index} />);\n\t\t\t\t\t}\n\t\t\t\t\tsections.push(<div className=\"section\">{section}</div>);\n\t\t\t\t}\n\t\t\t\treturn sections;\n\t\t\t})()}\n\t\t</div>\n\t);\n};\n\n/**\n * Create a search row with search input and buttons\n * @returns {Element}\n */\nexport const SearchRow1 = ({ inputRef }) => (\n\t<div className=\"button-container\" id=\"search_row1\">\n\t\t<input ref={inputRef} type=\"search\" placeholder={strings.search} />\n\t\t<RowItem icon=\"arrow_back\" action=\"search-prev\" />\n\t\t<RowItem icon=\"arrow_forward\" action=\"search-next\" />\n\t\t<RowItem icon=\"settings\" action=\"search-settings\" />\n\t</div>\n);\n\n/**\n * Create a search row with replace input and buttons\n * @returns {Element}\n */\nexport const SearchRow2 = ({ inputRef, posRef, totalRef }) => (\n\t<div className=\"button-container\" id=\"search_row2\">\n\t\t<input ref={inputRef} type=\"text\" placeholder={strings.replace} />\n\t\t<RowItem icon=\"replace\" action=\"search-replace\" />\n\t\t<RowItem icon=\"replace_all\" action=\"search-replace-all\" />\n\t\t<div className=\"search-status\">\n\t\t\t<span ref={posRef}>0</span>\n\t\t\t<span>of</span>\n\t\t\t<span ref={totalRef}>0</span>\n\t\t</div>\n\t</div>\n);\n\n/**@type {HTMLElement} */\nexport const $footer = <footer id=\"quick-tools\" tabIndex={-1}></footer>;\n\n/**@type {HTMLElement} */\nexport const $toggler = (\n\t<span\n\t\tclassName=\"floating icon keyboard_arrow_up\"\n\t\tid=\"quicktools-toggler\"\n\t></span>\n);\n\n/**@type {HTMLTextAreaElement} */\nexport const $input = (\n\t<textarea\n\t\tautocapitalize=\"none\"\n\t\tstyle={{\n\t\t\topacity: 0,\n\t\t\theight: 0,\n\t\t\twidth: 0,\n\t\t\tpointerEvent: \"none\",\n\t\t\tpointerEvents: \"none\",\n\t\t\tposition: \"fixed\",\n\t\t\ttop: 0,\n\t\t\tleft: 0,\n\t\t}}\n\t></textarea>\n);\n\n/**\n *\n * @param {RowItem} param0 Attributes\n * @param {string} param0.id Button id\n * @param {string} param0.icon Icon name\n * @param {string} param0.letters Letters to show on button\n * @param {'insert'|'command'|'key'|'custom'} param0.action Action type\n * @param {string|Function} param0.value Value of button\n * @param {Ref} param0.ref Reference to button\n * @param {boolean} param0.repeat Whether to repeat the action or not\n * @returns {HTMLButtonElement}\n */\nexport function RowItem({ id, icon, letters, action, value, ref, repeat }) {\n\tconst $item = (\n\t\t<button\n\t\t\tref={ref}\n\t\t\tclassName={`icon ${icon}`}\n\t\t\tdata-id={id}\n\t\t\tdata-letters={letters}\n\t\t\tdata-action={action}\n\t\t\tdata-repeat={repeat}\n\t\t></button>\n\t);\n\n\tif (typeof value === \"function\") {\n\t\t$item.value = value;\n\t} else if (value !== undefined) {\n\t\t$item.dataset.value = value;\n\t}\n\n\treturn $item;\n}\n\n/**\n * Create a list of RowItem components\n * @param {object} param0 Attributes\n * @param {Array<RowItem>} param0.extras Extra buttons\n * @returns {Array<Element>}\n */\nfunction Extras({ extras }) {\n\tconst div = <div className=\"section\"></div>;\n\tif (Array.isArray(extras)) {\n\t\textras.forEach((i) => {\n\t\t\tif (i instanceof HTMLElement) {\n\t\t\t\tdiv.appendChild(i);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdiv.append(<RowItem {...i} />);\n\t\t});\n\t}\n\treturn div;\n}\n\n/**\n * @typedef {object} RowItem\n * @property {string} icon\n * @property {string} letters\n * @property {'insert'|'command'|'key'|'custom'} action\n * @property {string|Function} value\n * @property {Ref} ref\n */\n"
  },
  {
    "path": "src/components/quickTools/index.js",
    "content": "import \"./style.scss\";\nimport Ref from \"html-tag-js/ref\";\nimport settings from \"lib/settings\";\nimport {\n\t$footer,\n\t$input,\n\t$toggler,\n\tRow,\n\tSearchRow1,\n\tSearchRow2,\n} from \"./footer\";\n\n/**@type {HTMLElement} */\nlet $row1;\n/**@type {HTMLElement} */\nlet $row2;\n/**@type {HTMLElement} */\nlet $searchRow1;\n/**@type {HTMLElement} */\nlet $searchRow2;\n\nconst $searchInput = Ref();\nconst $replaceInput = Ref();\nconst $searchPos = Ref();\nconst $searchTotal = Ref();\n\nexport default {\n\tget $footer() {\n\t\treturn $footer;\n\t},\n\tget $row1() {\n\t\tif ($row1) return $row1;\n\t\t$row1 = <Row row={1} />;\n\n\t\tsettings.on(\"update:quicktoolsItems:after\", () => {\n\t\t\t$row1 = <Row row={1} />;\n\t\t});\n\n\t\treturn $row1;\n\t},\n\tget $row2() {\n\t\tif ($row2) return $row2;\n\t\t$row2 = <Row row={2} />;\n\n\t\tsettings.on(\"update:quicktoolsItems:after\", () => {\n\t\t\t$row2 = <Row row={2} />;\n\t\t});\n\n\t\treturn $row2;\n\t},\n\tget $searchRow1() {\n\t\tif ($searchRow1) return $searchRow1;\n\t\t$searchRow1 = <SearchRow1 inputRef={$searchInput} />;\n\t\treturn $searchRow1;\n\t},\n\tget $searchRow2() {\n\t\tif ($searchRow2) return $searchRow2;\n\t\t$searchRow2 = (\n\t\t\t<SearchRow2\n\t\t\t\tinputRef={$replaceInput}\n\t\t\t\tposRef={$searchPos}\n\t\t\t\ttotalRef={$searchTotal}\n\t\t\t/>\n\t\t);\n\t\treturn $searchRow2;\n\t},\n\tget $input() {\n\t\treturn $input;\n\t},\n\tget $toggler() {\n\t\treturn $toggler;\n\t},\n\tget $searchInput() {\n\t\treturn $searchInput;\n\t},\n\tget $replaceInput() {\n\t\treturn $replaceInput;\n\t},\n\tget $searchPos() {\n\t\treturn $searchPos;\n\t},\n\tget $searchTotal() {\n\t\treturn $searchTotal;\n\t},\n};\n"
  },
  {
    "path": "src/components/quickTools/items.js",
    "content": "export default [\n\titem(\"ctrl-key\", \"letters\", \"ctrl\", undefined, \"ctrl\", false),\n\titem(\"tab-key\", \"keyboard_tab\", \"key\", 9),\n\titem(\"shift-key\", \"letters\", \"shift\", undefined, \"shft\", false),\n\titem(\"undo\", \"undo\", \"command\", \"undo\"),\n\titem(\"redo\", \"redo\", \"command\", \"redo\"),\n\titem(\"search\", \"search\", \"search\"),\n\titem(\"save\", \"save\", \"command\", \"saveFile\", undefined, false),\n\titem(\"esc-key\", \"letters\", \"key\", 27, \"esc\"),\n\titem(\"curlybracket\", \"letters\", \"insert\", \"{\", \"{\"),\n\titem(\"curlybracket\", \"letters\", \"insert\", \"}\", \"}\"),\n\titem(\"squarebracket\", \"letters\", \"insert\", \"[\", \"[\"),\n\titem(\"squarebracket\", \"letters\", \"insert\", \"]\", \"]\"),\n\titem(\"parentheses\", \"letters\", \"insert\", \"(\", \"(\"),\n\titem(\"parentheses\", \"letters\", \"insert\", \")\", \")\"),\n\titem(\"anglebracket\", \"letters\", \"insert\", \"<\", \"<\"),\n\titem(\"anglebracket\", \"letters\", \"insert\", \">\", \">\"),\n\titem(\"left-arrow-key\", \"keyboard_arrow_left\", \"key\", 37, undefined, true),\n\titem(\"right-arrow-key\", \"keyboard_arrow_right\", \"key\", 39, undefined, true),\n\titem(\"up-arrow-key\", \"keyboard_arrow_up\", \"key\", 38, undefined, true),\n\titem(\"down-arrow-key\", \"keyboard_arrow_down\", \"key\", 40, undefined, true),\n\titem(\"moveline-up\", \"moveline-up\", \"command\", \"movelinesup\"),\n\titem(\"moveline-down\", \"moveline-down\", \"command\", \"movelinesdown\"),\n\titem(\"copyline-up\", \"copyline-up\", \"command\", \"copylinesup\"),\n\titem(\"copyline-down\", \"copyline-down\", \"command\", \"copylinesdown\"),\n\titem(\"semicolon\", \"letters\", \"insert\", \";\", \";\"),\n\titem(\"quotation\", \"letters\", \"insert\", \"'\", \"'\"),\n\titem(\"quotation\", \"letters\", \"insert\", '\"', '\"'),\n\titem(\"and\", \"letters\", \"insert\", \"&\", \"&\"),\n\titem(\"bar\", \"letters\", \"insert\", \"|\", \"|\"),\n\titem(\"equal\", \"letters\", \"insert\", \"=\", \"=\"),\n\titem(\"slash\", \"letters\", \"insert\", \"/\", \"/\"),\n\titem(\"exclamation\", \"letters\", \"insert\", \"!\", \"!\"),\n\titem(\"command-palette\", \"keyboard_control\", \"command\", \"openCommandPalette\"),\n\titem(\"alt-key\", \"letters\", \"alt\", undefined, \"alt\", false),\n\titem(\"meta-key\", \"letters\", \"meta\", undefined, \"meta\", false),\n\titem(\"home-key\", \"letters\", \"key\", 36, \"home\", true),\n\titem(\"end-key\", \"letters\", \"key\", 35, \"end\", true),\n\titem(\"pageup-key\", \"letters\", \"key\", 33, \"pgup\", true),\n\titem(\"pagedown-key\", \"letters\", \"key\", 34, \"pgdn\", true),\n\titem(\"delete-key\", \"letters\", \"key\", 46, \"del\", true),\n\titem(\"tilde\", \"letters\", \"insert\", \"~\", \"~\"),\n\titem(\"backtick\", \"letters\", \"insert\", \"`\", \"`\"),\n\titem(\"hash\", \"letters\", \"insert\", \"#\", \"#\"),\n\titem(\"dollar\", \"letters\", \"insert\", \"$\", \"$\"),\n\titem(\"modulo\", \"letters\", \"insert\", \"%\", \"%\"),\n\titem(\"caret\", \"letters\", \"insert\", \"^\", \"^\"),\n\titem(\"hyphen\", \"letters\", \"insert\", \"-\", \"-\"),\n];\n\n/**\n * Get description of a button\n * @param {string} id button id\n * @returns\n */\nexport function description(id) {\n\treturn strings[`quicktools:${id}`];\n}\n\n/**\n *\n * @param {string} icon\n * @param {string} action\n * @param {string|number} value\n * @param {string} letters\n * @param {boolean} repeat\n * @returns\n */\nfunction item(id, icon, action, value, letters, repeat) {\n\treturn {\n\t\tid,\n\t\ticon,\n\t\taction,\n\t\tvalue,\n\t\tletters,\n\t\trepeat,\n\t};\n}\n"
  },
  {
    "path": "src/components/quickTools/style.scss",
    "content": "@use '../../styles/mixins.scss';\n\n#quick-tools {\n  .icon.click {\n    @include mixins.active-icon;\n    transition: all 0.3s ease-in-out;\n    transform: scale(1.2);\n  }\n\n  &[data-alt=\"true\"] {\n    [data-id=\"alt-key\"] {\n      @include mixins.active-icon;\n    }\n  }\n\n  &[data-shift=\"true\"] {\n    [data-id=\"shift-key\"] {\n      @include mixins.active-icon;\n    }\n  }\n\n  &[data-ctrl=\"true\"] {\n    [data-id=\"ctrl-key\"] {\n      @include mixins.active-icon;\n    }\n  }\n\n  &[data-meta=\"true\"] {\n    [data-id=\"meta-key\"] {\n      @include mixins.active-icon;\n    }\n  }\n\n  &[data-unsaved=\"true\"] {\n    [data-id=\"save\"] {\n      @include mixins.icon-badge;\n    }\n  }\n}"
  },
  {
    "path": "src/components/referencesPanel/index.js",
    "content": "import \"./styles.scss\";\nimport actionStack from \"lib/actionStack\";\nimport { openReferencesTab } from \"./referencesTab\";\nimport {\n\tbuildFlatList,\n\tclearHighlightCache,\n\tcreateReferenceItem,\n\tgetReferencesStats,\n\tnavigateToReference,\n\tsanitize,\n} from \"./utils\";\n\nlet currentPanel = null;\n\nfunction createReferencesPanel() {\n\tconst state = {\n\t\tvisible: false,\n\t\texpanded: false,\n\t\tloading: true,\n\t\tsymbolName: \"\",\n\t\treferences: [],\n\t\tcollapsedFiles: new Set(),\n\t\tflatItems: [],\n\t};\n\n\tconst $mask = <span className=\"references-panel-mask\" />;\n\tconst $panel = <div className=\"references-panel\" />;\n\tconst $dragHandle = <div className=\"drag-handle\" />;\n\tconst $title = <div className=\"header-title\" />;\n\tconst $subtitle = <span className=\"header-subtitle\" />;\n\tconst $content = <div className=\"panel-content\" />;\n\tconst $refList = <div className=\"references-list\" />;\n\n\tconst $openTabBtn = (\n\t\t<button\n\t\t\tclassName=\"action-btn open-tab-btn\"\n\t\t\ttitle=\"Open in Tab\"\n\t\t\tonclick={openInTab}\n\t\t>\n\t\t\t<span className=\"icon fullscreen\" />\n\t\t</button>\n\t);\n\n\tconst $closeBtn = (\n\t\t<button className=\"action-btn close-btn\" onclick={hide}>\n\t\t\t<span className=\"icon clearclose\" />\n\t\t</button>\n\t);\n\n\tconst $header = (\n\t\t<div className=\"panel-header\">\n\t\t\t<div className=\"header-content\">\n\t\t\t\t{$title}\n\t\t\t\t{$subtitle}\n\t\t\t</div>\n\t\t\t<div className=\"header-actions\">\n\t\t\t\t{$openTabBtn}\n\t\t\t\t{$closeBtn}\n\t\t\t</div>\n\t\t</div>\n\t);\n\n\t$panel.append($dragHandle, $header, $content);\n\t$mask.onclick = hide;\n\n\tlet startY = 0;\n\tlet currentY = 0;\n\tlet isDragging = false;\n\n\t$dragHandle.ontouchstart = onDragStart;\n\t$dragHandle.onmousedown = onDragStart;\n\n\tfunction onDragStart(e) {\n\t\tisDragging = true;\n\t\tstartY = e.touches ? e.touches[0].clientY : e.clientY;\n\t\tcurrentY = startY;\n\t\t$panel.style.transition = \"none\";\n\n\t\tdocument.addEventListener(\"touchmove\", onDragMove, { passive: false });\n\t\tdocument.addEventListener(\"mousemove\", onDragMove);\n\t\tdocument.addEventListener(\"touchend\", onDragEnd);\n\t\tdocument.addEventListener(\"mouseup\", onDragEnd);\n\t}\n\n\tfunction onDragMove(e) {\n\t\tif (!isDragging) return;\n\t\te.preventDefault();\n\t\tcurrentY = e.touches ? e.touches[0].clientY : e.clientY;\n\t\tconst deltaY = currentY - startY;\n\n\t\tif (deltaY > 0) {\n\t\t\t$panel.style.transform = `translateY(${deltaY}px)`;\n\t\t} else if (!state.expanded) {\n\t\t\tconst expansion = Math.min(Math.abs(deltaY), 100);\n\t\t\t$panel.style.maxHeight = `${60 + (expansion / 100) * 25}vh`;\n\t\t}\n\t}\n\n\tfunction onDragEnd() {\n\t\tisDragging = false;\n\t\tdocument.removeEventListener(\"touchmove\", onDragMove);\n\t\tdocument.removeEventListener(\"mousemove\", onDragMove);\n\t\tdocument.removeEventListener(\"touchend\", onDragEnd);\n\t\tdocument.removeEventListener(\"mouseup\", onDragEnd);\n\n\t\t$panel.style.transition = \"\";\n\t\tconst deltaY = currentY - startY;\n\n\t\tif (deltaY > 100) {\n\t\t\thide();\n\t\t} else if (deltaY < -50 && !state.expanded) {\n\t\t\tstate.expanded = true;\n\t\t\t$panel.classList.add(\"expanded\");\n\t\t\t$panel.style.transform = \"\";\n\t\t\t$panel.style.maxHeight = \"\";\n\t\t} else {\n\t\t\t$panel.style.transform = \"\";\n\t\t\t$panel.style.maxHeight = \"\";\n\t\t}\n\t}\n\n\tfunction setTitle(symbolName) {\n\t\t$title.innerHTML = \"\";\n\t\t$title.append(\n\t\t\t<span className=\"icon linkinsert_link\" />,\n\t\t\t<span>References to </span>,\n\t\t\t<span className=\"symbol-name\">{sanitize(symbolName)}</span>,\n\t\t);\n\t}\n\n\tfunction setSubtitle(text) {\n\t\t$subtitle.textContent = text;\n\t}\n\n\tfunction openInTab() {\n\t\tconst refs = state.references;\n\t\tconst sym = state.symbolName;\n\t\thide();\n\t\topenReferencesTab({\n\t\t\tsymbolName: sym,\n\t\t\treferences: refs,\n\t\t});\n\t}\n\n\tfunction toggleFile(uri) {\n\t\tif (state.collapsedFiles.has(uri)) {\n\t\t\tstate.collapsedFiles.delete(uri);\n\t\t} else {\n\t\t\tstate.collapsedFiles.add(uri);\n\t\t}\n\t\trenderReferencesList();\n\t}\n\n\tfunction renderLoading() {\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"loading-state\">\n\t\t\t\t<div className=\"loader\" />\n\t\t\t\t<span>Finding references...</span>\n\t\t\t</div>,\n\t\t);\n\t}\n\n\tfunction renderEmpty() {\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"empty-state\">\n\t\t\t\t<span className=\"icon search\" />\n\t\t\t\t<span>No references found</span>\n\t\t\t</div>,\n\t\t);\n\t}\n\n\tfunction renderReferencesList() {\n\t\t$refList.innerHTML = \"\";\n\n\t\tconst visibleItems = state.flatItems.filter((item) => {\n\t\t\tif (item.type === \"file-header\") return true;\n\t\t\treturn !state.collapsedFiles.has(item.uri);\n\t\t});\n\n\t\tconst fragment = document.createDocumentFragment();\n\n\t\tfor (const item of visibleItems) {\n\t\t\tconst $el = createReferenceItem(item, {\n\t\t\t\tcollapsedFiles: state.collapsedFiles,\n\t\t\t\tonToggleFile: toggleFile,\n\t\t\t\tonNavigate: (ref) => {\n\t\t\t\t\thide();\n\t\t\t\t\tnavigateToReference(ref);\n\t\t\t\t},\n\t\t\t});\n\t\t\tfragment.appendChild($el);\n\t\t}\n\n\t\t$refList.appendChild(fragment);\n\t\t$content.innerHTML = \"\";\n\t\t$content.appendChild($refList);\n\t}\n\n\tasync function renderReferences() {\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"loading-state\">\n\t\t\t\t<div className=\"loader\" />\n\t\t\t\t<span>Highlighting code...</span>\n\t\t\t</div>,\n\t\t);\n\n\t\tconst stats = getReferencesStats(state.references);\n\t\tsetSubtitle(stats.text);\n\n\t\tstate.flatItems = await buildFlatList(state.references, state.symbolName);\n\n\t\trenderReferencesList();\n\t}\n\n\tfunction show(options = {}) {\n\t\tif (currentPanel && currentPanel !== panelInstance) {\n\t\t\tcurrentPanel.hide();\n\t\t}\n\t\tcurrentPanel = panelInstance;\n\n\t\tstate.symbolName = options.symbolName || \"\";\n\t\tstate.references = [];\n\t\tstate.loading = true;\n\t\tstate.expanded = false;\n\t\tstate.collapsedFiles.clear();\n\t\tstate.flatItems = [];\n\n\t\tclearHighlightCache();\n\n\t\tsetTitle(state.symbolName);\n\t\tsetSubtitle(\"Searching...\");\n\t\trenderLoading();\n\n\t\tdocument.body.append($mask, $panel);\n\n\t\trequestAnimationFrame(() => {\n\t\t\t$mask.classList.add(\"visible\");\n\t\t\t$panel.classList.add(\"visible\");\n\t\t\t$panel.classList.remove(\"expanded\");\n\t\t});\n\n\t\tstate.visible = true;\n\n\t\tactionStack.push({\n\t\t\tid: \"references-panel\",\n\t\t\taction: hide,\n\t\t});\n\t}\n\n\tfunction hide() {\n\t\tif (!state.visible) return;\n\t\tstate.visible = false;\n\n\t\t$mask.classList.remove(\"visible\");\n\t\t$panel.classList.remove(\"visible\");\n\n\t\tactionStack.remove(\"references-panel\");\n\n\t\tsetTimeout(() => {\n\t\t\t$mask.remove();\n\t\t\t$panel.remove();\n\t\t}, 250);\n\n\t\tif (currentPanel === panelInstance) {\n\t\t\tcurrentPanel = null;\n\t\t}\n\t}\n\n\tfunction setReferences(references) {\n\t\tstate.loading = false;\n\t\tstate.references = references || [];\n\n\t\tif (state.references.length === 0) {\n\t\t\tsetSubtitle(\"No references found\");\n\t\t\trenderEmpty();\n\t\t} else {\n\t\t\trenderReferences();\n\t\t}\n\t}\n\n\tfunction setError(message) {\n\t\tstate.loading = false;\n\t\tsetSubtitle(\"Error\");\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"empty-state\">\n\t\t\t\t<span className=\"icon error_outline\" />\n\t\t\t\t<span>{sanitize(message)}</span>\n\t\t\t</div>,\n\t\t);\n\t}\n\n\tconst panelInstance = {\n\t\tshow,\n\t\thide,\n\t\tsetReferences,\n\t\tsetError,\n\t\tget visible() {\n\t\t\treturn state.visible;\n\t\t},\n\t};\n\n\treturn panelInstance;\n}\n\nlet panelSingleton = null;\n\nfunction getPanel() {\n\tif (!panelSingleton) {\n\t\tpanelSingleton = createReferencesPanel();\n\t}\n\treturn panelSingleton;\n}\n\nexport function showReferencesPanel(options) {\n\tconst panel = getPanel();\n\tpanel.show(options);\n\treturn panel;\n}\n\nexport function hideReferencesPanel() {\n\tconst panel = getPanel();\n\tpanel.hide();\n}\n\nexport { openReferencesTab };\n\nexport default {\n\tshow: showReferencesPanel,\n\thide: hideReferencesPanel,\n\tgetPanel,\n\topenReferencesTab,\n};\n"
  },
  {
    "path": "src/components/referencesPanel/referencesTab.js",
    "content": "import EditorFile from \"lib/editorFile\";\nimport {\n\tbuildFlatList,\n\tclearHighlightCache,\n\tcreateReferenceItem,\n\tgetReferencesStats,\n\tnavigateToReference,\n\tsanitize,\n} from \"./utils\";\n\nexport function createReferencesTab(options = {}) {\n\tconst {\n\t\tsymbolName = \"\",\n\t\treferences = [],\n\t\tflatItems: prebuiltItems = null,\n\t} = options;\n\tconst collapsedFiles = new Set();\n\tlet flatItems = prebuiltItems || [];\n\tlet isInitialized = false;\n\n\tconst $container = <div className=\"references-tab-container\" />;\n\tconst $listContainer = <div className=\"references-list-container\" />;\n\tconst $refList = <div className=\"references-list\" />;\n\n\tconst stats = getReferencesStats(references);\n\n\tconst $header = (\n\t\t<div className=\"references-tab-header\">\n\t\t\t<div className=\"header-info\">\n\t\t\t\t<span className=\"icon linkinsert_link\" />\n\t\t\t\t<span className=\"header-title\">\n\t\t\t\t\tReferences to <code>{sanitize(symbolName)}</code>\n\t\t\t\t</span>\n\t\t\t\t<span className=\"header-stats\">{stats.text}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n\n\tconst $loadingState = (\n\t\t<div className=\"loading-state\">\n\t\t\t<div className=\"loader\" />\n\t\t\t<span>Highlighting code...</span>\n\t\t</div>\n\t);\n\n\t$container.append($header, $listContainer);\n\n\tfunction getVisibleItems() {\n\t\treturn flatItems.filter((item) => {\n\t\t\tif (item.type === \"file-header\") return true;\n\t\t\treturn !collapsedFiles.has(item.uri);\n\t\t});\n\t}\n\n\tfunction toggleFile(uri) {\n\t\tif (collapsedFiles.has(uri)) {\n\t\t\tcollapsedFiles.delete(uri);\n\t\t} else {\n\t\t\tcollapsedFiles.add(uri);\n\t\t}\n\t\trenderList();\n\t}\n\n\tfunction renderList() {\n\t\t$refList.innerHTML = \"\";\n\n\t\tconst visibleItems = getVisibleItems();\n\t\tconst fragment = document.createDocumentFragment();\n\n\t\tfor (const item of visibleItems) {\n\t\t\tconst $el = createReferenceItem(item, {\n\t\t\t\tcollapsedFiles,\n\t\t\t\tonToggleFile: toggleFile,\n\t\t\t\tonNavigate: navigateToReference,\n\t\t\t});\n\t\t\tfragment.appendChild($el);\n\t\t}\n\n\t\t$refList.appendChild(fragment);\n\t}\n\n\tasync function init() {\n\t\tif (isInitialized) return;\n\t\tisInitialized = true;\n\n\t\tif (!prebuiltItems || prebuiltItems.length === 0) {\n\t\t\t$listContainer.appendChild($loadingState);\n\t\t\tflatItems = await buildFlatList(references, symbolName);\n\t\t\t$loadingState.remove();\n\t\t}\n\n\t\trenderList();\n\t\t$listContainer.appendChild($refList);\n\t}\n\n\tfunction destroy() {\n\t\t$refList.innerHTML = \"\";\n\t}\n\n\treturn {\n\t\tcontainer: $container,\n\t\tinit,\n\t\tdestroy,\n\t\tget symbolName() {\n\t\t\treturn symbolName;\n\t\t},\n\t\tget referenceCount() {\n\t\t\treturn references.length;\n\t\t},\n\t};\n}\n\nexport async function openReferencesTab(options = {}) {\n\tconst { symbolName = \"\", references = [] } = options;\n\n\tconst tabName = `Refs: ${symbolName}`;\n\tconst existingFile = editorManager.getFile(tabName, \"filename\");\n\tif (existingFile) {\n\t\texistingFile.makeActive();\n\t\treturn existingFile;\n\t}\n\n\tclearHighlightCache();\n\tconst flatItems = await buildFlatList(references, symbolName);\n\tconst stats = getReferencesStats(references);\n\n\tconst tabView = createReferencesTab({ symbolName, references, flatItems });\n\n\tconst file = new EditorFile(tabName, {\n\t\ttype: \"terminal\",\n\t\tcontent: tabView.container,\n\t\ttabIcon: \"icon linkinsert_link\",\n\t\trender: true,\n\t});\n\n\tfile.setCustomTitle(() => stats.text);\n\ttabView.init();\n\n\tfile.on(\"close\", () => {\n\t\ttabView.destroy();\n\t});\n\n\treturn file;\n}\n\nexport default {\n\tcreateReferencesTab,\n\topenReferencesTab,\n};\n"
  },
  {
    "path": "src/components/referencesPanel/styles.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n.references-panel-mask {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background-color: rgba(0, 0, 0, 0.4);\n  z-index: 100;\n  opacity: 0;\n  transition: opacity 200ms ease-out;\n  pointer-events: none;\n\n  &.visible {\n    opacity: 1;\n    pointer-events: auto;\n  }\n}\n\n.references-panel {\n  position: fixed;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 101;\n  background-color: var(--popup-background-color);\n  border-top-left-radius: 12px;\n  border-top-right-radius: 12px;\n  box-shadow: 0 -4px 20px var(--box-shadow-color);\n  transform: translateY(100%);\n  transition: transform 250ms ease-out, max-height 200ms ease-out;\n  display: flex;\n  flex-direction: column;\n  max-height: 60vh;\n  overflow: hidden;\n\n  &.visible {\n    transform: translateY(0);\n  }\n\n  &.expanded {\n    max-height: 85vh;\n  }\n\n  .drag-handle {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    padding: 8px 0 4px 0;\n    cursor: grab;\n    flex-shrink: 0;\n\n    &::after {\n      content: \"\";\n      width: 36px;\n      height: 4px;\n      background-color: var(--border-color);\n      border-radius: 2px;\n    }\n\n    &:active {\n      cursor: grabbing;\n    }\n  }\n\n  .panel-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 8px 12px 12px 16px;\n    border-bottom: 1px solid var(--border-color);\n    min-height: 44px;\n    flex-shrink: 0;\n\n    .header-content {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n      flex: 1;\n      min-width: 0;\n\n      .header-title {\n        display: flex;\n        align-items: center;\n        gap: 8px;\n        font-size: 1rem;\n        font-weight: 600;\n        color: var(--primary-text-color);\n\n        .icon {\n          font-size: 1.1em;\n          opacity: 0.7;\n        }\n\n        .symbol-name {\n          font-family: monospace;\n          background-color: var(--secondary-color);\n          padding: 2px 8px;\n          border-radius: 4px;\n          max-width: 160px;\n          overflow: hidden;\n          text-overflow: ellipsis;\n          white-space: nowrap;\n        }\n      }\n\n      .header-subtitle {\n        font-size: 0.85rem;\n        color: var(--secondary-text-color);\n        opacity: 0.8;\n      }\n    }\n\n    .header-actions {\n      display: flex;\n      gap: 4px;\n      flex-shrink: 0;\n    }\n\n    .action-btn {\n      width: 36px;\n      height: 36px;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      border: none;\n      background: transparent;\n      color: var(--secondary-text-color);\n      border-radius: 50%;\n      cursor: pointer;\n\n      &:active {\n        background-color: var(--active-icon-color);\n      }\n\n      .icon {\n        font-size: 1.2em;\n      }\n\n      &.open-tab-btn {\n        color: var(--active-color);\n      }\n    }\n  }\n\n  .panel-content {\n    flex: 1;\n    overflow-y: auto;\n    overflow-x: hidden;\n    -webkit-overflow-scrolling: touch;\n    overscroll-behavior: contain;\n\n    &::-webkit-scrollbar {\n      width: var(--scrollbar-width, 4px);\n    }\n\n    &::-webkit-scrollbar-track {\n      background: transparent;\n    }\n\n    &::-webkit-scrollbar-thumb {\n      background: var(--scrollbar-color, rgba(0, 0, 0, 0.333));\n      border-radius: calc(var(--scrollbar-width, 4px) / 2);\n    }\n  }\n\n  .loading-state {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 32px 16px;\n    color: var(--secondary-text-color);\n    gap: 12px;\n\n    .loader {\n      @include mixins.circular-loader(24px);\n    }\n  }\n\n  .empty-state {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    padding: 32px 16px;\n    color: var(--secondary-text-color);\n    text-align: center;\n\n    .icon {\n      font-size: 2rem;\n      margin-bottom: 8px;\n      opacity: 0.5;\n    }\n  }\n}\n\n.ref-file-header,\n.ref-item {\n  box-sizing: border-box;\n  display: flex;\n  align-items: center;\n  will-change: transform;\n}\n\n.ref-file-header {\n  gap: 8px;\n  padding: 0 16px;\n  cursor: pointer;\n  user-select: none;\n  background-color: color-mix(in srgb, var(--secondary-color) 50%, var(--primary-color));\n\n  &:active {\n    background-color: var(--active-icon-color);\n  }\n\n  .chevron {\n    font-size: 1rem;\n    color: var(--secondary-text-color);\n    transition: transform 150ms ease;\n    width: 18px;\n    text-align: center;\n    flex-shrink: 0;\n  }\n\n  &.collapsed .chevron {\n    transform: rotate(-90deg);\n  }\n\n  .file-icon {\n    font-size: 1rem;\n    flex-shrink: 0;\n  }\n\n  .file-name {\n    flex: 1;\n    font-size: 0.9rem;\n    font-weight: 500;\n    color: var(--primary-text-color);\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    min-width: 0;\n  }\n\n  .ref-count {\n    font-size: 0.75rem;\n    color: var(--secondary-text-color);\n    background-color: var(--popup-background-color);\n    padding: 2px 8px;\n    border-radius: 10px;\n    flex-shrink: 0;\n  }\n}\n\n.ref-item {\n  gap: 10px;\n  padding: 0 16px 0 36px;\n  cursor: pointer;\n\n  &:active {\n    background-color: var(--active-icon-color);\n  }\n\n  .line-number {\n    font-size: 0.75rem;\n    color: var(--secondary-text-color);\n    min-width: 32px;\n    text-align: right;\n    font-family: monospace;\n    flex-shrink: 0;\n    opacity: 0.7;\n  }\n\n  .ref-preview {\n    flex: 1;\n    font-size: 0.85rem;\n    font-family: monospace;\n    color: var(--primary-text-color);\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    line-height: 1.4;\n    min-width: 0;\n    direction: rtl;\n    text-align: left;\n\n    span {\n      direction: ltr;\n      unicode-bidi: bidi-override;\n    }\n\n    .symbol-match {\n      background-color: color-mix(in srgb, var(--active-color) 30%, transparent);\n      border-radius: 2px;\n      padding: 0 2px;\n    }\n  }\n}\n\n.references-tab-container {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  background-color: var(--primary-color);\n  color: var(--primary-text-color);\n\n  .references-tab-header {\n    padding: 16px;\n    border-bottom: 1px solid var(--border-color);\n    background-color: var(--secondary-color);\n    flex-shrink: 0;\n\n    .header-info {\n      display: flex;\n      align-items: center;\n      gap: 10px;\n      flex-wrap: wrap;\n\n      .icon {\n        font-size: 1.2em;\n        opacity: 0.7;\n      }\n\n      .header-title {\n        font-size: 1rem;\n        font-weight: 600;\n        color: var(--primary-text-color);\n\n        code {\n          font-family: monospace;\n          background-color: var(--popup-background-color);\n          padding: 2px 8px;\n          border-radius: 4px;\n          margin-left: 4px;\n        }\n      }\n\n      .header-stats {\n        font-size: 0.85rem;\n        color: var(--secondary-text-color);\n        margin-left: auto;\n      }\n    }\n  }\n\n  .references-list-container {\n    flex: 1;\n    overflow-y: auto;\n    overflow-x: hidden;\n    -webkit-overflow-scrolling: touch;\n    overscroll-behavior: contain;\n\n    &::-webkit-scrollbar {\n      width: var(--scrollbar-width, 4px);\n    }\n\n    &::-webkit-scrollbar-track {\n      background: transparent;\n    }\n\n    &::-webkit-scrollbar-thumb {\n      background: var(--scrollbar-color, rgba(0, 0, 0, 0.333));\n      border-radius: calc(var(--scrollbar-width, 4px) / 2);\n    }\n\n    .loading-state {\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      padding: 32px 16px;\n      color: var(--secondary-text-color);\n      gap: 12px;\n\n      .loader {\n        @include mixins.circular-loader(24px);\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/components/referencesPanel/utils.js",
    "content": "import { EditorView } from \"@codemirror/view\";\nimport Sidebar from \"components/sidebar\";\nimport DOMPurify from \"dompurify\";\nimport openFile from \"lib/openFile\";\nimport {\n\tclearHighlightCache,\n\thighlightLine,\n\tsanitize,\n} from \"utils/codeHighlight\";\nimport helpers from \"utils/helpers\";\n\nexport { clearHighlightCache, sanitize };\n\nexport function getFilename(uri) {\n\tif (!uri) return \"\";\n\ttry {\n\t\tconst decoded = decodeURIComponent(uri);\n\t\tconst parts = decoded.split(\"/\").filter(Boolean);\n\t\treturn parts.pop() || \"\";\n\t} catch {\n\t\tconst parts = uri.split(\"/\").filter(Boolean);\n\t\treturn parts.pop() || \"\";\n\t}\n}\n\nexport function escapeRegExp(string) {\n\treturn string.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function groupReferencesByFile(references) {\n\tconst grouped = {};\n\tfor (const ref of references) {\n\t\tif (!grouped[ref.uri]) {\n\t\t\tgrouped[ref.uri] = [];\n\t\t}\n\t\tgrouped[ref.uri].push(ref);\n\t}\n\treturn grouped;\n}\n\nexport async function buildFlatList(references, symbolName) {\n\tconst grouped = groupReferencesByFile(references);\n\n\tconst items = [];\n\tfor (const [uri, fileRefs] of Object.entries(grouped)) {\n\t\tfileRefs.sort((a, b) => a.range.start.line - b.range.start.line);\n\n\t\titems.push({\n\t\t\ttype: \"file-header\",\n\t\t\turi,\n\t\t\tfileName: getFilename(uri),\n\t\t\tcount: fileRefs.length,\n\t\t});\n\n\t\tfor (const ref of fileRefs) {\n\t\t\tconst highlightedText = await highlightLine(\n\t\t\t\tref.lineText || \"\",\n\t\t\t\turi,\n\t\t\t\tsymbolName,\n\t\t\t);\n\n\t\t\titems.push({\n\t\t\t\ttype: \"reference\",\n\t\t\t\turi,\n\t\t\t\tref,\n\t\t\t\tline: ref.range.start.line + 1,\n\t\t\t\tlineText: ref.lineText || \"\",\n\t\t\t\thighlightedText,\n\t\t\t\tsymbol: symbolName,\n\t\t\t});\n\t\t}\n\t}\n\treturn items;\n}\n\nexport function createReferenceItem(item, options = {}) {\n\tconst { collapsedFiles, onToggleFile, onNavigate } = options;\n\n\tif (item.type === \"file-header\") {\n\t\tconst isCollapsed = collapsedFiles?.has(item.uri);\n\t\tconst iconClass = helpers.getIconForFile(item.fileName);\n\n\t\tconst $el = (\n\t\t\t<div\n\t\t\t\tclassName={`ref-file-header ${isCollapsed ? \"collapsed\" : \"\"}`}\n\t\t\t\tonclick={() => onToggleFile?.(item.uri)}\n\t\t\t>\n\t\t\t\t<span className=\"icon chevron keyboard_arrow_down\" />\n\t\t\t\t<span className={`${iconClass} file-icon`} />\n\t\t\t\t<span className=\"file-name\">{sanitize(item.fileName)}</span>\n\t\t\t\t<span className=\"ref-count\">{item.count}</span>\n\t\t\t</div>\n\t\t);\n\n\t\treturn $el;\n\t}\n\n\tconst $el = (\n\t\t<div className=\"ref-item\" onclick={() => onNavigate?.(item.ref)}>\n\t\t\t<span className=\"line-number\">{item.line}</span>\n\t\t\t<span className=\"ref-preview\" />\n\t\t</div>\n\t);\n\n\t$el.get(\".ref-preview\").innerHTML = DOMPurify.sanitize(item.highlightedText);\n\n\treturn $el;\n}\n\nexport async function navigateToReference(ref) {\n\tSidebar.hide();\n\n\ttry {\n\t\tawait openFile(ref.uri, { render: true });\n\t\tconst { editor } = editorManager;\n\t\tif (!editor) return;\n\n\t\tconst doc = editor.state.doc;\n\t\tconst startLine = doc.line(ref.range.start.line + 1);\n\t\tconst endLine = doc.line(ref.range.end.line + 1);\n\t\tconst from = Math.min(\n\t\t\tstartLine.from + ref.range.start.character,\n\t\t\tstartLine.to,\n\t\t);\n\t\tconst to = Math.min(endLine.from + ref.range.end.character, endLine.to);\n\n\t\teditor.dispatch({\n\t\t\tselection: { anchor: from, head: to },\n\t\t\teffects: EditorView.scrollIntoView(from, { y: \"center\" }),\n\t\t});\n\t\teditor.focus();\n\t} catch (error) {\n\t\tconsole.error(\"Failed to navigate to reference:\", error);\n\t}\n}\n\nexport function getReferencesStats(references) {\n\tconst fileCount = new Set(references.map((r) => r.uri)).size;\n\tconst refCount = references.length;\n\treturn {\n\t\tfileCount,\n\t\trefCount,\n\t\ttext: `${refCount} reference${refCount !== 1 ? \"s\" : \"\"} in ${fileCount} file${fileCount !== 1 ? \"s\" : \"\"}`,\n\t};\n}\n"
  },
  {
    "path": "src/components/scrollbar/index.js",
    "content": "import \"./style.scss\";\nimport tag from \"html-tag-js\";\n\n/**\n * @typedef {object} Scrollbar\n * @property {function():void} destroy\n * @property {function():void} render\n * @property {function():void} show\n * @property {function():void} hide\n * @property {function():void} resize\n * @property {function():void} onshow\n * @property {function():void} onhide\n * @property {function():void} hideImmediately\n * @property {number} value\n * @property {number} size\n * @property {boolean} visible\n */\n\n/**\n * Create a scrollbar\n * @param {Object} options\n * @param {HTMLElement} [options.parent]\n * @param {\"top\"|\"left\"|\"right\"|\"bottom\"} [options.placement = \"right\"]\n * @param {Number} [options.width]\n * @param {function():void} [options.onscroll]\n * @param {function():void} [options.onscrollend]\n * @returns {Scrollbar & HTMLElement}\n */\nexport default function ScrollBar(options) {\n\tif (!options || !options.parent) {\n\t\tthrow new Error(\"ScrollBar.js: Parent element required.\");\n\t}\n\n\tconst { placement = \"right\" } = options;\n\tconst $cursor = tag(\"span\", {\n\t\tclassName: \"scroll-cursor\",\n\t\tstyle: {\n\t\t\ttop: 0,\n\t\t\tleft: 0,\n\t\t},\n\t});\n\tconst $thumb = tag(\"span\", {\n\t\tclassName: \"thumb\",\n\t});\n\tconst $container = tag(\"div\", {\n\t\tclassName: \"container\",\n\t\tchildren: [$cursor, $thumb],\n\t});\n\tconst $scrollbar = tag(\"div\", {\n\t\tclassName: `scrollbar-container ${placement}`,\n\t\tchild: $container,\n\t});\n\tconst config = {\n\t\tpassive: false,\n\t};\n\tconst TIMEOUT = 2000;\n\tconst isVertical = placement === \"right\" || placement === \"left\";\n\tconst observer = new MutationObserver(observerCallback);\n\tlet scroll = 0;\n\tlet touchStartValue = {\n\t\tx: 0,\n\t\ty: 0,\n\t};\n\tlet scrollbarSize = 20;\n\tlet height;\n\tlet width;\n\tlet rect;\n\tlet scrollbarTimeoutHide;\n\tlet scrollbarTimeoutRemove;\n\tlet onshow;\n\tlet onhide;\n\tlet touchStarted = false;\n\n\tif (options.width) scrollbarSize = options.width;\n\n\tsetWidth(scrollbarSize);\n\t$scrollbar.onScroll = options.onscroll;\n\t$scrollbar.onScrollEnd = options.onscrollend;\n\t$thumb.addEventListener(\"touchstart\", touchStart, config);\n\t$thumb.addEventListener(\"mousedown\", touchStart, config);\n\twindow.addEventListener(\"resize\", resize);\n\tobserver.observe($cursor, {\n\t\tattributes: true,\n\t});\n\n\tfunction observerCallback() {\n\t\t$thumb.style.top = $cursor.style.top;\n\t\t$thumb.style.left = $cursor.style.left;\n\t}\n\n\tfunction setWidth(width) {\n\t\tif (isVertical) $scrollbar.style.width = $cursor.style.width = width + \"px\";\n\t\telse $scrollbar.style.height = $cursor.style.height = width + \"px\";\n\t}\n\n\t/**\n\t *\n\t * @param {TouchEvent|MouseEvent} e\n\t */\n\tfunction touchStart(e) {\n\t\te.preventDefault();\n\t\ttouchStarted = true;\n\t\tif (!rect) resize();\n\t\tconst touch = e.type === \"touchstart\" ? e.touches[0] : e;\n\t\ttouchStartValue.x = touch.clientX;\n\t\ttouchStartValue.y = touch.clientY;\n\t\t$scrollbar.classList.add(\"active\");\n\t\tdocument.addEventListener(\"touchmove\", touchMove, config);\n\t\tdocument.addEventListener(\"mousemove\", touchMove, config);\n\t\tdocument.addEventListener(\"touchend\", touchEnd, config);\n\t\tdocument.addEventListener(\"mouseup\", touchEnd, config);\n\t\tdocument.addEventListener(\"touchcancel\", touchEnd, config);\n\t\tclearTimeout(scrollbarTimeoutHide);\n\t}\n\n\t/**\n\t *\n\t * @param {TouchEvent | MouseEvent} e\n\t */\n\tfunction touchMove(e) {\n\t\tconst touch = e.type === \"touchmove\" ? e.touches[0] : e;\n\t\tconst touchDiffX = touchStartValue.x - touch.clientX;\n\t\tconst touchDiffY = touchStartValue.y - touch.clientY;\n\t\ttouchStartValue.x = touch.clientX;\n\t\ttouchStartValue.y = touch.clientY;\n\n\t\tif (isVertical) {\n\t\t\tlet top = Number.parseFloat($cursor.style.top) - touchDiffY;\n\t\t\tconst currentTopValue = Number.parseFloat($cursor.style.top);\n\n\t\t\tif (top < 0) top = 0;\n\t\t\telse if (top > height) top = height;\n\n\t\t\tif (currentTopValue !== top) {\n\t\t\t\t$cursor.style.top = top + \"px\";\n\t\t\t\tscroll = top / height;\n\t\t\t\tif (typeof $scrollbar.onScroll === \"function\")\n\t\t\t\t\t$scrollbar.onScroll(scroll);\n\t\t\t}\n\t\t} else {\n\t\t\tlet left = Number.parseFloat($cursor.style.left) - touchDiffX;\n\t\t\tconst currentLeftValue = Number.parseFloat($cursor.style.left);\n\n\t\t\tif (left < 0) left = 0;\n\t\t\telse if (left > width) left = width;\n\n\t\t\tif (currentLeftValue !== left) {\n\t\t\t\t$cursor.style.left = left + \"px\";\n\t\t\t\tscroll = left / width;\n\t\t\t\tif (typeof $scrollbar.onScroll === \"function\")\n\t\t\t\t\t$scrollbar.onScroll(scroll);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {TouchEvent|MouseEvent} e\n\t */\n\tfunction touchEnd(e) {\n\t\te.preventDefault();\n\t\ttouchStarted = false;\n\t\t$scrollbar.classList.remove(\"active\");\n\t\tdocument.removeEventListener(\"touchmove\", touchMove, config);\n\t\tdocument.removeEventListener(\"mousemove\", touchMove, config);\n\t\tdocument.removeEventListener(\"touchend\", touchEnd, config);\n\t\tdocument.removeEventListener(\"mouseup\", touchEnd, config);\n\t\tdocument.removeEventListener(\"touchcancel\", touchEnd, config);\n\t\tif (typeof $scrollbar.onScrollEnd === \"function\") $scrollbar.onScrollEnd();\n\t\tscrollbarTimeoutHide = setTimeout(hide, TIMEOUT);\n\t}\n\n\tfunction resize(render = true) {\n\t\trect = $scrollbar.getBoundingClientRect();\n\t\theight = rect.height - 20;\n\t\twidth = rect.width - 20;\n\n\t\tif (height < 0) height = 0;\n\t\tif (width < 0) width = 0;\n\t\tif (render && height && width) setValue(scroll);\n\t}\n\n\tfunction setValue(val) {\n\t\tif (!height || !width) resize(false);\n\n\t\t//Make sure value is between 0 and 1\n\t\tif (val < 0) val = 0;\n\t\telse if (val > 1) val = 1;\n\n\t\tscroll = val;\n\t\tif (isVertical) $cursor.style.top = val * height + \"px\";\n\t\telse $cursor.style.left = val * width + \"px\";\n\t}\n\n\tfunction destroy() {\n\t\twindow.removeEventListener(\"resize\", resize);\n\t\t$thumb.removeEventListener(\"touchstart\", touchStart);\n\t\tobserver.disconnect();\n\t\tif (typeof onhide === \"function\") onhide();\n\t}\n\n\tfunction render() {\n\t\tshow();\n\t\tclearTimeout(scrollbarTimeoutHide);\n\t\tscrollbarTimeoutHide = setTimeout(hide, TIMEOUT);\n\t}\n\n\tfunction show() {\n\t\tif ($scrollbar.dataset.hidden === \"false\") {\n\t\t\treturn;\n\t\t}\n\t\t$scrollbar.dataset.hidden = false;\n\t\tclearTimeout(scrollbarTimeoutHide);\n\t\tclearTimeout(scrollbarTimeoutRemove);\n\t\t$scrollbar.classList.remove(\"hide\");\n\t\tif (!$scrollbar.isConnected) {\n\t\t\toptions.parent.append($scrollbar);\n\t\t\tif (typeof onshow === \"function\") onshow();\n\t\t}\n\t}\n\n\tfunction hide() {\n\t\tif (touchStarted) return;\n\t\t$scrollbar.dataset.hidden = true;\n\t\t$scrollbar.classList.add(\"hide\");\n\t\tscrollbarTimeoutRemove = setTimeout(() => $scrollbar.remove(), 300);\n\t\tif (typeof onhide === \"function\") onhide();\n\t}\n\n\tfunction hideImmediately() {\n\t\t$scrollbar.dataset.hidden = true;\n\t\t$scrollbar.classList.add(\"hide\");\n\t\t$scrollbar.remove();\n\t\tif (typeof onhide === \"function\") onhide();\n\t}\n\n\tObject.defineProperty($scrollbar, \"size\", {\n\t\tget: () => scrollbarSize,\n\t\tset: setWidth,\n\t});\n\n\tObject.defineProperty($scrollbar, \"resize\", {\n\t\tvalue: resize,\n\t});\n\n\tObject.defineProperty($scrollbar, \"value\", {\n\t\tget: () => scroll,\n\t\tset: setValue,\n\t});\n\n\tObject.defineProperty($scrollbar, \"destroy\", {\n\t\tvalue: destroy,\n\t});\n\n\tObject.defineProperty($scrollbar, \"render\", {\n\t\tvalue: render,\n\t});\n\n\tObject.defineProperty($scrollbar, \"show\", {\n\t\tvalue: show,\n\t});\n\n\tObject.defineProperty($scrollbar, \"hide\", {\n\t\tvalue: hide,\n\t});\n\n\tObject.defineProperty($scrollbar, \"visible\", {\n\t\tget() {\n\t\t\treturn this.dataset.hidden !== \"true\";\n\t\t},\n\t});\n\n\tObject.defineProperty($scrollbar, \"onshow\", {\n\t\tset(fun) {\n\t\t\tonshow = fun;\n\t\t},\n\t\tget() {\n\t\t\treturn onshow;\n\t\t},\n\t});\n\n\tObject.defineProperty($scrollbar, \"onhide\", {\n\t\tset(fun) {\n\t\t\tonhide = fun;\n\t\t},\n\t\tget() {\n\t\t\treturn onhide;\n\t\t},\n\t});\n\n\tObject.defineProperty($scrollbar, \"hideImmediately\", {\n\t\tvalue: hideImmediately,\n\t});\n\n\treturn $scrollbar;\n}\n"
  },
  {
    "path": "src/components/scrollbar/style.scss",
    "content": ".scrollbar-container {\n  position: absolute;\n  background-color: transparent;\n  padding: 10px;\n  opacity: 0.5;\n  z-index: 9999;\n  animation: scrollbar-show 300ms ease 1;\n  transition: all 300ms ease;\n  opacity: 1;\n\n  &.hide {\n    opacity: 0;\n  }\n\n  &.active {\n    opacity: 1;\n\n    .thumb {\n      box-shadow: 0 0 0 4px rgba($color: #fff, $alpha: 0.2);\n    }\n  }\n\n  .container {\n    height: 100%;\n    width: 100%;\n    position: relative;\n\n    .thumb,\n    .scroll-cursor {\n      position: absolute;\n      display: block;\n    }\n\n    .thumb {\n      background-color: #39f;\n      background-color: var(--button-background-color);\n      border-radius: 4px;\n    }\n  }\n\n  &.right,\n  &.left {\n    height: calc(100% - (50px * 2));\n    width: 10px;\n    top: 0;\n    bottom: 0;\n    margin: auto;\n\n    .thumb,\n    .cursor {\n      height: 1px;\n      width: 100%;\n    }\n\n    .thumb {\n      height: 50px;\n      margin-top: -25px;\n    }\n  }\n\n  &.right {\n    left: auto;\n    right: 0;\n    padding: 10px 17px 10px 10px;\n  }\n\n  &.left {\n    left: 0;\n    right: auto;\n  }\n\n  &.top,\n  &.bottom {\n    width: calc(100% - (50px * 2));\n    height: 10px;\n    left: 0;\n    right: 0;\n    margin: auto;\n\n    .thumb,\n    .cursor {\n      height: 100%;\n      width: 1px;\n    }\n\n    .thumb {\n      width: 50px;\n      margin-left: -25px;\n    }\n  }\n\n  &.top {\n    top: 0;\n    bottom: auto;\n  }\n\n  &.bottom {\n    bottom: 0;\n    top: auto;\n  }\n}"
  },
  {
    "path": "src/components/searchbar/index.js",
    "content": "import \"./style.scss\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\n\n/**\n * Create and activate search bar\n * @param {HTMLUListElement|HTMLOListElement} $list\n * @param {(hide:Function)=>void} setHide\n * @param {()=>void} onhideCb callback to be called when search bar is hidden\n * @param {(value:string)=>HTMLElement[]} searchFunction\n */\nfunction searchBar($list, setHide, onhideCb, searchFunction) {\n\tlet hideOnBlur = true;\n\tlet timeout = null;\n\tconst $searchInput = Ref();\n\t/**@type {HTMLDivElement} */\n\tconst $container = (\n\t\t<div id=\"search-bar\">\n\t\t\t<input\n\t\t\t\tref={$searchInput}\n\t\t\t\ttype=\"search\"\n\t\t\t\tplaceholder={strings.search}\n\t\t\t\tenterKeyHint=\"go\"\n\t\t\t/>\n\t\t\t<span className=\"icon clearclose\" onclick={hide}></span>\n\t\t</div>\n\t);\n\n\t/**@type {HTMLElement[]} */\n\tconst children = [...$list.children];\n\n\tif (typeof setHide === \"function\") {\n\t\thideOnBlur = false;\n\t\tsetHide(hide);\n\t}\n\tapp.appendChild($container);\n\n\t$searchInput.el.oninput = search;\n\t$searchInput.el.focus();\n\t$searchInput.el.onblur = () => {\n\t\tif (!hideOnBlur) return;\n\t\tsetTimeout(hide, 0);\n\t};\n\n\tactionStack.push({\n\t\tid: \"searchbar\",\n\t\taction: hide,\n\t});\n\n\tfunction hide() {\n\t\tactionStack.remove(\"searchbar\");\n\t\tif (typeof onhideCb === \"function\") onhideCb();\n\n\t\t$list.content = children;\n\t\t$container.classList.add(\"hide\");\n\t\tsetTimeout(() => {\n\t\t\t$container.remove();\n\t\t}, 300);\n\t}\n\n\tfunction search() {\n\t\tif (timeout) clearTimeout(timeout);\n\t\ttimeout = setTimeout(searchNow.bind(this), 500);\n\t}\n\n\t/**\n\t * @this {HTMLInputElement}\n\t */\n\tasync function searchNow() {\n\t\tconst val = $searchInput.value.toLowerCase();\n\n\t\tif (!val) {\n\t\t\t$list.content = children;\n\t\t\treturn;\n\t\t}\n\n\t\tlet result;\n\n\t\tif (searchFunction) {\n\t\t\tresult = searchFunction(val);\n\n\t\t\tif (result instanceof Promise) {\n\t\t\t\ttry {\n\t\t\t\t\tresult = await result;\n\t\t\t\t} catch (error) {\n\t\t\t\t\twindow.log(\"error\", \"Search function failed:\");\n\t\t\t\t\twindow.log(\"error\", error);\n\t\t\t\t\tresult = [];\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tresult = filterList(val);\n\t\t}\n\n\t\t$list.textContent = \"\";\n\t\t$list.append(...buildSearchContent(result, val));\n\t}\n\n\t/**\n\t * Search list items\n\t * @param {string} val\n\t * @returns\n\t */\n\tfunction filterList(val) {\n\t\treturn children.filter((child) => {\n\t\t\tconst text = child.textContent.toLowerCase();\n\t\t\treturn text.match(val, \"i\");\n\t\t});\n\t}\n\n\t/**\n\t * Keep grouped settings search results in section cards instead of flattening them.\n\t * @param {HTMLElement[]} result\n\t * @param {string} val\n\t * @returns {HTMLElement[]}\n\t */\n\tfunction buildSearchContent(result, val) {\n\t\tif (!val || !result.length) return result;\n\n\t\tconst groupedSections = [];\n\t\tconst sectionIndexByLabel = new Map();\n\t\tlet hasGroups = false;\n\n\t\tresult.forEach(($item) => {\n\t\t\tconst label = $item.dataset.searchGroup;\n\t\t\tif (!label) {\n\t\t\t\tgroupedSections.push({\n\t\t\t\t\titems: [$item],\n\t\t\t\t\ttype: \"ungrouped\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\thasGroups = true;\n\t\t\tconst existingSectionIndex = sectionIndexByLabel.get(label);\n\t\t\tif (existingSectionIndex !== undefined) {\n\t\t\t\tgroupedSections[existingSectionIndex].items.push($item);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsectionIndexByLabel.set(label, groupedSections.length);\n\t\t\tgroupedSections.push({\n\t\t\t\titems: [$item],\n\t\t\t\tlabel,\n\t\t\t\ttype: \"group\",\n\t\t\t});\n\t\t});\n\n\t\tif (!hasGroups) return result.map(cloneSearchItem);\n\n\t\tconst countLabel = `${result.length} ${\n\t\t\tresult.length === 1\n\t\t\t\t? strings[\"search result label singular\"]\n\t\t\t\t: strings[\"search result label plural\"]\n\t\t}`;\n\t\tconst content = [\n\t\t\t<div className=\"settings-search-summary\">{countLabel}</div>,\n\t\t];\n\n\t\tgroupedSections.forEach((section) => {\n\t\t\tif (section.type === \"ungrouped\") {\n\t\t\t\tcontent.push(...section.items.map(cloneSearchItem));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcontent.push(\n\t\t\t\t<section className=\"settings-section settings-search-section\">\n\t\t\t\t\t<div className=\"settings-section-label\">{section.label}</div>\n\t\t\t\t\t<div className=\"settings-section-card\">\n\t\t\t\t\t\t{section.items.map(cloneSearchItem)}\n\t\t\t\t\t</div>\n\t\t\t\t</section>,\n\t\t\t);\n\t\t});\n\n\t\treturn content;\n\t}\n\n\t/**\n\t * Render search results without moving the original list items out of their groups.\n\t * @param {HTMLElement} $item\n\t * @returns {HTMLElement}\n\t */\n\tfunction cloneSearchItem($item) {\n\t\tconst $clone = $item.cloneNode(true);\n\t\t$clone.addEventListener(\"click\", () => {\n\t\t\t$item.click();\n\t\t});\n\t\treturn $clone;\n\t}\n}\n\nexport default searchBar;\n"
  },
  {
    "path": "src/components/searchbar/style.scss",
    "content": "#search-bar {\n  position: fixed;\n  top: 0;\n  left: 0;\n  height: 45px;\n  width: 100%;\n  animation: show-searchbar 300ms ease 1;\n  z-index: 200;\n  background-color: inherit;\n  display: flex;\n\n  input {\n    flex: 1;\n    border-radius: 4px;\n    box-shadow: rgba(0, 0, 0, 0.2);\n    box-shadow: var(--box-shadow-color);\n    border: none;\n    margin: 5px;\n    box-sizing: border-box;\n    color: var(--primary-text-color);\n\n    &:focus {\n      outline: none;\n      box-shadow: rgba(0, 0, 0, 0.5);\n    }\n\n    &::-webkit-search-decoration,\n    &::-webkit-search-cancel-button,\n    &::-webkit-search-results-button,\n    &::-webkit-search-results-decoration {\n      display: none;\n    }\n  }\n\n  .icon {\n    height: 45px;\n    width: 45px;\n    font-size: 1.2em;\n    color: rgb(255, 255, 255);\n  }\n\n  &.hide {\n    transition: all 300ms ease;\n    opacity: 0;\n    transform: translate(0, -100%, 0);\n  }\n}"
  },
  {
    "path": "src/components/settingsPage.js",
    "content": "import \"./settingsPage.scss\";\nimport colorPicker from \"dialogs/color\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport appSettings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport { isValidColor } from \"utils/color/regex\";\nimport helpers from \"utils/helpers\";\nimport Checkbox from \"./checkbox\";\nimport Page from \"./page\";\nimport searchBar from \"./searchbar\";\n\n/**\n * @typedef {object} SettingsPage\n * @property {(goTo:string)=>void} show show settings page\n * @property {()=>void} hide hide settings page\n * @property {(key:string)=>HTMLElement[]} search search for a setting\n * @property {(title:string)=>void} setTitle set title of settings page\n * @property {()=>void} restoreList restore list to original state\n */\n\n/**\n * @typedef {Object} SettingsPageOptions\n * @property {boolean} [preserveOrder] - If true, items are listed in the order provided instead of alphabetical\n * @property {string} [pageClassName] - Extra classes to apply to the page element\n * @property {string} [listClassName] - Extra classes to apply to the list element\n * @property {string} [defaultSearchGroup] - Default search result group label for this page\n * @property {boolean} [infoAsDescription] - Override subtitle behavior; defaults to true when valueInTail is enabled\n * @property {boolean} [valueInTail] - Render item.value as a trailing control/value instead of subtitle\n * @property {boolean} [groupByDefault] - Wrap uncategorized settings in a grouped section shell\n * @property {\"top\"|\"bottom\"} [notePosition] - Render note before or after the settings list\n */\n\n/**\n *  Creates a settings page\n * @param {string} title\n * @param {ListItem[]} settings\n * @param {(key, value) => void} callback  called when setting is changed\n * @param {'united'|'separate'} [type='united']\n * @param {SettingsPageOptions} [options={}]\n * @returns {SettingsPage}\n */\nexport default function settingsPage(\n\ttitle,\n\tsettings,\n\tcallback,\n\ttype = \"united\",\n\toptions = {},\n) {\n\tlet hideSearchBar = () => {};\n\tconst $page = Page(title);\n\t$page.id = \"settings\";\n\n\tif (options.pageClassName) {\n\t\t$page.classList.add(...options.pageClassName.split(\" \").filter(Boolean));\n\t}\n\n\t/**@type {HTMLDivElement} */\n\tconst $list = <div tabIndex={0} className=\"main list\"></div>;\n\n\tif (options.listClassName) {\n\t\t$list.classList.add(...options.listClassName.split(\" \").filter(Boolean));\n\t}\n\n\tconst normalized = normalizeSettings(settings);\n\tsettings = normalized.settings;\n\n\t/** DISCLAIMER: do not assign hideSearchBar directly because it can change  */\n\t$page.ondisconnect = () => hideSearchBar();\n\t$page.onhide = () => {\n\t\thideAd();\n\t\tactionStack.remove(title);\n\t};\n\n\tconst state = listItems($list, settings, callback, {\n\t\tdefaultSearchGroup: title,\n\t\t...options,\n\t});\n\tlet children = [...state.children];\n\t$page.body = $list;\n\n\tconst searchableItems = state.searchItems;\n\n\tif (shouldEnableSearch(type, settings.length)) {\n\t\tconst $search = <span className=\"icon search\" attr-action=\"search\"></span>;\n\t\t$search.onclick = () =>\n\t\t\tsearchBar(\n\t\t\t\t$list,\n\t\t\t\t(hide) => {\n\t\t\t\t\thideSearchBar = hide;\n\t\t\t\t},\n\t\t\t\ttype === \"united\" ? restoreAllSettingsPages : null,\n\t\t\t\tcreateSearchHandler(type, state.searchItems),\n\t\t\t);\n\n\t\t$page.header.append($search);\n\t}\n\n\tif (normalized.note) {\n\t\tconst $note = createNote(normalized.note);\n\n\t\tif (options.notePosition === \"top\") {\n\t\t\tchildren.unshift($note);\n\t\t} else {\n\t\t\tchildren.push($note);\n\t\t}\n\t}\n\n\t$list.content = children;\n\t$page.append(<div style={{ height: \"50vh\" }}></div>);\n\n\treturn {\n\t\t/**\n\t\t * Show settings page\n\t\t * @param {string} goTo Key of setting to scroll to and select\n\t\t * @returns {void}\n\t\t */\n\t\tshow(goTo) {\n\t\t\tactionStack.push({\n\t\t\t\tid: title,\n\t\t\t\taction: $page.hide,\n\t\t\t});\n\t\t\tapp.append($page);\n\t\t\thelpers.showAd();\n\n\t\t\tif (goTo) {\n\t\t\t\tconst $item = $list.get(`[data-key=\"${goTo}\"]`);\n\t\t\t\tif (!$item) return;\n\n\t\t\t\t$item.scrollIntoView();\n\t\t\t\t$item.click();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.focus();\n\t\t},\n\t\thide() {\n\t\t\t$page.hide();\n\t\t},\n\t\t/**\n\t\t * Search for a setting\n\t\t * @param {string} key\n\t\t */\n\t\tsearch(key) {\n\t\t\treturn searchableItems.filter((child) => {\n\t\t\t\tconst text = child.textContent.toLowerCase();\n\t\t\t\treturn text.match(key, \"i\");\n\t\t\t});\n\t\t},\n\t\t/**\n\t\t * Restore list to original state\n\t\t */\n\t\trestoreList() {\n\t\t\t$list.content = children;\n\t\t},\n\t\t/**\n\t\t * Set title of settings page\n\t\t * @param {string} title\n\t\t */\n\t\tsetTitle(title) {\n\t\t\t$page.settitle(title);\n\t\t},\n\t};\n}\n\n/**\n * @typedef {Object} ListItem\n * @property {string} key\n * @property {string} text\n * @property {string} [icon]\n * @property {string} [iconColor]\n * @property {string} [info]\n * @property {string} [value]\n * @property {(value:string)=>string} [valueText]\n * @property {string} [category]\n * @property {string} [searchGroup]\n * @property {boolean} [checkbox]\n * @property {boolean} [chevron]\n * @property {string} [prompt]\n * @property {string} [promptType]\n * @property {import('dialogs/prompt').PromptOptions} [promptOptions]\n */\n\n/**\n * Creates a list of settings\n * @param {HTMLUListElement} $list\n * @param {Array<ListItem>} items\n * @param {()=>void} callback called when setting is changed\n * @param {SettingsPageOptions} [options={}]\n */\nfunction listItems($list, items, callback, options = {}) {\n\tconst renderedItems = [];\n\tconst $searchItems = [];\n\tconst useInfoAsDescription =\n\t\toptions.infoAsDescription ?? Boolean(options.valueInTail);\n\tconst itemByKey = new Map(items.map((item) => [item.key, item]));\n\n\t// sort settings by text before rendering (unless preserveOrder is true)\n\tif (!options.preserveOrder) {\n\t\titems.sort((acc, cur) => {\n\t\t\tif (!acc?.text || !cur?.text) return 0;\n\t\t\treturn acc.text.localeCompare(cur.text);\n\t\t});\n\t}\n\titems.forEach((item) => {\n\t\tconst $item = createListItemElement(item, options, useInfoAsDescription);\n\t\tinsertRenderedItem(renderedItems, item, $item);\n\t\t$item.addEventListener(\"click\", onclick);\n\t\t$searchItems.push($item);\n\t});\n\n\tconst topLevelChildren = buildListContent(renderedItems, options);\n\n\t$list.content = topLevelChildren;\n\n\treturn {\n\t\tchildren: topLevelChildren,\n\t\tsearchItems: $searchItems,\n\t};\n\n\t/**\n\t * Click handler for $list\n\t * @this {HTMLElement}\n\t * @param {MouseEvent} e\n\t */\n\tasync function onclick(e) {\n\t\tconst $target = e.currentTarget;\n\t\tconst { key } = $target.dataset;\n\n\t\tconst item = itemByKey.get(key);\n\t\tif (!item) return;\n\t\tconst result = await resolveItemInteraction(item, $target);\n\t\tif (result.shouldCallCallback === false) return;\n\t\tif (!result.shouldUpdateValue)\n\t\t\treturn callback.call($target, key, item.value);\n\n\t\titem.value = result.value;\n\t\tupdateItemValueDisplay($target, item, options, useInfoAsDescription);\n\n\t\tcallback.call($target, key, item.value);\n\t}\n}\n\nfunction normalizeSettings(settings) {\n\t/** @type {string | undefined} */\n\tlet note;\n\tconst normalizedSettings = settings.filter((setting) => {\n\t\tif (\"note\" in setting) {\n\t\t\tnote = setting.note;\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t});\n\n\treturn {\n\t\tnote,\n\t\tsettings: normalizedSettings,\n\t};\n}\n\nfunction shouldEnableSearch(type, settingsCount) {\n\treturn type === \"united\" || (type === \"separate\" && settingsCount > 5);\n}\n\nfunction restoreAllSettingsPages() {\n\tObject.values(appSettings.uiSettings).forEach((page) => {\n\t\tpage.restoreList();\n\t});\n}\n\nfunction createSearchHandler(type, searchItems) {\n\treturn (key) => {\n\t\tif (type === \"united\") {\n\t\t\tconst $items = [];\n\t\t\tObject.values(appSettings.uiSettings).forEach((page) => {\n\t\t\t\t$items.push(...page.search(key));\n\t\t\t});\n\t\t\treturn $items;\n\t\t}\n\n\t\treturn searchItems.filter((item) => {\n\t\t\tconst text = item.textContent.toLowerCase();\n\t\t\treturn text.match(key, \"i\");\n\t\t});\n\t};\n}\n\nfunction createNote(note) {\n\treturn (\n\t\t<div className=\"note\">\n\t\t\t<div className=\"note-title\">\n\t\t\t\t<span className=\"icon info_outline\"></span>\n\t\t\t\t<span>{strings.info}</span>\n\t\t\t</div>\n\t\t\t<p innerHTML={note}></p>\n\t\t</div>\n\t);\n}\n\nfunction createListItemElement(item, options, useInfoAsDescription) {\n\tconst $setting = Ref();\n\tconst $tail = Ref();\n\tconst isCheckboxItem = isBooleanSetting(item);\n\tconst state = getItemDisplayState(item, useInfoAsDescription);\n\t/**@type {HTMLDivElement} */\n\tconst $item = (\n\t\t<div\n\t\t\ttabIndex={1}\n\t\t\tclassName={`list-item ${item.sake ? \"sake\" : \"\"} ${item.icon ? \"\" : \"no-leading-icon\"}`}\n\t\t\tdata-key={item.key}\n\t\t\tdata-action=\"list-item\"\n\t\t>\n\t\t\t<span\n\t\t\t\tclassName={`icon ${item.icon || \"no-icon\"}`}\n\t\t\t\tstyle={{ color: item.iconColor }}\n\t\t\t></span>\n\t\t\t<div ref={$setting} className=\"container\">\n\t\t\t\t<div className=\"text\">{item.text?.capitalize?.(0) ?? item.text}</div>\n\t\t\t</div>\n\t\t\t<div ref={$tail} className=\"setting-tail\"></div>\n\t\t</div>\n\t);\n\tconst searchGroup =\n\t\titem.searchGroup || item.category || options.defaultSearchGroup;\n\n\tif (searchGroup) {\n\t\t$item.dataset.searchGroup = searchGroup;\n\t}\n\n\tif (isCheckboxItem) {\n\t\tconst $checkbox = Checkbox(\"\", item.checkbox || item.value);\n\t\t$tail.el.appendChild($checkbox);\n\t}\n\n\tif (state.hasSubtitle) {\n\t\tconst $valueText = createSubtitleElement(item, state);\n\t\t$setting.append($valueText);\n\t\t$item.classList.add(\"has-subtitle\");\n\t\tif (!state.showInfoAsSubtitle) {\n\t\t\tsetColor($item, item.value);\n\t\t}\n\t} else {\n\t\t$item.classList.add(\"compact\");\n\t}\n\n\tif (shouldShowTrailingValue(item, options)) {\n\t\t$item.classList.add(\"has-tail-value\");\n\t\tif (item.select) {\n\t\t\t$item.classList.add(\"has-tail-select\");\n\t\t}\n\t\t$tail.el.append(createTrailingValueDisplay(item));\n\t}\n\n\tif (shouldShowTailChevron(item)) {\n\t\t$tail.el.append(\n\t\t\t<span className=\"icon keyboard_arrow_right settings-chevron\"></span>,\n\t\t);\n\t}\n\n\tif (!$tail.el.children.length) {\n\t\t$tail.el.remove();\n\t}\n\n\treturn $item;\n}\n\nfunction isBooleanSetting(item) {\n\treturn item.checkbox !== undefined || typeof item.value === \"boolean\";\n}\n\nfunction getItemDisplayState(item, useInfoAsDescription) {\n\tconst isCheckboxItem = isBooleanSetting(item);\n\tconst subtitle = isCheckboxItem\n\t\t? item.info\n\t\t: getSubtitleText(item, useInfoAsDescription);\n\tconst showInfoAsSubtitle =\n\t\tisCheckboxItem ||\n\t\tuseInfoAsDescription ||\n\t\t(item.value === undefined && item.info);\n\n\treturn {\n\t\tsubtitle,\n\t\tshowInfoAsSubtitle,\n\t\thasSubtitle: subtitle !== undefined && subtitle !== null && subtitle !== \"\",\n\t};\n}\n\nfunction createSubtitleElement(item, state) {\n\tconst $valueText = <small className=\"value\"></small>;\n\tsetValueText(\n\t\t$valueText,\n\t\tstate.subtitle,\n\t\tstate.showInfoAsSubtitle ? null : item.valueText?.bind(item),\n\t);\n\n\tif (state.showInfoAsSubtitle) {\n\t\t$valueText.classList.add(\"setting-info\");\n\t}\n\n\treturn $valueText;\n}\n\nfunction shouldShowTrailingValue(item, options) {\n\treturn (\n\t\toptions.valueInTail &&\n\t\titem.value !== undefined &&\n\t\titem.checkbox === undefined &&\n\t\ttypeof item.value !== \"boolean\"\n\t);\n}\n\nfunction createTrailingValueDisplay(item) {\n\tconst $trailingValueText = (\n\t\t<small\n\t\t\tclassName={`setting-trailing-value ${item.select ? \"is-select\" : \"\"}`}\n\t\t></small>\n\t);\n\tsetValueText($trailingValueText, item.value, item.valueText?.bind(item));\n\n\treturn (\n\t\t<div className={`setting-value-display ${item.select ? \"is-select\" : \"\"}`}>\n\t\t\t{$trailingValueText}\n\t\t\t{item.select ? (\n\t\t\t\t<span className=\"icon keyboard_arrow_down setting-value-icon\"></span>\n\t\t\t) : null}\n\t\t</div>\n\t);\n}\n\nfunction shouldShowTailChevron(item) {\n\treturn (\n\t\titem.chevron ||\n\t\t(!item.select &&\n\t\t\tBoolean(item.prompt || item.file || item.folder || item.link))\n\t);\n}\n\nfunction insertRenderedItem(renderedItems, item, $item) {\n\tif (Number.isInteger(item.index)) {\n\t\trenderedItems.splice(item.index, 0, { item, $item });\n\t\treturn;\n\t}\n\n\trenderedItems.push({ item, $item });\n}\n\nfunction buildListContent(renderedItems, options) {\n\tconst $content = [];\n\t/** @type {HTMLElement | null} */\n\tlet $currentSectionCard = null;\n\tlet currentCategory = null;\n\n\trenderedItems.forEach(({ item, $item }) => {\n\t\tconst category =\n\t\t\titem.category?.trim() || (options.groupByDefault ? \"__default__\" : \"\");\n\n\t\tif (!category) {\n\t\t\tcurrentCategory = null;\n\t\t\t$currentSectionCard = null;\n\t\t\t$content.push($item);\n\t\t\treturn;\n\t\t}\n\n\t\tif (currentCategory !== category || !$currentSectionCard) {\n\t\t\tcurrentCategory = category;\n\t\t\tconst section = createSectionElements(category);\n\t\t\t$currentSectionCard = section.$card;\n\t\t\t$content.push(section.$section);\n\t\t}\n\n\t\t$currentSectionCard.append($item);\n\t});\n\n\treturn $content.length ? $content : renderedItems.map(({ $item }) => $item);\n}\n\nfunction createSectionElements(category) {\n\tconst shouldShowLabel = category !== \"__default__\";\n\tconst $label = shouldShowLabel ? (\n\t\t<div className=\"settings-section-label\">{category}</div>\n\t) : null;\n\tconst $card = <div className=\"settings-section-card\"></div>;\n\treturn {\n\t\t$card,\n\t\t$section: (\n\t\t\t<section className=\"settings-section\">\n\t\t\t\t{$label}\n\t\t\t\t{$card}\n\t\t\t</section>\n\t\t),\n\t};\n}\n\nasync function resolveItemInteraction(item, $target) {\n\tconst {\n\t\tselect: selectOptions,\n\t\tprompt: promptText,\n\t\tcolor: selectColor,\n\t\tcheckbox,\n\t\tfile,\n\t\tfolder,\n\t\tlink,\n\t\ttext,\n\t\tvalue,\n\t\tpromptType,\n\t\tpromptOptions,\n\t} = item;\n\n\ttry {\n\t\tif (selectOptions) {\n\t\t\tconst selectedValue = await select(text, selectOptions, {\n\t\t\t\tdefault: value,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tshouldUpdateValue: selectedValue !== undefined,\n\t\t\t\tvalue: selectedValue,\n\t\t\t};\n\t\t}\n\n\t\tif (checkbox !== undefined) {\n\t\t\tconst $checkbox = $target.get(\".input-checkbox\");\n\t\t\t$checkbox.toggle();\n\t\t\treturn {\n\t\t\t\tshouldUpdateValue: true,\n\t\t\t\tvalue: $checkbox.checked,\n\t\t\t};\n\t\t}\n\n\t\tif (promptText) {\n\t\t\tconst promptedValue = await prompt(\n\t\t\t\tpromptText,\n\t\t\t\tvalue,\n\t\t\t\tpromptType,\n\t\t\t\tpromptOptions,\n\t\t\t);\n\t\t\tif (promptedValue === null) {\n\t\t\t\treturn {\n\t\t\t\t\tshouldUpdateValue: false,\n\t\t\t\t\tshouldCallCallback: false,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tshouldUpdateValue: true,\n\t\t\t\tvalue: promptedValue,\n\t\t\t};\n\t\t}\n\n\t\tif (file || folder) {\n\t\t\tconst mode = file ? \"file\" : \"folder\";\n\t\t\tconst { url } = await FileBrowser(mode);\n\t\t\treturn {\n\t\t\t\tshouldUpdateValue: true,\n\t\t\t\tvalue: url,\n\t\t\t};\n\t\t}\n\n\t\tif (selectColor) {\n\t\t\tconst color = await colorPicker(value);\n\t\t\treturn {\n\t\t\t\tshouldUpdateValue: true,\n\t\t\t\tvalue: color,\n\t\t\t};\n\t\t}\n\n\t\tif (link) {\n\t\t\tsystem.openInBrowser(link);\n\t\t\treturn {\n\t\t\t\tshouldUpdateValue: false,\n\t\t\t\tshouldCallCallback: false,\n\t\t\t};\n\t\t}\n\t} catch (error) {\n\t\twindow.log(\"error\", error);\n\t}\n\n\treturn {\n\t\tshouldUpdateValue: false,\n\t\tshouldCallCallback: true,\n\t};\n}\n\nfunction updateItemValueDisplay($target, item, options, useInfoAsDescription) {\n\tif (options.valueInTail) {\n\t\tsyncTrailingValueDisplay($target, item, options);\n\t} else {\n\t\tsyncInlineValueDisplay($target, item, useInfoAsDescription);\n\t}\n\n\tsetColor($target, item.value);\n}\n\nfunction syncTrailingValueDisplay($target, item, options) {\n\tconst shouldRenderTrailingValue = shouldShowTrailingValue(item, options);\n\tlet $tail = $target.get(\".setting-tail\");\n\tlet $valueDisplay = $target.get(\".setting-value-display\");\n\n\tif (!shouldRenderTrailingValue) {\n\t\t$valueDisplay?.remove();\n\t\t$target.classList.remove(\"has-tail-value\", \"has-tail-select\");\n\t\tif ($tail && !$tail.children.length) {\n\t\t\t$tail.remove();\n\t\t}\n\t\treturn;\n\t}\n\n\tif (!$tail) {\n\t\t$tail = <div className=\"setting-tail\"></div>;\n\t\t$target.append($tail);\n\t}\n\n\tif (!$valueDisplay) {\n\t\t$valueDisplay = createTrailingValueDisplay(item);\n\t\tconst $chevron = $tail.get(\".settings-chevron\");\n\t\tif ($chevron) {\n\t\t\t$tail.insertBefore($valueDisplay, $chevron);\n\t\t} else {\n\t\t\t$tail.append($valueDisplay);\n\t\t}\n\t}\n\n\tconst $trailingValueText = $valueDisplay.get(\".setting-trailing-value\");\n\tsetValueText($trailingValueText, item.value, item.valueText?.bind(item));\n\t$target.classList.add(\"has-tail-value\");\n\t$target.classList.toggle(\"has-tail-select\", Boolean(item.select));\n}\n\n/**\n * Keeps the inline subtitle/value block in sync when a setting value changes.\n * @param {HTMLElement} $target\n * @param {ListItem} item\n * @param {boolean} useInfoAsDescription\n */\nfunction syncInlineValueDisplay($target, item, useInfoAsDescription) {\n\tconst state = getItemDisplayState(item, useInfoAsDescription);\n\tlet $valueText = $target.get(\".value\");\n\tconst $container = $target.get(\".container\");\n\n\tif (!$container) return;\n\n\tif (!state.hasSubtitle) {\n\t\t$valueText?.remove();\n\t\t$target.classList.remove(\"has-subtitle\");\n\t\t$target.classList.add(\"compact\");\n\t\treturn;\n\t}\n\n\tif (!$valueText) {\n\t\t$valueText = <small className=\"value\"></small>;\n\t\t$container.append($valueText);\n\t}\n\n\t$valueText.classList.toggle(\"setting-info\", state.showInfoAsSubtitle);\n\tsetValueText(\n\t\t$valueText,\n\t\tstate.subtitle,\n\t\tstate.showInfoAsSubtitle ? null : item.valueText?.bind(item),\n\t);\n\t$target.classList.add(\"has-subtitle\");\n\t$target.classList.remove(\"compact\");\n}\n\nfunction getSubtitleText(item, useInfoAsDescription) {\n\tif (useInfoAsDescription) {\n\t\treturn item.info;\n\t}\n\n\treturn item.value ?? item.info;\n}\n\n/**\n * Sets color decoration of a setting\n * @param {HTMLDivElement} $setting\n * @param {string} color\n * @returns\n */\nfunction setColor($setting, color) {\n\tif (!isValidColor(color)) return;\n\t/**@type {HTMLSpanElement} */\n\tconst $noIcon = $setting.get(\".no-icon\");\n\tif (!$noIcon) return;\n\t$noIcon.style.backgroundColor = color;\n}\n\n/**\n * Sets the value text of a setting\n * @param {HTMLSpanElement} $valueText\n * @param {string} value\n * @param {string} valueText\n * @returns\n */\nfunction setValueText($valueText, value, valueText) {\n\tif (!$valueText) return;\n\n\tif (typeof valueText === \"function\") {\n\t\tvalue = valueText(value);\n\t}\n\n\tif (typeof value === \"string\") {\n\t\tconst shouldPreserveFullText = $valueText.classList.contains(\"value\");\n\t\tif (!shouldPreserveFullText) {\n\t\t\tif (value.includes(\"\\n\")) [value] = value.split(\"\\n\");\n\n\t\t\tif (value.length > 47) {\n\t\t\t\tvalue = value.slice(0, 47) + \"...\";\n\t\t\t}\n\t\t}\n\t}\n\n\t$valueText.textContent = value;\n}\n"
  },
  {
    "path": "src/components/settingsPage.scss",
    "content": "@mixin settings-page-shell($list-selector) {\n\t#{$list-selector} {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 1.2rem;\n\t\twidth: 100%;\n\t\tmax-width: 48rem;\n\t\tmargin: 0 auto;\n\t\tpadding: 0.5rem 0 5.5rem;\n\t\tbox-sizing: border-box;\n\t\tbackground: var(--secondary-color);\n\t}\n\n\t.settings-section {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0;\n\t\twidth: 100%;\n\t}\n\n\t.settings-search-summary {\n\t\tpadding: 0.2rem 1rem;\n\t\tfont-size: 0.84rem;\n\t\tfont-weight: 600;\n\t\tline-height: 1.35;\n\t\tcolor: var(--secondary-text-color);\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 18%);\n\t}\n\n\t.settings-section-label {\n\t\tpadding: 0.5rem 1rem 0.45rem;\n\t\tfont-size: 0.84rem;\n\t\tfont-weight: 600;\n\t\tline-height: 1.3;\n\t\tcolor: color-mix(in srgb, var(--active-color), transparent 18%);\n\t}\n\n\t.settings-section-card {\n\t\twidth: 100%;\n\t\toverflow: hidden;\n\t\tbox-sizing: border-box;\n\t}\n}\n\n@mixin settings-icon-reset {\n\t.icon {\n\t\t&:active,\n\t\t&.active {\n\t\t\ttransform: none;\n\t\t\tbackground-color: transparent !important;\n\t\t}\n\t}\n}\n\nwc-page.main-settings-page {\n\tbackground: var(--secondary-color);\n\t@include settings-page-shell(\".main-settings-list\");\n\n\t.main-settings-list > .list-item,\n\t.settings-section-card > .list-item {\n\t\tdisplay: flex;\n\t\twidth: 100%;\n\t\tmin-height: 64px;\n\t\tmargin: 0;\n\t\tpadding: 0.75rem 1rem;\n\t\tbox-sizing: border-box;\n\t\talign-items: center;\n\t\tgap: 0.85rem;\n\t\tbackground: transparent;\n\t\tcursor: pointer;\n\n\t\t&.no-leading-icon {\n\t\t\tgap: 0;\n\t\t}\n\n\t\t&:not(:last-of-type) {\n\t\t\tborder-bottom: 1px solid var(--border-color);\n\t\t\tborder-bottom: 1px solid color-mix(in srgb, var(--border-color), transparent 20%);\n\t\t}\n\n\t\t&:focus,\n\t\t&:active {\n\t\t\tbackground: var(--popup-background-color);\n\t\t\tbackground: color-mix(in srgb, var(--secondary-color), var(--popup-text-color) 4%);\n\t\t}\n\n\t\t> .icon.no-icon {\n\t\t\twidth: 0;\n\t\t\tmin-width: 0;\n\t\t\theight: 0;\n\t\t\tmargin: 0;\n\t\t}\n\n\t\t> .icon:first-child:not(.no-icon) {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\twidth: 1.6rem;\n\t\t\tmin-width: 1.6rem;\n\t\t\theight: 1.6rem;\n\t\t\tfont-size: 1.3rem;\n\t\t\tcolor: var(--secondary-text-color);\n\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 18%);\n\t\t}\n\n\t\t> .container {\n\t\t\tflex: 1;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tmin-width: 0;\n\t\t\toverflow: visible;\n\t\t\tgap: 0.2rem;\n\n\t\t\t> .text {\n\t\t\t\tdisplay: block;\n\t\t\t\tflex: none;\n\t\t\t\tmin-width: 0;\n\t\t\t\tfont-size: 1rem;\n\t\t\t\tline-height: 1.3;\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: var(--popup-text-color);\n\t\t\t}\n\n\t\t\t> .value {\n\t\t\t\tflex: none;\n\t\t\t\tfont-size: 0.82rem;\n\t\t\t\tline-height: 1.35;\n\t\t\t\topacity: 1;\n\t\t\t\tcolor: var(--secondary-text-color);\n\t\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 30%);\n\t\t\t\ttext-transform: none;\n\t\t\t\twhite-space: normal;\n\t\t\t\toverflow: visible;\n\t\t\t\toverflow-wrap: anywhere;\n\t\t\t}\n\t\t}\n\n\t\t&.compact {\n\t\t\tmin-height: 56px;\n\t\t}\n\t}\n\n\t.main-settings-list .settings-chevron {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 1.2rem;\n\t\tmin-width: 1.2rem;\n\t\theight: 1.2rem;\n\t\tmargin-left: 0.35rem;\n\t\tfont-size: 1.1rem;\n\t\tcolor: var(--secondary-text-color);\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 40%);\n\t}\n\n\t.settings-search-section .list-item {\n\t\t> .container {\n\t\t\tpadding-right: 0.35rem;\n\t\t\tgap: 0.18rem;\n\t\t}\n\n\t\t> .setting-tail {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: flex-end;\n\t\t\tflex-shrink: 0;\n\t\t\tmin-height: 1.65rem;\n\t\t\tgap: 0.32rem;\n\t\t\tmargin-left: 0.9rem;\n\t\t\talign-self: center;\n\t\t}\n\n\t\t&.has-subtitle > .container {\n\t\t\tgap: 0.24rem;\n\t\t\tpadding-top: 0.14rem;\n\t\t\tpadding-right: 0.6rem;\n\t\t}\n\n\t\t&.has-subtitle.has-tail-select > .container {\n\t\t\tpadding-right: 0.95rem;\n\t\t}\n\n\t\t&.compact > .container {\n\t\t\talign-self: center;\n\t\t\tjustify-content: center;\n\t\t\tgap: 0;\n\t\t\tpadding-top: 0;\n\t\t}\n\n\t\t&.compact > .setting-tail {\n\t\t\talign-self: center;\n\t\t}\n\t}\n\n\t.settings-search-section .setting-value-display {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 0.15rem;\n\t\tmin-height: auto;\n\t\tpadding: 0;\n\t\tborder: none;\n\t\tborder-radius: 0;\n\t\tbackground: transparent;\n\t\tcolor: var(--secondary-text-color);\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 18%);\n\t\tbox-sizing: border-box;\n\n\t\t&.is-select {\n\t\t\tmin-width: clamp(6.75rem, 30vw, 8.5rem);\n\t\t\tmax-width: min(45vw, 11.5rem);\n\t\t\tmin-height: 2.35rem;\n\t\t\tpadding: 0 0.8rem 0 0.95rem;\n\t\t\tgap: 0.55rem;\n\t\t\tjustify-content: space-between;\n\t\t\tborder: 1px solid var(--border-color);\n\t\t\tborder: 1px solid color-mix(in srgb, var(--border-color), transparent 12%);\n\t\t\tborder-radius: 0.56rem;\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--secondary-color),\n\t\t\t\tvar(--popup-background-color) 42%\n\t\t\t);\n\t\t\tbox-shadow:\n\t\t\t\tinset 0 0 0 1px color-mix(in srgb, var(--border-color), transparent 44%),\n\t\t\t\t0 1px 2px rgba(0, 0, 0, 0.12);\n\t\t}\n\t}\n\n\t.settings-search-section .setting-trailing-value {\n\t\tfont-size: 0.88rem;\n\t\tline-height: 1.2;\n\t\tcolor: inherit;\n\t\ttext-transform: none;\n\t\twhite-space: nowrap;\n\n\t\t&.is-select {\n\t\t\tflex: 1;\n\t\t\tmin-width: 0;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t\tfont-size: 0.94rem;\n\t\t\tfont-weight: 500;\n\t\t\tletter-spacing: -0.01em;\n\t\t\tcolor: color-mix(in srgb, var(--popup-text-color), transparent 14%);\n\t\t}\n\t}\n\n\t.settings-search-section .setting-value-icon,\n\t.settings-search-section .settings-chevron,\n\t.main-settings-list .settings-chevron {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tcolor: var(--secondary-text-color);\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 40%);\n\t}\n\n\t.settings-search-section .setting-value-icon {\n\t\twidth: 1rem;\n\t\tmin-width: 1rem;\n\t\theight: 1rem;\n\t\tfont-size: 1rem;\n\n\t\t.setting-value-display.is-select & {\n\t\t\twidth: 0.95rem;\n\t\t\tmin-width: 0.95rem;\n\t\t\theight: 0.95rem;\n\t\t\tfont-size: 1rem;\n\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 22%);\n\t\t}\n\t}\n\n\t.settings-search-section .settings-chevron,\n\t.main-settings-list .settings-chevron {\n\t\twidth: 1.2rem;\n\t\tmin-width: 1.2rem;\n\t\theight: 1.2rem;\n\t\tmargin-left: 0.1rem;\n\t\tfont-size: 1.1rem;\n\t\tline-height: 1;\n\t}\n\n\t.settings-search-section .input-checkbox {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: flex-end;\n\t\tline-height: 0;\n\n\t\tspan:last-child {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t.box {\n\t\t\twidth: 2.8rem !important;\n\t\t\theight: 1.65rem !important;\n\t\t\tmargin: 0;\n\t\t\tborder: 1px solid var(--border-color);\n\t\t\tborder: 1px solid color-mix(in srgb, var(--border-color), transparent 6%);\n\t\t\tborder-radius: 999px;\n\t\t\tbackground: var(--popup-background-color);\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--secondary-color),\n\t\t\t\tvar(--popup-background-color) 30%\n\t\t\t);\n\t\t\ttransition:\n\t\t\t\tbackground-color 160ms ease,\n\t\t\t\tborder-color 160ms ease,\n\t\t\t\tbox-shadow 180ms ease;\n\n\t\t\t&::after {\n\t\t\t\twidth: 1.25rem;\n\t\t\t\theight: 1.25rem;\n\t\t\t\tmargin: 0.14rem;\n\t\t\t\tborder-radius: 999px;\n\t\t\t\tbackground: var(--popup-text-color);\n\t\t\t\tbackground: color-mix(\n\t\t\t\t\tin srgb,\n\t\t\t\t\tvar(--popup-text-color),\n\t\t\t\t\tvar(--popup-background-color) 18%\n\t\t\t\t);\n\t\t\t\topacity: 1;\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 0 0 1px color-mix(in srgb, var(--border-color), transparent 34%),\n\t\t\t\t\t0 1px 3px rgba(0, 0, 0, 0.22);\n\t\t\t\ttransition:\n\t\t\t\t\ttransform 180ms cubic-bezier(0.2, 0.9, 0.3, 1),\n\t\t\t\t\tbackground-color 160ms ease,\n\t\t\t\t\tbox-shadow 180ms ease;\n\t\t\t}\n\t\t}\n\n\t\tinput:checked + .box {\n\t\t\tbackground: var(--button-background-color);\n\t\t\tborder-color: color-mix(in srgb, var(--button-background-color), transparent 10%);\n\t\t\tbox-shadow: inset 0 0 0 1px\n\t\t\t\tcolor-mix(in srgb, var(--button-background-color), transparent 12%);\n\t\t}\n\n\t\tinput:checked + .box::after {\n\t\t\ttransform: translateX(1.12rem);\n\t\t\tbackground: var(--button-text-color);\n\t\t\topacity: 1;\n\t\t\tbox-shadow: 0 2px 8px color-mix(in srgb, var(--button-background-color), transparent 55%);\n\t\t}\n\n\t\tinput:not(:checked) + .box::after {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t.settings-search-section\n\t\t.list-item.has-tail-select:focus\n\t\t.setting-value-display.is-select,\n\t.settings-search-section\n\t\t.list-item.has-tail-select:active\n\t\t.setting-value-display.is-select {\n\t\tborder-color: color-mix(in srgb, var(--active-color), transparent 48%);\n\t\tbackground: color-mix(\n\t\t\tin srgb,\n\t\t\tvar(--popup-background-color),\n\t\t\tvar(--active-color) 9%\n\t\t);\n\t}\n\n\t@media screen and (min-width: 768px) {\n\t\t.main-settings-list {\n\t\t\tpadding-left: 0.5rem;\n\t\t\tpadding-right: 0.5rem;\n\t\t}\n\t}\n\n\t@media screen and (max-width: 480px) {\n\t\t.settings-search-section .setting-trailing-value {\n\t\t\tmax-width: 7rem;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t}\n\n\t\t.settings-search-section .setting-value-display.is-select {\n\t\t\tmin-width: 6.25rem;\n\t\t\tmax-width: 9rem;\n\t\t\tpadding-left: 0.85rem;\n\t\t\tpadding-right: 0.7rem;\n\t\t}\n\t}\n\n\t@include settings-icon-reset;\n}\n\nwc-page.detail-settings-page {\n\tbackground: var(--secondary-color);\n\t@include settings-page-shell(\".detail-settings-list\");\n\n\t.detail-settings-list > .list-item,\n\t.settings-section-card > .list-item {\n\t\tdisplay: flex;\n\t\twidth: 100%;\n\t\tmargin: 0;\n\t\tpadding: 0.75rem 1rem;\n\t\tmin-height: 3.2rem;\n\t\tbox-sizing: border-box;\n\t\talign-items: center;\n\t\tgap: 0.85rem;\n\t\tbackground: transparent;\n\t\tcursor: pointer;\n\t\ttransition: background-color 140ms ease;\n\n\t\t&.compact {\n\t\t\talign-items: center;\n\t\t}\n\n\t\t&.no-leading-icon {\n\t\t\tgap: 0;\n\t\t}\n\n\t\t&:not(:last-of-type) {\n\t\t\tborder-bottom: 1px solid var(--border-color);\n\t\t\tborder-bottom: 1px solid color-mix(in srgb, var(--border-color), transparent 20%);\n\t\t}\n\n\t\t&:focus,\n\t\t&:active {\n\t\t\tbackground: var(--popup-background-color);\n\t\t\tbackground: color-mix(in srgb, var(--secondary-color), var(--popup-text-color) 4%);\n\t\t}\n\n\t\t> .icon.no-icon {\n\t\t\twidth: 0;\n\t\t\tmin-width: 0;\n\t\t\theight: 0;\n\t\t\tmargin: 0;\n\t\t}\n\n\t\t> .icon:first-child:not(.no-icon) {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\twidth: 1.4rem;\n\t\t\tmin-width: 1.4rem;\n\t\t\theight: 1.4rem;\n\t\t\tfont-size: 1.15rem;\n\t\t\tcolor: var(--secondary-text-color);\n\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 18%);\n\t\t}\n\n\t\t> .container {\n\t\t\tflex: 1;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tmin-width: 0;\n\t\t\toverflow: visible;\n\t\t\tgap: 0.18rem;\n\t\t\tpadding-right: 0.35rem;\n\n\t\t\t> .text {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tflex: none;\n\t\t\t\tmin-width: 0;\n\t\t\t\tfont-size: 1rem;\n\t\t\t\tline-height: 1.2;\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: var(--popup-text-color);\n\t\t\t}\n\n\t\t\t> .value {\n\t\t\t\tflex: none;\n\t\t\t\tfont-size: 0.82rem;\n\t\t\t\tline-height: 1.35;\n\t\t\t\topacity: 1;\n\t\t\t\tcolor: var(--secondary-text-color);\n\t\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 30%);\n\t\t\t\ttext-transform: none;\n\t\t\t\twhite-space: normal;\n\t\t\t\toverflow: visible;\n\t\t\t\toverflow-wrap: anywhere;\n\t\t\t}\n\t\t}\n\n\t\t> .setting-tail {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: flex-end;\n\t\t\tflex-shrink: 0;\n\t\t\tmin-height: 1.65rem;\n\t\t\tgap: 0.32rem;\n\t\t\tmargin-left: 0.9rem;\n\t\t\talign-self: center;\n\t\t}\n\t}\n\n\t.detail-settings-list > .list-item.has-subtitle > .container,\n\t.settings-section-card > .list-item.has-subtitle > .container {\n\t\tgap: 0.24rem;\n\t\tpadding-top: 0.14rem;\n\t\tpadding-right: 0.6rem;\n\t}\n\n\t.detail-settings-list > .list-item.has-subtitle.has-tail-select > .container,\n\t.settings-section-card > .list-item.has-subtitle.has-tail-select > .container {\n\t\tpadding-right: 0.95rem;\n\t}\n\n\t.detail-settings-list > .list-item.compact > .container,\n\t.settings-section-card > .list-item.compact > .container {\n\t\talign-self: center;\n\t\tjustify-content: center;\n\t\tgap: 0;\n\t\tpadding-top: 0;\n\t}\n\n\t.detail-settings-list > .list-item.compact > .setting-tail,\n\t.settings-section-card > .list-item.compact > .setting-tail {\n\t\talign-self: center;\n\t}\n\n\t.setting-value-display {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 0.15rem;\n\t\tmin-height: auto;\n\t\tpadding: 0;\n\t\tborder: none;\n\t\tborder-radius: 0;\n\t\tbackground: transparent;\n\t\tcolor: var(--secondary-text-color);\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 18%);\n\t\tbox-sizing: border-box;\n\n\t\t&.is-select {\n\t\t\tmin-width: clamp(6.75rem, 30vw, 8.5rem);\n\t\t\tmax-width: min(45vw, 11.5rem);\n\t\t\tmin-height: 2.35rem;\n\t\t\tpadding: 0 0.8rem 0 0.95rem;\n\t\t\tgap: 0.55rem;\n\t\t\tjustify-content: space-between;\n\t\t\tborder: 1px solid var(--border-color);\n\t\t\tborder: 1px solid color-mix(in srgb, var(--border-color), transparent 12%);\n\t\t\tborder-radius: 0.56rem;\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--secondary-color),\n\t\t\t\tvar(--popup-background-color) 42%\n\t\t\t);\n\t\t\tbox-shadow:\n\t\t\t\tinset 0 0 0 1px color-mix(in srgb, var(--border-color), transparent 44%),\n\t\t\t\t0 1px 2px rgba(0, 0, 0, 0.12);\n\t\t}\n\t}\n\n\t.setting-trailing-value {\n\t\tfont-size: 0.88rem;\n\t\tline-height: 1.2;\n\t\tcolor: inherit;\n\t\ttext-transform: none;\n\t\twhite-space: nowrap;\n\n\t\t&.is-select {\n\t\t\tflex: 1;\n\t\t\tmin-width: 0;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t\tfont-size: 0.94rem;\n\t\t\tfont-weight: 500;\n\t\t\tletter-spacing: -0.01em;\n\t\t\tcolor: color-mix(in srgb, var(--popup-text-color), transparent 14%);\n\t\t}\n\t}\n\n\t.setting-value-icon,\n\t.settings-chevron {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tcolor: var(--secondary-text-color);\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 40%);\n\t}\n\n\t.setting-value-icon {\n\t\twidth: 1rem;\n\t\tmin-width: 1rem;\n\t\theight: 1rem;\n\t\tfont-size: 1rem;\n\n\t\t.setting-value-display.is-select & {\n\t\t\twidth: 0.95rem;\n\t\t\tmin-width: 0.95rem;\n\t\t\theight: 0.95rem;\n\t\t\tfont-size: 1rem;\n\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 22%);\n\t\t}\n\t}\n\n\t.settings-chevron {\n\t\twidth: 1.2rem;\n\t\tmin-width: 1.2rem;\n\t\theight: 1.2rem;\n\t\tmargin-left: 0.1rem;\n\t\tfont-size: 1.1rem;\n\t\tline-height: 1;\n\t}\n\n\t.input-checkbox {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: flex-end;\n\t\tline-height: 0;\n\n\t\tspan:last-child {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t.box {\n\t\t\twidth: 2.8rem !important;\n\t\t\theight: 1.65rem !important;\n\t\t\tmargin: 0;\n\t\t\tborder: 1px solid var(--border-color);\n\t\t\tborder: 1px solid color-mix(in srgb, var(--border-color), transparent 6%);\n\t\t\tborder-radius: 999px;\n\t\t\tbackground: var(--popup-background-color);\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--secondary-color),\n\t\t\t\tvar(--popup-background-color) 30%\n\t\t\t);\n\t\t\ttransition:\n\t\t\t\tbackground-color 160ms ease,\n\t\t\t\tborder-color 160ms ease,\n\t\t\t\tbox-shadow 180ms ease;\n\n\t\t\t&::after {\n\t\t\t\twidth: 1.25rem;\n\t\t\t\theight: 1.25rem;\n\t\t\t\tmargin: 0.14rem;\n\t\t\t\tborder-radius: 999px;\n\t\t\t\tbackground: var(--popup-text-color);\n\t\t\t\tbackground: color-mix(\n\t\t\t\t\tin srgb,\n\t\t\t\t\tvar(--popup-text-color),\n\t\t\t\t\tvar(--popup-background-color) 18%\n\t\t\t\t);\n\t\t\t\topacity: 1;\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 0 0 1px var(--border-color),\n\t\t\t\t\t0 1px 3px rgba(0, 0, 0, 0.22);\n\t\t\t\tbox-shadow:\n\t\t\t\t\t0 0 0 1px color-mix(in srgb, var(--border-color), transparent 34%),\n\t\t\t\t\t0 1px 3px rgba(0, 0, 0, 0.22);\n\t\t\t\ttransition:\n\t\t\t\t\ttransform 180ms cubic-bezier(0.2, 0.9, 0.3, 1),\n\t\t\t\t\tbackground-color 160ms ease,\n\t\t\t\t\tbox-shadow 180ms ease;\n\t\t\t}\n\t\t}\n\n\t\tinput:checked + .box {\n\t\t\tbackground: var(--button-background-color);\n\t\t\tborder-color: var(--button-background-color);\n\t\t\tborder-color: color-mix(in srgb, var(--button-background-color), transparent 10%);\n\t\t\tbox-shadow: inset 0 0 0 1px var(--button-background-color);\n\t\t\tbox-shadow: inset 0 0 0 1px\n\t\t\t\tcolor-mix(in srgb, var(--button-background-color), transparent 12%);\n\t\t}\n\n\t\tinput:checked + .box::after {\n\t\t\ttransform: translateX(1.12rem);\n\t\t\tbackground: var(--button-text-color);\n\t\t\topacity: 1;\n\t\t\tbox-shadow: 0 2px 8px var(--button-background-color);\n\t\t\tbox-shadow: 0 2px 8px color-mix(in srgb, var(--button-background-color), transparent 55%);\n\t\t}\n\n\t\tinput:not(:checked) + .box::after {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t.note {\n\t\tdisplay: flex;\n\t\talign-items: flex-start;\n\t\tgap: 0.6rem;\n\t\twidth: auto;\n\t\tbox-sizing: border-box;\n\t\tmargin: 0.25rem 0.75rem;\n\t\tpadding: 0.9rem 1rem;\n\t\tborder: 1px solid var(--active-color);\n\t\tborder: 1px solid color-mix(in srgb, var(--active-color), transparent 60%);\n\t\tborder-radius: 10px;\n\t\tbackground: var(--popup-background-color);\n\t\tbackground: color-mix(in srgb, var(--popup-background-color), var(--active-color) 8%);\n\n\t\t.note-title {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\tflex-shrink: 0;\n\t\t\theight: auto;\n\t\t\tpadding: 0;\n\t\t\tbackground: transparent;\n\t\t\ttext-transform: none;\n\n\t\t\t> .icon {\n\t\t\t\tdisplay: inline-flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\twidth: 1.15rem;\n\t\t\t\tmin-width: 1.15rem;\n\t\t\t\theight: 1.15rem;\n\t\t\t\tbackground: transparent;\n\t\t\t\tcolor: var(--active-color);\n\t\t\t\tcolor: color-mix(in srgb, var(--active-color), transparent 10%);\n\t\t\t\tfont-size: 1.1rem;\n\t\t\t\tmargin-top: 0.12rem;\n\t\t\t}\n\n\t\t\t> span:last-child {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\n\t\tp {\n\t\t\tmargin: 0;\n\t\t\tpadding: 0;\n\t\t\tfont-size: 0.84rem;\n\t\t\tline-height: 1.55;\n\t\t\tcolor: var(--secondary-text-color);\n\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 8%);\n\t\t}\n\t}\n\n\t@media screen and (max-width: 480px) {\n\t\t.setting-trailing-value {\n\t\t\tmax-width: 7rem;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t}\n\n\t\t.setting-value-display.is-select {\n\t\t\tmin-width: 6.25rem;\n\t\t\tmax-width: 9rem;\n\t\t\tpadding-left: 0.85rem;\n\t\t\tpadding-right: 0.7rem;\n\t\t}\n\t}\n\n\t.detail-settings-list > .list-item.has-tail-select:focus .setting-value-display.is-select,\n\t.detail-settings-list > .list-item.has-tail-select:active .setting-value-display.is-select,\n\t.settings-section-card\n\t\t> .list-item.has-tail-select:focus\n\t\t.setting-value-display.is-select,\n\t.settings-section-card\n\t\t> .list-item.has-tail-select:active\n\t\t.setting-value-display.is-select {\n\t\tborder-color: color-mix(in srgb, var(--active-color), transparent 48%);\n\t\tbackground: color-mix(\n\t\t\tin srgb,\n\t\t\tvar(--popup-background-color),\n\t\t\tvar(--active-color) 9%\n\t\t);\n\t}\n\n\t@include settings-icon-reset;\n}\n\nwc-page.detail-settings-page.formatter-settings-page {\n\t.detail-settings-list > .list-item,\n\t.settings-section-card > .list-item {\n\t\tpadding-top: 0.5rem;\n\t\tpadding-bottom: 0.5rem;\n\n\t\t&.compact {\n\t\t\tmin-height: 3.5rem;\n\t\t\tpadding-top: 0.18rem;\n\t\t\tpadding-bottom: 0.18rem;\n\t\t}\n\n\t\t&.has-subtitle {\n\t\t\tmin-height: 4.5rem;\n\t\t\tpadding-top: 0.74rem;\n\t\t\tpadding-bottom: 0.74rem;\n\t\t}\n\n\t\t> .icon:first-child:not(.no-icon) {\n\t\t\twidth: 1.1rem;\n\t\t\tmin-width: 1.1rem;\n\t\t\theight: 1.1rem;\n\t\t\tmargin-right: 0.7rem;\n\t\t\tfont-size: 1rem;\n\t\t}\n\n\t\t> .container {\n\t\t\tgap: 0.2rem;\n\t\t}\n\n\t\t> .container > .value {\n\t\t\t-webkit-line-clamp: unset;\n\t\t}\n\t}\n\n\t.detail-settings-list > .list-item.compact > .container,\n\t.settings-section-card > .list-item.compact > .container,\n\t.detail-settings-list > .list-item.compact > .setting-tail,\n\t.settings-section-card > .list-item.compact > .setting-tail {\n\t\talign-self: center;\n\t}\n}\n"
  },
  {
    "path": "src/components/sideButton/index.js",
    "content": "import \"./style.scss\";\n\n/**@type {HTMLDivElement} */\nexport const sideButtonContainer = <div className=\"side-buttons\"></div>;\n\nexport default function SideButtons({\n\ttext,\n\ticon,\n\tonclick,\n\tbackgroundColor,\n\ttextColor,\n}) {\n\tconst $button = (\n\t\t<button\n\t\t\tclassName=\"side-button\"\n\t\t\tonclick={onclick}\n\t\t\tstyle={{ backgroundColor, color: textColor }}\n\t\t>\n\t\t\t<spam className={`icon ${icon}`}></spam>\n\t\t\t<span>{text}</span>\n\t\t</button>\n\t);\n\n\treturn {\n\t\tshow() {\n\t\t\tsideButtonContainer.append($button);\n\t\t},\n\t\thide() {\n\t\t\t$button.remove();\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "src/components/sideButton/style.scss",
    "content": ".side-button {\n  position: relative;\n  width: 15px;\n  padding: 2.5px 0;\n  border: none;\n  box-sizing: border-box;\n\n  .icon {\n    margin: 0 0 5px 0;\n  }\n\n  span {\n    writing-mode: vertical-rl;\n  }\n\n  &:active::before {\n    content: '';\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    background-color: rgba($color: #000000, $alpha: 0.5);\n  }\n}\n\n.side-buttons {\n  position: absolute;\n  right: 0;\n  top: 0;\n  z-index: 999;\n  display: flex;\n  flex-direction: column;\n  gap: 5px;\n}"
  },
  {
    "path": "src/components/sidebar/index.js",
    "content": "import \"./style.scss\";\nimport toast from \"components/toast\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport auth, { loginEvents } from \"lib/auth\";\nimport constants from \"lib/constants\";\n\nlet $sidebar;\n/**@type {Array<(el:HTMLElement)=>boolean>} */\nlet preventSlideTests = [];\n\nconst events = {\n\tshow: [],\n\thide: [],\n};\n\n/**\n * @typedef {object} SideBar\n * @extends HTMLElement\n * @property {function():void} hide\n * @property {function():void} toggle\n * @property {function():void} onshow\n */\n\n/**\n * Create a sidebar\n * @param {HTMLElement} [$container] - the element that will contain the sidebar\n * @param {HTMLElement} [$toggler] - the element that will toggle the sidebar\n * @returns {Sidebar}\n */\nfunction create($container, $toggler) {\n\tlet { innerWidth } = window;\n\n\tconst START_THRESHOLD = constants.SIDEBAR_SLIDE_START_THRESHOLD_PX; //Point where to start swipe\n\tconst MIN_WIDTH = 200; //Min width of the side bar\n\tconst MAX_WIDTH = () => innerWidth * 0.7; //Max width of the side bar\n\tconst resizeBar = Ref();\n\tconst userAvatar = Ref();\n\tconst userContextMenu = Ref();\n\n\t$container = $container || app;\n\tlet mode = innerWidth > 600 ? \"tab\" : \"phone\";\n\tlet width = +(localStorage.sideBarWidth || MIN_WIDTH);\n\n\tconst eventOptions = { passive: false };\n\tconst $el = (\n\t\t<div id=\"sidebar\" className={mode}>\n\t\t\t<div className=\"apps\">\n\t\t\t\t<div className=\"app-icons-container\"></div>\n\t\t\t\t<div\n\t\t\t\t\tref={userAvatar}\n\t\t\t\t\tclassName=\"user-icon-container\"\n\t\t\t\t\tonclick={handleUserIconClick}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"icon account_circle\"></span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"container\"></div>\n\t\t\t<div\n\t\t\t\tclassName=\"resize-bar w-resize\"\n\t\t\t\tonmousedown={onresize}\n\t\t\t\tontouchstart={onresize}\n\t\t\t></div>\n\n\t\t\t<div ref={userContextMenu} className=\"user-menu\">\n\t\t\t\t<div className=\"user-menu-header\">\n\t\t\t\t\t<div className=\"user-menu-name\"></div>\n\t\t\t\t\t<div className=\"user-menu-email\"></div>\n\t\t\t\t</div>\n\t\t\t\t{/* <div className=\"user-menu-separator\"></div> */}\n\t\t\t\t<div className=\"user-menu-item\" onclick={handleLogout}>\n\t\t\t\t\t<span className=\"icon logout\"></span>\n\t\t\t\t\t{strings.logout}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n\tconst mask = <span className=\"mask\" onclick={hide}></span>;\n\tconst touch = {\n\t\tstartX: 0,\n\t\ttotalX: 0,\n\t\tendX: 0,\n\t\tstartY: 0,\n\t\ttotalY: 0,\n\t\tendY: 0,\n\t\ttarget: null,\n\t};\n\tlet openedFolders = [];\n\tlet resizeTimeout = null;\n\tlet setWidthTimeout = null;\n\n\t$toggler?.addEventListener(\"click\", toggle);\n\t$container.addEventListener(\"touchstart\", ontouchstart, eventOptions);\n\twindow.addEventListener(\"resize\", onWindowResize);\n\n\tif (mode === \"tab\" && localStorage.sidebarShown === \"1\") {\n\t\tshow();\n\t}\n\n\tloginEvents.on(() => {\n\t\tupdateSidebarAvatar();\n\t});\n\n\tasync function handleUserIconClick(e) {\n\t\ttry {\n\t\t\tconst isLoggedIn = await auth.isLoggedIn();\n\n\t\t\tif (!isLoggedIn) {\n\t\t\t\tauth.openLoginUrl();\n\t\t\t} else {\n\t\t\t\ttoggleUserMenu();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error checking login status:\", error);\n\t\t\ttoast(\"Error checking login status\", 3000);\n\t\t}\n\t}\n\n\tfunction toggleUserMenu() {\n\t\tconst menu = userContextMenu.el;\n\t\tconst isActive = menu.classList.toggle(\"active\");\n\n\t\tif (isActive) {\n\t\t\t// Populate user info\n\t\t\tupdateUserMenuInfo();\n\n\t\t\t// Add click outside listener\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.addEventListener(\"click\", handleClickOutside);\n\t\t\t}, 10);\n\t\t} else {\n\t\t\tdocument.removeEventListener(\"click\", handleClickOutside);\n\t\t}\n\t}\n\n\tfunction handleClickOutside(e) {\n\t\tif (\n\t\t\t!userContextMenu.el.contains(e.target) &&\n\t\t\te.target !== userAvatar.el &&\n\t\t\t!userAvatar.el.contains(e.target)\n\t\t) {\n\t\t\tuserContextMenu.el.classList.remove(\"active\");\n\t\t\tdocument.removeEventListener(\"click\", handleClickOutside);\n\t\t}\n\t}\n\n\tasync function updateUserMenuInfo() {\n\t\ttry {\n\t\t\tconst userInfo = await auth.getUserInfo();\n\t\t\tif (userInfo) {\n\t\t\t\tconst menuName = userContextMenu.el.querySelector(\".user-menu-name\");\n\t\t\t\tconst menuEmail = userContextMenu.el.querySelector(\".user-menu-email\");\n\t\t\t\tmenuName.textContent = userInfo.name || \"Anonymous\";\n\t\t\t\tif (userInfo.isAdmin) {\n\t\t\t\t\tmenuName.innerHTML += ' <span class=\"badge\">Admin</span>';\n\t\t\t\t}\n\t\t\t\tmenuEmail.textContent = userInfo.email || \"\";\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error fetching user info:\", error);\n\t\t}\n\t}\n\n\tasync function handleLogout() {\n\t\ttry {\n\t\t\tconst success = await auth.logout();\n\t\t\tif (success) {\n\t\t\t\tuserContextMenu.el.classList.remove(\"active\");\n\t\t\t\tdocument.removeEventListener(\"click\", handleClickOutside);\n\t\t\t\ttoast(\"Logged out successfully\");\n\t\t\t\tupdateSidebarAvatar();\n\t\t\t} else {\n\t\t\t\ttoast(\"Failed to logout\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error during logout:\", error);\n\t\t}\n\t}\n\n\tasync function updateSidebarAvatar() {\n\t\tconst avatarUrl = await auth.getAvatar();\n\t\t// Remove existing icon or avatar\n\t\tconst existingIcon = userAvatar.el.querySelector(\".icon\");\n\t\tconst existingAvatar = userAvatar.el.querySelector(\".avatar\");\n\n\t\tif (existingIcon) {\n\t\t\texistingIcon.remove();\n\t\t}\n\t\tif (existingAvatar) {\n\t\t\texistingAvatar.remove();\n\t\t}\n\n\t\tif (avatarUrl?.startsWith(\"data:\") || avatarUrl?.startsWith(\"http\")) {\n\t\t\t// Create and add avatar image\n\t\t\tconst avatarImg = document.createElement(\"img\");\n\t\t\tavatarImg.className = \"avatar\";\n\t\t\tavatarImg.src = avatarUrl;\n\t\t\tuserAvatar.append(avatarImg);\n\t\t} else {\n\t\t\t// Fallback to default icon\n\t\t\tconst defaultIcon = document.createElement(\"span\");\n\t\t\tdefaultIcon.className = \"icon account_circle\";\n\t\t\tuserAvatar.append(defaultIcon);\n\t\t}\n\t}\n\n\tfunction onWindowResize() {\n\t\tclearTimeout(resizeTimeout);\n\t\tresizeTimeout = setTimeout(() => {\n\t\t\tconst { innerWidth: currentWidth } = window;\n\t\t\tif (innerWidth === currentWidth) return;\n\t\t\thide(true);\n\t\t\tinnerWidth = currentWidth;\n\t\t\t$el.classList.remove(mode);\n\t\t\tmode = innerWidth > 750 ? \"tab\" : \"phone\";\n\t\t\t$el.classList.add(mode);\n\t\t}, 300);\n\t}\n\n\tfunction toggle() {\n\t\tif ($el.activated) return hide(true);\n\t\tshow();\n\t}\n\n\tfunction show() {\n\t\tlocalStorage.sidebarShown = 1;\n\t\t$el.activated = true;\n\t\t$el.onclick = null;\n\n\t\tif (mode === \"phone\") {\n\t\t\tresizeBar.style.display = \"none\";\n\t\t\t$el.onshow();\n\t\t\tapp.append($el, mask);\n\t\t\t$el.classList.add(\"show\");\n\t\t\tdocument.ontouchstart = ontouchstart;\n\n\t\t\tactionStack.push({\n\t\t\t\tid: \"sidebar\",\n\t\t\t\taction: hideMaster,\n\t\t\t});\n\t\t} else {\n\t\t\tsetWidth(width);\n\t\t\tresizeBar.style.display = \"block\";\n\t\t\tapp.append($el);\n\t\t\t$el.onclick = () => {\n\t\t\t\tif (!$el.textContent) acode.exec(\"open-folder\");\n\t\t\t};\n\t\t}\n\t\tonshow();\n\t}\n\n\tfunction hide(hideIfTab = false) {\n\t\tlocalStorage.sidebarShown = 0;\n\t\tif (mode === \"phone\") {\n\t\t\tactionStack.remove(\"sidebar\");\n\t\t\thideMaster();\n\t\t} else if (hideIfTab) {\n\t\t\t$el.activated = false;\n\t\t\troot.style.removeProperty(\"margin-left\");\n\t\t\troot.style.removeProperty(\"width\");\n\t\t\t$el.remove();\n\t\t\t// TODO : Codemirror\n\t\t\t//editorManager.editor.resize(true);\n\t\t}\n\t}\n\n\tfunction hideMaster() {\n\t\t$el.style.transform = null;\n\t\t$el.classList.remove(\"show\");\n\t\tsetTimeout(() => {\n\t\t\t$el.activated = false;\n\t\t\tmask.remove();\n\t\t\t$el.remove();\n\t\t\t$container.style.overflow = null;\n\t\t\tonhide();\n\t\t}, 300);\n\t\tdocument.ontouchstart = null;\n\t\tresetState();\n\n\t\topenedFolders.map(($) => ($.onscroll = null));\n\t\topenedFolders = [];\n\t}\n\n\tasync function onshow() {\n\t\tif ($el.onshow) $el.onshow.call($el);\n\t\tevents.show.forEach((fn) => fn());\n\n\t\t// try {\n\t\t// \tif (await auth.isLoggedIn()) {\n\t\t// \t\tconst avatar = await auth.getAvatar();\n\t\t// \t\tif (avatar) {\n\t\t// \t\t\tauth.updateSidebarAvatar(avatar);\n\t\t// \t\t}\n\t\t// \t}\n\t\t// } catch (error) {\n\t\t// \tconsole.error(\"Error updating avatar:\", error);\n\t\t// }\n\t}\n\n\tfunction onhide() {\n\t\tif ($el.onhide) $el.onhide.call($el);\n\t\tevents.hide.forEach((fn) => fn());\n\t}\n\n\t/**\n\t * Event handler for touchstart event\n\t * @param {TouchEvent} e\n\t */\n\tfunction ontouchstart(e) {\n\t\tconst { target } = e;\n\t\tconst { clientX, clientY } = getClientCoords(e);\n\n\t\tif (preventSlideTests.find((test) => test(target))) return;\n\t\tif (mode === \"tab\") return;\n\n\t\t$el.style.transition = \"none\";\n\t\ttouch.startX = clientX;\n\t\ttouch.startY = clientY;\n\t\ttouch.target = target;\n\n\t\tif ($el.activated && !$el.contains(target) && target !== mask) {\n\t\t\treturn;\n\t\t} else if (\n\t\t\t(!$el.activated && touch.startX > START_THRESHOLD) ||\n\t\t\ttarget === $toggler\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tdocument.addEventListener(\"touchmove\", ontouchmove, eventOptions);\n\t\tdocument.addEventListener(\"touchend\", ontouchend, eventOptions);\n\t}\n\n\t/**\n\t * Event handler for resize event\n\t * @param {MouseEvent | TouchEvent} e\n\t * @returns\n\t */\n\tfunction onresize(e) {\n\t\tconst { clientX } = getClientCoords(e);\n\t\tlet deltaX = 0;\n\t\tconst onMove = (e) => {\n\t\t\tconst { clientX: currentX } = getClientCoords(e);\n\t\t\tdeltaX = currentX - clientX;\n\t\t\tresize(deltaX);\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tconst newWidth = width + deltaX;\n\t\t\tif (newWidth <= MIN_WIDTH) width = MIN_WIDTH;\n\t\t\telse if (newWidth >= MAX_WIDTH()) width = MAX_WIDTH();\n\t\t\telse width = newWidth;\n\t\t\tlocalStorage.sideBarWidth = width;\n\t\t\tdocument.removeEventListener(\"touchmove\", onMove, eventOptions);\n\t\t\tdocument.removeEventListener(\"mousemove\", onMove, eventOptions);\n\t\t\tdocument.removeEventListener(\"touchend\", onEnd, eventOptions);\n\t\t\tdocument.removeEventListener(\"mouseup\", onEnd, eventOptions);\n\t\t\tdocument.removeEventListener(\"mouseleave\", onEnd, eventOptions);\n\t\t\tdocument.removeEventListener(\"touchcancel\", onEnd, eventOptions);\n\t\t};\n\t\tdocument.addEventListener(\"touchmove\", onMove, eventOptions);\n\t\tdocument.addEventListener(\"mousemove\", onMove, eventOptions);\n\t\tdocument.addEventListener(\"touchend\", onEnd, eventOptions);\n\t\tdocument.addEventListener(\"mouseup\", onEnd, eventOptions);\n\t\tdocument.addEventListener(\"mouseleave\", onEnd, eventOptions);\n\t\tdocument.addEventListener(\"touchcancel\", onEnd, eventOptions);\n\t\treturn;\n\t}\n\n\t/**\n\t * Resize the sidebar\n\t * @param {number} deltaX\n\t * @returns\n\t */\n\tfunction resize(deltaX) {\n\t\tconst newWidth = width + deltaX;\n\t\tif (newWidth >= MAX_WIDTH()) return;\n\t\tif (newWidth <= MIN_WIDTH) return;\n\t\tsetWidth(newWidth);\n\t}\n\n\t/**\n\t * Event handler for touchmove event\n\t * @param {TouchEvent} e\n\t */\n\tfunction ontouchmove(e) {\n\t\te.preventDefault();\n\n\t\tconst { clientX, clientY } = getClientCoords(e);\n\t\ttouch.endX = clientX;\n\t\ttouch.endY = clientY;\n\t\ttouch.totalX = touch.endX - touch.startX;\n\t\ttouch.totalY = touch.endY - touch.startY;\n\n\t\tlet width = $el.getWidth();\n\n\t\tif (\n\t\t\t!$el.activated &&\n\t\t\ttouch.totalX < width &&\n\t\t\ttouch.startX < START_THRESHOLD\n\t\t) {\n\t\t\tif (!$el.isConnected) {\n\t\t\t\tapp.append($el, mask);\n\t\t\t\t$container.style.overflow = \"hidden\";\n\t\t\t}\n\n\t\t\t$el.style.transform = `translate3d(${-(width - touch.totalX)}px, 0, 0)`;\n\t\t} else if (touch.totalX < 0 && $el.activated) {\n\t\t\t$el.style.transform = `translate3d(${touch.totalX}px, 0, 0)`;\n\t\t}\n\t}\n\n\t/**\n\t * Event handler for touchend event\n\t * @param {TouchEvent} e\n\t */\n\tfunction ontouchend(e) {\n\t\tif (e.target !== mask && touch.totalX === 0) return resetState();\n\t\telse if (e.target === mask && touch.totalX === 0) return hide();\n\t\te.preventDefault();\n\n\t\tconst threshold = $el.getWidth() / 3;\n\n\t\tif (\n\t\t\t($el.activated && touch.totalX > -threshold) ||\n\t\t\t(!$el.activated && touch.totalX >= threshold)\n\t\t) {\n\t\t\tlclShow();\n\t\t} else if (\n\t\t\t(!$el.activated && touch.totalX < threshold) ||\n\t\t\t($el.activated && touch.totalX <= -threshold)\n\t\t) {\n\t\t\thide();\n\t\t}\n\n\t\tfunction lclShow() {\n\t\t\tonshow();\n\t\t\t$el.activated = true;\n\t\t\t$el.style.transform = `translate3d(0, 0, 0)`;\n\t\t\tdocument.addEventListener(\"touchstart\", ontouchstart, eventOptions);\n\t\t\tactionStack.remove(\"sidebar\");\n\t\t\tactionStack.push({\n\t\t\t\tid: \"sidebar\",\n\t\t\t\taction: hideMaster,\n\t\t\t});\n\t\t\tresetState();\n\t\t}\n\t}\n\n\t/**\n\t * Reset the touch state\n\t */\n\tfunction resetState() {\n\t\ttouch.totalY = 0;\n\t\ttouch.startY = 0;\n\t\ttouch.endY = 0;\n\t\ttouch.totalX = 0;\n\t\ttouch.startX = 0;\n\t\ttouch.endX = 0;\n\t\ttouch.target = null;\n\t\t$el.style.transition = null;\n\t\tdocument.removeEventListener(\"touchmove\", ontouchmove, eventOptions);\n\t\tdocument.removeEventListener(\"touchend\", ontouchend, eventOptions);\n\t}\n\n\t/**\n\t * Set the width of the sidebar\n\t * @param {number} width\n\t */\n\tfunction setWidth(width) {\n\t\t$el.style.transition = \"none\";\n\t\t$el.style.maxWidth = width + \"px\";\n\t\troot.style.marginLeft = width + \"px\";\n\t\troot.style.width = `calc(100% - ${width}px)`;\n\t\tclearTimeout(setWidthTimeout);\n\t\tsetWidthTimeout = setTimeout(() => {\n\t\t\teditorManager?.editor?.resize(true);\n\t\t}, 300);\n\t}\n\n\t/**\n\t * Get the clientX and clientY from the event\n\t * @param {TouchEvent | MouseEvent} e\n\t * @returns {{clientX: number, clientY: number}}\n\t */\n\tfunction getClientCoords(e) {\n\t\tconst { clientX, clientY } = (e.touches ?? [])[0] ?? e;\n\t\treturn { clientX, clientY };\n\t}\n\n\t$el.show = show;\n\t$el.hide = hide;\n\t$el.toggle = toggle;\n\t$el.onshow = () => {};\n\t$el.getWidth = function () {\n\t\tconst width = innerWidth * 0.7;\n\t\treturn mode === \"phone\" ? (width >= 350 ? 350 : width) : MIN_WIDTH;\n\t};\n\n\treturn $el;\n}\n\n/**\n * Create a sidebar or return the existing one\n * @param {object} [arg0] - the element that will activate the sidebar\n * @param {HTMLElement} [arg0.container] - the element that will contain the sidebar\n * @param {HTMLElement} [arg0.toggler] - the element that will toggle the sidebar\n * @returns {HTMLElement & SideBar}\n */\nfunction Sidebar({ container, toggler }) {\n\t$sidebar = $sidebar ?? create(container, toggler);\n\treturn $sidebar;\n}\n\nSidebar.hide = () => $sidebar?.hide();\nSidebar.show = () => $sidebar?.show();\nSidebar.toggle = () => $sidebar?.toggle();\n\nSidebar.on = (\n\t/**@type {'hide'|'show'} */ event,\n\t/**@type {Function} */ callback,\n) => {\n\tif (!events[event]) return;\n\tevents[event].push(callback);\n};\n\nSidebar.off = (\n\t/**@type {'hide'|'show'} */ event,\n\t/**@type {Function} */ callback,\n) => {\n\tif (!events[event]) return;\n\tevents[event] = events[event].filter((cb) => cb !== callback);\n};\n\n/**@type {HTMLElement} */\nSidebar.el = null;\n\nObject.defineProperty(Sidebar, \"el\", {\n\tget() {\n\t\treturn $sidebar;\n\t},\n});\n\npreventSlideTests.push((target) => {\n\tlet lastEl;\n\treturn testScrollable(target.closest(\".scroll\"));\n\n\t/**\n\t * Test if the element is scrollable recursively\n\t * @param {HTMLElement} container\n\t * @returns\n\t */\n\tfunction testScrollable(container) {\n\t\tif (!container || container === lastEl) return false;\n\n\t\tconst { scrollHeight, offsetHeight, scrollWidth, offsetWidth } = container;\n\n\t\tif (scrollHeight > offsetHeight) return true;\n\t\tif (scrollWidth > offsetWidth) return true;\n\n\t\tlastEl = container;\n\n\t\treturn testScrollable(container.parentElement.closest(\".scroll\"));\n\t}\n});\n\npreventSlideTests.push((target) => {\n\treturn (\n\t\ttarget instanceof HTMLInputElement ||\n\t\ttarget instanceof HTMLTextAreaElement ||\n\t\ttarget.contentEditable === \"true\"\n\t);\n});\n\nexport default Sidebar;\n\n/**\n * Prevent the sidebar from sliding when the test returns true\n * @param {(target:Element)=>boolean} test\n */\nexport function preventSlide(test) {\n\tpreventSlideTests.push(test);\n}\n"
  },
  {
    "path": "src/components/sidebar/style.scss",
    "content": "body.no-animation {\n  #sidebar {\n    border-right: solid 1px rgba(0, 0, 0, 0.2);\n  }\n}\n\n.page-replacement~#sidebar {\n  opacity: 0;\n}\n\n#sidebar {\n  z-index: 109;\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 70vw;\n  max-width: 350px;\n  height: 100vh;\n  display: flex;\n  background-color: rgb(153, 153, 255);\n  background-color: var(--primary-color);\n  color: rgb(255, 255, 255);\n  color: var(--primary-text-color);\n  overflow: hidden;\n  box-sizing: border-box;\n\n  &+.mask {\n    z-index: 108;\n  }\n\n  &.phone {\n    transition: transform 300ms ease;\n    transform: translate(-100%, 0);\n    box-shadow: 2px 0 4px rgba(0, 0, 0, 0.07);\n\n    .resize-bar {\n      pointer-events: none;\n    }\n  }\n\n  &.tab {\n    max-width: 250px;\n  }\n\n  &.show {\n    transform: translate(0, 0);\n    animation: show-sidebar 300ms ease 1;\n  }\n\n  .resize-bar {\n    height: 100vh;\n    width: 5px;\n    margin-left: -2.5px;\n    z-index: 110;\n  }\n\n  .apps {\n    width: 52px;\n    min-width: 52px;\n    height: 100%;\n    background-color: rgba(0, 0, 0, 0.15);\n    padding: 0;\n    display: flex;\n    flex-direction: column;\n    position: relative;\n    box-sizing: border-box;\n\n    .app-icons-container {\n      flex: 1;\n      overflow-y: auto;\n      overflow-x: hidden;\n      padding: 8px 0;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n      gap: 4px;\n      scrollbar-width: none;\n      -ms-overflow-style: none;\n\n      &::-webkit-scrollbar {\n        display: none;\n      }\n    }\n\n    .user-icon-container {\n      position: sticky;\n      bottom: 0;\n      width: 100%;\n      padding: 12px 0;\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      background-color: rgba(0, 0, 0, 0.1);\n      border-top: 1px solid rgba(255, 255, 255, 0.08);\n\n      .avatar {\n        display: block;\n        height: 32px;\n        width: 32px;\n        border-radius: 50%;\n        object-fit: cover;\n        object-position: center;\n        cursor: pointer;\n        transition: all 0.2s ease;\n        border: 2px solid transparent;\n\n        &:hover {\n          transform: scale(1.05);\n          border-color: rgba(255, 255, 255, 0.3);\n        }\n      }\n\n      .icon {\n        height: 40px;\n        width: 40px;\n        color: currentColor;\n        font-size: 1.4em;\n        border-radius: 10px;\n        opacity: 0.6;\n        transition: all 0.2s ease;\n        margin: 0 auto;\n        cursor: pointer;\n        text-align: center;\n        line-height: 40px;\n\n        &:hover {\n          opacity: 0.9;\n          background-color: rgba(255, 255, 255, 0.08);\n        }\n\n        &.active {\n          opacity: 1;\n        }\n      }\n    }\n\n    .icon {\n      height: 40px;\n      width: 40px;\n      min-height: 40px;\n      color: currentColor;\n      font-size: 1.3em;\n      border-radius: 10px;\n      opacity: 0.55;\n      transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n      margin: 0 auto;\n      cursor: pointer;\n      position: relative;\n      box-sizing: border-box;\n      text-align: center;\n      line-height: 40px;\n\n      // Active indicator bar using ::after to not interfere with font icon's ::before\n      &::after {\n        content: '';\n        position: absolute;\n        left: -6px;\n        top: 50%;\n        transform: translateY(-50%) scaleY(0);\n        width: 3px;\n        height: 20px;\n        background-color: currentColor;\n        border-radius: 0 2px 2px 0;\n        transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;\n        opacity: 0;\n      }\n\n      &:hover {\n        opacity: 0.85;\n        background-color: rgba(255, 255, 255, 0.08);\n      }\n\n      &.active {\n        opacity: 1;\n        background-color: rgba(255, 255, 255, 0.12);\n\n        &::after {\n          transform: translateY(-50%) scaleY(1);\n          opacity: 1;\n        }\n      }\n\n      // Special styling for the favorites/sponsors icon\n      &.favorite {\n        margin-top: 8px;\n\n        &::after {\n          display: none;\n        }\n\n        &:hover {\n          color: #ff6b8a;\n          opacity: 1;\n          background-color: rgba(255, 107, 138, 0.12);\n        }\n      }\n    }\n  }\n\n  .container {\n    overflow: hidden;\n    box-sizing: border-box;\n    display: flex;\n    flex-direction: column;\n    width: 100%;\n    height: 100%;\n\n    &.files {\n      overflow-x: auto;\n    }\n\n    >.list {\n      width: 100%;\n      max-width: 100%;\n      max-height: 100%;\n\n      &.hidden {\n        max-height: 36px !important;\n        min-height: 36px !important;\n        overflow: hidden !important;\n      }\n\n      .tile {\n        &:not(:first-child) {\n          background-color: inherit;\n        }\n\n        &.notice {\n          &::before {\n            content: \"\\2022\";\n            color: rgb(212, 250, 150);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 1em;\n          }\n        }\n      }\n\n      .icon {\n        height: 34px;\n        width: 34px;\n        min-width: 34px;\n        color: currentColor;\n        font-size: 1.15em;\n        text-align: center;\n        line-height: 34px;\n      }\n\n      >ul {\n        overflow: auto;\n        width: 100%;\n        max-width: 100%;\n        max-height: calc(100% - 36px);\n        height: calc(100% - 36px);\n\n        .tile:active {\n          >*:nth-child(2) {\n            color: rgb(255, 215, 0);\n          }\n        }\n\n        li {\n          >*:nth-child(2) {\n            color: currentColor;\n          }\n\n          &.active {\n            >*:nth-child(2) {\n              color: rgb(255, 215, 0);\n            }\n          }\n        }\n      }\n    }\n\n    .tile {\n      user-select: none;\n    }\n  }\n\n  textarea {\n    padding: 5px !important;\n    box-sizing: border-box;\n  }\n\n  textarea,\n  input {\n    color: rgb(255, 255, 255);\n    color: var(--primary-text-color);\n    border: solid 1px var(--border-color);\n    border: solid 1px color-mix(in srgb, var(--border-color) 70%, transparent);\n    border-radius: 8px;\n    height: 30px;\n    width: 90%;\n    padding: 0 12px;\n    text-indent: 0;\n    margin-bottom: 12px;\n    background: transparent;\n    transition: all 0.2s ease;\n\n    &:focus {\n      border-color: var(--border-color) !important;\n      background: var(--secondary-color);\n      background: color-mix(in srgb, var(--secondary-color) 10%, transparent);\n      box-shadow: 0 0 0 2px var(---box-shadow-color);\n    }\n\n    &::placeholder {\n      opacity: 0.5;\n    }\n  }\n\n  .box {\n    border: solid 1px rgb(255, 255, 255);\n    border: solid 1px var(--primary-text-color);\n\n    &::after {\n      background-color: rgb(255, 255, 255);\n      background-color: var(--primary-text-color);\n    }\n  }\n\n  .header {\n    display: flex;\n    flex-shrink: 0;\n    align-items: center;\n    justify-content: center;\n    flex-direction: column;\n\n    .title {\n      display: flex;\n      width: 100%;\n      height: 36px;\n      align-items: center;\n      justify-content: center;\n      font-weight: 600;\n    }\n  }\n}\n\n.user-menu {\n  position: absolute;\n  bottom: 55px;\n  left: 30px;\n  width: 200px;\n  background-color: var(--popup-background-color);\n  border: 1px solid var(--border-color);\n  border-radius: 6px;\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n  display: none;\n  z-index: 1000;\n  overflow: hidden;\n\n  &.active {\n    display: block;\n  }\n\n  .user-menu-header {\n    padding: 12px;\n    border-bottom: 1px solid var(--border-color);\n\n    .user-menu-name {\n      font-weight: 500;\n      margin-bottom: 4px;\n\n      .badge {\n        display: inline-flex;\n        align-items: center;\n        background-color: var(--popup-background-color);\n        background-color: color-mix(in srgb,\n            var(--error-text-color) 20%,\n            transparent);\n        color: var(--error-text-color);\n        padding: 2px 6px;\n        border-radius: 4px;\n        font-size: 12px;\n        font-weight: 500;\n        margin-left: 8px;\n      }\n    }\n\n    .user-menu-email {\n      font-size: 12px;\n      color: var(--popup-text-color);\n      color: color-mix(in srgb, var(--popup-text-color) 70%, transparent);\n    }\n  }\n\n  .user-menu-item {\n    padding: 10px 12px;\n    cursor: pointer;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n\n    &:hover {\n      background-color: var(--active-icon-color);\n    }\n  }\n\n  .user-menu-separator {\n    height: 1px;\n    background-color: var(--border-color);\n    margin: 4px 0;\n  }\n}\n"
  },
  {
    "path": "src/components/symbolsPanel/index.js",
    "content": "import \"./styles.scss\";\nimport { fetchDocumentSymbols, navigateToSymbol } from \"cm/lsp\";\nimport actionStack from \"lib/actionStack\";\n\nlet currentPanel = null;\n\nconst SYMBOL_KIND_ABBREV = {\n\t1: \"Fi\",\n\t2: \"Mo\",\n\t3: \"Ns\",\n\t4: \"Pk\",\n\t5: \"C\",\n\t6: \"M\",\n\t7: \"P\",\n\t8: \"F\",\n\t9: \"Co\",\n\t10: \"E\",\n\t11: \"I\",\n\t12: \"fn\",\n\t13: \"V\",\n\t14: \"c\",\n\t15: \"S\",\n\t16: \"#\",\n\t17: \"B\",\n\t18: \"[]\",\n\t19: \"{}\",\n\t20: \"K\",\n\t21: \"∅\",\n\t22: \"Em\",\n\t23: \"St\",\n\t24: \"Ev\",\n\t25: \"Op\",\n\t26: \"T\",\n};\n\nfunction sanitize(str) {\n\tif (!str) return \"\";\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\");\n}\n\nfunction flattenSymbolsForDisplay(symbols, depth = 0) {\n\tconst result = [];\n\tfor (const sym of symbols) {\n\t\tresult.push({\n\t\t\t...sym,\n\t\t\tdepth,\n\t\t\tid: `${sym.selectionRange.startLine}-${sym.selectionRange.startCharacter}-${sym.name}`,\n\t\t});\n\t\tif (sym.children && sym.children.length > 0) {\n\t\t\tresult.push(...flattenSymbolsForDisplay(sym.children, depth + 1));\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction createSymbolsPanel() {\n\tconst state = {\n\t\tvisible: false,\n\t\texpanded: false,\n\t\tloading: true,\n\t\tsymbols: [],\n\t\tflatSymbols: [],\n\t\tfilteredSymbols: [],\n\t\tsearchQuery: \"\",\n\t\teditorView: null,\n\t};\n\n\tconst $mask = <span className=\"symbols-panel-mask\" />;\n\tconst $panel = <div className=\"symbols-panel\" />;\n\tconst $dragHandle = <div className=\"drag-handle\" />;\n\tconst $title = <div className=\"header-title\" />;\n\tconst $subtitle = <span className=\"header-subtitle\" />;\n\tconst $searchInput = (\n\t\t<input\n\t\t\ttype=\"text\"\n\t\t\tplaceholder=\"Filter symbols...\"\n\t\t\toninput={onSearchInput}\n\t\t/>\n\t);\n\tconst $content = <div className=\"panel-content\" />;\n\tconst $symbolsList = <div className=\"symbols-list\" />;\n\n\tconst $closeBtn = (\n\t\t<button className=\"action-btn close-btn\" onclick={hide}>\n\t\t\t<span className=\"icon clearclose\" />\n\t\t</button>\n\t);\n\n\tconst $header = (\n\t\t<div className=\"panel-header\">\n\t\t\t<div className=\"header-content\">\n\t\t\t\t{$title}\n\t\t\t\t{$subtitle}\n\t\t\t</div>\n\t\t\t<div className=\"header-actions\">{$closeBtn}</div>\n\t\t</div>\n\t);\n\n\tconst $search = <div className=\"panel-search\">{$searchInput}</div>;\n\n\t$panel.append($dragHandle, $header, $search, $content);\n\t$mask.onclick = hide;\n\n\tlet startY = 0;\n\tlet currentY = 0;\n\tlet isDragging = false;\n\n\t$dragHandle.ontouchstart = onDragStart;\n\t$dragHandle.onmousedown = onDragStart;\n\n\tfunction onDragStart(e) {\n\t\tisDragging = true;\n\t\tstartY = e.touches ? e.touches[0].clientY : e.clientY;\n\t\tcurrentY = startY;\n\t\t$panel.style.transition = \"none\";\n\n\t\tdocument.addEventListener(\"touchmove\", onDragMove, { passive: false });\n\t\tdocument.addEventListener(\"mousemove\", onDragMove);\n\t\tdocument.addEventListener(\"touchend\", onDragEnd);\n\t\tdocument.addEventListener(\"mouseup\", onDragEnd);\n\t}\n\n\tfunction onDragMove(e) {\n\t\tif (!isDragging) return;\n\t\te.preventDefault();\n\t\tcurrentY = e.touches ? e.touches[0].clientY : e.clientY;\n\t\tconst deltaY = currentY - startY;\n\n\t\tif (deltaY > 0) {\n\t\t\t$panel.style.transform = `translateY(${deltaY}px)`;\n\t\t} else if (!state.expanded) {\n\t\t\tconst expansion = Math.min(Math.abs(deltaY), 100);\n\t\t\t$panel.style.maxHeight = `${60 + (expansion / 100) * 25}vh`;\n\t\t}\n\t}\n\n\tfunction onDragEnd() {\n\t\tisDragging = false;\n\t\tdocument.removeEventListener(\"touchmove\", onDragMove);\n\t\tdocument.removeEventListener(\"mousemove\", onDragMove);\n\t\tdocument.removeEventListener(\"touchend\", onDragEnd);\n\t\tdocument.removeEventListener(\"mouseup\", onDragEnd);\n\n\t\t$panel.style.transition = \"\";\n\t\tconst deltaY = currentY - startY;\n\n\t\tif (deltaY > 100) {\n\t\t\thide();\n\t\t} else if (deltaY < -50 && !state.expanded) {\n\t\t\tstate.expanded = true;\n\t\t\t$panel.classList.add(\"expanded\");\n\t\t\t$panel.style.transform = \"\";\n\t\t\t$panel.style.maxHeight = \"\";\n\t\t} else {\n\t\t\t$panel.style.transform = \"\";\n\t\t\t$panel.style.maxHeight = \"\";\n\t\t}\n\t}\n\n\tfunction setTitle(text) {\n\t\t$title.innerHTML = \"\";\n\t\t$title.append(\n\t\t\t<span className=\"icon document-code\" />,\n\t\t\t<span>{sanitize(text)}</span>,\n\t\t);\n\t}\n\n\tfunction setSubtitle(text) {\n\t\t$subtitle.textContent = text;\n\t}\n\n\tfunction onSearchInput(e) {\n\t\tstate.searchQuery = e.target.value.trim();\n\t\tfilterSymbols();\n\t}\n\n\tfunction clearSearch() {\n\t\t$searchInput.value = \"\";\n\t\tstate.searchQuery = \"\";\n\t}\n\n\tfunction filterSymbols() {\n\t\tconst query = state.searchQuery.toLowerCase();\n\n\t\tif (!query) {\n\t\t\tstate.filteredSymbols = state.flatSymbols;\n\t\t} else {\n\t\t\tstate.filteredSymbols = state.flatSymbols.filter((sym) => {\n\t\t\t\tconst nameMatch = sym.name.toLowerCase().includes(query);\n\t\t\t\tconst kindMatch = sym.kindName.toLowerCase().includes(query);\n\t\t\t\tconst detailMatch = sym.detail?.toLowerCase().includes(query);\n\t\t\t\treturn nameMatch || kindMatch || detailMatch;\n\t\t\t});\n\t\t}\n\n\t\tupdateList();\n\t}\n\n\tfunction updateList() {\n\t\tsetSubtitle(getSubtitle());\n\t\trenderSymbolsList();\n\t}\n\n\tfunction getSubtitle() {\n\t\tconst total = state.flatSymbols.length;\n\t\tconst filtered = state.filteredSymbols.length;\n\n\t\tif (total === 0) return \"No symbols\";\n\t\tif (filtered === total) return `${total} symbol${total !== 1 ? \"s\" : \"\"}`;\n\t\treturn `${filtered} of ${total} symbols`;\n\t}\n\n\tfunction renderLoading() {\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"loading-state\">\n\t\t\t\t<div className=\"loader\" />\n\t\t\t\t<span>Loading symbols...</span>\n\t\t\t</div>,\n\t\t);\n\t}\n\n\tfunction renderEmpty() {\n\t\tconst message = state.searchQuery\n\t\t\t? \"No matching symbols\"\n\t\t\t: \"No symbols found\";\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"empty-state\">\n\t\t\t\t<span className=\"icon search\" />\n\t\t\t\t<span>{message}</span>\n\t\t\t</div>,\n\t\t);\n\t}\n\n\tfunction renderNotSupported() {\n\t\t$content.innerHTML = \"\";\n\t\t$content.append(\n\t\t\t<div className=\"empty-state\">\n\t\t\t\t<span className=\"icon error_outline\" />\n\t\t\t\t<span>Language server does not support document symbols</span>\n\t\t\t</div>,\n\t\t);\n\t}\n\n\tfunction highlightMatch(text, query) {\n\t\tif (!query) return sanitize(text);\n\n\t\tconst lowerText = text.toLowerCase();\n\t\tconst lowerQuery = query.toLowerCase();\n\t\tconst index = lowerText.indexOf(lowerQuery);\n\n\t\tif (index === -1) return sanitize(text);\n\n\t\tconst before = sanitize(text.slice(0, index));\n\t\tconst match = sanitize(text.slice(index, index + query.length));\n\t\tconst after = sanitize(text.slice(index + query.length));\n\n\t\treturn `${before}<span class=\"symbol-match\">${match}</span>${after}`;\n\t}\n\n\tfunction createSymbolItem(symbol) {\n\t\tconst kindName = symbol.kindName || \"Unknown\";\n\t\tconst kindClass = `kind-${kindName.toLowerCase().replace(/\\s+/g, \"\")}`;\n\t\tconst abbrev = SYMBOL_KIND_ABBREV[symbol.kind] || \"?\";\n\t\tconst indent = (symbol.depth || 0) * 16;\n\t\tconst startLine = symbol.selectionRange?.startLine ?? 0;\n\n\t\tconst $item = (\n\t\t\t<div className=\"symbol-item\" onclick={() => onSymbolClick(symbol)}>\n\t\t\t\t<span className=\"symbol-indent\" style={`width: ${indent}px`} />\n\t\t\t\t<span className={`symbol-icon ${kindClass}`}>{abbrev}</span>\n\t\t\t\t<span className=\"symbol-info\">\n\t\t\t\t\t<span className=\"symbol-name\" />\n\t\t\t\t\t{symbol.detail && (\n\t\t\t\t\t\t<span className=\"symbol-detail\">{sanitize(symbol.detail)}</span>\n\t\t\t\t\t)}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"symbol-line\">:{startLine + 1}</span>\n\t\t\t</div>\n\t\t);\n\n\t\t$item.get(\".symbol-name\").innerHTML = highlightMatch(\n\t\t\tsymbol.name,\n\t\t\tstate.searchQuery,\n\t\t);\n\n\t\treturn $item;\n\t}\n\n\tfunction renderSymbolsList() {\n\t\t$symbolsList.innerHTML = \"\";\n\n\t\tif (state.filteredSymbols.length === 0) {\n\t\t\trenderEmpty();\n\t\t\treturn;\n\t\t}\n\n\t\t$content.innerHTML = \"\";\n\t\tconst fragment = document.createDocumentFragment();\n\n\t\tfor (const symbol of state.filteredSymbols) {\n\t\t\tfragment.appendChild(createSymbolItem(symbol));\n\t\t}\n\n\t\t$symbolsList.appendChild(fragment);\n\t\t$content.appendChild($symbolsList);\n\t}\n\n\tfunction onSymbolClick(symbol) {\n\t\tif (!state.editorView) return;\n\t\thide();\n\t\tnavigateToSymbol(state.editorView, symbol);\n\t}\n\n\tasync function loadSymbols() {\n\t\tif (!state.editorView) {\n\t\t\trenderNotSupported();\n\t\t\treturn;\n\t\t}\n\n\t\trenderLoading();\n\n\t\ttry {\n\t\t\tconst symbols = await fetchDocumentSymbols(state.editorView);\n\n\t\t\tif (symbols === null) {\n\t\t\t\tstate.loading = false;\n\t\t\t\tsetSubtitle(\"Not supported\");\n\t\t\t\trenderNotSupported();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tstate.loading = false;\n\t\t\tstate.symbols = symbols;\n\t\t\tstate.flatSymbols = flattenSymbolsForDisplay(symbols);\n\t\t\tstate.filteredSymbols = state.flatSymbols;\n\n\t\t\tif (state.flatSymbols.length === 0) {\n\t\t\t\tsetSubtitle(\"No symbols\");\n\t\t\t\trenderEmpty();\n\t\t\t} else {\n\t\t\t\tsetSubtitle(getSubtitle());\n\t\t\t\trenderSymbolsList();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to load symbols:\", error);\n\t\t\tstate.loading = false;\n\t\t\tsetSubtitle(\"Error\");\n\t\t\t$content.innerHTML = \"\";\n\t\t\t$content.append(\n\t\t\t\t<div className=\"empty-state\">\n\t\t\t\t\t<span className=\"icon error_outline\" />\n\t\t\t\t\t<span>{sanitize(error.message || \"Failed to load symbols\")}</span>\n\t\t\t\t</div>,\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction show(options = {}) {\n\t\tif (currentPanel && currentPanel !== panelInstance) {\n\t\t\tcurrentPanel.hide();\n\t\t}\n\t\tcurrentPanel = panelInstance;\n\n\t\tstate.editorView = options.view || null;\n\t\tstate.symbols = [];\n\t\tstate.flatSymbols = [];\n\t\tstate.filteredSymbols = [];\n\t\tstate.searchQuery = \"\";\n\t\tstate.loading = true;\n\t\tstate.expanded = false;\n\n\t\tclearSearch();\n\t\tsetTitle(\"Document Outline\");\n\t\tsetSubtitle(\"Loading...\");\n\t\trenderLoading();\n\n\t\tdocument.body.append($mask, $panel);\n\n\t\trequestAnimationFrame(() => {\n\t\t\t$mask.classList.add(\"visible\");\n\t\t\t$panel.classList.add(\"visible\");\n\t\t\t$panel.classList.remove(\"expanded\");\n\t\t});\n\n\t\tstate.visible = true;\n\n\t\tactionStack.push({\n\t\t\tid: \"symbols-panel\",\n\t\t\taction: hide,\n\t\t});\n\n\t\tloadSymbols();\n\t}\n\n\tfunction hide() {\n\t\tif (!state.visible) return;\n\t\tstate.visible = false;\n\n\t\t$mask.classList.remove(\"visible\");\n\t\t$panel.classList.remove(\"visible\");\n\n\t\tactionStack.remove(\"symbols-panel\");\n\n\t\tsetTimeout(() => {\n\t\t\t$mask.remove();\n\t\t\t$panel.remove();\n\t\t}, 250);\n\n\t\tif (currentPanel === panelInstance) {\n\t\t\tcurrentPanel = null;\n\t\t}\n\n\t\tif (state.editorView) {\n\t\t\tstate.editorView.focus();\n\t\t}\n\t}\n\n\tconst panelInstance = {\n\t\tshow,\n\t\thide,\n\t\tget visible() {\n\t\t\treturn state.visible;\n\t\t},\n\t};\n\n\treturn panelInstance;\n}\n\nlet panelSingleton = null;\n\nfunction getPanel() {\n\tif (!panelSingleton) {\n\t\tpanelSingleton = createSymbolsPanel();\n\t}\n\treturn panelSingleton;\n}\n\nexport function showSymbolsPanel(options) {\n\tconst panel = getPanel();\n\tpanel.show(options);\n\treturn panel;\n}\n\nexport function hideSymbolsPanel() {\n\tconst panel = getPanel();\n\tpanel.hide();\n}\n\nexport async function showDocumentSymbols(view) {\n\tif (!view) {\n\t\tconst em = globalThis.editorManager;\n\t\tview = em?.editor;\n\t}\n\n\tif (!view) {\n\t\tconst toast = globalThis.toast;\n\t\ttoast?.(\"No editor available\");\n\t\treturn false;\n\t}\n\n\tshowSymbolsPanel({ view });\n\treturn true;\n}\n\nexport default {\n\tshow: showSymbolsPanel,\n\thide: hideSymbolsPanel,\n\tshowDocumentSymbols,\n\tgetPanel,\n};\n"
  },
  {
    "path": "src/components/symbolsPanel/styles.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n.symbols-panel-mask {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    background-color: rgba(0, 0, 0, 0.4);\n    z-index: 100;\n    opacity: 0;\n    transition: opacity 200ms ease-out;\n    pointer-events: none;\n\n    &.visible {\n        opacity: 1;\n        pointer-events: auto;\n    }\n}\n\n.symbols-panel {\n    position: fixed;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    z-index: 101;\n    background-color: var(--popup-background-color);\n    border-top-left-radius: 12px;\n    border-top-right-radius: 12px;\n    box-shadow: 0 -4px 20px var(--box-shadow-color);\n    transform: translateY(100%);\n    transition: transform 250ms ease-out, max-height 200ms ease-out;\n    display: flex;\n    flex-direction: column;\n    max-height: 60vh;\n    overflow: hidden;\n\n    &.visible {\n        transform: translateY(0);\n    }\n\n    &.expanded {\n        max-height: 85vh;\n    }\n\n    .drag-handle {\n        display: flex;\n        justify-content: center;\n        align-items: center;\n        padding: 8px 0 4px 0;\n        cursor: grab;\n        flex-shrink: 0;\n\n        &::after {\n            content: \"\";\n            width: 36px;\n            height: 4px;\n            background-color: var(--border-color);\n            border-radius: 2px;\n        }\n\n        &:active {\n            cursor: grabbing;\n        }\n    }\n\n    .panel-header {\n        display: flex;\n        align-items: center;\n        justify-content: space-between;\n        padding: 8px 12px 12px 16px;\n        border-bottom: 1px solid var(--border-color);\n        min-height: 44px;\n        flex-shrink: 0;\n\n        .header-content {\n            display: flex;\n            flex-direction: column;\n            gap: 2px;\n            flex: 1;\n            min-width: 0;\n\n            .header-title {\n                display: flex;\n                align-items: center;\n                gap: 8px;\n                font-size: 1rem;\n                font-weight: 600;\n                color: var(--primary-text-color);\n\n                .icon {\n                    font-size: 1.1em;\n                    opacity: 0.7;\n                }\n            }\n\n            .header-subtitle {\n                font-size: 0.85rem;\n                color: var(--secondary-text-color);\n                opacity: 0.8;\n            }\n        }\n\n        .header-actions {\n            display: flex;\n            gap: 4px;\n            flex-shrink: 0;\n        }\n\n        .action-btn {\n            width: 36px;\n            height: 36px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            border: none;\n            background: transparent;\n            color: var(--secondary-text-color);\n            border-radius: 50%;\n            cursor: pointer;\n\n            &:active {\n                background-color: var(--active-icon-color);\n            }\n\n            .icon {\n                font-size: 1.2em;\n            }\n        }\n    }\n\n    .panel-search {\n        padding: 8px 16px;\n        border-bottom: 1px solid var(--border-color);\n        flex-shrink: 0;\n\n        input {\n            width: 100%;\n            background-color: var(--secondary-color);\n            border: 1px solid var(--border-color);\n            border-radius: 8px;\n            padding: 10px 12px;\n            font-size: 0.9rem;\n            color: var(--primary-text-color);\n            outline: none;\n\n            &::placeholder {\n                color: var(--secondary-text-color);\n                opacity: 0.7;\n            }\n\n            &:focus {\n                border-color: var(--active-color);\n            }\n        }\n    }\n\n    .panel-content {\n        flex: 1;\n        overflow-y: auto;\n        overflow-x: hidden;\n        -webkit-overflow-scrolling: touch;\n        overscroll-behavior: contain;\n\n        &::-webkit-scrollbar {\n            width: var(--scrollbar-width, 4px);\n        }\n\n        &::-webkit-scrollbar-track {\n            background: transparent;\n        }\n\n        &::-webkit-scrollbar-thumb {\n            background: var(--scrollbar-color, rgba(0, 0, 0, 0.333));\n            border-radius: calc(var(--scrollbar-width, 4px) / 2);\n        }\n    }\n\n    .loading-state {\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        padding: 32px 16px;\n        color: var(--secondary-text-color);\n        gap: 12px;\n\n        .loader {\n            @include mixins.circular-loader(24px);\n        }\n    }\n\n    .empty-state {\n        display: flex;\n        flex-direction: column;\n        align-items: center;\n        justify-content: center;\n        padding: 32px 16px;\n        color: var(--secondary-text-color);\n        text-align: center;\n\n        .icon {\n            font-size: 2rem;\n            margin-bottom: 8px;\n            opacity: 0.5;\n        }\n    }\n}\n\n.symbol-item {\n    box-sizing: border-box;\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 0 16px;\n    height: 44px;\n    cursor: pointer;\n    user-select: none;\n    will-change: transform;\n\n    &:active {\n        background-color: var(--active-icon-color);\n    }\n\n    .symbol-indent {\n        flex-shrink: 0;\n    }\n\n    .symbol-icon {\n        width: 24px;\n        height: 24px;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        border-radius: 4px;\n        flex-shrink: 0;\n        font-size: 0.8rem;\n        font-weight: 600;\n        text-transform: uppercase;\n\n        &.kind-file {\n            background-color: rgba(158, 158, 158, 0.2);\n            color: #9e9e9e;\n        }\n\n        &.kind-module {\n            background-color: rgba(255, 152, 0, 0.2);\n            color: #ff9800;\n        }\n\n        &.kind-namespace {\n            background-color: rgba(255, 152, 0, 0.2);\n            color: #ff9800;\n        }\n\n        &.kind-package {\n            background-color: rgba(121, 85, 72, 0.2);\n            color: #795548;\n        }\n\n        &.kind-class {\n            background-color: rgba(255, 193, 7, 0.2);\n            color: #ffc107;\n        }\n\n        &.kind-method {\n            background-color: rgba(156, 39, 176, 0.2);\n            color: #9c27b0;\n        }\n\n        &.kind-property {\n            background-color: rgba(0, 150, 136, 0.2);\n            color: #009688;\n        }\n\n        &.kind-field {\n            background-color: rgba(0, 150, 136, 0.2);\n            color: #009688;\n        }\n\n        &.kind-constructor {\n            background-color: rgba(156, 39, 176, 0.2);\n            color: #9c27b0;\n        }\n\n        &.kind-enum {\n            background-color: rgba(255, 152, 0, 0.2);\n            color: #ff9800;\n        }\n\n        &.kind-interface {\n            background-color: rgba(0, 188, 212, 0.2);\n            color: #00bcd4;\n        }\n\n        &.kind-function {\n            background-color: rgba(103, 58, 183, 0.2);\n            color: #673ab7;\n        }\n\n        &.kind-variable {\n            background-color: rgba(33, 150, 243, 0.2);\n            color: #2196f3;\n        }\n\n        &.kind-constant {\n            background-color: rgba(33, 150, 243, 0.2);\n            color: #2196f3;\n        }\n\n        &.kind-string {\n            background-color: rgba(76, 175, 80, 0.2);\n            color: #4caf50;\n        }\n\n        &.kind-number {\n            background-color: rgba(139, 195, 74, 0.2);\n            color: #8bc34a;\n        }\n\n        &.kind-boolean {\n            background-color: rgba(33, 150, 243, 0.2);\n            color: #2196f3;\n        }\n\n        &.kind-array {\n            background-color: rgba(255, 87, 34, 0.2);\n            color: #ff5722;\n        }\n\n        &.kind-object {\n            background-color: rgba(96, 125, 139, 0.2);\n            color: #607d8b;\n        }\n\n        &.kind-key {\n            background-color: rgba(233, 30, 99, 0.2);\n            color: #e91e63;\n        }\n\n        &.kind-null {\n            background-color: rgba(158, 158, 158, 0.2);\n            color: #9e9e9e;\n        }\n\n        &.kind-enummember {\n            background-color: rgba(255, 152, 0, 0.2);\n            color: #ff9800;\n        }\n\n        &.kind-struct {\n            background-color: rgba(96, 125, 139, 0.2);\n            color: #607d8b;\n        }\n\n        &.kind-event {\n            background-color: rgba(255, 235, 59, 0.2);\n            color: #fdd835;\n        }\n\n        &.kind-operator {\n            background-color: rgba(158, 158, 158, 0.2);\n            color: #9e9e9e;\n        }\n\n        &.kind-typeparameter {\n            background-color: rgba(0, 188, 212, 0.2);\n            color: #00bcd4;\n        }\n    }\n\n    .symbol-info {\n        flex: 1;\n        min-width: 0;\n        display: flex;\n        flex-direction: column;\n\n        .symbol-name {\n            font-size: 0.9rem;\n            font-weight: 500;\n            color: var(--primary-text-color);\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n\n            .symbol-match {\n                background-color: color-mix(in srgb, var(--active-color) 30%, transparent);\n                border-radius: 2px;\n                padding: 0 2px;\n            }\n        }\n\n        .symbol-detail {\n            font-size: 0.75rem;\n            color: var(--secondary-text-color);\n            opacity: 0.8;\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n        }\n    }\n\n    .symbol-line {\n        font-size: 0.75rem;\n        color: var(--secondary-text-color);\n        font-family: monospace;\n        flex-shrink: 0;\n        opacity: 0.7;\n    }\n}"
  },
  {
    "path": "src/components/tabView.js",
    "content": "import Ref from \"html-tag-js/ref\";\n\n/**\n *\n * @param {object} param0\n * @param {string} param0.id\n * @returns\n */\nexport default function TabView({ id, disableSwipe = false }, children) {\n\tlet moveX = 0;\n\tlet moveY = 0;\n\tlet lastX = 0;\n\tlet lastY = 0;\n\tlet isScrolling = false;\n\tconst el = Ref();\n\treturn (\n\t\t<div\n\t\t\tref={el}\n\t\t\tonclick={changeTab}\n\t\t\tontouchstart={!disableSwipe ? ontouchstart : null}\n\t\t\tclassName=\"main\"\n\t\t\tid={id}\n\t\t>\n\t\t\t{children}\n\t\t</div>\n\t);\n\n\tfunction ontouchstart(e) {\n\t\tmoveX = 0;\n\t\tmoveY = 0;\n\t\tlastX = e.touches[0].clientX;\n\t\tlastY = e.touches[0].clientY;\n\t\tisScrolling = false;\n\n\t\tdocument.addEventListener(\"touchmove\", omtouchmove, { passive: true });\n\t\tdocument.addEventListener(\"touchend\", omtouchend);\n\t\tdocument.addEventListener(\"touchcancel\", omtouchend);\n\t}\n\n\tfunction omtouchmove(e) {\n\t\tconst { clientX, clientY } = e.touches[0];\n\t\tconst deltaX = lastX - clientX;\n\t\tconst deltaY = lastY - clientY;\n\n\t\t// Determine if the user is primarily scrolling vertically\n\t\tif (!isScrolling) {\n\t\t\tisScrolling = Math.abs(deltaY) > Math.abs(deltaX);\n\t\t}\n\n\t\tif (!isScrolling) {\n\t\t\tmoveX += deltaX;\n\t\t\te.preventDefault();\n\t\t}\n\n\t\tlastX = clientX;\n\t\tlastY = clientY;\n\t}\n\n\tfunction omtouchend() {\n\t\tdocument.removeEventListener(\"touchmove\", omtouchmove);\n\t\tdocument.removeEventListener(\"touchend\", omtouchend);\n\t\tdocument.removeEventListener(\"touchcancel\", omtouchend);\n\n\t\t// Only change tabs when a significant horizontal swipe is detected and not scrolling vertically\n\t\tif (!isScrolling && Math.abs(moveX) > 100) {\n\t\t\tconst tabs = Array.from(el.get(\".options\").children);\n\t\t\tconst currentTab = el.get(\".options>span.active\");\n\t\t\tconst direction = moveX > 0 ? 1 : -1;\n\t\t\tconst currentTabIndex = tabs.indexOf(currentTab);\n\t\t\tconst nextTabIndex =\n\t\t\t\t(currentTabIndex + direction + tabs.length) % tabs.length;\n\t\t\ttabs[nextTabIndex].click();\n\t\t\tcurrentTab.classList.remove(\"active\");\n\t\t\ttabs[nextTabIndex].classList.add(\"active\");\n\t\t}\n\t}\n\n\tfunction changeTab(e) {\n\t\tconst { target } = e;\n\t\tif (!target.matches(\".options>span\")) return;\n\t\tconst currentTab = el.get(\".options>span.active\");\n\t\tif (target === currentTab) return;\n\t\tcurrentTab.classList.remove(\"active\");\n\t\ttarget.classList.add(\"active\");\n\t}\n}\n"
  },
  {
    "path": "src/components/terminal/index.js",
    "content": "/**\n * Terminal Components Export\n */\n\nimport TerminalComponent from \"./terminal\";\nimport { DEFAULT_TERMINAL_SETTINGS } from \"./terminalDefaults\";\nimport TerminalManager from \"./terminalManager\";\nimport TerminalThemeManager from \"./terminalThemeManager\";\n\nexport {\n\tDEFAULT_TERMINAL_SETTINGS,\n\tTerminalComponent,\n\tTerminalManager,\n\tTerminalThemeManager,\n};\n\nexport default {\n\tTerminalComponent,\n\tTerminalManager,\n\tTerminalThemeManager,\n\tDEFAULT_TERMINAL_SETTINGS,\n};\n"
  },
  {
    "path": "src/components/terminal/ligatures.js",
    "content": "// pretty basic ligature implementation for webview\nexport default class LigaturesAddon {\n\tconstructor(options = {}) {\n\t\t// fallback ligatures if a font does not support ligatures natively\n\t\tthis._fallbackLigatures =\n\t\t\toptions.fallbackLigatures ||\n\t\t\t[\n\t\t\t\t\"<--\",\n\t\t\t\t\"<---\",\n\t\t\t\t\"<<-\",\n\t\t\t\t\"<-\",\n\t\t\t\t\"->\",\n\t\t\t\t\"->>\",\n\t\t\t\t\"-->\",\n\t\t\t\t\"--->\",\n\t\t\t\t\"<==\",\n\t\t\t\t\"<===\",\n\t\t\t\t\"<<=\",\n\t\t\t\t\"<=\",\n\t\t\t\t\"=>\",\n\t\t\t\t\"=>>\",\n\t\t\t\t\"==>\",\n\t\t\t\t\"===>\",\n\t\t\t\t\">=\",\n\t\t\t\t\">>=\",\n\t\t\t\t\"<->\",\n\t\t\t\t\"<-->\",\n\t\t\t\t\"<--->\",\n\t\t\t\t\"<---->\",\n\t\t\t\t\"<=>\",\n\t\t\t\t\"<==>\",\n\t\t\t\t\"<===>\",\n\t\t\t\t\"<====>\",\n\t\t\t\t\"<~~\",\n\t\t\t\t\"<~\",\n\t\t\t\t\"~>\",\n\t\t\t\t\"~~>\",\n\t\t\t\t\"::\",\n\t\t\t\t\":::\",\n\t\t\t\t\"==\",\n\t\t\t\t\"!=\",\n\t\t\t\t\"===\",\n\t\t\t\t\"!==\",\n\t\t\t\t\":=\",\n\t\t\t\t\":-\",\n\t\t\t\t\":+\",\n\t\t\t\t\"<*\",\n\t\t\t\t\"<*>\",\n\t\t\t\t\"*>\",\n\t\t\t\t\"<|\",\n\t\t\t\t\"<|>\",\n\t\t\t\t\"|>\",\n\t\t\t\t\"+:\",\n\t\t\t\t\"-:\",\n\t\t\t\t\"=:\",\n\t\t\t\t\":>\",\n\t\t\t\t\"++\",\n\t\t\t\t\"+++\",\n\t\t\t\t\"<!--\",\n\t\t\t\t\"<!---\",\n\t\t\t\t\"<***>\",\n\t\t\t].sort((a, b) => b.length - a.length);\n\t\tthis._characterJoinerId = undefined;\n\t\tthis._terminal = undefined;\n\t}\n\n\tactivate(terminal) {\n\t\tthis._terminal = terminal;\n\t\tthis._characterJoinerId = terminal.registerCharacterJoiner(\n\t\t\tthis._joinCharacters.bind(this),\n\t\t);\n\t\tterminal.element.style.fontFeatureSettings = `\"liga\" on, \"calt\" on`;\n\t}\n\n\tdispose() {\n\t\tif (this._characterJoinerId !== undefined) {\n\t\t\tthis._terminal?.deregisterCharacterJoiner(this._characterJoinerId);\n\t\t\tthis._characterJoinerId = undefined;\n\t\t}\n\t\tif (this._terminal?.element) {\n\t\t\tthis._terminal.element.style.fontFeatureSettings = \"\";\n\t\t}\n\t}\n\n\t_joinCharacters(text) {\n\t\treturn this._findLigatureRanges(text, this._fallbackLigatures);\n\t}\n\n\t_findLigatureRanges(text, ligatures) {\n\t\tconst ranges = [];\n\t\tfor (let i = 0; i < text.length; i++) {\n\t\t\tfor (const ligature of ligatures) {\n\t\t\t\tif (text.startsWith(ligature, i)) {\n\t\t\t\t\tranges.push([i, i + ligature.length]);\n\t\t\t\t\ti += ligature.length - 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn ranges;\n\t}\n}\n"
  },
  {
    "path": "src/components/terminal/terminal.js",
    "content": "/**\n * Terminal Component using xtermjs\n * Provides a pluggable and customizable terminal interface\n */\n\nimport { AttachAddon } from \"@xterm/addon-attach\";\nimport { FitAddon } from \"@xterm/addon-fit\";\nimport { ImageAddon } from \"@xterm/addon-image\";\nimport { SearchAddon } from \"@xterm/addon-search\";\nimport { Unicode11Addon } from \"@xterm/addon-unicode11\";\nimport { WebLinksAddon } from \"@xterm/addon-web-links\";\nimport { WebglAddon } from \"@xterm/addon-webgl\";\nimport { Terminal as Xterm } from \"@xterm/xterm\";\nimport {\n\tgetResolvedKeyBindings,\n\tgetResolvedKeyBindingsVersion,\n} from \"cm/commandRegistry\";\nimport toast from \"components/toast\";\nimport confirm from \"dialogs/confirm\";\nimport fonts from \"lib/fonts\";\nimport appSettings from \"lib/settings\";\nimport LigaturesAddon from \"./ligatures\";\nimport { getTerminalSettings } from \"./terminalDefaults\";\nimport TerminalThemeManager from \"./terminalThemeManager\";\nimport TerminalTouchSelection from \"./terminalTouchSelection\";\n\nexport default class TerminalComponent {\n\tconstructor(options = {}) {\n\t\t// Get terminal settings from shared defaults\n\t\tconst terminalSettings = getTerminalSettings();\n\n\t\tthis.options = {\n\t\t\tallowProposedApi: true,\n\t\t\tscrollOnUserInput: true,\n\t\t\trows: options.rows || 24,\n\t\t\tcols: options.cols || 80,\n\t\t\tport: options.port || 8767,\n\t\t\trenderer: options.renderer || \"auto\", // 'auto' | 'canvas' | 'webgl'\n\t\t\tfontSize: terminalSettings.fontSize,\n\t\t\tfontFamily: terminalSettings.fontFamily,\n\t\t\tfontWeight: terminalSettings.fontWeight,\n\t\t\ttheme: TerminalThemeManager.getTheme(terminalSettings.theme),\n\t\t\tcursorBlink: terminalSettings.cursorBlink,\n\t\t\tcursorStyle: terminalSettings.cursorStyle,\n\t\t\tcursorInactiveStyle: terminalSettings.cursorInactiveStyle,\n\t\t\tscrollback: terminalSettings.scrollback,\n\t\t\ttabStopWidth: terminalSettings.tabStopWidth,\n\t\t\tconvertEol: terminalSettings.convertEol,\n\t\t\tletterSpacing: terminalSettings.letterSpacing,\n\t\t\t...options,\n\t\t};\n\n\t\tthis.terminal = null;\n\t\tthis.fitAddon = null;\n\t\tthis.attachAddon = null;\n\t\tthis.unicode11Addon = null;\n\t\tthis.searchAddon = null;\n\t\tthis.webLinksAddon = null;\n\t\tthis.imageAddon = null;\n\t\tthis.ligaturesAddon = null;\n\t\tthis.container = null;\n\t\tthis.websocket = null;\n\t\tthis.pid = null;\n\t\tthis.isConnected = false;\n\t\tthis.serverMode = options.serverMode !== false; // Default true\n\t\tthis.touchSelection = null;\n\t\tthis.parsedAppKeybindings = [];\n\t\tthis.parsedAppKeybindingsVersion = -1;\n\n\t\tthis.init();\n\t}\n\n\tinit() {\n\t\tthis.terminal = new Xterm(this.options);\n\n\t\t// Initialize addons\n\t\tthis.fitAddon = new FitAddon();\n\t\tthis.unicode11Addon = new Unicode11Addon();\n\t\tthis.searchAddon = new SearchAddon();\n\t\tthis.webLinksAddon = new WebLinksAddon(async (event, uri) => {\n\t\t\tconst linkOpenConfirm = await confirm(\n\t\t\t\t\"Terminal\",\n\t\t\t\t`Do you want to open ${uri} in browser?`,\n\t\t\t);\n\t\t\tif (linkOpenConfirm) {\n\t\t\t\tsystem.openInBrowser(uri);\n\t\t\t}\n\t\t});\n\t\tthis.webglAddon = null;\n\n\t\t// Load addons\n\t\tthis.terminal.loadAddon(this.fitAddon);\n\t\tthis.terminal.loadAddon(this.unicode11Addon);\n\t\tthis.terminal.loadAddon(this.searchAddon);\n\t\tthis.terminal.loadAddon(this.webLinksAddon);\n\n\t\t// Load conditional addons based on settings\n\t\tconst terminalSettings = getTerminalSettings();\n\n\t\t// Load image addon if enabled\n\t\tif (terminalSettings.imageSupport) {\n\t\t\tthis.loadImageAddon();\n\t\t}\n\n\t\t// Load font if specified\n\t\tthis.loadTerminalFont();\n\n\t\t// Set up terminal event handlers\n\t\tthis.setupEventHandlers();\n\t}\n\n\tsetupEventHandlers() {\n\t\t// terminal resize handling\n\t\tthis.setupResizeHandling();\n\n\t\t// Handle terminal title changes\n\t\tthis.terminal.onTitleChange((title) => {\n\t\t\tthis.onTitleChange?.(title);\n\t\t});\n\n\t\t// Handle bell\n\t\tthis.terminal.onBell(() => {\n\t\t\tthis.onBell?.();\n\t\t});\n\n\t\t// Handle copy/paste keybindings\n\t\tthis.setupCopyPasteHandlers();\n\n\t\t// Handle custom OSC 7777 for acode CLI commands\n\t\tthis.setupOscHandler();\n\t}\n\n\t/**\n\t * Setup custom OSC handler for acode CLI integration\n\t * OSC 7777 format: \\e]7777;command;arg1;arg2;...\\a\n\t */\n\tsetupOscHandler() {\n\t\t// Register custom OSC handler for ID 7777\n\t\t// Format: command;arg1;arg2;... where arg2 (path) may contain semicolons\n\t\tthis.terminal.parser.registerOscHandler(7777, (data) => {\n\t\t\tconst firstSemi = data.indexOf(\";\");\n\t\t\tif (firstSemi === -1) {\n\t\t\t\tconsole.warn(\"Invalid OSC 7777 format:\", data);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst command = data.substring(0, firstSemi);\n\t\t\tconst rest = data.substring(firstSemi + 1);\n\n\t\t\tswitch (command) {\n\t\t\t\tcase \"open\": {\n\t\t\t\t\t// Format: open;type;path (path may contain semicolons)\n\t\t\t\t\tconst secondSemi = rest.indexOf(\";\");\n\t\t\t\t\tif (secondSemi === -1) {\n\t\t\t\t\t\tconsole.warn(\"Invalid OSC 7777 open format:\", data);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tconst type = rest.substring(0, secondSemi);\n\t\t\t\t\tconst path = rest.substring(secondSemi + 1);\n\t\t\t\t\tthis.handleOscOpen(type, path);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tconsole.warn(\"Unknown OSC 7777 command:\", command);\n\t\t\t}\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/**\n\t * Handle OSC open command from acode CLI\n\t * @param {string} type - \"file\" or \"folder\"\n\t * @param {string} path - Path to open\n\t */\n\thandleOscOpen(type, path) {\n\t\tif (!path) return;\n\n\t\t// Emit event for the app to handle\n\t\tthis.onOscOpen?.(type, path);\n\t}\n\n\t/**\n\t * Setup resize handling for keyboard events and content preservation\n\t */\n\tsetupResizeHandling() {\n\t\tlet resizeTimeout = null;\n\t\tlet lastKnownScrollPosition = 0;\n\t\tlet isResizing = false;\n\t\tlet resizeCount = 0;\n\t\tconst RESIZE_DEBOUNCE = 100;\n\t\tconst MAX_RAPID_RESIZES = 3;\n\n\t\t// Store original dimensions for comparison\n\t\tlet originalRows = this.terminal.rows;\n\t\tlet originalCols = this.terminal.cols;\n\n\t\tthis.terminal.onResize((size) => {\n\t\t\t// Track resize events\n\t\t\tresizeCount++;\n\t\t\tisResizing = true;\n\n\t\t\t// Store current scroll position before resize\n\t\t\tif (this.terminal.buffer && this.terminal.buffer.active) {\n\t\t\t\tlastKnownScrollPosition = this.terminal.buffer.active.viewportY;\n\t\t\t}\n\n\t\t\t// Clear any existing timeout\n\t\t\tif (resizeTimeout) {\n\t\t\t\tclearTimeout(resizeTimeout);\n\t\t\t}\n\n\t\t\t// Debounced resize handling\n\t\t\tresizeTimeout = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\t// Only proceed with server resize if dimensions actually changed significantly\n\t\t\t\t\tconst rowDiff = Math.abs(size.rows - originalRows);\n\t\t\t\t\tconst colDiff = Math.abs(size.cols - originalCols);\n\n\t\t\t\t\t// If this is a minor resize (likely intermediate state), skip server update\n\t\t\t\t\tif (rowDiff < 2 && colDiff < 2 && resizeCount > 1) {\n\t\t\t\t\t\tconsole.log(\"Skipping minor resize to prevent instability\");\n\t\t\t\t\t\tisResizing = false;\n\t\t\t\t\t\tresizeCount = 0;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Handle server resize\n\t\t\t\t\tif (this.serverMode) {\n\t\t\t\t\t\tawait this.resizeTerminal(size.cols, size.rows);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Handle keyboard resize cursor positioning\n\t\t\t\t\tconst heightRatio = size.rows / originalRows;\n\t\t\t\t\tif (\n\t\t\t\t\t\theightRatio < 0.75 &&\n\t\t\t\t\t\tthis.terminal.buffer &&\n\t\t\t\t\t\tthis.terminal.buffer.active\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Keyboard resize detected - ensure cursor is visible\n\t\t\t\t\t\tconst buffer = this.terminal.buffer.active;\n\t\t\t\t\t\tconst cursorY = buffer.cursorY;\n\t\t\t\t\t\tconst cursorViewportPos = buffer.baseY + cursorY;\n\t\t\t\t\t\tconst viewportTop = buffer.viewportY;\n\t\t\t\t\t\tconst viewportBottom = viewportTop + this.terminal.rows - 1;\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tcursorViewportPos <= viewportTop + 1 ||\n\t\t\t\t\t\t\tcursorViewportPos >= viewportBottom - 1\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tconst targetScroll = Math.max(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tMath.min(\n\t\t\t\t\t\t\t\t\tbuffer.length - this.terminal.rows,\n\t\t\t\t\t\t\t\t\tcursorViewportPos - Math.floor(this.terminal.rows * 0.25),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tthis.terminal.scrollToLine(targetScroll);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Regular resize - preserve scroll position\n\t\t\t\t\t\tthis.preserveViewportPosition(lastKnownScrollPosition);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update stored dimensions\n\t\t\t\t\toriginalRows = size.rows;\n\t\t\t\t\toriginalCols = size.cols;\n\n\t\t\t\t\t// Mark resize as complete\n\t\t\t\t\tisResizing = false;\n\t\t\t\t\tresizeCount = 0;\n\n\t\t\t\t\t// Notify touch selection if it exists\n\t\t\t\t\tif (this.touchSelection) {\n\t\t\t\t\t\tthis.touchSelection.onTerminalResize(size);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Resize handling failed:\", error);\n\t\t\t\t\tisResizing = false;\n\t\t\t\t\tresizeCount = 0;\n\t\t\t\t}\n\t\t\t}, RESIZE_DEBOUNCE);\n\t\t});\n\n\t\t// Also handle viewport changes for scroll position preservation\n\t\tthis.terminal.onData(() => {\n\t\t\t// If we're not resizing and user types, everything is stable\n\t\t\tif (!isResizing && this.terminal.buffer && this.terminal.buffer.active) {\n\t\t\t\tlastKnownScrollPosition = this.terminal.buffer.active.viewportY;\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Preserve viewport position during resize to prevent jumping\n\t */\n\tpreserveViewportPosition(targetScrollPosition) {\n\t\tif (!this.terminal.buffer || !this.terminal.buffer.active) return;\n\n\t\tconst buffer = this.terminal.buffer.active;\n\t\tconst maxScroll = Math.max(0, buffer.length - this.terminal.rows);\n\n\t\t// Ensure scroll position is within valid bounds\n\t\tconst safeScrollPosition = Math.min(targetScrollPosition, maxScroll);\n\n\t\t// Only adjust if we have significant content and the position differs\n\t\tif (\n\t\t\tbuffer.length > this.terminal.rows &&\n\t\t\tbuffer.viewportY !== safeScrollPosition\n\t\t) {\n\t\t\tthis.terminal.scrollToLine(safeScrollPosition);\n\t\t}\n\t}\n\n\t/**\n\t * Setup touch selection for mobile devices\n\t */\n\tsetupTouchSelection() {\n\t\t// Only initialize touch selection on mobile devices\n\t\tif (window.cordova && this.container) {\n\t\t\tconst terminalSettings = getTerminalSettings();\n\t\t\tthis.touchSelection = new TerminalTouchSelection(\n\t\t\t\tthis.terminal,\n\t\t\t\tthis.container,\n\t\t\t\t{\n\t\t\t\t\ttapHoldDuration:\n\t\t\t\t\t\tterminalSettings.touchSelectionTapHoldDuration || 600,\n\t\t\t\t\tmoveThreshold: terminalSettings.touchSelectionMoveThreshold || 8,\n\t\t\t\t\thandleSize: terminalSettings.touchSelectionHandleSize || 24,\n\t\t\t\t\thapticFeedback:\n\t\t\t\t\t\tterminalSettings.touchSelectionHapticFeedback !== false,\n\t\t\t\t\tshowContextMenu:\n\t\t\t\t\t\tterminalSettings.touchSelectionShowContextMenu !== false,\n\t\t\t\t\tonFontSizeChange: (fontSize) => this.updateFontSize(fontSize),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Parse app keybindings into a format usable by the keyboard handler\n\t */\n\tparseAppKeybindings() {\n\t\tconst version = getResolvedKeyBindingsVersion();\n\t\tif (this.parsedAppKeybindingsVersion === version) {\n\t\t\treturn this.parsedAppKeybindings;\n\t\t}\n\n\t\tconst parsedBindings = [];\n\n\t\tObject.values(getResolvedKeyBindings()).forEach((binding) => {\n\t\t\tif (!binding.key) return;\n\n\t\t\t// Skip editor-only keybindings in terminal\n\t\t\tif (binding.editorOnly) return;\n\n\t\t\t// Handle multiple key combinations separated by |\n\t\t\tconst keys = binding.key.split(\"|\");\n\n\t\t\tkeys.forEach((keyCombo) => {\n\t\t\t\tconst parts = keyCombo.split(\"-\");\n\t\t\t\tconst parsed = {\n\t\t\t\t\tctrl: false,\n\t\t\t\t\tshift: false,\n\t\t\t\t\talt: false,\n\t\t\t\t\tmeta: false,\n\t\t\t\t\tkey: \"\",\n\t\t\t\t};\n\n\t\t\t\tparts.forEach((part) => {\n\t\t\t\t\tconst lowerPart = part.toLowerCase();\n\t\t\t\t\tif (lowerPart === \"ctrl\") {\n\t\t\t\t\t\tparsed.ctrl = true;\n\t\t\t\t\t} else if (lowerPart === \"shift\") {\n\t\t\t\t\t\tparsed.shift = true;\n\t\t\t\t\t} else if (lowerPart === \"alt\") {\n\t\t\t\t\t\tparsed.alt = true;\n\t\t\t\t\t} else if (lowerPart === \"meta\" || lowerPart === \"cmd\") {\n\t\t\t\t\t\tparsed.meta = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// This is the actual key\n\t\t\t\t\t\tparsed.key = part.toLowerCase();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (parsed.key) {\n\t\t\t\t\tparsedBindings.push(parsed);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\tthis.parsedAppKeybindings = parsedBindings;\n\t\tthis.parsedAppKeybindingsVersion = version;\n\n\t\treturn this.parsedAppKeybindings;\n\t}\n\n\t/**\n\t * Setup copy/paste keyboard handlers\n\t */\n\tsetupCopyPasteHandlers() {\n\t\t// Add keyboard event listener to terminal element\n\t\tthis.terminal.attachCustomKeyEventHandler((event) => {\n\t\t\t// Check for Ctrl+Shift+C (copy)\n\t\t\tif (event.ctrlKey && event.shiftKey && event.key === \"C\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.copySelection();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check for Ctrl+Shift+V (paste)\n\t\t\tif (event.ctrlKey && event.shiftKey && event.key === \"V\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.pasteFromClipboard();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check for Ctrl+= or Ctrl++ (increase font size)\n\t\t\tif (event.ctrlKey && (event.key === \"+\" || event.key === \"=\")) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.increaseFontSize();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check for Ctrl+- (decrease font size)\n\t\t\tif (event.ctrlKey && event.key === \"-\") {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.decreaseFontSize();\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Only intercept specific app-wide keybindings, let terminal handle the rest\n\t\t\tif (event.ctrlKey || event.altKey || event.metaKey) {\n\t\t\t\t// Skip modifier-only keys\n\t\t\t\tif ([\"Control\", \"Alt\", \"Meta\", \"Shift\"].includes(event.key)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Get parsed app keybindings\n\t\t\t\tconst appKeybindings = this.parseAppKeybindings();\n\n\t\t\t\t// Check if this is an app-specific keybinding\n\t\t\t\tconst isAppKeybinding = appKeybindings.some(\n\t\t\t\t\t(binding) =>\n\t\t\t\t\t\tbinding.ctrl === event.ctrlKey &&\n\t\t\t\t\t\tbinding.shift === event.shiftKey &&\n\t\t\t\t\t\tbinding.alt === event.altKey &&\n\t\t\t\t\t\tbinding.meta === event.metaKey &&\n\t\t\t\t\t\tbinding.key === event.key.toLowerCase(),\n\t\t\t\t);\n\n\t\t\t\tif (isAppKeybinding) {\n\t\t\t\t\tconst appEvent = new KeyboardEvent(\"keydown\", {\n\t\t\t\t\t\tkey: event.key,\n\t\t\t\t\t\tctrlKey: event.ctrlKey,\n\t\t\t\t\t\tshiftKey: event.shiftKey,\n\t\t\t\t\t\taltKey: event.altKey,\n\t\t\t\t\t\tmetaKey: event.metaKey,\n\t\t\t\t\t\tbubbles: true,\n\t\t\t\t\t\tcancelable: true,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Dispatch to document so it gets picked up by the app's keyboard handler\n\t\t\t\t\tdocument.dispatchEvent(appEvent);\n\n\t\t\t\t\t// Return false to prevent terminal from processing this key\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// For all other modifier combinations, let the terminal handle them\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t// Return true to allow normal processing for other keys\n\t\t\treturn true;\n\t\t});\n\t}\n\n\t/**\n\t * Copy selected text to clipboard\n\t */\n\tcopySelection() {\n\t\tif (!this.terminal?.hasSelection()) return;\n\t\tconst selectedStr = this.terminal?.getSelection();\n\t\tif (selectedStr && cordova?.plugins?.clipboard) {\n\t\t\tcordova.plugins.clipboard.copy(selectedStr);\n\t\t}\n\t}\n\n\t/**\n\t * Paste text from clipboard\n\t */\n\tpasteFromClipboard() {\n\t\tif (cordova?.plugins?.clipboard) {\n\t\t\tcordova.plugins.clipboard.paste((text) => {\n\t\t\t\tthis.terminal?.paste(text);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Create terminal container element\n\t * @returns {HTMLElement} Container element\n\t */\n\tcreateContainer() {\n\t\tthis.container = document.createElement(\"div\");\n\t\tthis.container.className = \"terminal-container\";\n\t\tthis.container.style.cssText = `\n      width: 100%;\n      height: 100%;\n      position: relative;\n      background: ${this.options.theme.background};\n      overflow: hidden;\n      box-sizing: border-box;\n    `;\n\n\t\treturn this.container;\n\t}\n\n\t/**\n\t * Mount terminal to container\n\t * @param {HTMLElement} container - Container element\n\t */\n\tmount(container) {\n\t\tif (!container) {\n\t\t\tcontainer = this.createContainer();\n\t\t}\n\n\t\tthis.container = container;\n\n\t\t// Apply terminal background color to container to match theme\n\t\tthis.container.style.background = this.options.theme.background;\n\n\t\ttry {\n\t\t\t// Open first to ensure a stable renderer is attached\n\t\t\tthis.terminal.open(container);\n\n\t\t\t// Renderer selection: 'canvas' (default core), 'webgl', or 'auto'\n\t\t\tif (\n\t\t\t\tthis.options.renderer === \"webgl\" ||\n\t\t\t\tthis.options.renderer === \"auto\"\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tconst addon = new WebglAddon();\n\t\t\t\t\tthis.terminal.loadAddon(addon);\n\t\t\t\t\tif (typeof addon.onContextLoss === \"function\") {\n\t\t\t\t\t\taddon.onContextLoss(() => this._handleWebglContextLoss());\n\t\t\t\t\t}\n\t\t\t\t\tthis.webglAddon = addon;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Failed to enable WebGL renderer:\", error);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.webglAddon?.dispose?.();\n\t\t\t\t\t} catch {}\n\t\t\t\t\tthis.webglAddon = null; // stay on canvas\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst terminalSettings = getTerminalSettings();\n\t\t\t// Load ligatures addon if enabled\n\t\t\tif (terminalSettings.fontLigatures) {\n\t\t\t\tthis.loadLigaturesAddon();\n\t\t\t}\n\n\t\t\t// First render pass: schedule a fit + focus once the frame is ready\n\t\t\tif (typeof requestAnimationFrame === \"function\") {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tthis.fitAddon.fit();\n\t\t\t\t\tthis.terminal.focus();\n\t\t\t\t\tthis.setupTouchSelection();\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tthis.fitAddon.fit();\n\t\t\t\t\tthis.terminal.focus();\n\t\t\t\t\tthis.setupTouchSelection();\n\t\t\t\t}, 0);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to mount terminal:\", error);\n\t\t}\n\n\t\treturn container;\n\t}\n\n\t/**\n\t * Create new terminal session using global Terminal API\n\t * @returns {Promise<string>} Terminal PID\n\t */\n\tasync createSession() {\n\t\tif (!this.serverMode) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Terminal is in local mode, cannot create server session\",\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\t// Check if terminal is installed before starting AXS\n\t\t\tif (!(await Terminal.isInstalled())) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Terminal not installed. Please install terminal first.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Start AXS if not running\n\t\t\tif (!(await Terminal.isAxsRunning())) {\n\t\t\t\tawait Terminal.startAxs(false, () => {}, console.error);\n\n\t\t\t\t// Check if AXS started with interval polling\n\t\t\t\tconst maxRetries = 10;\n\t\t\t\tlet retries = 0;\n\t\t\t\twhile (retries < maxRetries) {\n\t\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 1000));\n\t\t\t\t\tif (await Terminal.isAxsRunning()) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tretries++;\n\t\t\t\t}\n\n\t\t\t\t// If AXS still not running after retries, throw error\n\t\t\t\tif (!(await Terminal.isAxsRunning())) {\n\t\t\t\t\ttoast(\"Failed to start AXS server after multiple attempts\");\n\t\t\t\t\t//throw new Error(\"Failed to start AXS server after multiple attempts\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst requestBody = {\n\t\t\t\tcols: this.terminal.cols,\n\t\t\t\trows: this.terminal.rows,\n\t\t\t};\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`http://localhost:${this.options.port}/terminals`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`HTTP error! status: ${response.status}`);\n\t\t\t}\n\n\t\t\tconst data = await response.text();\n\t\t\tthis.pid = data.trim();\n\t\t\treturn this.pid;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to create terminal session:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Connect to terminal session via WebSocket\n\t * @param {string} pid - Terminal PID\n\t */\n\tasync connectToSession(pid) {\n\t\tif (!this.serverMode) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Terminal is in local mode, cannot connect to server session\",\n\t\t\t);\n\t\t}\n\n\t\tif (!pid) {\n\t\t\tpid = await this.createSession();\n\t\t}\n\n\t\tthis.pid = pid;\n\n\t\tconst wsUrl = `ws://localhost:${this.options.port}/terminals/${pid}`;\n\n\t\tawait new Promise((resolve, reject) => {\n\t\t\tconst websocket = new WebSocket(wsUrl);\n\t\t\tconst CONNECT_TIMEOUT = 5000;\n\t\t\tlet settled = false;\n\t\t\tlet hasOpened = false;\n\n\t\t\tthis.websocket = websocket;\n\n\t\t\tconst rejectInitialConnect = (message, error) => {\n\t\t\t\tif (settled || hasOpened) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.isConnected = false;\n\t\t\t\ttry {\n\t\t\t\t\twebsocket.close();\n\t\t\t\t} catch {}\n\t\t\t\treject(error || new Error(message));\n\t\t\t};\n\n\t\t\tconst connectionTimeout = setTimeout(() => {\n\t\t\t\trejectInitialConnect(\n\t\t\t\t\t`Timed out while connecting to terminal session ${pid}`,\n\t\t\t\t);\n\t\t\t}, CONNECT_TIMEOUT);\n\n\t\t\twebsocket.onopen = () => {\n\t\t\t\tclearTimeout(connectionTimeout);\n\t\t\t\thasOpened = true;\n\t\t\t\tthis.isConnected = true;\n\t\t\t\tthis.onConnect?.();\n\n\t\t\t\t// Load attach addon after connection\n\t\t\t\tthis.attachAddon = new AttachAddon(websocket);\n\t\t\t\tthis.terminal.loadAddon(this.attachAddon);\n\t\t\t\tthis.terminal.unicode.activeVersion = \"11\";\n\n\t\t\t\t// Focus terminal and ensure it's ready\n\t\t\t\tthis.terminal.focus();\n\t\t\t\tthis.fit();\n\n\t\t\t\tif (!settled) {\n\t\t\t\t\tsettled = true;\n\t\t\t\t\tresolve();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\twebsocket.onmessage = (event) => {\n\t\t\t\t// Handle text messages (exit events)\n\t\t\t\tif (typeof event.data === \"string\") {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst message = JSON.parse(event.data);\n\t\t\t\t\t\tif (message.type === \"exit\") {\n\t\t\t\t\t\t\tthis.onProcessExit?.(message.data);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// Not a JSON message, let attachAddon handle it\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// For binary data or non-exit text messages, let attachAddon handle them\n\t\t\t};\n\n\t\t\twebsocket.onclose = (event) => {\n\t\t\t\tclearTimeout(connectionTimeout);\n\t\t\t\tthis.isConnected = false;\n\n\t\t\t\tif (!hasOpened) {\n\t\t\t\t\tconst code = event?.code ? ` (code ${event.code})` : \"\";\n\t\t\t\t\tconst reason = event?.reason ? `: ${event.reason}` : \"\";\n\t\t\t\t\trejectInitialConnect(\n\t\t\t\t\t\t`Terminal session ${pid} is unavailable${code}${reason}`,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.onDisconnect?.();\n\t\t\t};\n\n\t\t\twebsocket.onerror = (error) => {\n\t\t\t\tif (!hasOpened) {\n\t\t\t\t\tclearTimeout(connectionTimeout);\n\t\t\t\t\trejectInitialConnect(\n\t\t\t\t\t\t`Failed to connect to terminal session ${pid}`,\n\t\t\t\t\t\tnew Error(`Failed to connect to terminal session ${pid}`),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconsole.error(\"WebSocket error:\", error);\n\t\t\t\tthis.onError?.(error);\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Resize terminal\n\t * @param {number} cols - Number of columns\n\t * @param {number} rows - Number of rows\n\t */\n\tasync resizeTerminal(cols, rows) {\n\t\tif (!this.pid || !this.serverMode) return;\n\n\t\ttry {\n\t\t\tawait fetch(\n\t\t\t\t`http://localhost:${this.options.port}/terminals/${this.pid}/resize`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({ cols, rows }),\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to resize terminal:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Fit terminal to container\n\t */\n\tfit() {\n\t\tif (this.fitAddon) {\n\t\t\tthis.fitAddon.fit();\n\t\t}\n\t}\n\n\t/**\n\t * Write data to terminal\n\t * @param {string} data - Data to write\n\t */\n\twrite(data) {\n\t\tif (\n\t\t\tthis.serverMode &&\n\t\t\tthis.isConnected &&\n\t\t\tthis.websocket &&\n\t\t\tthis.websocket.readyState === WebSocket.OPEN\n\t\t) {\n\t\t\t// Send data through WebSocket instead of direct write\n\t\t\tthis.websocket.send(data);\n\t\t} else {\n\t\t\t// For local mode or disconnected terminals, write directly\n\t\t\tthis.terminal.write(data);\n\t\t}\n\t}\n\n\t/**\n\t * Write line to terminal\n\t * @param {string} data - Data to write\n\t */\n\twriteln(data) {\n\t\tthis.terminal.writeln(data);\n\t}\n\n\t/**\n\t * Clear terminal\n\t */\n\tclear() {\n\t\tthis.terminal.clear();\n\t}\n\n\t/**\n\t * Focus terminal\n\t */\n\tfocus() {\n\t\tthis.terminal.focus();\n\t}\n\n\t/**\n\t * Blur terminal\n\t */\n\tblur() {\n\t\tthis.terminal.blur();\n\t}\n\n\t/**\n\t * Search in terminal\n\t * @param {string} term - Search term\n\t * @param {number} skip Number of search results to skip\n\t * @param {boolean} backward Whether to search backward\n\t */\n\tsearch(term, skip, backward) {\n\t\tif (this.searchAddon) {\n\t\t\tconst searchOptions = {\n\t\t\t\tregex: appSettings.value.search.regExp || false,\n\t\t\t\twholeWord: appSettings.value.search.wholeWord || false,\n\t\t\t\tcaseSensitive: appSettings.value.search.caseSensitive || false,\n\t\t\t\tdecorations: {\n\t\t\t\t\tmatchBorder: \"#FFA500\",\n\t\t\t\t\tactiveMatchBorder: \"#FFFF00\",\n\t\t\t\t},\n\t\t\t};\n\t\t\tif (!term) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (backward) {\n\t\t\t\treturn this.searchAddon.findPrevious(term, searchOptions);\n\t\t\t} else {\n\t\t\t\treturn this.searchAddon.findNext(term, searchOptions);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Update terminal theme\n\t * @param {object|string} theme - Theme object or theme name\n\t */\n\tupdateTheme(theme) {\n\t\tif (typeof theme === \"string\") {\n\t\t\ttheme = TerminalThemeManager.getTheme(theme);\n\t\t}\n\t\tthis.options.theme = { ...this.options.theme, ...theme };\n\t\tthis.terminal.options.theme = this.options.theme;\n\t}\n\n\t/**\n\t * Update terminal options\n\t * @param {object} options - Options to update\n\t */\n\tupdateOptions(options) {\n\t\tObject.keys(options).forEach((key) => {\n\t\t\tif (key === \"theme\") {\n\t\t\t\tthis.updateTheme(options.theme);\n\t\t\t} else {\n\t\t\t\tthis.terminal.options[key] = options[key];\n\t\t\t\tthis.options[key] = options[key];\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Load image addon\n\t */\n\tloadImageAddon() {\n\t\tif (!this.imageAddon) {\n\t\t\ttry {\n\t\t\t\tthis.imageAddon = new ImageAddon();\n\t\t\t\tthis.terminal.loadAddon(this.imageAddon);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to load ImageAddon:\", error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Dispose image addon\n\t */\n\tdisposeImageAddon() {\n\t\tif (this.imageAddon) {\n\t\t\ttry {\n\t\t\t\tthis.imageAddon.dispose();\n\t\t\t\tthis.imageAddon = null;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to dispose ImageAddon:\", error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Update image support setting\n\t * @param {boolean} enabled - Whether to enable image support\n\t */\n\tupdateImageSupport(enabled) {\n\t\tif (enabled) {\n\t\t\tthis.loadImageAddon();\n\t\t} else {\n\t\t\tthis.disposeImageAddon();\n\t\t}\n\t}\n\n\t/**\n\t * Load ligatures addon\n\t */\n\tloadLigaturesAddon() {\n\t\tif (!this.ligaturesAddon) {\n\t\t\ttry {\n\t\t\t\tthis.ligaturesAddon = new LigaturesAddon();\n\t\t\t\tthis.terminal.loadAddon(this.ligaturesAddon);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to load LigaturesAddon:\", error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Dispose ligatures addon\n\t */\n\tdisposeLigaturesAddon() {\n\t\tif (this.ligaturesAddon) {\n\t\t\ttry {\n\t\t\t\tthis.ligaturesAddon.dispose();\n\t\t\t\tthis.ligaturesAddon = null;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to dispose LigaturesAddon:\", error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Update font ligatures setting\n\t * @param {boolean} enabled - Whether to enable font ligatures\n\t */\n\tupdateFontLigatures(enabled) {\n\t\tif (enabled) {\n\t\t\tthis.loadLigaturesAddon();\n\t\t} else {\n\t\t\tthis.disposeLigaturesAddon();\n\t\t}\n\t}\n\n\t/**\n\t * Load terminal font if it's not already loaded\n\t */\n\tasync loadTerminalFont() {\n\t\tconst fontFamily = this.options.fontFamily;\n\t\tif (fontFamily && fonts.get(fontFamily)) {\n\t\t\ttry {\n\t\t\t\tawait fonts.loadFont(fontFamily);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(`Failed to load terminal font ${fontFamily}:`, error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Increase terminal font size\n\t */\n\tincreaseFontSize() {\n\t\tconst currentSize = this.terminal.options.fontSize;\n\t\tconst newSize = Math.min(currentSize + 1, 24); // Max font size 24\n\t\tthis.updateFontSize(newSize);\n\t}\n\n\t/**\n\t * Decrease terminal font size\n\t */\n\tdecreaseFontSize() {\n\t\tconst currentSize = this.terminal.options.fontSize;\n\t\tconst newSize = Math.max(currentSize - 1, 8); // Min font size 8\n\t\tthis.updateFontSize(newSize);\n\t}\n\n\t/**\n\t * Update terminal font size and refresh display\n\t */\n\tupdateFontSize(fontSize) {\n\t\tif (fontSize === this.terminal.options.fontSize) return;\n\n\t\tthis.terminal.options.fontSize = fontSize;\n\t\tthis.options.fontSize = fontSize;\n\n\t\t// Update terminal settings properly\n\t\tconst currentSettings = appSettings.value.terminalSettings || {};\n\t\tconst updatedSettings = { ...currentSettings, fontSize };\n\t\tappSettings.update({ terminalSettings: updatedSettings }, false);\n\n\t\t// Refresh terminal display\n\t\tthis.terminal.refresh(0, this.terminal.rows - 1);\n\n\t\t// Fit terminal to container after font size change to prevent empty space\n\t\tsetTimeout(() => {\n\t\t\tif (this.fitAddon) {\n\t\t\t\tthis.fitAddon.fit();\n\t\t\t}\n\t\t}, 50);\n\n\t\t// Update touch selection cell dimensions if it exists\n\t\tif (this.touchSelection) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.touchSelection.updateCellDimensions();\n\t\t\t}, 100);\n\t\t}\n\t}\n\n\t/**\n\t * Terminate terminal session\n\t */\n\tasync terminate() {\n\t\tif (this.websocket) {\n\t\t\tthis.websocket.close();\n\t\t}\n\n\t\tif (this.pid && this.serverMode) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\t`http://localhost:${this.options.port}/terminals/${this.pid}/terminate`,\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to terminate terminal:\", error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Dispose terminal\n\t */\n\tdispose() {\n\t\tthis.terminate();\n\n\t\t// Dispose touch selection\n\t\tif (this.touchSelection) {\n\t\t\tthis.touchSelection.destroy();\n\t\t\tthis.touchSelection = null;\n\t\t}\n\n\t\t// Dispose addons\n\t\tthis.disposeImageAddon();\n\t\tthis.disposeLigaturesAddon();\n\n\t\tif (this.terminal) {\n\t\t\tthis.terminal.dispose();\n\t\t}\n\n\t\tif (this.container) {\n\t\t\tthis.container.remove();\n\t\t}\n\t}\n\n\t// Event handlers (can be overridden)\n\tonConnect() {}\n\tonDisconnect() {}\n\tonError(error) {}\n\tonTitleChange(title) {}\n\tonBell() {}\n\tonProcessExit(exitData) {}\n}\n\n// Internal helpers for WebGL renderer lifecycle\nTerminalComponent.prototype._handleWebglContextLoss = function () {\n\ttry {\n\t\tconsole.warn(\"WebGL context lost; falling back to canvas renderer\");\n\t\ttry {\n\t\t\tthis.webglAddon?.dispose?.();\n\t\t} catch {}\n\t\tthis.webglAddon = null;\n\t} catch (e) {\n\t\tconsole.error(\"Error handling WebGL context loss:\", e);\n\t}\n};\n"
  },
  {
    "path": "src/components/terminal/terminalDefaults.js",
    "content": "import appSettings from \"lib/settings\";\n\nexport const DEFAULT_TERMINAL_SETTINGS = {\n\tfontSize: 12,\n\tfontFamily: \"MesloLGS NF Regular\",\n\tfontWeight: \"normal\",\n\tcursorBlink: true,\n\tcursorStyle: \"block\",\n\tcursorInactiveStyle: \"outline\",\n\tscrollback: 1000,\n\ttheme: \"dark\",\n\ttabStopWidth: 4,\n\tconvertEol: true,\n\tletterSpacing: 0,\n\timageSupport: false,\n\tfontLigatures: false,\n\tconfirmTabClose: true,\n\t// Touch selection settings\n\ttouchSelectionTapHoldDuration: 600,\n\ttouchSelectionMoveThreshold: 8,\n\ttouchSelectionHandleSize: 24,\n\ttouchSelectionHapticFeedback: true,\n\ttouchSelectionShowContextMenu: true,\n};\n\nexport function getTerminalSettings() {\n\tconst settings = appSettings.value.terminalSettings || {};\n\treturn {\n\t\t...DEFAULT_TERMINAL_SETTINGS,\n\t\t...settings,\n\t};\n}\n"
  },
  {
    "path": "src/components/terminal/terminalManager.js",
    "content": "/**\n * Terminal Manager\n * Handles terminal session creation and management\n */\n\nimport EditorFile from \"lib/editorFile\";\nimport TerminalComponent from \"./terminal\";\nimport TerminalTouchSelection from \"./terminalTouchSelection\";\nimport \"@xterm/xterm/css/xterm.css\";\nimport quickTools from \"components/quickTools\";\nimport toast from \"components/toast\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport openFile from \"lib/openFile\";\nimport openFolder from \"lib/openFolder\";\nimport appSettings from \"lib/settings\";\nimport helpers from \"utils/helpers\";\n\nconst TERMINAL_SESSION_STORAGE_KEY = \"acodeTerminalSessions\";\n\nclass TerminalManager {\n\tconstructor() {\n\t\tthis.terminals = new Map();\n\t\tthis.terminalCounter = 0;\n\t}\n\n\textractTerminalNumber(name) {\n\t\tif (!name) return null;\n\t\tconst match = String(name).match(/^Terminal\\s+(\\d+)(?:\\b| - )/i);\n\t\tif (!match) return null;\n\t\tconst number = Number.parseInt(match[1], 10);\n\t\treturn Number.isInteger(number) && number > 0 ? number : null;\n\t}\n\n\tgetNextAvailableTerminalNumber() {\n\t\tconst usedNumbers = new Set();\n\n\t\tfor (const terminal of this.terminals.values()) {\n\t\t\tconst number = terminal?.terminalNumber;\n\t\t\tif (Number.isInteger(number) && number > 0) {\n\t\t\t\tusedNumbers.add(number);\n\t\t\t}\n\t\t}\n\n\t\tlet nextNumber = 1;\n\t\twhile (usedNumbers.has(nextNumber)) {\n\t\t\tnextNumber++;\n\t\t}\n\n\t\treturn nextNumber;\n\t}\n\n\tnormalizePersistedSessions(stored) {\n\t\tif (!Array.isArray(stored)) {\n\t\t\treturn {\n\t\t\t\tsessions: [],\n\t\t\t\tchanged: stored != null,\n\t\t\t};\n\t\t}\n\n\t\tconst sessions = [];\n\t\tconst uniqueSessions = [];\n\t\tconst seenPids = new Set();\n\t\tlet changed = false;\n\n\t\tfor (const entry of stored) {\n\t\t\tif (!entry) {\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (typeof entry === \"string\") {\n\t\t\t\tsessions.push({\n\t\t\t\t\tpid: entry,\n\t\t\t\t\tname: `Terminal ${entry}`,\n\t\t\t\t\tpinned: false,\n\t\t\t\t});\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (typeof entry !== \"object\" || !entry.pid) {\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst pid = String(entry.pid);\n\t\t\tconst name =\n\t\t\t\ttypeof entry.name === \"string\" && entry.name.trim()\n\t\t\t\t\t? entry.name.trim()\n\t\t\t\t\t: `Terminal ${pid}`;\n\t\t\tconst pinned = entry.pinned === true;\n\n\t\t\tif (entry.pid !== pid || entry.name !== name || entry.pinned !== pinned) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\n\t\t\tsessions.push({ pid, name, pinned });\n\t\t}\n\n\t\tfor (const session of sessions) {\n\t\t\tconst pid = String(session.pid);\n\t\t\tif (seenPids.has(pid)) {\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tseenPids.add(pid);\n\t\t\tuniqueSessions.push({\n\t\t\t\tpid,\n\t\t\t\tname:\n\t\t\t\t\ttypeof session.name === \"string\" && session.name.trim()\n\t\t\t\t\t\t? session.name.trim()\n\t\t\t\t\t\t: `Terminal ${pid}`,\n\t\t\t\tpinned: session.pinned === true,\n\t\t\t});\n\t\t}\n\n\t\tif (uniqueSessions.length !== stored.length) {\n\t\t\tchanged = true;\n\t\t}\n\n\t\treturn {\n\t\t\tsessions: uniqueSessions,\n\t\t\tchanged,\n\t\t};\n\t}\n\n\treadPersistedSessions() {\n\t\ttry {\n\t\t\treturn this.normalizePersistedSessions(\n\t\t\t\thelpers.parseJSON(localStorage.getItem(TERMINAL_SESSION_STORAGE_KEY)),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to read persisted terminal sessions:\", error);\n\t\t\treturn {\n\t\t\t\tsessions: [],\n\t\t\t\tchanged: false,\n\t\t\t};\n\t\t}\n\t}\n\n\tasync getPersistedSessions() {\n\t\ttry {\n\t\t\tconst { sessions, changed } = this.readPersistedSessions();\n\t\t\tif (!sessions.length) {\n\t\t\t\tif (changed) {\n\t\t\t\t\tthis.savePersistedSessions([]);\n\t\t\t\t}\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (!(await Terminal.isAxsRunning())) {\n\t\t\t\t// Once the backend is gone, previously persisted PIDs are invalid.\n\t\t\t\tthis.savePersistedSessions([]);\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tif (changed) {\n\t\t\t\tthis.savePersistedSessions(sessions);\n\t\t\t}\n\n\t\t\treturn sessions;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to read persisted terminal sessions:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\tsavePersistedSessions(sessions) {\n\t\ttry {\n\t\t\tlocalStorage.setItem(\n\t\t\t\tTERMINAL_SESSION_STORAGE_KEY,\n\t\t\t\tJSON.stringify(sessions),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to persist terminal sessions:\", error);\n\t\t}\n\t}\n\n\tasync persistTerminalSession(pid, name, pinned = false) {\n\t\tif (!pid) return;\n\n\t\tconst pidStr = String(pid);\n\t\tconst { sessions } = this.readPersistedSessions();\n\t\tconst existingIndex = sessions.findIndex(\n\t\t\t(session) => session.pid === pidStr,\n\t\t);\n\t\tconst sessionData = {\n\t\t\tpid: pidStr,\n\t\t\tname: name || `Terminal ${pidStr}`,\n\t\t\tpinned: pinned === true,\n\t\t};\n\n\t\tif (existingIndex >= 0) {\n\t\t\tsessions[existingIndex] = {\n\t\t\t\t...sessions[existingIndex],\n\t\t\t\t...sessionData,\n\t\t\t};\n\t\t} else {\n\t\t\tsessions.push(sessionData);\n\t\t}\n\n\t\tthis.savePersistedSessions(sessions);\n\t}\n\n\tasync removePersistedSession(pid) {\n\t\tif (!pid) return;\n\n\t\tconst pidStr = String(pid);\n\t\tconst { sessions } = this.readPersistedSessions();\n\t\tconst nextSessions = sessions.filter((session) => session.pid !== pidStr);\n\n\t\tif (nextSessions.length !== sessions.length) {\n\t\t\tthis.savePersistedSessions(nextSessions);\n\t\t}\n\t}\n\n\tasync restorePersistedSessions() {\n\t\tconst sessions = await this.getPersistedSessions();\n\t\tif (!sessions.length) return;\n\n\t\tconst manager = window.editorManager;\n\t\tconst activeFileId = manager?.activeFile?.id;\n\t\tconst restoredTerminals = [];\n\t\tconst failedSessions = [];\n\n\t\tfor (const session of sessions) {\n\t\t\tif (!session?.pid) continue;\n\t\t\tif (this.terminals.has(session.pid)) continue;\n\n\t\t\ttry {\n\t\t\t\tconst instance = await this.createServerTerminal({\n\t\t\t\t\tpid: session.pid,\n\t\t\t\t\tname: session.name,\n\t\t\t\t\tpinned: session.pinned === true,\n\t\t\t\t\treconnecting: true,\n\t\t\t\t\trender: false,\n\t\t\t\t});\n\t\t\t\tif (instance) restoredTerminals.push(instance);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`Failed to restore terminal session ${session.pid}:`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tfailedSessions.push(session.name || session.pid);\n\t\t\t\tawait this.removePersistedSession(session.pid);\n\t\t\t}\n\t\t}\n\n\t\t// Stale session entries are expected after force-closes; keep startup quiet.\n\t\tif (failedSessions.length > 0) {\n\t\t\tconst message =\n\t\t\t\tfailedSessions.length === 1\n\t\t\t\t\t? `Skipped unavailable terminal: ${failedSessions[0]}`\n\t\t\t\t\t: `Skipped ${failedSessions.length} unavailable terminals`;\n\t\t\ttoast(message);\n\t\t}\n\n\t\tif (activeFileId && manager?.getFile) {\n\t\t\tconst fileToRestore = manager.getFile(activeFileId, \"id\");\n\t\t\tfileToRestore?.makeActive();\n\t\t} else if (!manager?.activeFile && restoredTerminals.length) {\n\t\t\trestoredTerminals[0]?.file?.makeActive();\n\t\t}\n\t}\n\n\t/**\n\t * Create a new terminal session\n\t * @param {object} options - Terminal options\n\t * @returns {Promise<object>} Terminal instance info\n\t */\n\tasync createTerminal(options = {}) {\n\t\ttry {\n\t\t\tconst { render, serverMode, reconnecting, pinned, ...terminalOptions } =\n\t\t\t\toptions;\n\t\t\tconst shouldRender = render !== false;\n\t\t\tconst isServerMode = serverMode !== false;\n\t\t\tconst isReconnecting = reconnecting === true;\n\n\t\t\tconst terminalId = `terminal_${++this.terminalCounter}`;\n\t\t\tconst providedName =\n\t\t\t\ttypeof options.name === \"string\" ? options.name.trim() : \"\";\n\t\t\tconst terminalNumber = providedName\n\t\t\t\t? this.extractTerminalNumber(providedName)\n\t\t\t\t: this.getNextAvailableTerminalNumber();\n\t\t\tconst terminalName = providedName || `Terminal ${terminalNumber}`;\n\t\t\tconst titlePrefix = terminalNumber\n\t\t\t\t? `Terminal ${terminalNumber}`\n\t\t\t\t: terminalName;\n\n\t\t\t// Check if terminal is installed before proceeding\n\t\t\tif (isServerMode) {\n\t\t\t\tconst installationResult = await this.checkAndInstallTerminal();\n\t\t\t\tif (!installationResult.success) {\n\t\t\t\t\tthrow new Error(installationResult.error);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Create terminal component\n\t\t\tconst terminalComponent = new TerminalComponent({\n\t\t\t\tserverMode: isServerMode,\n\t\t\t\t...terminalOptions,\n\t\t\t});\n\n\t\t\t// Create container\n\t\t\tconst terminalContainer = tag(\"div\", {\n\t\t\t\tclassName: \"terminal-content\",\n\t\t\t\tid: `terminal-${terminalId}`,\n\t\t\t});\n\n\t\t\t// Terminal styles (inject once)\n\t\t\tif (!document.getElementById(\"acode-terminal-styles\")) {\n\t\t\t\tconst terminalStyles = this.getTerminalStyles();\n\t\t\t\tconst terminalStyle = tag(\"style\", {\n\t\t\t\t\tid: \"acode-terminal-styles\",\n\t\t\t\t\ttextContent: terminalStyles,\n\t\t\t\t});\n\t\t\t\tdocument.body.appendChild(terminalStyle);\n\t\t\t}\n\n\t\t\t// Create EditorFile for terminal\n\t\t\tconst terminalFile = new EditorFile(terminalName, {\n\t\t\t\ttype: \"terminal\",\n\t\t\t\tcontent: terminalContainer,\n\t\t\t\ttabIcon: \"icon square-terminal\",\n\t\t\t\tpinned,\n\t\t\t\trender: shouldRender,\n\t\t\t});\n\n\t\t\t// Wait for tab creation and setup\n\t\t\treturn await new Promise((resolve, reject) => {\n\t\t\t\tsetTimeout(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Mount terminal component\n\t\t\t\t\t\tterminalComponent.mount(terminalContainer);\n\n\t\t\t\t\t\t// Connect to session if in server mode\n\t\t\t\t\t\tif (terminalComponent.serverMode) {\n\t\t\t\t\t\t\tawait terminalComponent.connectToSession(terminalOptions.pid);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// For local mode, just write a welcome message\n\t\t\t\t\t\t\tterminalComponent.write(\n\t\t\t\t\t\t\t\t\"Local terminal mode - ready for output\\r\\n\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Use PID as unique ID if available, otherwise fall back to terminalId\n\t\t\t\t\t\tconst uniqueId = terminalComponent.pid || terminalId;\n\n\t\t\t\t\t\t// Setup event handlers\n\t\t\t\t\t\tthis.setupTerminalHandlers(\n\t\t\t\t\t\t\tterminalFile,\n\t\t\t\t\t\t\tterminalComponent,\n\t\t\t\t\t\t\tuniqueId,\n\t\t\t\t\t\t\ttitlePrefix,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst instance = {\n\t\t\t\t\t\t\tid: uniqueId,\n\t\t\t\t\t\t\tname: terminalName,\n\t\t\t\t\t\t\tterminalNumber,\n\t\t\t\t\t\t\tcomponent: terminalComponent,\n\t\t\t\t\t\t\tfile: terminalFile,\n\t\t\t\t\t\t\tcontainer: terminalContainer,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tthis.terminals.set(uniqueId, instance);\n\n\t\t\t\t\t\tif (terminalComponent.serverMode && terminalComponent.pid) {\n\t\t\t\t\t\t\tawait this.persistTerminalSession(\n\t\t\t\t\t\t\t\tterminalComponent.pid,\n\t\t\t\t\t\t\t\tterminalName,\n\t\t\t\t\t\t\t\tterminalFile.pinned,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve(instance);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(\"Failed to initialize terminal:\", error);\n\n\t\t\t\t\t\t// Cleanup on failure - dispose component and remove broken tab\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tterminalComponent.dispose();\n\t\t\t\t\t\t} catch (disposeError) {\n\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\"Error disposing terminal component:\",\n\t\t\t\t\t\t\t\tdisposeError,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Force remove the tab without confirmation\n\t\t\t\t\t\t\tterminalFile._skipTerminalCloseConfirm = true;\n\t\t\t\t\t\t\tterminalFile.remove(true, { ignorePinned: true });\n\t\t\t\t\t\t} catch (removeError) {\n\t\t\t\t\t\t\tconsole.error(\"Error removing terminal tab:\", removeError);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Show alert for terminal creation failure\n\t\t\t\t\t\tif (!isReconnecting) {\n\t\t\t\t\t\t\tconst errorMessage = error?.message || \"Unknown error\";\n\t\t\t\t\t\t\talert(\n\t\t\t\t\t\t\t\tstrings[\"error\"],\n\t\t\t\t\t\t\t\t`Failed to create terminal: ${errorMessage}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t}\n\t\t\t\t}, 100);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to create terminal:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Check if terminal is installed and install if needed\n\t * @returns {Promise<{success: boolean, error?: string}>}\n\t */\n\tasync checkAndInstallTerminal() {\n\t\ttry {\n\t\t\t// Check if terminal is already installed\n\t\t\tconst isInstalled = await Terminal.isInstalled();\n\t\t\tif (isInstalled) {\n\t\t\t\treturn { success: true };\n\t\t\t}\n\n\t\t\t// Check if terminal is supported on this device\n\t\t\tconst isSupported = await Terminal.isSupported();\n\t\t\tif (!isSupported) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: \"Terminal is not supported on this device architecture\",\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Create installation progress terminal\n\t\t\tconst installTerminal = await this.createInstallationTerminal();\n\n\t\t\t// Install terminal with progress logging\n\t\t\tconst installResult = await Terminal.install(\n\t\t\t\t(message) => {\n\t\t\t\t\t// Remove stdout/stderr prefix for\n\t\t\t\t\tconst cleanMessage = message.replace(/^(stdout|stderr)\\s+/, \"\");\n\t\t\t\t\tinstallTerminal.component.write(`${cleanMessage}\\r\\n`);\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\t// Remove stdout/stderr prefix\n\t\t\t\t\tconst cleanError = error.replace(/^(stdout|stderr)\\s+/, \"\");\n\t\t\t\t\tinstallTerminal.component.write(\n\t\t\t\t\t\t`\\x1b[31mError: ${cleanError}\\x1b[0m\\r\\n`,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// Only return success if Terminal.install() indicates success (exit code 0)\n\t\t\tif (installResult === true) {\n\t\t\t\treturn { success: true };\n\t\t\t} else {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror:\n\t\t\t\t\t\t\"Terminal installation failed - process did not exit with code 0\",\n\t\t\t\t};\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Terminal installation failed:\", error);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: `Terminal installation failed: ${error.message}`,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Create a terminal for showing installation progress\n\t * @returns {Promise<object>} Installation terminal instance\n\t */\n\tasync createInstallationTerminal() {\n\t\tconst terminalId = `install_terminal_${++this.terminalCounter}`;\n\t\tconst terminalName = \"Terminal Installation\";\n\n\t\t// Create terminal component in local mode (no server needed)\n\t\tconst terminalComponent = new TerminalComponent({\n\t\t\tserverMode: false,\n\t\t});\n\n\t\t// Create container\n\t\tconst terminalContainer = tag(\"div\", {\n\t\t\tclassName: \"terminal-content\",\n\t\t\tid: `terminal-${terminalId}`,\n\t\t});\n\n\t\t// Terminal styles (inject once)\n\t\tif (!document.getElementById(\"acode-terminal-styles\")) {\n\t\t\tconst terminalStyles = this.getTerminalStyles();\n\t\t\tconst terminalStyle = tag(\"style\", {\n\t\t\t\tid: \"acode-terminal-styles\",\n\t\t\t\ttextContent: terminalStyles,\n\t\t\t});\n\t\t\tdocument.body.appendChild(terminalStyle);\n\t\t}\n\n\t\t// Create EditorFile for terminal\n\t\tconst terminalFile = new EditorFile(terminalName, {\n\t\t\ttype: \"terminal\",\n\t\t\tcontent: terminalContainer,\n\t\t\ttabIcon: \"icon save_alt\",\n\t\t\trender: true,\n\t\t});\n\n\t\t// Wait for tab creation and setup\n\t\treturn await new Promise((resolve, reject) => {\n\t\t\tsetTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\t// Mount terminal component\n\t\t\t\t\tterminalComponent.mount(terminalContainer);\n\n\t\t\t\t\t// Write initial message\n\t\t\t\t\tterminalComponent.write(\"🚀 Installing Terminal Environment...\\r\\n\");\n\t\t\t\t\tterminalComponent.write(\n\t\t\t\t\t\t\"This may take a few minutes depending on your connection.\\r\\n\\r\\n\",\n\t\t\t\t\t);\n\n\t\t\t\t\t// Setup event handlers\n\t\t\t\t\tthis.setupTerminalHandlers(\n\t\t\t\t\t\tterminalFile,\n\t\t\t\t\t\tterminalComponent,\n\t\t\t\t\t\tterminalId,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Set up custom title for installation terminal\n\t\t\t\t\tterminalFile.setCustomTitle(\n\t\t\t\t\t\t() => \"Installing Terminal Environment...\",\n\t\t\t\t\t);\n\n\t\t\t\t\tconst instance = {\n\t\t\t\t\t\tid: terminalId,\n\t\t\t\t\t\tname: terminalName,\n\t\t\t\t\t\tcomponent: terminalComponent,\n\t\t\t\t\t\tfile: terminalFile,\n\t\t\t\t\t\tcontainer: terminalContainer,\n\t\t\t\t\t};\n\n\t\t\t\t\tthis.terminals.set(terminalId, instance);\n\t\t\t\t\tresolve(instance);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"Failed to create installation terminal:\", error);\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t}, 100);\n\t\t});\n\t}\n\n\t/**\n\t * Setup terminal event handlers\n\t * @param {EditorFile} terminalFile - Terminal file instance\n\t * @param {TerminalComponent} terminalComponent - Terminal component\n\t * @param {string} terminalId - Terminal ID\n\t */\n\tasync setupTerminalHandlers(\n\t\tterminalFile,\n\t\tterminalComponent,\n\t\tterminalId,\n\t\ttitlePrefix = terminalId,\n\t) {\n\t\tconst textarea = terminalComponent.terminal?.textarea;\n\t\tif (textarea) {\n\t\t\tconst onFocus = () => {\n\t\t\t\tconst { $toggler } = quickTools;\n\t\t\t\t$toggler.classList.add(\"hide\");\n\t\t\t\tclearTimeout(this.togglerTimeout);\n\t\t\t\tthis.togglerTimeout = setTimeout(() => {\n\t\t\t\t\t$toggler.style.display = \"none\";\n\t\t\t\t}, 300);\n\t\t\t};\n\n\t\t\tconst onBlur = () => {\n\t\t\t\tconst { $toggler } = quickTools;\n\t\t\t\tclearTimeout(this.togglerTimeout);\n\t\t\t\t$toggler.style.display = \"\";\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t$toggler.classList.remove(\"hide\");\n\t\t\t\t}, 10);\n\t\t\t};\n\n\t\t\ttextarea.addEventListener(\"focus\", onFocus);\n\t\t\ttextarea.addEventListener(\"blur\", onBlur);\n\n\t\t\tterminalComponent.cleanupFocusHandlers = () => {\n\t\t\t\ttextarea.removeEventListener(\"focus\", onFocus);\n\t\t\t\ttextarea.removeEventListener(\"blur\", onBlur);\n\t\t\t};\n\t\t}\n\n\t\t// Handle tab focus/blur\n\t\tterminalFile.onfocus = () => {\n\t\t\t// Guarded fit on focus: only fit if cols/rows would change, then focus\n\t\t\tconst run = () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst pd = terminalComponent.fitAddon?.proposeDimensions?.();\n\t\t\t\t\tif (\n\t\t\t\t\t\tpd &&\n\t\t\t\t\t\t(pd.cols !== terminalComponent.terminal.cols ||\n\t\t\t\t\t\t\tpd.rows !== terminalComponent.terminal.rows)\n\t\t\t\t\t) {\n\t\t\t\t\t\tterminalComponent.fitAddon.fit();\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tterminalComponent.focus();\n\t\t\t};\n\t\t\tif (typeof requestAnimationFrame === \"function\") {\n\t\t\t\trequestAnimationFrame(run);\n\t\t\t} else {\n\t\t\t\tsetTimeout(run, 0);\n\t\t\t}\n\t\t};\n\n\t\t// Handle tab close\n\t\tterminalFile.onclose = () => {\n\t\t\tthis.closeTerminal(terminalId);\n\t\t};\n\t\tterminalFile.onpinstatechange = (pinned) => {\n\t\t\tif (!terminalComponent.serverMode || !terminalComponent.pid) return;\n\t\t\tvoid this.persistTerminalSession(\n\t\t\t\tterminalComponent.pid,\n\t\t\t\tterminalFile.filename,\n\t\t\t\tpinned,\n\t\t\t);\n\t\t};\n\n\t\tterminalFile._skipTerminalCloseConfirm = false;\n\t\tconst originalRemove = terminalFile.remove.bind(terminalFile);\n\t\tterminalFile.remove = async (force = false, options = {}) => {\n\t\t\tif (terminalFile.pinned && !options?.ignorePinned) {\n\t\t\t\treturn originalRemove(force, options);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t!terminalFile._skipTerminalCloseConfirm &&\n\t\t\t\tthis.shouldConfirmTerminalClose()\n\t\t\t) {\n\t\t\t\tconst message = `${strings[\"close\"]} ${strings[\"terminal\"]}?`;\n\t\t\t\tconst shouldClose = await confirm(strings[\"confirm\"], message);\n\t\t\t\tif (!shouldClose) return;\n\t\t\t}\n\n\t\t\tterminalFile._skipTerminalCloseConfirm = false;\n\t\t\treturn originalRemove(force, options);\n\t\t};\n\n\t\t// Enhanced resize handling with debouncing\n\t\tlet resizeTimeout = null;\n\t\tconst RESIZE_DEBOUNCE = 200;\n\t\tlet lastResizeTime = 0;\n\n\t\tlet lastWidth = 0;\n\t\tlet lastHeight = 0;\n\t\tconst resizeObserver = new ResizeObserver((entries) => {\n\t\t\tconst now = Date.now();\n\t\t\tconst entry = entries && entries[0];\n\t\t\tconst cr = entry?.contentRect;\n\t\t\tconst width = cr?.width ?? terminalFile.content?.clientWidth ?? 0;\n\t\t\tconst height = cr?.height ?? terminalFile.content?.clientHeight ?? 0;\n\n\t\t\t// Clear any pending resize\n\t\t\tif (resizeTimeout) {\n\t\t\t\tclearTimeout(resizeTimeout);\n\t\t\t}\n\n\t\t\t// Debounce rapid resize events (common during keyboard open/close)\n\t\t\tresizeTimeout = setTimeout(() => {\n\t\t\t\ttry {\n\t\t\t\t\t// Check if terminal is still available and mounted\n\t\t\t\t\tif (!terminalComponent.terminal || !terminalComponent.container) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only fit if actual size changed to reduce reflows\n\t\t\t\t\tif (\n\t\t\t\t\t\tMath.abs(width - lastWidth) > 0.5 ||\n\t\t\t\t\t\tMath.abs(height - lastHeight) > 0.5\n\t\t\t\t\t) {\n\t\t\t\t\t\tterminalComponent.fit();\n\t\t\t\t\t\tlastWidth = width;\n\t\t\t\t\t\tlastHeight = height;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update last resize time\n\t\t\t\t\tlastResizeTime = now;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Resize error for terminal ${terminalId}:`, error);\n\t\t\t\t}\n\t\t\t}, RESIZE_DEBOUNCE);\n\t\t});\n\n\t\t// Wait for the terminal container to be available, then observe it\n\t\tsetTimeout(() => {\n\t\t\tconst containerElement = terminalFile.content;\n\t\t\tif (containerElement && containerElement instanceof Element) {\n\t\t\t\tresizeObserver.observe(containerElement);\n\t\t\t\t// store observer so we can disconnect on close\n\t\t\t\tterminalFile._resizeObserver = resizeObserver;\n\t\t\t} else {\n\t\t\t\tconsole.warn(\"Terminal container not available for ResizeObserver\");\n\t\t\t}\n\t\t}, 200);\n\n\t\t// Terminal event handlers\n\t\tterminalComponent.onConnect = () => {\n\t\t\tconsole.log(`Terminal ${terminalId} connected`);\n\t\t};\n\n\t\tterminalComponent.onDisconnect = () => {\n\t\t\tconsole.log(`Terminal ${terminalId} disconnected`);\n\t\t};\n\n\t\tterminalComponent.onError = (error) => {\n\t\t\tconsole.error(`Terminal ${terminalId} error:`, error);\n\n\t\t\t// Close the terminal and remove the tab\n\t\t\tthis.closeTerminal(terminalId, true);\n\n\t\t\t// Show alert for connection error\n\t\t\tconst errorMessage = error?.message || \"Connection lost\";\n\t\t\talert(strings[\"error\"], `Terminal connection error: ${errorMessage}`);\n\t\t};\n\n\t\tterminalComponent.onTitleChange = async (title) => {\n\t\t\tif (title) {\n\t\t\t\t// Keep the tab prefix stable for this terminal instance.\n\t\t\t\tconst formattedTitle = `${titlePrefix} - ${title}`;\n\t\t\t\tterminalFile.filename = formattedTitle;\n\n\t\t\t\tif (terminalComponent.serverMode && terminalComponent.pid) {\n\t\t\t\t\tawait this.persistTerminalSession(\n\t\t\t\t\t\tterminalComponent.pid,\n\t\t\t\t\t\tformattedTitle,\n\t\t\t\t\t\tterminalFile.pinned,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Refresh the header subtitle if this terminal is active\n\t\t\t\tif (\n\t\t\t\t\teditorManager.activeFile &&\n\t\t\t\t\teditorManager.activeFile.id === terminalFile.id\n\t\t\t\t) {\n\t\t\t\t\t// Force refresh of the header subtitle\n\t\t\t\t\tterminalFile.setCustomTitle(getTerminalTitle);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tterminalComponent.onProcessExit = (exitData) => {\n\t\t\t// Format exit message based on exit code and signal\n\t\t\tlet message;\n\t\t\tif (exitData.signal) {\n\t\t\t\tmessage = `Process terminated by signal ${exitData.signal}`;\n\t\t\t} else if (exitData.exit_code === 0) {\n\t\t\t\tmessage = `Process exited successfully (code ${exitData.exit_code})`;\n\t\t\t} else {\n\t\t\t\tmessage = `Process exited with code ${exitData.exit_code}`;\n\t\t\t}\n\n\t\t\tthis.closeTerminal(terminalId);\n\t\t\tterminalFile._skipTerminalCloseConfirm = true;\n\t\t\tterminalFile.remove(true, { ignorePinned: true });\n\t\t\ttoast(message);\n\t\t};\n\n\t\t// Handle acode CLI open commands (OSC 7777)\n\t\tterminalComponent.onOscOpen = async (type, path) => {\n\t\t\tif (!path) return;\n\n\t\t\t// Convert proot path\n\t\t\tconst fileUri = this.convertProotPath(path);\n\t\t\t// Extract folder/file name from normalized path\n\t\t\tconst name = this.getPathDisplayName(path);\n\n\t\t\ttry {\n\t\t\t\tif (type === \"folder\") {\n\t\t\t\t\t// Open folder in sidebar\n\t\t\t\t\tawait openFolder(fileUri, { name, saveState: true, listFiles: true });\n\t\t\t\t\ttoast(`Opened folder: ${name}`);\n\t\t\t\t} else {\n\t\t\t\t\t// Open file in editor\n\t\t\t\t\tawait openFile(fileUri, { render: true });\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to open from terminal:\", error);\n\t\t\t\ttoast(`Failed to open: ${path}`);\n\t\t\t}\n\t\t};\n\n\t\t// Store references for cleanup\n\t\tterminalFile._terminalId = terminalId;\n\t\tterminalFile.terminalComponent = terminalComponent;\n\t\tterminalFile._resizeObserver = resizeObserver;\n\n\t\t// Set up custom title function for terminal\n\t\tconst getTerminalTitle = () => {\n\t\t\tif (terminalComponent.pid) {\n\t\t\t\treturn `PID: ${terminalComponent.pid}`;\n\t\t\t}\n\t\t\t// fallback to terminal name\n\t\t\treturn `${terminalId}`;\n\t\t};\n\n\t\tterminalFile.setCustomTitle(getTerminalTitle);\n\t}\n\n\t/**\n\t * Close a terminal session\n\t * @param {string} terminalId - Terminal ID\n\t */\n\tcloseTerminal(terminalId, removeTab = false) {\n\t\tconst terminal = this.terminals.get(terminalId);\n\t\tif (!terminal) return;\n\n\t\ttry {\n\t\t\tif (terminal.component.serverMode && terminal.component.pid) {\n\t\t\t\tthis.removePersistedSession(terminal.component.pid);\n\t\t\t}\n\n\t\t\t// Cleanup resize observer\n\t\t\tif (terminal.file._resizeObserver) {\n\t\t\t\tterminal.file._resizeObserver.disconnect();\n\t\t\t\tterminal.file._resizeObserver = null;\n\t\t\t}\n\n\t\t\t// Cleanup focus handlers\n\t\t\tif (terminal.component.cleanupFocusHandlers) {\n\t\t\t\tterminal.component.cleanupFocusHandlers();\n\t\t\t}\n\n\t\t\t// Dispose terminal component\n\t\t\tterminal.component.dispose();\n\n\t\t\t// Remove from map\n\t\t\tthis.terminals.delete(terminalId);\n\n\t\t\t// Optionally remove the tab as well\n\t\t\tif (removeTab && terminal.file) {\n\t\t\t\ttry {\n\t\t\t\t\tterminal.file._skipTerminalCloseConfirm = true;\n\t\t\t\t\tterminal.file.remove(true, { ignorePinned: true });\n\t\t\t\t} catch (removeError) {\n\t\t\t\t\tconsole.error(\"Error removing terminal tab:\", removeError);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.getAllTerminals().size <= 0) {\n\t\t\t\tExecutor.stopService();\n\t\t\t}\n\n\t\t\tconsole.log(`Terminal ${terminalId} closed`);\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error closing terminal ${terminalId}:`, error);\n\t\t}\n\t}\n\n\t/**\n\t * Get terminal by ID\n\t * @param {string} terminalId - Terminal ID\n\t * @returns {object|null} Terminal instance\n\t */\n\tgetTerminal(terminalId) {\n\t\treturn this.terminals.get(terminalId) || null;\n\t}\n\n\t/**\n\t * Get all active terminals\n\t * @returns {Map} All terminals\n\t */\n\tgetAllTerminals() {\n\t\treturn this.terminals;\n\t}\n\n\t/**\n\t * Register a touch-selection \"More\" menu option.\n\t * @param {object} option\n\t * @returns {string|null}\n\t */\n\taddTouchSelectionMoreOption(option) {\n\t\treturn TerminalTouchSelection.addMoreOption(option);\n\t}\n\n\t/**\n\t * Remove a touch-selection \"More\" menu option.\n\t * @param {string} id\n\t * @returns {boolean}\n\t */\n\tremoveTouchSelectionMoreOption(id) {\n\t\treturn TerminalTouchSelection.removeMoreOption(id);\n\t}\n\n\t/**\n\t * List touch-selection \"More\" menu options.\n\t * @returns {Array<object>}\n\t */\n\tgetTouchSelectionMoreOptions() {\n\t\treturn TerminalTouchSelection.getMoreOptions();\n\t}\n\n\t/**\n\t * Write to a specific terminal\n\t * @param {string} terminalId - Terminal ID\n\t * @param {string} data - Data to write\n\t */\n\twriteToTerminal(terminalId, data) {\n\t\tconst terminal = this.getTerminal(terminalId);\n\t\tif (terminal) {\n\t\t\tterminal.component.write(data);\n\t\t}\n\t}\n\n\t/**\n\t * Clear a specific terminal\n\t * @param {string} terminalId - Terminal ID\n\t */\n\tclearTerminal(terminalId) {\n\t\tconst terminal = this.getTerminal(terminalId);\n\t\tif (terminal) {\n\t\t\tterminal.component.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Get terminal styles for shadow DOM\n\t * @returns {string} CSS styles\n\t */\n\tgetTerminalStyles() {\n\t\treturn `\n\t\t\t.terminal-content {\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\tbox-sizing: border-box;\n\t\t\t\tbackground: #1e1e1e;\n\t\t\t\toverflow: hidden;\n\t\t\t\tposition: relative;\n\t\t\t}\n\n\t\t\t.terminal-content .xterm {\n\t\t\t\tpadding: 0.25rem;\n\t\t\t\tbox-sizing: border-box;\n\t\t\t}\n\t\t`;\n\t}\n\n\t/**\n\t * Create a local terminal (no server connection)\n\t * @param {object} options - Terminal options\n\t * @returns {Promise<object>} Terminal instance\n\t */\n\tasync createLocalTerminal(options = {}) {\n\t\treturn this.createTerminal({\n\t\t\t...options,\n\t\t\tserverMode: false,\n\t\t});\n\t}\n\n\t/**\n\t * Create a server terminal (with backend connection)\n\t * @param {object} options - Terminal options\n\t * @returns {Promise<object>} Terminal instance\n\t */\n\tasync createServerTerminal(options = {}) {\n\t\treturn this.createTerminal({\n\t\t\t...options,\n\t\t\tserverMode: true,\n\t\t});\n\t}\n\n\t/**\n\t * Handle keyboard resize events for all terminals\n\t * This is called when the virtual keyboard opens/closes on mobile\n\t */\n\thandleKeyboardResize() {\n\t\t// Add a small delay to let the UI settle\n\t\tsetTimeout(() => {\n\t\t\tthis.terminals.forEach((terminal) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (terminal.component && terminal.component.terminal) {\n\t\t\t\t\t\t// Force a re-fit for all terminals\n\t\t\t\t\t\tterminal.component.fit();\n\n\t\t\t\t\t\t// If terminal has lots of content, try to preserve scroll position\n\t\t\t\t\t\tconst buffer = terminal.component.terminal.buffer?.active;\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tbuffer &&\n\t\t\t\t\t\t\tbuffer.length > terminal.component.terminal.rows * 2\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// For content-heavy terminals, ensure we stay near the bottom if we were there\n\t\t\t\t\t\t\tconst wasNearBottom =\n\t\t\t\t\t\t\t\tbuffer.viewportY >=\n\t\t\t\t\t\t\t\tbuffer.length - terminal.component.terminal.rows - 5;\n\t\t\t\t\t\t\tif (wasNearBottom) {\n\t\t\t\t\t\t\t\t// Scroll to bottom after resize\n\t\t\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t\t\tterminal.component.terminal.scrollToBottom();\n\t\t\t\t\t\t\t\t}, 100);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`Error handling keyboard resize for terminal ${terminal.id}:`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t}, 150);\n\t}\n\n\t/**\n\t * Stabilize terminal viewport after resize operations\n\t */\n\tstabilizeTerminals() {\n\t\tthis.terminals.forEach((terminal) => {\n\t\t\ttry {\n\t\t\t\tif (terminal.component && terminal.component.terminal) {\n\t\t\t\t\t// Clear any touch selections during stabilization\n\t\t\t\t\tif (\n\t\t\t\t\t\tterminal.component.touchSelection &&\n\t\t\t\t\t\tterminal.component.touchSelection.isSelecting\n\t\t\t\t\t) {\n\t\t\t\t\t\tterminal.component.touchSelection.clearSelection();\n\t\t\t\t\t}\n\n\t\t\t\t\t// Re-fit and refresh\n\t\t\t\t\tterminal.component.fit();\n\n\t\t\t\t\t// Focus the active terminal to ensure proper state\n\t\t\t\t\tif (terminal.file && terminal.file.isOpen) {\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tterminal.component.focus();\n\t\t\t\t\t\t}, 50);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Error stabilizing terminal ${terminal.id}:`, error);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Convert proot internal path to app-accessible path\n\t * @param {string} prootPath - Path from inside proot environment\n\t * @returns {string} App filesystem path\n\t */\n\tconvertProotPath(prootPath) {\n\t\tif (!prootPath) return prootPath;\n\n\t\tconst packageName = window.BuildInfo?.packageName || \"com.foxdebug.acode\";\n\t\tconst dataDir = `/data/user/0/${packageName}`;\n\t\tconst alpineRoot = `${dataDir}/files/alpine`;\n\n\t\tlet convertedPath;\n\n\t\tif (prootPath.startsWith(\"/public\")) {\n\t\t\t// /public -> /data/user/0/com.foxdebug.acode/files/public\n\t\t\tconvertedPath = `file://${dataDir}/files${prootPath}`;\n\t\t} else if (\n\t\t\tprootPath.startsWith(\"/sdcard\") ||\n\t\t\tprootPath.startsWith(\"/storage\") ||\n\t\t\tprootPath.startsWith(\"/data\")\n\t\t) {\n\t\t\tconvertedPath = `file://${prootPath}`;\n\t\t} else if (prootPath.startsWith(\"/\")) {\n\t\t\t// Everything else is relative to alpine root\n\t\t\tconvertedPath = `file://${alpineRoot}${prootPath}`;\n\t\t} else {\n\t\t\tconvertedPath = prootPath;\n\t\t}\n\n\t\t//console.log(`Path conversion: ${prootPath} -> ${convertedPath}`);\n\t\treturn convertedPath;\n\t}\n\n\t/**\n\t * Get a stable display name from a filesystem path.\n\t * Handles trailing \".\" and \"..\" segments (e.g. \"/a/b/.\" -> \"b\").\n\t * @param {string} path\n\t * @returns {string}\n\t */\n\tgetPathDisplayName(path) {\n\t\tif (!path) return \"folder\";\n\n\t\tconst normalized = [];\n\t\tfor (const segment of String(path).split(\"/\")) {\n\t\t\tif (!segment || segment === \".\") continue;\n\t\t\tif (segment === \"..\") {\n\t\t\t\tif (normalized.length) normalized.pop();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tnormalized.push(segment);\n\t\t}\n\n\t\treturn normalized.pop() || \"folder\";\n\t}\n\n\tshouldConfirmTerminalClose() {\n\t\tconst settings = appSettings?.value?.terminalSettings;\n\t\tif (settings && settings.confirmTabClose === false) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n}\n\n// Create singleton instance\nconst terminalManager = new TerminalManager();\n\nexport default terminalManager;\n"
  },
  {
    "path": "src/components/terminal/terminalThemeManager.js",
    "content": "/**\n * Terminal Theme Manager\n * Manages terminal themes and provides plugin API for custom themes\n */\n\nclass TerminalThemeManager {\n\tconstructor() {\n\t\tthis.themes = {\n\t\t\tdark: {\n\t\t\t\tbackground: \"#23272a\",\n\t\t\t\tforeground: \"#f5f5f5\",\n\t\t\t\tcursor: \"#4285f4\",\n\t\t\t\tcursorAccent: \"#23272a\",\n\t\t\t\tselection: \"#ffffff40\",\n\t\t\t\tblack: \"#181b1e\",\n\t\t\t\tred: \"#f07178\",\n\t\t\t\tgreen: \"#c3e88d\",\n\t\t\t\tyellow: \"#ffcb6b\",\n\t\t\t\tblue: \"#4285f4\",\n\t\t\t\tmagenta: \"#c792ea\",\n\t\t\t\tcyan: \"#89ddff\",\n\t\t\t\twhite: \"#f5f5f5\",\n\t\t\t\tbrightBlack: \"#2d3134\",\n\t\t\t\tbrightRed: \"#ff6b6b\",\n\t\t\t\tbrightGreen: \"#5af78e\",\n\t\t\t\tbrightYellow: \"#f4f99d\",\n\t\t\t\tbrightBlue: \"#82aaff\",\n\t\t\t\tbrightMagenta: \"#c594c5\",\n\t\t\t\tbrightCyan: \"#9aedfe\",\n\t\t\t\tbrightWhite: \"#ffffff\",\n\t\t\t},\n\t\t\tlight: {\n\t\t\t\tbackground: \"#ffffff\",\n\t\t\t\tforeground: \"#0f1729\",\n\t\t\t\tcursor: \"#3b82f6\",\n\t\t\t\tcursorAccent: \"#ffffff\",\n\t\t\t\tselection: \"#e2e8f0\",\n\t\t\t\tblack: \"#1e293b\",\n\t\t\t\tred: \"#dc2626\",\n\t\t\t\tgreen: \"#16a34a\",\n\t\t\t\tyellow: \"#ca8a04\",\n\t\t\t\tblue: \"#3b82f6\",\n\t\t\t\tmagenta: \"#9333ea\",\n\t\t\t\tcyan: \"#0891b2\",\n\t\t\t\twhite: \"#64748b\",\n\t\t\t\tbrightBlack: \"#475569\",\n\t\t\t\tbrightRed: \"#ef4444\",\n\t\t\t\tbrightGreen: \"#22c55e\",\n\t\t\t\tbrightYellow: \"#eab308\",\n\t\t\t\tbrightBlue: \"#6366f1\",\n\t\t\t\tbrightMagenta: \"#a855f7\",\n\t\t\t\tbrightCyan: \"#06b6d4\",\n\t\t\t\tbrightWhite: \"#0f1729\",\n\t\t\t},\n\t\t\tsolarizedDark: {\n\t\t\t\tbackground: \"#002b36\",\n\t\t\t\tforeground: \"#839496\",\n\t\t\t\tcursor: \"#839496\",\n\t\t\t\tcursorAccent: \"#002b36\",\n\t\t\t\tselection: \"#073642\",\n\t\t\t\tblack: \"#073642\",\n\t\t\t\tred: \"#dc322f\",\n\t\t\t\tgreen: \"#859900\",\n\t\t\t\tyellow: \"#b58900\",\n\t\t\t\tblue: \"#268bd2\",\n\t\t\t\tmagenta: \"#d33682\",\n\t\t\t\tcyan: \"#2aa198\",\n\t\t\t\twhite: \"#eee8d5\",\n\t\t\t\tbrightBlack: \"#002b36\",\n\t\t\t\tbrightRed: \"#cb4b16\",\n\t\t\t\tbrightGreen: \"#586e75\",\n\t\t\t\tbrightYellow: \"#657b83\",\n\t\t\t\tbrightBlue: \"#839496\",\n\t\t\t\tbrightMagenta: \"#6c71c4\",\n\t\t\t\tbrightCyan: \"#93a1a1\",\n\t\t\t\tbrightWhite: \"#fdf6e3\",\n\t\t\t},\n\t\t\tsolarizedLight: {\n\t\t\t\tbackground: \"#fdf6e3\",\n\t\t\t\tforeground: \"#657b83\",\n\t\t\t\tcursor: \"#657b83\",\n\t\t\t\tcursorAccent: \"#fdf6e3\",\n\t\t\t\tselection: \"#eee8d5\",\n\t\t\t\tblack: \"#073642\",\n\t\t\t\tred: \"#dc322f\",\n\t\t\t\tgreen: \"#859900\",\n\t\t\t\tyellow: \"#b58900\",\n\t\t\t\tblue: \"#268bd2\",\n\t\t\t\tmagenta: \"#d33682\",\n\t\t\t\tcyan: \"#2aa198\",\n\t\t\t\twhite: \"#eee8d5\",\n\t\t\t\tbrightBlack: \"#002b36\",\n\t\t\t\tbrightRed: \"#cb4b16\",\n\t\t\t\tbrightGreen: \"#586e75\",\n\t\t\t\tbrightYellow: \"#657b83\",\n\t\t\t\tbrightBlue: \"#839496\",\n\t\t\t\tbrightMagenta: \"#6c71c4\",\n\t\t\t\tbrightCyan: \"#93a1a1\",\n\t\t\t\tbrightWhite: \"#fdf6e3\",\n\t\t\t},\n\t\t\tmonokai: {\n\t\t\t\tbackground: \"#272822\",\n\t\t\t\tforeground: \"#f8f8f2\",\n\t\t\t\tcursor: \"#f8f8f0\",\n\t\t\t\tcursorAccent: \"#272822\",\n\t\t\t\tselection: \"#49483e\",\n\t\t\t\tblack: \"#272822\",\n\t\t\t\tred: \"#f92672\",\n\t\t\t\tgreen: \"#a6e22e\",\n\t\t\t\tyellow: \"#e6db74\",\n\t\t\t\tblue: \"#66d9ef\",\n\t\t\t\tmagenta: \"#ae81ff\",\n\t\t\t\tcyan: \"#a1efe4\",\n\t\t\t\twhite: \"#f8f8f2\",\n\t\t\t\tbrightBlack: \"#75715e\",\n\t\t\t\tbrightRed: \"#ff6188\",\n\t\t\t\tbrightGreen: \"#a6e22e\",\n\t\t\t\tbrightYellow: \"#ffd866\",\n\t\t\t\tbrightBlue: \"#78dce8\",\n\t\t\t\tbrightMagenta: \"#ab9df2\",\n\t\t\t\tbrightCyan: \"#a1efe4\",\n\t\t\t\tbrightWhite: \"#f9f8f5\",\n\t\t\t},\n\t\t\tdracula: {\n\t\t\t\tbackground: \"#282a36\",\n\t\t\t\tforeground: \"#f8f8f2\",\n\t\t\t\tcursor: \"#f8f8f0\",\n\t\t\t\tcursorAccent: \"#282a36\",\n\t\t\t\tselection: \"#44475a\",\n\t\t\t\tblack: \"#000000\",\n\t\t\t\tred: \"#ff5555\",\n\t\t\t\tgreen: \"#50fa7b\",\n\t\t\t\tyellow: \"#f1fa8c\",\n\t\t\t\tblue: \"#bd93f9\",\n\t\t\t\tmagenta: \"#ff79c6\",\n\t\t\t\tcyan: \"#8be9fd\",\n\t\t\t\twhite: \"#bfbfbf\",\n\t\t\t\tbrightBlack: \"#4d4d4d\",\n\t\t\t\tbrightRed: \"#ff6e67\",\n\t\t\t\tbrightGreen: \"#5af78e\",\n\t\t\t\tbrightYellow: \"#f4f99d\",\n\t\t\t\tbrightBlue: \"#caa9fa\",\n\t\t\t\tbrightMagenta: \"#ff92d0\",\n\t\t\t\tbrightCyan: \"#9aedfe\",\n\t\t\t\tbrightWhite: \"#e6e6e6\",\n\t\t\t},\n\t\t\tnord: {\n\t\t\t\tbackground: \"#2e3440\",\n\t\t\t\tforeground: \"#d8dee9\",\n\t\t\t\tcursor: \"#d8dee9\",\n\t\t\t\tcursorAccent: \"#2e3440\",\n\t\t\t\tselection: \"#434c5e\",\n\t\t\t\tblack: \"#3b4252\",\n\t\t\t\tred: \"#bf616a\",\n\t\t\t\tgreen: \"#a3be8c\",\n\t\t\t\tyellow: \"#ebcb8b\",\n\t\t\t\tblue: \"#81a1c1\",\n\t\t\t\tmagenta: \"#b48ead\",\n\t\t\t\tcyan: \"#88c0d0\",\n\t\t\t\twhite: \"#e5e9f0\",\n\t\t\t\tbrightBlack: \"#4c566a\",\n\t\t\t\tbrightRed: \"#d08770\",\n\t\t\t\tbrightGreen: \"#a3be8c\",\n\t\t\t\tbrightYellow: \"#ebcb8b\",\n\t\t\t\tbrightBlue: \"#5e81ac\",\n\t\t\t\tbrightMagenta: \"#b48ead\",\n\t\t\t\tbrightCyan: \"#8fbcbb\",\n\t\t\t\tbrightWhite: \"#eceff4\",\n\t\t\t},\n\t\t\tgruvbox: {\n\t\t\t\tbackground: \"#282828\",\n\t\t\t\tforeground: \"#ebdbb2\",\n\t\t\t\tcursor: \"#ebdbb2\",\n\t\t\t\tcursorAccent: \"#282828\",\n\t\t\t\tselection: \"#665c54\",\n\t\t\t\tblack: \"#282828\",\n\t\t\t\tred: \"#cc241d\",\n\t\t\t\tgreen: \"#98971a\",\n\t\t\t\tyellow: \"#d79921\",\n\t\t\t\tblue: \"#458588\",\n\t\t\t\tmagenta: \"#b16286\",\n\t\t\t\tcyan: \"#689d6a\",\n\t\t\t\twhite: \"#a89984\",\n\t\t\t\tbrightBlack: \"#928374\",\n\t\t\t\tbrightRed: \"#fb4934\",\n\t\t\t\tbrightGreen: \"#b8bb26\",\n\t\t\t\tbrightYellow: \"#fabd2f\",\n\t\t\t\tbrightBlue: \"#83a598\",\n\t\t\t\tbrightMagenta: \"#d3869b\",\n\t\t\t\tbrightCyan: \"#8ec07c\",\n\t\t\t\tbrightWhite: \"#ebdbb2\",\n\t\t\t},\n\t\t\toneDark: {\n\t\t\t\tbackground: \"#21252b\",\n\t\t\t\tforeground: \"#abb2bf\",\n\t\t\t\tcursor: \"#528bff\",\n\t\t\t\tcursorAccent: \"#21252b\",\n\t\t\t\tselection: \"#3e4451\",\n\t\t\t\tblack: \"#21252b\",\n\t\t\t\tred: \"#e86671\",\n\t\t\t\tgreen: \"#98c379\",\n\t\t\t\tyellow: \"#e5c07b\",\n\t\t\t\tblue: \"#61afef\",\n\t\t\t\tmagenta: \"#c678dd\",\n\t\t\t\tcyan: \"#56b6c2\",\n\t\t\t\twhite: \"#abb2bf\",\n\t\t\t\tbrightBlack: \"#5c6370\",\n\t\t\t\tbrightRed: \"#f07178\",\n\t\t\t\tbrightGreen: \"#a6e22e\",\n\t\t\t\tbrightYellow: \"#f9e79f\",\n\t\t\t\tbrightBlue: \"#73d0ff\",\n\t\t\t\tbrightMagenta: \"#d19a66\",\n\t\t\t\tbrightCyan: \"#7fdbca\",\n\t\t\t\tbrightWhite: \"#ffffff\",\n\t\t\t},\n\t\t\tmaterial: {\n\t\t\t\tbackground: \"#263238\",\n\t\t\t\tforeground: \"#eeffff\",\n\t\t\t\tcursor: \"#ffcc00\",\n\t\t\t\tcursorAccent: \"#263238\",\n\t\t\t\tselection: \"#37474f\",\n\t\t\t\tblack: \"#263238\",\n\t\t\t\tred: \"#f07178\",\n\t\t\t\tgreen: \"#c3e88d\",\n\t\t\t\tyellow: \"#ffcb6b\",\n\t\t\t\tblue: \"#82aaff\",\n\t\t\t\tmagenta: \"#c792ea\",\n\t\t\t\tcyan: \"#89ddff\",\n\t\t\t\twhite: \"#eeffff\",\n\t\t\t\tbrightBlack: \"#546e7a\",\n\t\t\t\tbrightRed: \"#f07178\",\n\t\t\t\tbrightGreen: \"#c3e88d\",\n\t\t\t\tbrightYellow: \"#ffcb6b\",\n\t\t\t\tbrightBlue: \"#82aaff\",\n\t\t\t\tbrightMagenta: \"#c792ea\",\n\t\t\t\tbrightCyan: \"#89ddff\",\n\t\t\t\tbrightWhite: \"#ffffff\",\n\t\t\t},\n\t\t\ttokyoNight: {\n\t\t\t\tbackground: \"#1a1b26\",\n\t\t\t\tforeground: \"#c0caf5\",\n\t\t\t\tcursor: \"#c0caf5\",\n\t\t\t\tcursorAccent: \"#1a1b26\",\n\t\t\t\tselection: \"#33467c\",\n\t\t\t\tblack: \"#15161e\",\n\t\t\t\tred: \"#f7768e\",\n\t\t\t\tgreen: \"#9ece6a\",\n\t\t\t\tyellow: \"#e0af68\",\n\t\t\t\tblue: \"#7aa2f7\",\n\t\t\t\tmagenta: \"#bb9af7\",\n\t\t\t\tcyan: \"#7dcfff\",\n\t\t\t\twhite: \"#a9b1d6\",\n\t\t\t\tbrightBlack: \"#414868\",\n\t\t\t\tbrightRed: \"#f7768e\",\n\t\t\t\tbrightGreen: \"#9ece6a\",\n\t\t\t\tbrightYellow: \"#e0af68\",\n\t\t\t\tbrightBlue: \"#7aa2f7\",\n\t\t\t\tbrightMagenta: \"#bb9af7\",\n\t\t\t\tbrightCyan: \"#7dcfff\",\n\t\t\t\tbrightWhite: \"#c0caf5\",\n\t\t\t},\n\t\t\tcatppuccin: {\n\t\t\t\tbackground: \"#1e1e2e\",\n\t\t\t\tforeground: \"#cdd6f4\",\n\t\t\t\tcursor: \"#f5e0dc\",\n\t\t\t\tcursorAccent: \"#1e1e2e\",\n\t\t\t\tselection: \"#585b70\",\n\t\t\t\tblack: \"#45475a\",\n\t\t\t\tred: \"#f38ba8\",\n\t\t\t\tgreen: \"#a6e3a1\",\n\t\t\t\tyellow: \"#f9e2af\",\n\t\t\t\tblue: \"#89b4fa\",\n\t\t\t\tmagenta: \"#f5c2e7\",\n\t\t\t\tcyan: \"#94e2d5\",\n\t\t\t\twhite: \"#bac2de\",\n\t\t\t\tbrightBlack: \"#585b70\",\n\t\t\t\tbrightRed: \"#f38ba8\",\n\t\t\t\tbrightGreen: \"#a6e3a1\",\n\t\t\t\tbrightYellow: \"#f9e2af\",\n\t\t\t\tbrightBlue: \"#89b4fa\",\n\t\t\t\tbrightMagenta: \"#f5c2e7\",\n\t\t\t\tbrightCyan: \"#94e2d5\",\n\t\t\t\tbrightWhite: \"#a6adc8\",\n\t\t\t},\n\t\t\tsynthwave: {\n\t\t\t\tbackground: \"#241b2f\",\n\t\t\t\tforeground: \"#f92aad\",\n\t\t\t\tcursor: \"#f4f99d\",\n\t\t\t\tcursorAccent: \"#241b2f\",\n\t\t\t\tselection: \"#495495\",\n\t\t\t\tblack: \"#241b2f\",\n\t\t\t\tred: \"#ff8b94\",\n\t\t\t\tgreen: \"#a8e6cf\",\n\t\t\t\tyellow: \"#f4f99d\",\n\t\t\t\tblue: \"#88d8c0\",\n\t\t\t\tmagenta: \"#ff8b94\",\n\t\t\t\tcyan: \"#88d8c0\",\n\t\t\t\twhite: \"#f92aad\",\n\t\t\t\tbrightBlack: \"#495495\",\n\t\t\t\tbrightRed: \"#ff8b94\",\n\t\t\t\tbrightGreen: \"#a8e6cf\",\n\t\t\t\tbrightYellow: \"#f4f99d\",\n\t\t\t\tbrightBlue: \"#88d8c0\",\n\t\t\t\tbrightMagenta: \"#ff8b94\",\n\t\t\t\tbrightCyan: \"#88d8c0\",\n\t\t\t\tbrightWhite: \"#f92aad\",\n\t\t\t},\n\t\t\tcyberpunk: {\n\t\t\t\tbackground: \"#000b1e\",\n\t\t\t\tforeground: \"#0abdc6\",\n\t\t\t\tcursor: \"#ea00d9\",\n\t\t\t\tcursorAccent: \"#000b1e\",\n\t\t\t\tselection: \"#711c91\",\n\t\t\t\tblack: \"#000b1e\",\n\t\t\t\tred: \"#ff0040\",\n\t\t\t\tgreen: \"#00ff41\",\n\t\t\t\tyellow: \"#ffff00\",\n\t\t\t\tblue: \"#0080ff\",\n\t\t\t\tmagenta: \"#ea00d9\",\n\t\t\t\tcyan: \"#0abdc6\",\n\t\t\t\twhite: \"#ffffff\",\n\t\t\t\tbrightBlack: \"#133e7c\",\n\t\t\t\tbrightRed: \"#ff0040\",\n\t\t\t\tbrightGreen: \"#00ff41\",\n\t\t\t\tbrightYellow: \"#ffff00\",\n\t\t\t\tbrightBlue: \"#0080ff\",\n\t\t\t\tbrightMagenta: \"#ea00d9\",\n\t\t\t\tbrightCyan: \"#0abdc6\",\n\t\t\t\tbrightWhite: \"#ffffff\",\n\t\t\t},\n\t\t\tforest: {\n\t\t\t\tbackground: \"#1b2b1b\",\n\t\t\t\tforeground: \"#d4ddd4\",\n\t\t\t\tcursor: \"#87ceeb\",\n\t\t\t\tcursorAccent: \"#1b2b1b\",\n\t\t\t\tselection: \"#3a4f3a\",\n\t\t\t\tblack: \"#1b2b1b\",\n\t\t\t\tred: \"#d75f5f\",\n\t\t\t\tgreen: \"#87af87\",\n\t\t\t\tyellow: \"#d7af5f\",\n\t\t\t\tblue: \"#87ceeb\",\n\t\t\t\tmagenta: \"#af87af\",\n\t\t\t\tcyan: \"#5fafaf\",\n\t\t\t\twhite: \"#d4ddd4\",\n\t\t\t\tbrightBlack: \"#5f6f5f\",\n\t\t\t\tbrightRed: \"#d75f5f\",\n\t\t\t\tbrightGreen: \"#87af87\",\n\t\t\t\tbrightYellow: \"#d7af5f\",\n\t\t\t\tbrightBlue: \"#87ceeb\",\n\t\t\t\tbrightMagenta: \"#af87af\",\n\t\t\t\tbrightCyan: \"#5fafaf\",\n\t\t\t\tbrightWhite: \"#ffffff\",\n\t\t\t},\n\t\t\tsunset: {\n\t\t\t\tbackground: \"#3c1f1f\",\n\t\t\t\tforeground: \"#ffd5a5\",\n\t\t\t\tcursor: \"#ff8c42\",\n\t\t\t\tcursorAccent: \"#3c1f1f\",\n\t\t\t\tselection: \"#8b4513\",\n\t\t\t\tblack: \"#3c1f1f\",\n\t\t\t\tred: \"#ff6b6b\",\n\t\t\t\tgreen: \"#ffa500\",\n\t\t\t\tyellow: \"#ffd700\",\n\t\t\t\tblue: \"#ff8c42\",\n\t\t\t\tmagenta: \"#ff69b4\",\n\t\t\t\tcyan: \"#ffa500\",\n\t\t\t\twhite: \"#ffd5a5\",\n\t\t\t\tbrightBlack: \"#8b4513\",\n\t\t\t\tbrightRed: \"#ff6b6b\",\n\t\t\t\tbrightGreen: \"#ffa500\",\n\t\t\t\tbrightYellow: \"#ffd700\",\n\t\t\t\tbrightBlue: \"#ff8c42\",\n\t\t\t\tbrightMagenta: \"#ff69b4\",\n\t\t\t\tbrightCyan: \"#ffa500\",\n\t\t\t\tbrightWhite: \"#fff8dc\",\n\t\t\t},\n\t\t\tocean: {\n\t\t\t\tbackground: \"#0f1419\",\n\t\t\t\tforeground: \"#e6e1cf\",\n\t\t\t\tcursor: \"#f29718\",\n\t\t\t\tcursorAccent: \"#0f1419\",\n\t\t\t\tselection: \"#253340\",\n\t\t\t\tblack: \"#0f1419\",\n\t\t\t\tred: \"#ff3333\",\n\t\t\t\tgreen: \"#b8cc52\",\n\t\t\t\tyellow: \"#e7c547\",\n\t\t\t\tblue: \"#36a3d9\",\n\t\t\t\tmagenta: \"#f07178\",\n\t\t\t\tcyan: \"#95e6cb\",\n\t\t\t\twhite: \"#ffffff\",\n\t\t\t\tbrightBlack: \"#4d5566\",\n\t\t\t\tbrightRed: \"#ff3333\",\n\t\t\t\tbrightGreen: \"#b8cc52\",\n\t\t\t\tbrightYellow: \"#e7c547\",\n\t\t\t\tbrightBlue: \"#36a3d9\",\n\t\t\t\tbrightMagenta: \"#f07178\",\n\t\t\t\tbrightCyan: \"#95e6cb\",\n\t\t\t\tbrightWhite: \"#ffffff\",\n\t\t\t},\n\t\t\tglass: {\n\t\t\t\tbackground: \"#ffffff\",\n\t\t\t\tforeground: \"#111827\",\n\t\t\t\tcursor: \"#6366f1\",\n\t\t\t\tcursorAccent: \"#ffffff\",\n\t\t\t\tselection: \"#e0e7ff\",\n\t\t\t\tblack: \"#374151\",\n\t\t\t\tred: \"#dc2626\",\n\t\t\t\tgreen: \"#16a34a\",\n\t\t\t\tyellow: \"#ca8a04\",\n\t\t\t\tblue: \"#6366f1\",\n\t\t\t\tmagenta: \"#9333ea\",\n\t\t\t\tcyan: \"#0891b2\",\n\t\t\t\twhite: \"#6b7280\",\n\t\t\t\tbrightBlack: \"#4b5563\",\n\t\t\t\tbrightRed: \"#ef4444\",\n\t\t\t\tbrightGreen: \"#22c55e\",\n\t\t\t\tbrightYellow: \"#eab308\",\n\t\t\t\tbrightBlue: \"#8b5cf6\",\n\t\t\t\tbrightMagenta: \"#a855f7\",\n\t\t\t\tbrightCyan: \"#06b6d4\",\n\t\t\t\tbrightWhite: \"#111827\",\n\t\t\t},\n\t\t\tglassDark: {\n\t\t\t\tbackground: \"#181820\",\n\t\t\t\tforeground: \"#e5e7eb\",\n\t\t\t\tcursor: \"#6366f1\",\n\t\t\t\tcursorAccent: \"#181820\",\n\t\t\t\tselection: \"#374151\",\n\t\t\t\tblack: \"#0f0f14\",\n\t\t\t\tred: \"#f87171\",\n\t\t\t\tgreen: \"#4ade80\",\n\t\t\t\tyellow: \"#fbbf24\",\n\t\t\t\tblue: \"#6366f1\",\n\t\t\t\tmagenta: \"#a78bfa\",\n\t\t\t\tcyan: \"#22d3ee\",\n\t\t\t\twhite: \"#e5e7eb\",\n\t\t\t\tbrightBlack: \"#1f1f2a\",\n\t\t\t\tbrightRed: \"#fca5a5\",\n\t\t\t\tbrightGreen: \"#86efac\",\n\t\t\t\tbrightYellow: \"#fcd34d\",\n\t\t\t\tbrightBlue: \"#818cf8\",\n\t\t\t\tbrightMagenta: \"#c4b5fd\",\n\t\t\t\tbrightCyan: \"#67e8f9\",\n\t\t\t\tbrightWhite: \"#f3f4f6\",\n\t\t\t},\n\t\t};\n\n\t\tthis.pluginThemes = new Map();\n\t}\n\n\t/**\n\t * Get a theme by name\n\t * @param {string} themeName - Theme name\n\t * @returns {object} Theme object\n\t */\n\tgetTheme(themeName) {\n\t\t// Check plugin themes first\n\t\tif (this.pluginThemes.has(themeName)) {\n\t\t\treturn this.pluginThemes.get(themeName);\n\t\t}\n\n\t\t// Check built-in themes\n\t\treturn this.themes[themeName] || this.themes.dark;\n\t}\n\n\t/**\n\t * Get all available themes\n\t * @returns {object} All themes\n\t */\n\tgetAllThemes() {\n\t\tconst allThemes = { ...this.themes };\n\n\t\t// Add plugin themes\n\t\tfor (const [name, theme] of this.pluginThemes) {\n\t\t\tallThemes[name] = theme;\n\t\t}\n\n\t\treturn allThemes;\n\t}\n\n\t/**\n\t * Get all theme names\n\t * @returns {string[]} Array of theme names\n\t */\n\tgetThemeNames() {\n\t\treturn Object.keys(this.getAllThemes());\n\t}\n\n\t/**\n\t * Register a plugin theme\n\t * @param {string} name - Theme name\n\t * @param {object} theme - Theme object\n\t * @param {string} pluginId - Plugin ID for cleanup\n\t */\n\tregisterTheme(name, theme, pluginId) {\n\t\tif (this.themes[name]) {\n\t\t\tconsole.warn(\n\t\t\t\t`Terminal theme '${name}' conflicts with built-in theme. Use a different name.`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Validate theme structure\n\t\tif (!this.validateTheme(theme)) {\n\t\t\tconsole.error(`Invalid terminal theme '${name}':`, theme);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Store theme with plugin metadata\n\t\tthis.pluginThemes.set(name, {\n\t\t\t...theme,\n\t\t\t_pluginId: pluginId,\n\t\t\t_isPlugin: true,\n\t\t});\n\n\t\tconsole.log(`Terminal theme '${name}' registered by plugin ${pluginId}`);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Unregister a plugin theme\n\t * @param {string} name - Theme name\n\t * @param {string} pluginId - Plugin ID for verification\n\t */\n\tunregisterTheme(name, pluginId) {\n\t\tconst theme = this.pluginThemes.get(name);\n\n\t\tif (!theme) {\n\t\t\tconsole.warn(`Terminal theme '${name}' not found`);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (theme._pluginId !== pluginId) {\n\t\t\tconsole.warn(\n\t\t\t\t`Terminal theme '${name}' was not registered by plugin ${pluginId}`,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.pluginThemes.delete(name);\n\t\tconsole.log(`Terminal theme '${name}' unregistered`);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Unregister all themes from a plugin\n\t * @param {string} pluginId - Plugin ID\n\t */\n\tunregisterPluginThemes(pluginId) {\n\t\tconst themesToRemove = [];\n\n\t\tfor (const [name, theme] of this.pluginThemes) {\n\t\t\tif (theme._pluginId === pluginId) {\n\t\t\t\tthemesToRemove.push(name);\n\t\t\t}\n\t\t}\n\n\t\tthemesToRemove.forEach((name) => {\n\t\t\tthis.pluginThemes.delete(name);\n\t\t});\n\n\t\tif (themesToRemove.length > 0) {\n\t\t\tconsole.log(\n\t\t\t\t`Unregistered ${themesToRemove.length} terminal themes from plugin ${pluginId}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Validate theme structure\n\t * @param {object} theme - Theme object to validate\n\t * @returns {boolean} True if valid\n\t */\n\tvalidateTheme(theme) {\n\t\tconst requiredColors = [\n\t\t\t\"background\",\n\t\t\t\"foreground\",\n\t\t\t\"cursor\",\n\t\t\t\"black\",\n\t\t\t\"red\",\n\t\t\t\"green\",\n\t\t\t\"yellow\",\n\t\t\t\"blue\",\n\t\t\t\"magenta\",\n\t\t\t\"cyan\",\n\t\t\t\"white\",\n\t\t\t\"brightBlack\",\n\t\t\t\"brightRed\",\n\t\t\t\"brightGreen\",\n\t\t\t\"brightYellow\",\n\t\t\t\"brightBlue\",\n\t\t\t\"brightMagenta\",\n\t\t\t\"brightCyan\",\n\t\t\t\"brightWhite\",\n\t\t];\n\n\t\tif (!theme || typeof theme !== \"object\") {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check required colors\n\t\tfor (const color of requiredColors) {\n\t\t\tif (!theme[color] || typeof theme[color] !== \"string\") {\n\t\t\t\tconsole.warn(`Terminal theme missing or invalid color: ${color}`);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Create a theme variant based on existing theme\n\t * @param {string} baseName - Base theme name\n\t * @param {object} overrides - Color overrides\n\t * @returns {object} New theme object\n\t */\n\tcreateVariant(baseName, overrides) {\n\t\tconst baseTheme = this.getTheme(baseName);\n\t\treturn { ...baseTheme, ...overrides };\n\t}\n}\n\n// Create singleton instance\nconst terminalThemeManager = new TerminalThemeManager();\n\nexport default terminalThemeManager;\n"
  },
  {
    "path": "src/components/terminal/terminalTouchSelection.css",
    "content": "/**\n * Terminal Touch Selection Styles\n */\n\n.terminal-selection-overlay {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tpointer-events: none;\n\tz-index: 100;\n\toverflow: hidden;\n}\n\n.terminal-selection-handle {\n\tposition: absolute;\n\tbackground: #2196f3;\n\tborder: 2px solid #fff;\n\tbox-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n\tdisplay: none;\n\tpointer-events: auto;\n\ttouch-action: none;\n\tz-index: 101;\n\tcursor: grab;\n\ttransition: transform 0.15s ease;\n\tborder-radius: 50% 50% 50% 0;\n}\n\n.terminal-selection-handle:active {\n\tcursor: grabbing;\n}\n\n.terminal-selection-handle-start {\n\ttransform: rotate(180deg) translateX(87%);\n}\n.terminal-selection-handle-end {\n\ttransform: rotate(90deg) translateY(-13%);\n}\n\n.terminal-context-menu {\n\tposition: absolute;\n\tbackground-color: var(--secondary-color);\n\tborder-radius: 4px;\n\theight: 40px;\n\tbox-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);\n\tbox-shadow: 0 4px 12px var(--box-shadow-color);\n\tborder: none;\n\tborder: solid 1px var(--popup-border-color);\n\tz-index: 1000;\n\tmin-width: 200px;\n\tanimation: fadeIn 0.2s ease;\n\tflex-direction: row;\n\talign-items: center;\n\tjustify-content: space-around;\n\tpointer-events: auto;\n}\n\n@keyframes fadeIn {\n\tfrom {\n\t\topacity: 0;\n\t\ttransform: translateY(10px);\n\t}\n\tto {\n\t\topacity: 1;\n\t\ttransform: translateY(0);\n\t}\n}\n\n.terminal-context-menu button {\n\tdisplay: inline-block;\n\tflex: 1;\n\tpadding: 12px 16px;\n\tborder: none;\n\tbackground: transparent;\n\tcolor: var(--primary-text-color);\n\tfont-size: 14px;\n\tfont-weight: 500;\n\ttext-align: center;\n\tcursor: pointer;\n\tborder-radius: 16px;\n\tmargin: 0 4px;\n\ttransition: background-color 0.15s ease;\n\twhite-space: nowrap;\n\tpointer-events: auto;\n}\n\n.terminal-context-menu button:hover,\n.terminal-context-menu button:focus {\n\tbackground: var(--popup-border-color);\n\toutline: none;\n}\n\n.terminal-context-menu button:active {\n\tbackground: rgba(var(--secondary-color), 0.5);\n}\n\n@media (min-width: 768px) {\n\t.terminal-selection-handle {\n\t\twidth: 28px;\n\t\theight: 28px;\n\t}\n\n\t.terminal-context-menu {\n\t\tmin-width: 240px;\n\t}\n\n\t.terminal-context-menu button {\n\t\tpadding: 14px 18px;\n\t\tfont-size: 16px;\n\t}\n}\n\n@media (prefers-contrast: high) {\n\t.terminal-selection-handle {\n\t\tborder-width: 3px;\n\t\tbox-shadow: 0 3px 12px rgba(0, 0, 0, 0.6);\n\t}\n\n\t.terminal-context-menu {\n\t\tborder: 2px solid #000;\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.terminal-selection-handle {\n\t\ttransition: none;\n\t}\n\n\t.terminal-context-menu {\n\t\tanimation: none;\n\t}\n\n\t.terminal-context-menu button {\n\t\ttransition: none;\n\t}\n}\n"
  },
  {
    "path": "src/components/terminal/terminalTouchSelection.js",
    "content": "/**\n * Touch Selection for Terminal\n */\nimport select from \"dialogs/select\";\nimport \"./terminalTouchSelection.css\";\n\nconst DEFAULT_MORE_OPTION_ID = \"__acode_terminal_select_all__\";\nconst terminalMoreOptions = new Map();\nlet terminalMoreOptionCounter = 0;\n\nfunction ensureDefaultMoreOption() {\n\tif (terminalMoreOptions.has(DEFAULT_MORE_OPTION_ID)) return;\n\n\tterminalMoreOptions.set(DEFAULT_MORE_OPTION_ID, {\n\t\tid: DEFAULT_MORE_OPTION_ID,\n\t\tlabel: () => strings[\"select all\"] || \"Select all\",\n\t\ticon: \"text_format\",\n\t\taction: ({ touchSelection }) => touchSelection.selectAllText(),\n\t});\n}\n\nfunction normalizeMoreOption(option) {\n\tif (!option || typeof option !== \"object\" || Array.isArray(option)) {\n\t\tconsole.warn(\n\t\t\t\"[TerminalTouchSelection] addMoreOption expects an option object.\",\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst id =\n\t\toption.id != null && option.id !== \"\"\n\t\t\t? String(option.id)\n\t\t\t: `terminal_more_option_${++terminalMoreOptionCounter}`;\n\tconst label = option.label ?? option.text ?? option.title;\n\tconst action = option.action || option.onselect || option.onclick;\n\n\tif (!label) {\n\t\tconsole.warn(\n\t\t\t`[TerminalTouchSelection] More option '${id}' must provide a label/text/title.`,\n\t\t);\n\t\treturn null;\n\t}\n\n\tif (typeof action !== \"function\") {\n\t\tconsole.warn(\n\t\t\t`[TerminalTouchSelection] More option '${id}' must provide an action function.`,\n\t\t);\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tid,\n\t\tlabel,\n\t\ticon: option.icon || null,\n\t\tenabled: option.enabled,\n\t\taction,\n\t};\n}\n\nfunction resolveMoreOptionLabel(option, context) {\n\ttry {\n\t\tconst value =\n\t\t\ttypeof option.label === \"function\" ? option.label(context) : option.label;\n\t\treturn value == null ? \"\" : String(value);\n\t} catch (error) {\n\t\tconsole.warn(\n\t\t\t`[TerminalTouchSelection] Failed to resolve label for option '${option.id}'.`,\n\t\t\terror,\n\t\t);\n\t\treturn \"\";\n\t}\n}\n\nfunction isMoreOptionEnabled(option, context) {\n\ttry {\n\t\tif (typeof option.enabled === \"function\") {\n\t\t\treturn option.enabled(context) !== false;\n\t\t}\n\t\tif (option.enabled === undefined) return true;\n\t\treturn option.enabled !== false;\n\t} catch (error) {\n\t\tconsole.warn(\n\t\t\t`[TerminalTouchSelection] Failed to resolve enabled state for option '${option.id}'.`,\n\t\t\terror,\n\t\t);\n\t\treturn true;\n\t}\n}\n\nexport default class TerminalTouchSelection {\n\t/**\n\t * Register an option for the \"More\" menu in touch selection.\n\t * @param {{\n\t *  id?: string,\n\t *  label?: string|function(object):string,\n\t *  text?: string,\n\t *  title?: string,\n\t *  icon?: string,\n\t *  enabled?: boolean|function(object):boolean,\n\t *  action?: function(object):void|Promise<void>,\n\t *  onselect?: function(object):void|Promise<void>,\n\t *  onclick?: function(object):void|Promise<void>\n\t * }} option\n\t * @returns {string|null}\n\t */\n\tstatic addMoreOption(option) {\n\t\tensureDefaultMoreOption();\n\t\tconst normalized = normalizeMoreOption(option);\n\t\tif (!normalized) return null;\n\t\tterminalMoreOptions.set(normalized.id, normalized);\n\t\treturn normalized.id;\n\t}\n\n\t/**\n\t * Remove a registered \"More\" menu option by id.\n\t * @param {string} id\n\t * @returns {boolean}\n\t */\n\tstatic removeMoreOption(id) {\n\t\tensureDefaultMoreOption();\n\t\tif (id == null || id === \"\") return false;\n\t\treturn terminalMoreOptions.delete(String(id));\n\t}\n\n\t/**\n\t * List all registered \"More\" menu options.\n\t * @returns {Array<object>}\n\t */\n\tstatic getMoreOptions() {\n\t\tensureDefaultMoreOption();\n\t\treturn [...terminalMoreOptions.values()].map((option) => ({ ...option }));\n\t}\n\n\tconstructor(terminal, container, options = {}) {\n\t\tensureDefaultMoreOption();\n\n\t\tthis.terminal = terminal;\n\t\tthis.container = container;\n\t\tthis.options = {\n\t\t\ttapHoldDuration: 600,\n\t\t\tmoveThreshold: 8,\n\t\t\thandleSize: 24,\n\t\t\thapticFeedback: true,\n\t\t\tshowContextMenu: true,\n\t\t\tfingerOffset: 40, // Offset in pixels to position selection above finger during drag\n\t\t\t...options,\n\t\t};\n\n\t\t// Selection state\n\t\tthis.isSelecting = false;\n\t\tthis.isHandleDragging = false;\n\t\tthis.selectionStart = null;\n\t\tthis.selectionEnd = null;\n\t\tthis.currentSelection = null;\n\n\t\t// Touch tracking\n\t\tthis.touchStartTime = 0;\n\t\tthis.touchStartPos = { x: 0, y: 0 };\n\t\tthis.initialTouchPos = { x: 0, y: 0 };\n\t\tthis.tapHoldTimeout = null;\n\t\tthis.dragHandle = null;\n\t\tthis.isSelectionTouchActive = false;\n\t\tthis.pendingSelectionClearTouch = null;\n\n\t\t// Zoom tracking\n\t\tthis.pinchStartDistance = 0;\n\t\tthis.lastPinchDistance = 0;\n\t\tthis.isPinching = false;\n\t\tthis.initialFontSize = 0;\n\t\tthis.lastZoomTime = 0;\n\t\tthis.zoomThrottle = 50; // ms\n\n\t\t// DOM elements\n\t\tthis.selectionOverlay = null;\n\t\tthis.startHandle = null;\n\t\tthis.endHandle = null;\n\t\tthis.contextMenu = null;\n\n\t\t// Cell dimensions cache\n\t\tthis.cellDimensions = { width: 0, height: 0 };\n\n\t\t// Event handlers\n\t\tthis.boundHandlers = {};\n\n\t\t// Focus tracking\n\t\tthis.wasFocusedBeforeSelection = false;\n\t\tthis.contextMenuShouldStayVisible = false;\n\n\t\t// Selection protection during keyboard events\n\t\tthis.selectionProtected = false;\n\t\tthis.protectionTimeout = null;\n\n\t\t// Scroll tracking\n\t\tthis.scrollElement = null;\n\t\tthis.isTerminalScrolling = false;\n\t\tthis.scrollEndTimeout = null;\n\t\tthis.scrollEndDelay = 100;\n\n\t\tthis.init();\n\t}\n\n\tinit() {\n\t\tthis.createSelectionOverlay();\n\t\tthis.createHandles();\n\t\tthis.attachEventListeners();\n\t\tthis.updateCellDimensions();\n\t}\n\n\tcreateSelectionOverlay() {\n\t\tthis.selectionOverlay = document.createElement(\"div\");\n\t\tthis.selectionOverlay.className = \"terminal-selection-overlay\";\n\t\tthis.selectionOverlay.style.cssText = `\n      position: absolute;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      pointer-events: none;\n      z-index: 100;\n      overflow: hidden;\n    `;\n\t\tthis.container.appendChild(this.selectionOverlay);\n\t}\n\n\tcreateHandles() {\n\t\tthis.startHandle = this.createHandle(\"start\");\n\t\tthis.startHandle.style.cssText += `\n      border-radius: 50% 50% 50% 0;\n    `;\n\t\tthis.setHandleOrientation(this.startHandle, \"start\");\n\n\t\tthis.endHandle = this.createHandle(\"end\");\n\t\tthis.endHandle.style.cssText += `\n      border-radius: 50% 50% 50% 0;\n    `;\n\t\tthis.setHandleOrientation(this.endHandle, \"end\");\n\n\t\tthis.selectionOverlay.appendChild(this.startHandle);\n\t\tthis.selectionOverlay.appendChild(this.endHandle);\n\t}\n\n\tcreateHandle(type) {\n\t\tconst handle = document.createElement(\"div\");\n\t\thandle.className = `terminal-selection-handle terminal-selection-handle-${type}`;\n\t\thandle.style.cssText = `\n      position: absolute;\n      width: ${this.options.handleSize}px;\n      height: ${this.options.handleSize}px;\n      background: #2196F3;\n      border: 2px solid #fff;\n      box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n      display: none;\n      pointer-events: auto;\n      touch-action: none;\n      z-index: 101;\n      cursor: grab;\n    `;\n\n\t\t// Ensure dataset is properly set\n\t\thandle.dataset.handleType = type;\n\n\t\treturn handle;\n\t}\n\n\tattachEventListeners() {\n\t\t// Terminal touch events\n\t\tthis.boundHandlers.terminalTouchStart =\n\t\t\tthis.onTerminalTouchStart.bind(this);\n\t\tthis.boundHandlers.terminalTouchMove = this.onTerminalTouchMove.bind(this);\n\t\tthis.boundHandlers.terminalTouchEnd = this.onTerminalTouchEnd.bind(this);\n\n\t\tthis.terminal.element.addEventListener(\n\t\t\t\"touchstart\",\n\t\t\tthis.boundHandlers.terminalTouchStart,\n\t\t\t{ passive: false },\n\t\t);\n\t\tthis.terminal.element.addEventListener(\n\t\t\t\"touchmove\",\n\t\t\tthis.boundHandlers.terminalTouchMove,\n\t\t\t{ passive: false },\n\t\t);\n\t\tthis.terminal.element.addEventListener(\n\t\t\t\"touchend\",\n\t\t\tthis.boundHandlers.terminalTouchEnd,\n\t\t\t{ passive: false },\n\t\t);\n\n\t\t// Handle touch events\n\t\tthis.boundHandlers.handleTouchStart = this.onHandleTouchStart.bind(this);\n\t\tthis.boundHandlers.handleTouchMove = this.onHandleTouchMove.bind(this);\n\t\tthis.boundHandlers.handleTouchEnd = this.onHandleTouchEnd.bind(this);\n\n\t\tthis.startHandle.addEventListener(\n\t\t\t\"touchstart\",\n\t\t\tthis.boundHandlers.handleTouchStart,\n\t\t\t{ passive: false },\n\t\t);\n\t\tthis.startHandle.addEventListener(\n\t\t\t\"touchmove\",\n\t\t\tthis.boundHandlers.handleTouchMove,\n\t\t\t{ passive: false },\n\t\t);\n\t\tthis.startHandle.addEventListener(\n\t\t\t\"touchend\",\n\t\t\tthis.boundHandlers.handleTouchEnd,\n\t\t\t{ passive: false },\n\t\t);\n\n\t\tthis.endHandle.addEventListener(\n\t\t\t\"touchstart\",\n\t\t\tthis.boundHandlers.handleTouchStart,\n\t\t\t{ passive: false },\n\t\t);\n\t\tthis.endHandle.addEventListener(\n\t\t\t\"touchmove\",\n\t\t\tthis.boundHandlers.handleTouchMove,\n\t\t\t{ passive: false },\n\t\t);\n\t\tthis.endHandle.addEventListener(\n\t\t\t\"touchend\",\n\t\t\tthis.boundHandlers.handleTouchEnd,\n\t\t\t{ passive: false },\n\t\t);\n\n\t\t// Selection change listener\n\t\tthis.boundHandlers.selectionChange = this.onSelectionChange.bind(this);\n\t\tthis.terminal.onSelectionChange(this.boundHandlers.selectionChange);\n\n\t\t// Orientation change\n\t\tthis.boundHandlers.orientationChange = this.onOrientationChange.bind(this);\n\t\twindow.addEventListener(\n\t\t\t\"orientationchange\",\n\t\t\tthis.boundHandlers.orientationChange,\n\t\t);\n\t\twindow.addEventListener(\"resize\", this.boundHandlers.orientationChange);\n\n\t\t// Terminal scroll listener\n\t\tthis.boundHandlers.terminalScroll = this.onTerminalScroll.bind(this);\n\t\tthis.scrollElement =\n\t\t\tthis.terminal.element.querySelector(\".xterm-viewport\") ||\n\t\t\tthis.terminal.element;\n\t\tthis.scrollElement.addEventListener(\n\t\t\t\"scroll\",\n\t\t\tthis.boundHandlers.terminalScroll,\n\t\t\t{ passive: true },\n\t\t);\n\n\t\t// Terminal resize listener (for keyboard events)\n\t\tthis.boundHandlers.terminalResize = this.onTerminalResize.bind(this);\n\t\tthis.terminal.onResize(this.boundHandlers.terminalResize);\n\t}\n\n\tonTerminalTouchStart(event) {\n\t\t// Handle pinch zoom\n\t\tif (event.touches.length === 2) {\n\t\t\tevent.preventDefault();\n\t\t\tthis.startPinchZoom(event);\n\t\t\treturn;\n\t\t}\n\n\t\t// Only handle single touch for selection\n\t\tif (event.touches.length !== 1) return;\n\n\t\tconst touch = event.touches[0];\n\t\tthis.touchStartTime = Date.now();\n\t\tthis.touchStartPos = { x: touch.clientX, y: touch.clientY };\n\t\tthis.initialTouchPos = { x: touch.clientX, y: touch.clientY };\n\n\t\t// If already selecting, don't start new selection\n\t\tif (this.isSelecting) {\n\t\t\tthis.isSelectionTouchActive = false;\n\t\t\tthis.pendingSelectionClearTouch = {\n\t\t\t\tx: touch.clientX,\n\t\t\t\ty: touch.clientY,\n\t\t\t\tmoved: false,\n\t\t\t};\n\t\t\t// Hide menu while user scrolls or repositions, then restore on touch end.\n\t\t\tthis.hideContextMenu(true);\n\t\t\treturn;\n\t\t}\n\n\t\t// Check if touch is near screen edge (likely Android back gesture)\n\t\tif (this.isEdgeGesture(touch)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Clear any existing tap-hold timeout\n\t\tif (this.tapHoldTimeout) {\n\t\t\tclearTimeout(this.tapHoldTimeout);\n\t\t}\n\n\t\t// Start tap-hold timer\n\t\tthis.pendingSelectionClearTouch = null;\n\t\tthis.isSelectionTouchActive = false;\n\t\tthis.tapHoldTimeout = setTimeout(() => {\n\t\t\tif (!this.isSelecting && !this.isPinching) {\n\t\t\t\tthis.startSelection(touch);\n\t\t\t}\n\t\t}, this.options.tapHoldDuration);\n\t}\n\n\tonTerminalTouchMove(event) {\n\t\t// Handle pinch zoom\n\t\tif (event.touches.length === 2) {\n\t\t\tevent.preventDefault();\n\t\t\tthis.handlePinchZoom(event);\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.touches.length !== 1) return;\n\n\t\t// Don't handle single touch if we're pinching\n\t\tif (this.isPinching) return;\n\n\t\tconst touch = event.touches[0];\n\t\tconst deltaX = Math.abs(touch.clientX - this.touchStartPos.x);\n\t\tconst deltaY = Math.abs(touch.clientY - this.touchStartPos.y);\n\t\tconst horizontalDelta = touch.clientX - this.touchStartPos.x;\n\t\tconst clearTouch = this.pendingSelectionClearTouch;\n\n\t\tif (clearTouch) {\n\t\t\tconst clearDeltaX = Math.abs(touch.clientX - clearTouch.x);\n\t\t\tconst clearDeltaY = Math.abs(touch.clientY - clearTouch.y);\n\t\t\tif (\n\t\t\t\tclearDeltaX > this.options.moveThreshold ||\n\t\t\t\tclearDeltaY > this.options.moveThreshold\n\t\t\t) {\n\t\t\t\tclearTouch.moved = true;\n\t\t\t}\n\t\t}\n\n\t\t// Check if this looks like a back gesture (started near edge and moving horizontally inward)\n\t\tif (\n\t\t\tthis.isEdgeGesture(this.initialTouchPos) &&\n\t\t\tMath.abs(horizontalDelta) > deltaY &&\n\t\t\tdeltaX > this.options.moveThreshold\n\t\t) {\n\t\t\t// This looks like a back gesture, cancel selection\n\t\t\tif (this.tapHoldTimeout) {\n\t\t\t\tclearTimeout(this.tapHoldTimeout);\n\t\t\t\tthis.tapHoldTimeout = null;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// If significant movement, cancel tap-hold\n\t\tif (\n\t\t\tdeltaX > this.options.moveThreshold ||\n\t\t\tdeltaY > this.options.moveThreshold\n\t\t) {\n\t\t\tif (this.tapHoldTimeout) {\n\t\t\t\tclearTimeout(this.tapHoldTimeout);\n\t\t\t\tthis.tapHoldTimeout = null;\n\t\t\t}\n\n\t\t\t// If we're selecting, extend selection\n\t\t\tif (\n\t\t\t\tthis.isSelecting &&\n\t\t\t\t!this.isHandleDragging &&\n\t\t\t\tthis.isSelectionTouchActive\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.extendSelection(touch);\n\t\t\t}\n\t\t}\n\t}\n\n\tonTerminalTouchEnd(event) {\n\t\t// Handle end of pinch zoom\n\t\tif (this.isPinching) {\n\t\t\tthis.endPinchZoom();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.tapHoldTimeout) {\n\t\t\tclearTimeout(this.tapHoldTimeout);\n\t\t\tthis.tapHoldTimeout = null;\n\t\t}\n\n\t\tconst shouldClearSelectionByTap =\n\t\t\tthis.isSelecting &&\n\t\t\t!this.isHandleDragging &&\n\t\t\tthis.pendingSelectionClearTouch &&\n\t\t\t!this.pendingSelectionClearTouch.moved &&\n\t\t\t!this.isTerminalScrolling &&\n\t\t\t!this.selectionProtected;\n\n\t\tthis.pendingSelectionClearTouch = null;\n\t\tthis.isSelectionTouchActive = false;\n\n\t\tif (shouldClearSelectionByTap) {\n\t\t\tthis.clearSelection();\n\t\t\treturn;\n\t\t}\n\n\t\t// If we were selecting and not dragging handles, finalize selection\n\t\tif (this.isSelecting && !this.isHandleDragging) {\n\t\t\tif (this.isTerminalScrolling) return;\n\t\t\tthis.finalizeSelection();\n\t\t} else if (!this.isSelecting) {\n\t\t\t// Only focus terminal on touch end if not selecting and terminal was already focused\n\t\t\t// This prevents keyboard popup when just starting selection\n\t\t\tconst currentlyFocused = this.isTerminalFocused();\n\t\t\tif (currentlyFocused) {\n\t\t\t\t// Terminal is already focused, maintain focus\n\t\t\t\tthis.terminal.focus();\n\t\t\t}\n\t\t\t// If terminal wasn't focused, don't focus it (prevents keyboard popup)\n\t\t}\n\t}\n\n\tonHandleTouchStart(event) {\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tif (event.touches.length !== 1) return;\n\n\t\t// Ensure we have the correct handle type\n\t\tlet handleType = event.target.dataset.handleType;\n\t\tif (!handleType) {\n\t\t\t// Fallback to checking which handle was touched\n\t\t\tif (\n\t\t\t\tevent.target === this.startHandle ||\n\t\t\t\tthis.startHandle.contains(event.target)\n\t\t\t) {\n\t\t\t\thandleType = \"start\";\n\t\t\t} else if (\n\t\t\t\tevent.target === this.endHandle ||\n\t\t\t\tthis.endHandle.contains(event.target)\n\t\t\t) {\n\t\t\t\thandleType = \"end\";\n\t\t\t}\n\t\t}\n\n\t\tif (!handleType) {\n\t\t\tconsole.warn(\"Could not determine handle type for drag\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.isHandleDragging = true;\n\t\tthis.dragHandle = handleType;\n\t\tthis.isSelectionTouchActive = false;\n\t\tthis.pendingSelectionClearTouch = null;\n\n\t\t// Store the initial touch position for delta calculations\n\t\tconst touch = event.touches[0];\n\t\tthis.initialTouchPos = { x: touch.clientX, y: touch.clientY };\n\n\t\t// Update handle appearance - ensure we're updating the correct handle\n\t\tconst targetHandle =\n\t\t\thandleType === \"start\" ? this.startHandle : this.endHandle;\n\t\ttargetHandle.style.cursor = \"grabbing\";\n\t\tif (!targetHandle.style.transform.includes(\"scale\")) {\n\t\t\ttargetHandle.style.transform += \" scale(1.2)\";\n\t\t}\n\t}\n\n\tonHandleTouchMove(event) {\n\t\tif (!this.isHandleDragging || event.touches.length !== 1) return;\n\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tconst touch = event.touches[0];\n\n\t\t// Check if there's significant movement before updating selection\n\t\tconst deltaX = Math.abs(touch.clientX - this.initialTouchPos.x);\n\t\tconst deltaY = Math.abs(touch.clientY - this.initialTouchPos.y);\n\n\t\t// Only update selection if there's significant movement (prevents micro-movements)\n\t\tif (\n\t\t\tdeltaX < this.options.moveThreshold &&\n\t\t\tdeltaY < this.options.moveThreshold\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Apply finger offset for better visibility during drag\n\t\tconst adjustedTouch = {\n\t\t\tclientX: touch.clientX,\n\t\t\tclientY: touch.clientY - this.options.fingerOffset,\n\t\t};\n\n\t\tconst coords = this.touchToTerminalCoords(adjustedTouch);\n\n\t\tif (coords) {\n\t\t\t// Allow handle swapping but manage it properly\n\t\t\tif (this.dragHandle === \"start\") {\n\t\t\t\tthis.selectionStart = coords;\n\n\t\t\t\t// If start goes past end, swap the handles logically\n\t\t\t\tif (\n\t\t\t\t\tthis.selectionEnd &&\n\t\t\t\t\t(coords.row > this.selectionEnd.row ||\n\t\t\t\t\t\t(coords.row === this.selectionEnd.row &&\n\t\t\t\t\t\t\tcoords.col > this.selectionEnd.col))\n\t\t\t\t) {\n\t\t\t\t\t// Swap internally but keep drag handle reference\n\t\t\t\t\tconst temp = this.selectionStart;\n\t\t\t\t\tthis.selectionStart = this.selectionEnd;\n\t\t\t\t\tthis.selectionEnd = temp;\n\t\t\t\t\tthis.dragHandle = \"end\"; // Continue dragging as end handle\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.selectionEnd = coords;\n\n\t\t\t\t// If end goes before start, swap the handles logically\n\t\t\t\tif (\n\t\t\t\t\tthis.selectionStart &&\n\t\t\t\t\t(coords.row < this.selectionStart.row ||\n\t\t\t\t\t\t(coords.row === this.selectionStart.row &&\n\t\t\t\t\t\t\tcoords.col < this.selectionStart.col))\n\t\t\t\t) {\n\t\t\t\t\t// Swap internally but keep drag handle reference\n\t\t\t\t\tconst temp = this.selectionEnd;\n\t\t\t\t\tthis.selectionEnd = this.selectionStart;\n\t\t\t\t\tthis.selectionStart = temp;\n\t\t\t\t\tthis.dragHandle = \"start\"; // Continue dragging as start handle\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.updateSelection();\n\t\t}\n\t}\n\n\tonHandleTouchEnd(event) {\n\t\tif (!this.isHandleDragging) return;\n\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tthis.isHandleDragging = false;\n\t\tthis.dragHandle = null;\n\n\t\t// Reset handle appearance - reset both handles to be safe\n\t\tconst handles = [this.startHandle, this.endHandle];\n\t\thandles.forEach((handle) => {\n\t\t\thandle.style.cursor = \"grab\";\n\t\t\t// More robust transform cleanup\n\t\t\thandle.style.transform = handle.style.transform\n\t\t\t\t.replace(/\\s*scale\\([^)]*\\)/g, \"\")\n\t\t\t\t.trim();\n\t\t});\n\n\t\tthis.finalizeSelection();\n\t}\n\n\tonSelectionChange() {\n\t\tif (!this.isSelecting) return;\n\n\t\tconst selection = this.terminal.getSelection();\n\t\tif (selection && selection.length > 0) {\n\t\t\tthis.currentSelection = selection;\n\t\t\tthis.updateHandlePositions();\n\t\t}\n\t}\n\n\tonOrientationChange() {\n\t\t// Update cell dimensions and handle positions after orientation change\n\t\tsetTimeout(() => {\n\t\t\tthis.updateCellDimensions();\n\t\t\tif (this.isSelecting) {\n\t\t\t\tthis.updateHandlePositions();\n\t\t\t}\n\t\t}, 100);\n\t}\n\n\tonTerminalScroll() {\n\t\tif (!this.isSelecting || this.isHandleDragging) return;\n\n\t\tthis.isTerminalScrolling = true;\n\t\tthis.hideHandles();\n\t\tthis.hideContextMenu(true);\n\n\t\tif (this.scrollEndTimeout) {\n\t\t\tclearTimeout(this.scrollEndTimeout);\n\t\t}\n\t\tthis.scrollEndTimeout = setTimeout(() => {\n\t\t\tthis.onTerminalScrollEnd();\n\t\t}, this.scrollEndDelay);\n\t}\n\n\tonTerminalScrollEnd() {\n\t\tthis.scrollEndTimeout = null;\n\t\tthis.isTerminalScrolling = false;\n\t\tif (!this.isSelecting || this.isHandleDragging) return;\n\n\t\tthis.updateHandlePositions();\n\t\tif (this.contextMenuShouldStayVisible && this.options.showContextMenu) {\n\t\t\tthis.showContextMenu();\n\t\t}\n\t}\n\n\tonTerminalResize(size) {\n\t\t// Handle terminal resize (keyboard open/close on Android)\n\t\tsetTimeout(() => {\n\t\t\tthis.updateCellDimensions();\n\t\t\tif (this.isSelecting) {\n\t\t\t\t// Don't clear selection if it's protected (during keyboard events)\n\t\t\t\tif (this.selectionProtected) {\n\t\t\t\t\t// Just update handle positions during protected period\n\t\t\t\t\tthis.updateHandlePositions();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Only clear selection if it becomes invalid due to actual content resize\n\t\t\t\t// Don't clear selection for keyboard-related resizes\n\t\t\t\tif (\n\t\t\t\t\tthis.selectionStart &&\n\t\t\t\t\tthis.selectionEnd &&\n\t\t\t\t\t(this.selectionStart.row >= size.rows ||\n\t\t\t\t\t\tthis.selectionEnd.row >= size.rows)\n\t\t\t\t) {\n\t\t\t\t\tthis.clearSelection();\n\t\t\t\t} else if (this.isSelecting) {\n\t\t\t\t\t// Maintain selection and update handle positions\n\t\t\t\t\tthis.updateHandlePositions();\n\t\t\t\t\t// Temporarily hide context menu during resize but keep selection\n\t\t\t\t\tif (this.contextMenu && this.contextMenu.style.display === \"flex\") {\n\t\t\t\t\t\tthis.hideContextMenu(true);\n\t\t\t\t\t}\n\t\t\t\t\t// Re-show context menu after resize if selection is still active\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tif (this.isSelecting && this.options.showContextMenu) {\n\t\t\t\t\t\t\tthis.showContextMenu();\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 100);\n\t\t\t\t}\n\t\t\t}\n\t\t}, 50);\n\t}\n\n\tstartSelection(touch) {\n\t\tconst coords = this.touchToTerminalCoords(touch);\n\t\tif (!coords) return;\n\n\t\t// Store initial focus state\n\t\tthis.wasFocusedBeforeSelection = this.isTerminalFocused();\n\n\t\t// Protect selection from being cancelled by keyboard events\n\t\tthis.selectionProtected = true;\n\t\tif (this.protectionTimeout) {\n\t\t\tclearTimeout(this.protectionTimeout);\n\t\t}\n\t\t// Remove protection after keyboard events have settled\n\t\tthis.protectionTimeout = setTimeout(() => {\n\t\t\tthis.selectionProtected = false;\n\t\t}, 1000);\n\n\t\tthis.isSelecting = true;\n\t\tthis.isSelectionTouchActive = true;\n\t\tthis.pendingSelectionClearTouch = null;\n\n\t\t// Try to auto-select word at touch position\n\t\tconst wordBounds = this.getWordBoundsAt(coords);\n\t\tif (wordBounds) {\n\t\t\t// Select the entire word\n\t\t\tthis.selectionStart = wordBounds.start;\n\t\t\tthis.selectionEnd = wordBounds.end;\n\t\t} else {\n\t\t\t// Fallback to single character selection\n\t\t\tthis.selectionStart = coords;\n\t\t\tthis.selectionEnd = coords;\n\t\t}\n\n\t\t// Clear any existing selection\n\t\tthis.terminal.clearSelection();\n\n\t\t// Apply the selection\n\t\tthis.updateSelection();\n\n\t\t// Store current selection for immediate access\n\t\tthis.currentSelection = this.terminal.getSelection();\n\n\t\t// Show handles\n\t\tthis.showHandles();\n\n\t\tif (this.options.showContextMenu) {\n\t\t\tthis.showContextMenu();\n\t\t}\n\n\t\t// Haptic feedback\n\t\tif (this.options.hapticFeedback && navigator.vibrate) {\n\t\t\tnavigator.vibrate(50);\n\t\t}\n\n\t\t// Don't change focus state during selection\n\t\t// Terminal should maintain its current focus state\n\t}\n\n\textendSelection(touch) {\n\t\tconst coords = this.touchToTerminalCoords(touch);\n\t\tif (!coords) return;\n\n\t\tthis.selectionEnd = coords;\n\t\tthis.updateSelection();\n\t}\n\n\tupdateSelection() {\n\t\tif (!this.selectionStart || !this.selectionEnd) return;\n\n\t\tconst start = this.selectionStart;\n\t\tconst end = this.selectionEnd;\n\n\t\t// Ensure start is before end\n\t\tlet startRow = start.row;\n\t\tlet startCol = start.col;\n\t\tlet endRow = end.row;\n\t\tlet endCol = end.col;\n\n\t\tif (startRow > endRow || (startRow === endRow && startCol > endCol)) {\n\t\t\t[startRow, startCol, endRow, endCol] = [\n\t\t\t\tendRow,\n\t\t\t\tendCol,\n\t\t\t\tstartRow,\n\t\t\t\tstartCol,\n\t\t\t];\n\t\t}\n\n\t\t// Calculate selection length\n\t\tconst length = this.calculateSelectionLength(\n\t\t\tstartRow,\n\t\t\tstartCol,\n\t\t\tendRow,\n\t\t\tendCol,\n\t\t);\n\n\t\t// Clear and set new selection\n\t\tthis.terminal.clearSelection();\n\t\tthis.terminal.select(startCol, startRow, length);\n\n\t\tthis.updateHandlePositions();\n\n\t\t// Ensure context menu stays visible if it should be\n\t\tif (this.contextMenuShouldStayVisible && this.options.showContextMenu) {\n\t\t\tthis.showContextMenu();\n\t\t}\n\t}\n\n\tcalculateSelectionLength(startRow, startCol, endRow, endCol) {\n\t\tif (startRow === endRow) {\n\t\t\treturn endCol - startCol + 1;\n\t\t}\n\n\t\tconst cols = this.terminal.cols;\n\t\tlet length = cols - startCol; // First row\n\t\tlength += (endRow - startRow - 1) * cols; // Middle rows\n\t\tlength += endCol + 1; // Last row\n\n\t\treturn length;\n\t}\n\n\tfinalizeSelection() {\n\t\tif (this.options.showContextMenu && this.currentSelection) {\n\t\t\tthis.showContextMenu();\n\t\t}\n\t}\n\n\tshowHandles() {\n\t\tthis.startHandle.style.display = \"block\";\n\t\tthis.endHandle.style.display = \"block\";\n\t\tthis.updateHandlePositions();\n\t}\n\n\thideHandles() {\n\t\tthis.startHandle.style.display = \"none\";\n\t\tthis.endHandle.style.display = \"none\";\n\t}\n\n\tgetHandleBaseTransform(orientation) {\n\t\tif (orientation === \"start\") {\n\t\t\treturn \"rotate(180deg) translateX(87%)\";\n\t\t}\n\t\treturn \"rotate(90deg) translateY(-13%)\";\n\t}\n\n\tsetHandleOrientation(handle, orientation) {\n\t\tif (!handle) return;\n\n\t\tconst baseTransform = this.getHandleBaseTransform(orientation);\n\t\tconst hasScale = /\\bscale\\(/.test(handle.style.transform || \"\");\n\t\thandle.dataset.orientation = orientation;\n\t\thandle.style.transform = hasScale\n\t\t\t? `${baseTransform} scale(1.2)`\n\t\t\t: baseTransform;\n\t}\n\n\tupdateHandleOrientationForViewportEdges() {\n\t\tconst overlayRect = this.selectionOverlay.getBoundingClientRect();\n\n\t\tif (this.startHandle.style.display !== \"none\") {\n\t\t\tthis.setHandleOrientation(this.startHandle, \"start\");\n\t\t\tconst startRect = this.startHandle.getBoundingClientRect();\n\t\t\tif (startRect.left < overlayRect.left) {\n\t\t\t\tthis.setHandleOrientation(this.startHandle, \"end\");\n\t\t\t}\n\t\t}\n\n\t\tif (this.endHandle.style.display !== \"none\") {\n\t\t\tthis.setHandleOrientation(this.endHandle, \"end\");\n\t\t\tconst endRect = this.endHandle.getBoundingClientRect();\n\t\t\tif (endRect.right > overlayRect.right) {\n\t\t\t\tthis.setHandleOrientation(this.endHandle, \"start\");\n\t\t\t}\n\t\t}\n\t}\n\n\tupdateHandlePositions() {\n\t\tif (!this.selectionStart || !this.selectionEnd) return;\n\n\t\tlet logicalStart, logicalEnd;\n\t\tif (\n\t\t\tthis.selectionStart.row < this.selectionEnd.row ||\n\t\t\t(this.selectionStart.row === this.selectionEnd.row &&\n\t\t\t\tthis.selectionStart.col <= this.selectionEnd.col)\n\t\t) {\n\t\t\tlogicalStart = this.selectionStart;\n\t\t\tlogicalEnd = this.selectionEnd;\n\t\t} else {\n\t\t\tlogicalStart = this.selectionEnd;\n\t\t\tlogicalEnd = this.selectionStart;\n\t\t}\n\n\t\tconst startPos = this.terminalCoordsToPixels(logicalStart);\n\t\tconst endPos = this.terminalCoordsToPixels(logicalEnd);\n\n\t\t// Show/hide start handle at logical start position\n\t\tif (startPos) {\n\t\t\tthis.startHandle.style.display = \"block\";\n\t\t\tthis.startHandle.style.left = `${startPos.x}px`;\n\t\t\tthis.startHandle.style.top = `${startPos.y + this.cellDimensions.height + 4}px`;\n\t\t} else {\n\t\t\tthis.startHandle.style.display = \"none\";\n\t\t}\n\n\t\t// Show/hide end handle at logical end position\n\t\tif (endPos) {\n\t\t\tthis.endHandle.style.display = \"block\";\n\t\t\tthis.endHandle.style.left = `${endPos.x + this.cellDimensions.width}px`;\n\t\t\tthis.endHandle.style.top = `${endPos.y + this.cellDimensions.height + 4}px`;\n\t\t} else {\n\t\t\tthis.endHandle.style.display = \"none\";\n\t\t}\n\n\t\tthis.updateHandleOrientationForViewportEdges();\n\t}\n\n\tshowContextMenu() {\n\t\tif (!this.contextMenu) {\n\t\t\tthis.createContextMenu();\n\t\t}\n\n\t\t// Mark that context menu should stay visible\n\t\tthis.contextMenuShouldStayVisible = true;\n\n\t\t// Position context menu - center it on selection (or fallback to center).\n\t\tconst startPos = this.selectionStart\n\t\t\t? this.terminalCoordsToPixels(this.selectionStart)\n\t\t\t: null;\n\t\tconst endPos = this.selectionEnd\n\t\t\t? this.terminalCoordsToPixels(this.selectionEnd)\n\t\t\t: null;\n\n\t\tconst menuWidth = this.contextMenu.offsetWidth || 200;\n\t\tconst menuHeight = this.contextMenu.offsetHeight || 50;\n\t\tconst containerRect = this.container.getBoundingClientRect();\n\n\t\tlet menuX;\n\t\tlet menuY;\n\n\t\tif (startPos || endPos) {\n\t\t\tlet centerX;\n\t\t\tlet baseY;\n\n\t\t\tif (startPos && endPos) {\n\t\t\t\tcenterX = (startPos.x + endPos.x) / 2;\n\t\t\t\tbaseY = Math.max(startPos.y, endPos.y);\n\t\t\t} else if (startPos) {\n\t\t\t\tcenterX = startPos.x;\n\t\t\t\tbaseY = startPos.y;\n\t\t\t} else {\n\t\t\t\tcenterX = endPos.x;\n\t\t\t\tbaseY = endPos.y;\n\t\t\t}\n\n\t\t\tmenuX = centerX - menuWidth / 2;\n\t\t\tmenuY = baseY + this.cellDimensions.height + 40;\n\n\t\t\t// If menu would overflow below, prefer placing it above selection.\n\t\t\tconst maxY = containerRect.height - menuHeight - 10;\n\t\t\tif (menuY > maxY) {\n\t\t\t\tconst topY =\n\t\t\t\t\tstartPos && endPos ? Math.min(startPos.y, endPos.y) : baseY;\n\t\t\t\tmenuY = topY - menuHeight - 10;\n\t\t\t}\n\t\t} else {\n\t\t\tmenuX = (containerRect.width - menuWidth) / 2;\n\t\t\tmenuY = containerRect.height - menuHeight - 20;\n\t\t}\n\n\t\tconst minX = 10;\n\t\tconst maxX = containerRect.width - menuWidth - 10;\n\t\tmenuX = Math.max(minX, Math.min(menuX, maxX));\n\n\t\tconst minY = 10;\n\t\tconst maxY = containerRect.height - menuHeight - 10;\n\t\tmenuY = Math.max(minY, Math.min(menuY, maxY));\n\n\t\tthis.contextMenu.style.left = `${menuX}px`;\n\t\tthis.contextMenu.style.top = `${menuY}px`;\n\t\tthis.contextMenu.style.display = \"flex\";\n\t}\n\n\tcreateContextMenu() {\n\t\tthis.contextMenu = document.createElement(\"div\");\n\t\tthis.contextMenu.className = \"terminal-context-menu\";\n\n\t\t// Add menu items\n\t\tconst menuItems = [\n\t\t\t{ label: strings[\"copy\"], action: this.copySelection.bind(this) },\n\t\t\t{ label: strings[\"paste\"], action: this.pasteFromClipboard.bind(this) },\n\t\t\t{\n\t\t\t\tlabel: `${strings[\"more\"] || \"More\"}...`,\n\t\t\t\taction: this.showMoreOptions.bind(this),\n\t\t\t},\n\t\t];\n\n\t\tmenuItems.forEach((item) => {\n\t\t\tconst button = document.createElement(\"button\");\n\t\t\tbutton.textContent = item.label;\n\n\t\t\t// Flag to prevent multiple activations\n\t\t\tlet actionExecuted = false;\n\n\t\t\t// Handle touch interactions\n\t\t\tbutton.addEventListener(\"touchstart\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tactionExecuted = false;\n\t\t\t});\n\n\t\t\tbutton.addEventListener(\"touchend\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tif (!actionExecuted) {\n\t\t\t\t\tactionExecuted = true;\n\t\t\t\t\titem.action();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Handle mouse interactions\n\t\t\tbutton.addEventListener(\"mousedown\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tactionExecuted = false;\n\t\t\t});\n\n\t\t\tbutton.addEventListener(\"mouseup\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tif (!actionExecuted) {\n\t\t\t\t\tactionExecuted = true;\n\t\t\t\t\titem.action();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Prevent default click\n\t\t\tbutton.addEventListener(\"click\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t});\n\n\t\t\tthis.contextMenu.appendChild(button);\n\t\t});\n\n\t\tthis.selectionOverlay.appendChild(this.contextMenu);\n\t}\n\n\thideContextMenu(force = false) {\n\t\t// Only hide if explicitly requested or if context menu should not stay visible\n\t\tif (this.contextMenu && (force || !this.contextMenuShouldStayVisible)) {\n\t\t\tthis.contextMenu.style.display = \"none\";\n\t\t}\n\t}\n\n\tforceHideContextMenu() {\n\t\t// Force hide context menu regardless of flags\n\t\tif (this.contextMenu) {\n\t\t\tthis.contextMenu.style.display = \"none\";\n\t\t\tthis.contextMenuShouldStayVisible = false;\n\t\t}\n\t}\n\n\tcopySelection() {\n\t\t// Store selection before clearing\n\t\tconst selectionText = this.currentSelection || this.terminal.getSelection();\n\n\t\tif (selectionText && cordova?.plugins?.clipboard) {\n\t\t\tcordova.plugins.clipboard.copy(selectionText);\n\t\t}\n\n\t\tthis.forceClearSelection();\n\t}\n\n\tpasteFromClipboard() {\n\t\tif (cordova?.plugins?.clipboard) {\n\t\t\tcordova.plugins.clipboard.paste((text) => {\n\t\t\t\tthis.terminal.paste(text);\n\t\t\t\tthis.forceClearSelection();\n\t\t\t});\n\t\t}\n\t}\n\n\tselectAllText() {\n\t\tif (!this.terminal?.selectAll) return;\n\t\tthis.terminal.selectAll();\n\t\tthis.currentSelection = this.terminal.getSelection();\n\t\tthis.isSelecting = !!this.currentSelection;\n\t\tthis.selectionStart = null;\n\t\tthis.selectionEnd = null;\n\t\tthis.hideHandles();\n\n\t\tif (this.options.showContextMenu && this.currentSelection) {\n\t\t\tthis.showContextMenu();\n\t\t}\n\t}\n\n\tgetMoreOptionsContext() {\n\t\treturn {\n\t\t\tterminal: this.terminal,\n\t\t\ttouchSelection: this,\n\t\t\tselection: this.currentSelection || this.terminal.getSelection(),\n\t\t\tclearSelection: () => this.forceClearSelection(),\n\t\t\tcopySelection: () => this.copySelection(),\n\t\t\tpasteFromClipboard: () => this.pasteFromClipboard(),\n\t\t\tselectAll: () => this.selectAllText(),\n\t\t};\n\t}\n\n\tgetResolvedMoreOptions() {\n\t\tensureDefaultMoreOption();\n\t\tconst context = this.getMoreOptionsContext();\n\n\t\treturn [...terminalMoreOptions.values()]\n\t\t\t.map((option) => {\n\t\t\t\tconst label = resolveMoreOptionLabel(option, context);\n\t\t\t\tif (!label) return null;\n\n\t\t\t\treturn {\n\t\t\t\t\t...option,\n\t\t\t\t\tlabel,\n\t\t\t\t\tdisabled: !isMoreOptionEnabled(option, context),\n\t\t\t\t};\n\t\t\t})\n\t\t\t.filter(Boolean);\n\t}\n\n\tasync executeMoreOption(option) {\n\t\tif (!option || typeof option.action !== \"function\" || option.disabled) {\n\t\t\tif (this.isSelecting && this.options.showContextMenu) {\n\t\t\t\tthis.showContextMenu();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tawait option.action(this.getMoreOptionsContext());\n\t\t} catch (error) {\n\t\t\tconsole.error(\n\t\t\t\t`[TerminalTouchSelection] Failed to execute more option '${option.id}'.`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\twindow.toast?.(\"Failed to execute action.\");\n\t\t} finally {\n\t\t\tif (this.isSelecting && this.options.showContextMenu) {\n\t\t\t\tthis.showContextMenu();\n\t\t\t}\n\t\t}\n\t}\n\n\tshowMoreOptions() {\n\t\tconst moreOptions = this.getResolvedMoreOptions();\n\t\tif (!moreOptions.length) return;\n\n\t\tconst items = moreOptions.map((option) => ({\n\t\t\tvalue: option.id,\n\t\t\ttext: option.label,\n\t\t\ticon: option.icon,\n\t\t\tdisabled: option.disabled,\n\t\t}));\n\n\t\tthis.hideContextMenu(true);\n\n\t\tselect(strings[\"more\"] || \"More\", items, true)\n\t\t\t.then((selectedId) => {\n\t\t\t\tconst option = moreOptions.find((entry) => entry.id === selectedId);\n\t\t\t\treturn this.executeMoreOption(option);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tif (this.isSelecting && this.options.showContextMenu) {\n\t\t\t\t\tthis.showContextMenu();\n\t\t\t\t}\n\t\t\t});\n\t}\n\n\tclearSelection() {\n\t\t// Don't clear if selection is protected\n\t\tif (this.selectionProtected) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Store focus state before clearing\n\t\tconst shouldRestoreFocus =\n\t\t\tthis.wasFocusedBeforeSelection && this.isSelecting;\n\n\t\tthis.isSelecting = false;\n\t\tthis.isHandleDragging = false;\n\t\tthis.selectionStart = null;\n\t\tthis.selectionEnd = null;\n\t\tthis.currentSelection = null;\n\t\tthis.dragHandle = null;\n\t\tthis.pendingSelectionClearTouch = null;\n\t\tthis.isSelectionTouchActive = false;\n\t\tthis.isTerminalScrolling = false;\n\n\t\tthis.terminal.clearSelection();\n\t\tthis.hideHandles();\n\t\tthis.forceHideContextMenu();\n\n\t\tif (this.tapHoldTimeout) {\n\t\t\tclearTimeout(this.tapHoldTimeout);\n\t\t\tthis.tapHoldTimeout = null;\n\t\t}\n\t\tif (this.scrollEndTimeout) {\n\t\t\tclearTimeout(this.scrollEndTimeout);\n\t\t\tthis.scrollEndTimeout = null;\n\t\t}\n\n\t\t// Clear protection timeout\n\t\tif (this.protectionTimeout) {\n\t\t\tclearTimeout(this.protectionTimeout);\n\t\t\tthis.protectionTimeout = null;\n\t\t}\n\t\tthis.selectionProtected = false;\n\n\t\t// Only restore focus if explicitly clearing selection (not due to keyboard close)\n\t\t// and if terminal was focused before selection\n\t\tif (shouldRestoreFocus && !this.isTerminalFocused()) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!this.isSelecting) {\n\t\t\t\t\tthis.terminal.focus();\n\t\t\t\t}\n\t\t\t}, 150);\n\t\t}\n\n\t\t// Reset focus tracking\n\t\tthis.wasFocusedBeforeSelection = false;\n\t}\n\n\tforceClearSelection() {\n\t\t// Temporarily disable protection to force clear\n\t\tthis.selectionProtected = false;\n\t\tthis.clearSelection();\n\t\t// Don't restore protection state since we're clearing\n\t}\n\n\ttouchToTerminalCoords(touch) {\n\t\tconst rect = this.terminal.element.getBoundingClientRect();\n\t\tconst x = touch.clientX - rect.left;\n\t\tconst y = touch.clientY - rect.top;\n\n\t\tif (x < 0 || y < 0 || x > rect.width || y > rect.height) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst col = Math.floor(x / this.cellDimensions.width);\n\t\tconst row =\n\t\t\tMath.floor(y / this.cellDimensions.height) +\n\t\t\tthis.terminal.buffer.active.viewportY;\n\n\t\treturn {\n\t\t\tcol: Math.max(0, Math.min(col, this.terminal.cols - 1)),\n\t\t\trow: Math.max(0, row),\n\t\t};\n\t}\n\n\tterminalCoordsToPixels(coords) {\n\t\tconst rect = this.terminal.element.getBoundingClientRect();\n\t\tconst containerRect = this.container.getBoundingClientRect();\n\n\t\tconst x =\n\t\t\tcoords.col * this.cellDimensions.width + (rect.left - containerRect.left);\n\t\tconst y =\n\t\t\t(coords.row - this.terminal.buffer.active.viewportY) *\n\t\t\t\tthis.cellDimensions.height +\n\t\t\t(rect.top - containerRect.top);\n\n\t\t// Check if coordinates are within visible viewport\n\t\tconst isVisible =\n\t\t\tcoords.row >= this.terminal.buffer.active.viewportY &&\n\t\t\tcoords.row < this.terminal.buffer.active.viewportY + this.terminal.rows;\n\n\t\treturn isVisible ? { x, y } : null;\n\t}\n\n\tupdateCellDimensions() {\n\t\tif (this.terminal._core && this.terminal._core._renderService) {\n\t\t\tconst dimensions = this.terminal._core._renderService.dimensions;\n\t\t\tif (dimensions && dimensions.css && dimensions.css.cell) {\n\t\t\t\tthis.cellDimensions = {\n\t\t\t\t\twidth: dimensions.css.cell.width,\n\t\t\t\t\theight: dimensions.css.cell.height,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check if terminal is currently focused\n\t */\n\tisTerminalFocused() {\n\t\ttry {\n\t\t\t// Check if terminal element has focus\n\t\t\treturn (\n\t\t\t\tdocument.activeElement === this.terminal.element ||\n\t\t\t\tthis.terminal.element.contains(document.activeElement) ||\n\t\t\t\t(this.terminal._core && this.terminal._core._hasFocus)\n\t\t\t);\n\t\t} catch (error) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get word boundaries at the given coordinates\n\t */\n\tgetWordBoundsAt(coords) {\n\t\ttry {\n\t\t\tconst buffer = this.terminal.buffer.active;\n\t\t\tconst line = buffer.getLine(coords.row);\n\t\t\tif (!line) return null;\n\n\t\t\tconst lineText = line.translateToString(false);\n\t\t\tif (!lineText || coords.col >= lineText.length) return null;\n\n\t\t\tconst char = lineText[coords.col];\n\t\t\tif (!this.isWordCharacter(char)) return null;\n\n\t\t\t// Find word start\n\t\t\tlet startCol = coords.col;\n\t\t\twhile (startCol > 0 && this.isWordCharacter(lineText[startCol - 1])) {\n\t\t\t\tstartCol--;\n\t\t\t}\n\n\t\t\t// Find word end\n\t\t\tlet endCol = coords.col;\n\t\t\twhile (\n\t\t\t\tendCol < lineText.length - 1 &&\n\t\t\t\tthis.isWordCharacter(lineText[endCol + 1])\n\t\t\t) {\n\t\t\t\tendCol++;\n\t\t\t}\n\n\t\t\t// Only auto-select if we found a meaningful word (more than just one character)\n\t\t\tif (endCol > startCol) {\n\t\t\t\treturn {\n\t\t\t\t\tstart: { row: coords.row, col: startCol },\n\t\t\t\t\tend: { row: coords.row, col: endCol },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Error finding word bounds:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Check if a character is part of a word\n\t */\n\tisWordCharacter(char) {\n\t\tif (!char) return false;\n\t\t// Word characters: letters, numbers, underscore, hyphen, dot\n\t\treturn /[a-zA-Z0-9_\\-.]/.test(char);\n\t}\n\n\t/**\n\t * Start pinch zoom gesture\n\t */\n\tstartPinchZoom(event) {\n\t\tif (event.touches.length !== 2) return;\n\n\t\tthis.isPinching = true;\n\t\tthis.initialFontSize = this.terminal.options.fontSize;\n\n\t\tconst touch1 = event.touches[0];\n\t\tconst touch2 = event.touches[1];\n\n\t\tthis.pinchStartDistance = this.getDistance(touch1, touch2);\n\t\tthis.lastPinchDistance = this.pinchStartDistance;\n\n\t\t// Clear any selection timeouts\n\t\tif (this.tapHoldTimeout) {\n\t\t\tclearTimeout(this.tapHoldTimeout);\n\t\t\tthis.tapHoldTimeout = null;\n\t\t}\n\t}\n\n\t/**\n\t * Handle pinch zoom gesture\n\t */\n\thandlePinchZoom(event) {\n\t\tif (!this.isPinching || event.touches.length !== 2) return;\n\n\t\tconst now = Date.now();\n\t\tif (now - this.lastZoomTime < this.zoomThrottle) return;\n\t\tthis.lastZoomTime = now;\n\n\t\tconst touch1 = event.touches[0];\n\t\tconst touch2 = event.touches[1];\n\t\tconst currentDistance = this.getDistance(touch1, touch2);\n\n\t\tconst scale = currentDistance / this.pinchStartDistance;\n\t\tconst newFontSize = Math.round(this.initialFontSize * scale);\n\n\t\t// Clamp font size between reasonable limits\n\t\tconst minFontSize = 8;\n\t\tconst maxFontSize = 24;\n\t\tconst clampedFontSize = Math.max(\n\t\t\tminFontSize,\n\t\t\tMath.min(maxFontSize, newFontSize),\n\t\t);\n\n\t\tif (clampedFontSize !== this.terminal.options.fontSize) {\n\t\t\tthis.options.onFontSizeChange(clampedFontSize);\n\t\t}\n\t}\n\n\t/**\n\t * End pinch zoom gesture\n\t */\n\tendPinchZoom() {\n\t\tthis.isPinching = false;\n\t\tthis.pinchStartDistance = 0;\n\t\tthis.lastPinchDistance = 0;\n\t\tthis.initialFontSize = 0;\n\t}\n\n\t/**\n\t * Get distance between two touch points\n\t */\n\tgetDistance(touch1, touch2) {\n\t\tconst dx = touch2.clientX - touch1.clientX;\n\t\tconst dy = touch2.clientY - touch1.clientY;\n\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t}\n\n\t/**\n\t * Check if touch is likely an Android back gesture (starts near screen edge)\n\t */\n\tisEdgeGesture(touch) {\n\t\tconst edgeThreshold = 30; // pixels from screen edge\n\t\tconst screenWidth = window.innerWidth;\n\n\t\t// Check if touch starts near left edge (most common for back gesture)\n\t\tif (touch.clientX <= edgeThreshold) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if touch starts near right edge (for RTL languages or right-handed back gesture)\n\t\tif (touch.clientX >= screenWidth - edgeThreshold) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tdestroy() {\n\t\t// Clear selection\n\t\tthis.forceClearSelection();\n\n\t\t// Remove event listeners\n\t\tthis.terminal.element.removeEventListener(\n\t\t\t\"touchstart\",\n\t\t\tthis.boundHandlers.terminalTouchStart,\n\t\t);\n\t\tthis.terminal.element.removeEventListener(\n\t\t\t\"touchmove\",\n\t\t\tthis.boundHandlers.terminalTouchMove,\n\t\t);\n\t\tthis.terminal.element.removeEventListener(\n\t\t\t\"touchend\",\n\t\t\tthis.boundHandlers.terminalTouchEnd,\n\t\t);\n\n\t\tthis.startHandle.removeEventListener(\n\t\t\t\"touchstart\",\n\t\t\tthis.boundHandlers.handleTouchStart,\n\t\t);\n\t\tthis.startHandle.removeEventListener(\n\t\t\t\"touchmove\",\n\t\t\tthis.boundHandlers.handleTouchMove,\n\t\t);\n\t\tthis.startHandle.removeEventListener(\n\t\t\t\"touchend\",\n\t\t\tthis.boundHandlers.handleTouchEnd,\n\t\t);\n\n\t\tthis.endHandle.removeEventListener(\n\t\t\t\"touchstart\",\n\t\t\tthis.boundHandlers.handleTouchStart,\n\t\t);\n\t\tthis.endHandle.removeEventListener(\n\t\t\t\"touchmove\",\n\t\t\tthis.boundHandlers.handleTouchMove,\n\t\t);\n\t\tthis.endHandle.removeEventListener(\n\t\t\t\"touchend\",\n\t\t\tthis.boundHandlers.handleTouchEnd,\n\t\t);\n\n\t\tif (this.scrollElement) {\n\t\t\tthis.scrollElement.removeEventListener(\n\t\t\t\t\"scroll\",\n\t\t\t\tthis.boundHandlers.terminalScroll,\n\t\t\t);\n\t\t\tthis.scrollElement = null;\n\t\t}\n\t\twindow.removeEventListener(\n\t\t\t\"orientationchange\",\n\t\t\tthis.boundHandlers.orientationChange,\n\t\t);\n\t\twindow.removeEventListener(\"resize\", this.boundHandlers.orientationChange);\n\n\t\tif (this.scrollEndTimeout) {\n\t\t\tclearTimeout(this.scrollEndTimeout);\n\t\t\tthis.scrollEndTimeout = null;\n\t\t}\n\n\t\t// Remove selection change listener\n\t\tif (this.terminal.onSelectionChange) {\n\t\t\tthis.terminal.onSelectionChange(null);\n\t\t}\n\n\t\t// Remove DOM elements\n\t\tif (this.selectionOverlay && this.selectionOverlay.parentNode) {\n\t\t\tthis.selectionOverlay.parentNode.removeChild(this.selectionOverlay);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/components/tile/index.js",
    "content": "import \"./style.scss\";\n\n/**\n * @typedef {object} Tile\n * @property {String} text\n * @property {String} subText\n * @property {function(HTMLElement):void} lead\n * @property {function(HTMLElement):void} tail\n */\n\n/**\n *\n * @param {object} [options]\n * @param {HTMLElement} [options.lead]\n * @param {HTMLElement} [options.tail]\n * @param {string | HTMLElement} [options.text]\n * @param {string} [options.subText]\n * @param {string} [options.type]\n * @returns {HTMLElement & Tile}\n */\nfunction tile(options = {}) {\n\tconst $el = tag(options.type || \"li\", {\n\t\tclassName: \"tile\",\n\t});\n\n\tconst $titleEl =\n\t\ttypeof options.text === \"string\"\n\t\t\t? tag(\"span\", {\n\t\t\t\t\ttextContent: options.text || \"\",\n\t\t\t\t\tclassName: \"text\",\n\t\t\t\t})\n\t\t\t: options.text;\n\tconst leadEl =\n\t\toptions.lead ||\n\t\ttag(\"span\", {\n\t\t\tclassName: \"lead\",\n\t\t});\n\tconst tailEl =\n\t\toptions.tail ||\n\t\ttag(\"span\", {\n\t\t\tclassName: \"tail\",\n\t\t});\n\n\tif (options.subText) {\n\t\t$titleEl.setAttribute(\"data-subtext\", options.subText);\n\t\t$titleEl.classList.add(\"sub-text\");\n\t}\n\n\t$el.append(leadEl, $titleEl, tailEl);\n\n\tObject.defineProperties($el, {\n\t\ttext: {\n\t\t\tget() {\n\t\t\t\treturn $titleEl.textContent;\n\t\t\t},\n\t\t\tset(text) {\n\t\t\t\t$titleEl.textContent = text;\n\t\t\t},\n\t\t},\n\t\tsubText: {\n\t\t\tget() {\n\t\t\t\treturn $titleEl.getAttribute(\"data-subtext\");\n\t\t\t},\n\t\t\tset(text) {\n\t\t\t\tif (text) {\n\t\t\t\t\t$titleEl.setAttribute(\"data-subtext\", text);\n\t\t\t\t\t$titleEl.classList.add(\"sub-text\");\n\t\t\t\t} else {\n\t\t\t\t\t$titleEl.classList.remove(\"sub-text\");\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\tlead: {\n\t\t\tvalue($newLead) {\n\t\t\t\t$el.replaceChild($newLead, $el.firstChild);\n\t\t\t},\n\t\t},\n\t\ttail: {\n\t\t\tvalue($newTail) {\n\t\t\t\t$el.replaceChild($newTail, $el.lastChild);\n\t\t\t},\n\t\t},\n\t});\n\n\treturn $el;\n}\n\nexport default tile;\n"
  },
  {
    "path": "src/components/tile/style.scss",
    "content": ".header,\nheader {\n  &.tile {\n    z-index: 98;\n    height: 45px;\n    background-color: rgb(153, 153, 255);\n    background-color: var(--primary-color);\n    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 2px 4px var(--box-shadow-color);\n    color: rgb(255, 255, 255);\n    color: var(--primary-text-color);\n\n    >.text {\n      pointer-events: all !important;\n      margin: auto;\n      font-weight: bold;\n      font-size: 1.2em;\n      white-space: nowrap;\n      overflow: auto;\n    }\n  }\n}\n\n.tile {\n  display: flex;\n  flex-wrap: nowrap;\n  align-items: center;\n  box-sizing: border-box;\n\n  &.drag {\n    pointer-events: none;\n    border: solid 1px rgb(255, 215, 0);\n    background-color: inherit !important;\n    pointer-events: none;\n    position: fixed;\n    font-size: 0.8em;\n    z-index: 9999;\n\n    .file,\n    .icon {\n      height: 24px;\n      width: 24px;\n      font-size: 1em;\n      background-size: 22px;\n      flex-shrink: 0;\n    }\n\n    .file {\n      padding: 0 !important;\n    }\n\n    .cancel {\n      display: none;\n    }\n  }\n\n  &:disabled,\n  &.disabled {\n    opacity: 0.6;\n    pointer-events: none;\n  }\n\n  * {\n    pointer-events: none;\n  }\n\n  [data-action],\n  [action] {\n    pointer-events: all !important;\n  }\n\n  &.cut {\n    opacity: 0.6;\n  }\n\n  >.text {\n    flex: 1;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n\n    &.sub-text {\n      display: block;\n\n      &::after {\n        content: attr(data-subText);\n        font-size: 0.58em;\n        opacity: 0.6;\n        display: block;\n        position: sticky;\n        left: 0;\n      }\n    }\n  }\n\n  .icon {\n    height: 45px;\n    width: 45px;\n    font-size: 2em;\n\n    background-repeat: no-repeat;\n    background-position: center;\n    background-size: 1.5em;\n\n    &:active {\n      transition: all 100ms ease;\n      transform: scale(0.95) translateZ(0);\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/toast/index.js",
    "content": "import \"./style.scss\";\n\n/**@type {Array<HTMLElement>} */\nconst toastQueue = [];\n\n/**\n * Show a toast message\n * @param {string|HTMLElement} message\n * @param {number|false} [duration=0]\n * @param {string} [bgColor]\n * @param {string} [color]\n */\nexport default function toast(message, duration = 0, bgColor, color) {\n\tconst $oldToast = tag.get(\"#toast\");\n\tconst $toast = (\n\t\t<div\n\t\t\tid=\"toast\"\n\t\t\tattr-clickable={typeof duration !== \"number\"}\n\t\t\tstyle={{ backgroundColor: bgColor, color }}\n\t\t>\n\t\t\t<span className=\"message\">{message}</span>\n\t\t\t{duration === false ? (\n\t\t\t\t<button\n\t\t\t\t\tclassName=\"icon clearclose\"\n\t\t\t\t\tonclick={() => $toast.hide()}\n\t\t\t\t></button>\n\t\t\t) : (\n\t\t\t\t\"\"\n\t\t\t)}\n\t\t</div>\n\t);\n\n\tObject.defineProperties($toast, {\n\t\thide: {\n\t\t\tvalue() {\n\t\t\t\tthis.classList.add(\"hide\");\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tthis.remove();\n\t\t\t\t\tconst $toast = toastQueue.splice(0, 1)[0];\n\t\t\t\t\tif ($toast) $toast.show();\n\t\t\t\t}, 500);\n\t\t\t},\n\t\t},\n\t\tshow: {\n\t\t\tvalue() {\n\t\t\t\tapp.append(this);\n\n\t\t\t\tif (typeof duration === \"number\") {\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tthis.hide();\n\t\t\t\t\t}, duration || 3000);\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t});\n\n\tif (!$oldToast) {\n\t\t$toast.show();\n\t} else {\n\t\ttoastQueue.push($toast);\n\t}\n}\n\ntoast.hide = () => {\n\tconst $toast = tag.get(\"#toast\");\n\tif ($toast) $toast.hide();\n};\n"
  },
  {
    "path": "src/components/toast/style.scss",
    "content": "#toast {\n  position: fixed;\n  bottom: 10px;\n  left: 0;\n  right: 0;\n  margin: 0 auto;\n  background-color: rgba(9, 14, 29, 0.9);\n  box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.5);\n  color: white;\n  font-size: 0.95rem;\n  font-weight: 300;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  max-height: 50vh;\n  max-width: 80vw;\n  width: fit-content;\n  overflow: auto;\n  animation: slow-appear 500ms linear 1;\n  z-index: 9999;\n  border-radius: 4px;\n  pointer-events: none;\n\n  &.hide {\n    transition: all 1s linear;\n    opacity: 0;\n  }\n\n  &[clickable='true'] {\n    pointer-events: all;\n  }\n\n  .message {\n    padding: 10px;\n  }\n\n  .icon {\n    background-color: inherit;\n    color: inherit;\n    border: none;\n    min-height: 30px;\n    min-width: 30px;\n  }\n}"
  },
  {
    "path": "src/components/tutorial.js",
    "content": "import toast from \"./toast\";\n\n/**\n *\n * @param {string} id\n * @param {string|HTMLElement|(hide: ()=>void)=>HTMLElement} message\n * @returns\n */\nexport default function tutorial(id, message) {\n\tif (localStorage.getItem(id) === \"true\") return;\n\tlocalStorage.setItem(id, \"true\");\n\n\tif (typeof message === \"function\") {\n\t\tmessage = message(toast.hide);\n\t}\n\n\ttoast(message, false, \"#17c\", \"#fff\");\n}\n"
  },
  {
    "path": "src/components/virtualList/index.js",
    "content": "import \"./style.scss\";\nimport tag from \"html-tag-js\";\n\n/**\n * @typedef {object} VirtualListOptions\n * @property {number} [itemHeight=30] - Height of each item in pixels\n * @property {number} [buffer=10] - Extra items to render above/below viewport\n * @property {function(any, HTMLElement?): HTMLElement} renderItem - Function to render an item\n */\n\n/**\n * Virtual scrolling list component\n * Only renders items visible in viewport + buffer for performance\n */\nexport default class VirtualList {\n\t/**\n\t * @param {HTMLElement} container\n\t * @param {VirtualListOptions} options\n\t */\n\tconstructor(container, options = {}) {\n\t\tthis.container = container;\n\t\tthis.itemHeight = options.itemHeight || 30;\n\t\tthis.buffer = options.buffer || 10;\n\t\tthis.renderItem =\n\t\t\toptions.renderItem ||\n\t\t\t((item) => {\n\t\t\t\treturn tag(\"div\", { textContent: String(item) });\n\t\t\t});\n\n\t\tthis.items = [];\n\t\tthis.renderedRange = { start: -1, end: -1 };\n\t\tthis.pool = []; // DOM recycling pool\n\n\t\t// Create structure using html-tag-js\n\t\tthis.topSpacer = tag(\"div\", {\n\t\t\tclassName: \"virtual-spacer virtual-spacer-top\",\n\t\t});\n\t\tthis.bottomSpacer = tag(\"div\", {\n\t\t\tclassName: \"virtual-spacer virtual-spacer-bottom\",\n\t\t});\n\t\tthis.itemContainer = tag(\"div\", { className: \"virtual-items\" });\n\n\t\tthis.container.append(\n\t\t\tthis.topSpacer,\n\t\t\tthis.itemContainer,\n\t\t\tthis.bottomSpacer,\n\t\t);\n\n\t\t// Bind scroll handler with RAF throttling\n\t\tthis.rafId = null;\n\t\tthis.onScrollBound = this.onScroll.bind(this);\n\t\tthis.container.addEventListener(\"scroll\", this.onScrollBound, {\n\t\t\tpassive: true,\n\t\t});\n\t}\n\n\t/**\n\t * Set the items to render\n\t * @param {Array} items\n\t */\n\tsetItems(items) {\n\t\tthis.items = items;\n\t\tthis.renderedRange = { start: -1, end: -1 };\n\t\tthis.render();\n\t}\n\n\t/**\n\t * Handle scroll events with RAF throttling\n\t */\n\tonScroll() {\n\t\tif (this.rafId) return;\n\t\tthis.rafId = requestAnimationFrame(() => {\n\t\t\tthis.rafId = null;\n\t\t\tthis.render();\n\t\t});\n\t}\n\n\t/**\n\t * Render visible items\n\t */\n\trender() {\n\t\tif (!this.items.length) {\n\t\t\tthis.topSpacer.style.height = \"0px\";\n\t\t\tthis.bottomSpacer.style.height = \"0px\";\n\t\t\treturn;\n\t\t}\n\n\t\tconst scrollTop = this.container.scrollTop;\n\t\tconst viewportHeight = this.container.clientHeight;\n\n\t\t// Calculate visible range\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.floor(scrollTop / this.itemHeight) - this.buffer,\n\t\t);\n\t\tconst endIndex = Math.min(\n\t\t\tthis.items.length,\n\t\t\tMath.ceil((scrollTop + viewportHeight) / this.itemHeight) + this.buffer,\n\t\t);\n\n\t\t// Skip if range hasn't changed\n\t\tif (\n\t\t\tstartIndex === this.renderedRange.start &&\n\t\t\tendIndex === this.renderedRange.end\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Update spacers\n\t\tthis.topSpacer.style.height = `${startIndex * this.itemHeight}px`;\n\t\tthis.bottomSpacer.style.height = `${Math.max(0, (this.items.length - endIndex) * this.itemHeight)}px`;\n\n\t\t// Recycle existing elements\n\t\twhile (this.itemContainer.firstChild) {\n\t\t\tconst child = this.itemContainer.removeChild(\n\t\t\t\tthis.itemContainer.firstChild,\n\t\t\t);\n\t\t\tthis.pool.push(child);\n\t\t}\n\n\t\t// Render visible items using DocumentFragment\n\t\tconst fragment = document.createDocumentFragment();\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.items[i];\n\t\t\tconst recycledEl = this.pool.pop();\n\t\t\tconst el = this.renderItem(item, recycledEl);\n\t\t\tel.style.height = `${this.itemHeight}px`;\n\t\t\tfragment.appendChild(el);\n\t\t}\n\n\t\tthis.itemContainer.appendChild(fragment);\n\t\tthis.renderedRange = { start: startIndex, end: endIndex };\n\t}\n\n\t/**\n\t * Scroll to a specific item index\n\t * @param {number} index\n\t */\n\tscrollToIndex(index) {\n\t\tthis.container.scrollTop = index * this.itemHeight;\n\t}\n\n\t/**\n\t * Get the currently visible range\n\t * @returns {{start: number, end: number}}\n\t */\n\tgetVisibleRange() {\n\t\treturn { ...this.renderedRange };\n\t}\n\n\t/**\n\t * Destroy and cleanup\n\t */\n\tdestroy() {\n\t\tif (this.rafId) {\n\t\t\tcancelAnimationFrame(this.rafId);\n\t\t\tthis.rafId = null;\n\t\t}\n\t\tthis.container.removeEventListener(\"scroll\", this.onScrollBound);\n\t\tthis.topSpacer.remove();\n\t\tthis.bottomSpacer.remove();\n\t\tthis.itemContainer.remove();\n\t\tthis.pool = [];\n\t\tthis.items = [];\n\t}\n}\n"
  },
  {
    "path": "src/components/virtualList/style.scss",
    "content": ".virtual-spacer {\n\twidth: 100%;\n\tpointer-events: none;\n}\n\n.virtual-items {\n\tposition: relative;\n\tmin-width: 100%;\n}\n"
  },
  {
    "path": "src/dialogs/alert.js",
    "content": "import DOMPurify from \"dompurify\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\n/**\n * Alert dialog\n * @param {string} titleText Title text\n * @param {string} message Alert message\n * @param {function():void} [onhide] Callback function\n */\nfunction alert(titleText, message, onhide) {\n\tif (!message && titleText) {\n\t\tmessage = titleText;\n\t\ttitleText = \"\";\n\t}\n\n\tconst regex = /(https?:\\/\\/[^\\s]+)/g;\n\tif (regex.test(message)) {\n\t\tmessage = message.replace(regex, function (url) {\n\t\t\treturn `<a href='${url}'>${url}</a>`;\n\t\t});\n\t}\n\n\tconst titleSpan = tag(\"strong\", {\n\t\tclassName: \"title\",\n\t\ttextContent: titleText,\n\t});\n\tconst messageSpan = tag(\"span\", {\n\t\tclassName: \"message scroll\",\n\t\tinnerHTML: DOMPurify.sanitize(message),\n\t});\n\tconst okBtn = tag(\"button\", {\n\t\ttextContent: strings.ok,\n\t\tonclick: hide,\n\t});\n\tconst alertDiv = tag(\"div\", {\n\t\tclassName: \"prompt alert\",\n\t\tchildren: [\n\t\t\ttitleSpan,\n\t\t\tmessageSpan,\n\t\t\ttag(\"div\", {\n\t\t\t\tclassName: \"button-container\",\n\t\t\t\tchild: okBtn,\n\t\t\t}),\n\t\t],\n\t});\n\tconst mask = tag(\"span\", {\n\t\tclassName: \"mask\",\n\t\tonclick: hide,\n\t});\n\n\tactionStack.push({\n\t\tid: \"alert\",\n\t\taction: hideAlert,\n\t});\n\n\tapp.append(alertDiv, mask);\n\trestoreTheme(true);\n\n\tfunction hideAlert() {\n\t\talertDiv.classList.add(\"hide\");\n\t\trestoreTheme();\n\t\tsetTimeout(() => {\n\t\t\tapp.removeChild(alertDiv);\n\t\t\tapp.removeChild(mask);\n\t\t}, 300);\n\t}\n\n\tfunction hide() {\n\t\tif (onhide) onhide();\n\t\tactionStack.remove(\"alert\");\n\t\thideAlert();\n\t}\n}\n\nexport default alert;\n"
  },
  {
    "path": "src/dialogs/box.js",
    "content": "import \"./style.scss\";\n\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\n/**\n * Confirm dialog box\n * @param {string} titleText Title text\n * @param {string} html HTML string\n * @param {string} [hideButtonText] Text for hide button\n * @param {string} [cancelButtonText] Text for cancel button\n * @returns {PromiseLike}\n */\nfunction box(titleText, html, hideButtonText, cancelButtonText) {\n\tlet waitFor = 0,\n\t\tstrOK = hideButtonText || strings.ok,\n\t\t_onclick = () => {},\n\t\t_onhide = () => {},\n\t\t_then = () => {},\n\t\t_onOk = _hide,\n\t\t_onCancel = () => {};\n\n\tconst promiseLike = {\n\t\thide,\n\t\twait,\n\t\tonclick,\n\t\tonhide,\n\t\tthen,\n\t\tok,\n\t\tcancel,\n\t};\n\n\tlet cancelBtn;\n\tlet hideButton = typeof hideButtonText === \"boolean\" ? hideButtonText : false;\n\n\tif (cancelButtonText) {\n\t\tcancelBtn = tag(\"button\", {\n\t\t\tclassName: \"disabled\",\n\t\t\ttextContent: cancelButtonText,\n\t\t\tonclick: () => {\n\t\t\t\t_onCancel();\n\t\t\t},\n\t\t});\n\t}\n\n\tconst okBtn = tag(\"button\", {\n\t\tclassName: \"disabled\",\n\t\ttextContent: strOK,\n\t\tonclick: () => {\n\t\t\t_onOk();\n\t\t},\n\t});\n\tconst body = tag(\"div\", {\n\t\tclassName: \"message\",\n\t\tinnerHTML: html,\n\t\tonclick: __onclick,\n\t});\n\tconst box = tag(\"div\", {\n\t\tclassName: \"prompt box\",\n\t\tchildren: [\n\t\t\ttag(\"strong\", {\n\t\t\t\tclassName: \"title\",\n\t\t\t\ttextContent: titleText,\n\t\t\t}),\n\t\t\tbody,\n\t\t],\n\t});\n\tconst mask = tag(\"span\", {\n\t\tclassName: \"mask\",\n\t\tonclick: _hide,\n\t});\n\n\tif (!hideButton) {\n\t\tbox.append(\n\t\t\ttag(\"div\", {\n\t\t\t\tclassName: \"button-container\",\n\t\t\t\tchildren: cancelBtn ? [cancelBtn, okBtn] : [okBtn],\n\t\t\t}),\n\t\t);\n\t}\n\n\tsetTimeout(() => {\n\t\tdecTime();\n\t\tactionStack.push({\n\t\t\tid: \"box\",\n\t\t\taction: hideBox,\n\t\t});\n\n\t\tdocument.body.append(box, mask);\n\t\t__then();\n\n\t\trestoreTheme(true);\n\t}, 0);\n\n\tfunction decTime() {\n\t\tif (waitFor >= 1000) {\n\t\t\tokBtn.textContent = `${strOK} (${Number.parseInt(waitFor / 1000)}sec)`;\n\t\t\twaitFor -= 1000;\n\t\t\tsetTimeout(decTime, 1000);\n\t\t} else {\n\t\t\tokBtn.textContent = strOK;\n\t\t\tokBtn.classList.remove(\"disabled\");\n\t\t\tcancelBtn?.classList.remove(\"disabled\");\n\t\t}\n\t}\n\n\tfunction hideBox() {\n\t\tbox.classList.add(\"hide\");\n\t\trestoreTheme();\n\t\tsetTimeout(() => {\n\t\t\tdocument.body.removeChild(box);\n\t\t\tdocument.body.removeChild(mask);\n\t\t}, 300);\n\t}\n\n\tfunction hide() {\n\t\tif (waitFor) return;\n\t\tconst imgs = box.getAll(\"img\");\n\t\tif (imgs) {\n\t\t\tfor (let img of imgs) {\n\t\t\t\tURL.revokeObjectURL(img.src);\n\t\t\t}\n\t\t}\n\t\tactionStack.remove(\"box\");\n\t\thideBox();\n\t}\n\n\tfunction _hide() {\n\t\thide();\n\t\tif (_onhide) _onhide.call(promiseLike);\n\t}\n\n\tfunction wait(time) {\n\t\ttime -= time % 1000;\n\t\twaitFor = time;\n\t\treturn promiseLike;\n\t}\n\n\tfunction __onclick(e) {\n\t\tif (_onclick) _onclick.call(this, e);\n\t}\n\n\tfunction __then() {\n\t\tif (_then) _then(body.children);\n\t}\n\n\t/**\n\t * Set callback function\n\t * @param {function(HTMLCollection)} callback Callback function\n\t * @returns {PromiseLike}\n\t */\n\tfunction then(callback) {\n\t\t_then = callback;\n\t\treturn promiseLike;\n\t}\n\n\t/**\n\t * Set onclick callback function\n\t * @param {function(this:HTMLElement, Event):void} onclick Callback function\n\t * @returns {PromiseLike}\n\t */\n\tfunction onclick(onclick) {\n\t\t_onclick = onclick;\n\t\treturn promiseLike;\n\t}\n\n\t/**\n\t * Set onhide callback function\n\t * @param {function():void} onhide Callback function\n\t * @returns {PromiseLike}\n\t */\n\tfunction onhide(onhide) {\n\t\t_onhide = onhide;\n\t\treturn promiseLike;\n\t}\n\n\t/**\n\t * Set onOk callback function\n\t * @param {function():void} onOk Callback function\n\t * @returns {PromiseLike}\n\t */\n\tfunction ok(onOk) {\n\t\t_onOk = onOk;\n\t\treturn promiseLike;\n\t}\n\n\t/**\n\t * Set onCancel callback function\n\t * @param {function():void} onCancel Callback function\n\t * @returns {PromiseLike}\n\t */\n\tfunction cancel(onCancel) {\n\t\t_onCancel = onCancel;\n\t\treturn promiseLike;\n\t}\n\n\treturn promiseLike;\n}\n\nexport default box;\n"
  },
  {
    "path": "src/dialogs/color.js",
    "content": "import actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\nimport Picker from \"vanilla-picker\";\n\nlet lastPicked = localStorage.__picker_last_picked || \"#fff\";\n\n/**\n * Choose color\n * @param {string} defaultColor Default color\n * @param {Function} [onhide] Callback function\n * @returns {Promise<string>}\n */\nfunction color(defaultColor, onhide) {\n\tdefaultColor = defaultColor || lastPicked;\n\tlet type = checkColorType(defaultColor) || \"hex\";\n\treturn new Promise((resolve) => {\n\t\tconst colorModes = [\"hsl\", \"hex\", \"rgb\"];\n\t\tlet mode = colorModes.indexOf(type);\n\t\tlet color = null;\n\n\t\tconst parent = tag(\"div\", {\n\t\t\tclassName: \"message color-picker\",\n\t\t});\n\t\tconst okBtn = tag(\"button\", {\n\t\t\ttextContent: strings.ok,\n\t\t\tonclick: function () {\n\t\t\t\thide();\n\t\t\t\tlastPicked = color;\n\t\t\t\tlocalStorage.__picker_last_picked = color;\n\t\t\t\tresolve(color);\n\t\t\t},\n\t\t});\n\t\tconst toggleMode = tag(\"button\", {\n\t\t\ttextContent: type,\n\t\t\tonclick: function (e) {\n\t\t\t\t++mode;\n\t\t\t\tif (mode >= colorModes.length) mode = 0;\n\t\t\t\ttype = colorModes[mode];\n\t\t\t\tthis.textContent = type;\n\t\t\t\tpicker.setOptions({\n\t\t\t\t\tcolor,\n\t\t\t\t\teditorFormat: type,\n\t\t\t\t});\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\te.stopImmediatePropagation();\n\t\t\t},\n\t\t});\n\t\tconst box = tag(\"div\", {\n\t\t\tclassName: \"prompt box\",\n\t\t\tchildren: [\n\t\t\t\ttag(\"strong\", {\n\t\t\t\t\tclassName: \"title\",\n\t\t\t\t\ttextContent: strings[\"choose color\"],\n\t\t\t\t}),\n\t\t\t\tparent,\n\t\t\t\ttag(\"div\", {\n\t\t\t\t\tclassName: \"button-container\",\n\t\t\t\t\tchildren: [toggleMode, okBtn],\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\t\tconst mask = tag(\"span\", {\n\t\t\tclassName: \"mask\",\n\t\t\tonclick: hide,\n\t\t});\n\t\tconst picker = new Picker({\n\t\t\tparent,\n\t\t\tpopup: false,\n\t\t\teditor: true,\n\t\t\tcolor: defaultColor,\n\t\t\tonChange,\n\t\t\talpha: true,\n\t\t\teditorFormat: type,\n\t\t});\n\n\t\tpicker.show();\n\n\t\tactionStack.push({\n\t\t\tid: \"box\",\n\t\t\taction: hideSelect,\n\t\t});\n\n\t\tdocument.body.append(box, mask);\n\n\t\trestoreTheme(true);\n\n\t\tfunction hideSelect() {\n\t\t\tbox.classList.add(\"hide\");\n\t\t\trestoreTheme();\n\t\t\tsetTimeout(() => {\n\t\t\t\tdocument.body.removeChild(box);\n\t\t\t\tdocument.body.removeChild(mask);\n\t\t\t\tif (typeof onhide === \"function\") onhide();\n\t\t\t}, 300);\n\t\t}\n\n\t\tfunction hide() {\n\t\t\tactionStack.remove(\"box\");\n\t\t\tconst height = box.clientHeight;\n\t\t\tbox.style.height = height + \"px\";\n\t\t\tpicker.destroy();\n\t\t\thideSelect();\n\t\t}\n\n\t\tfunction onChange(c) {\n\t\t\tif (!c) return;\n\n\t\t\tconst alpha = c.rgba[3] < 1 ? true : false;\n\t\t\tif (type === \"hex\") {\n\t\t\t\tif (alpha) color = c.hex;\n\t\t\t\telse color = c.hex.slice(0, -2);\n\t\t\t} else if (type === \"rgb\") {\n\t\t\t\tif (alpha) color = c.rgbaString;\n\t\t\t\telse color = c.rgbString;\n\t\t\t} else {\n\t\t\t\tif (alpha) color = c.hslaString;\n\t\t\t\telse color = c.hslString;\n\t\t\t}\n\n\t\t\tif (color) {\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tconst $editor = box.get(\".picker_editor\");\n\t\t\t\t\tif ($editor) $editor.style.backgroundColor = color;\n\t\t\t\t}, 0);\n\t\t\t}\n\t\t}\n\t});\n}\n\n/**\n *\n * @param {string} color\n * @returns {'hex'|'rgb'|'hsl'}\n */\nfunction checkColorType(color) {\n\tif (color.startsWith(\"#\")) return \"hex\";\n\tif (color.startsWith(\"rgb\")) return \"rgb\";\n\tif (color.startsWith(\"hsl\")) return \"hsl\";\n\treturn null;\n}\n\nexport default color;\n"
  },
  {
    "path": "src/dialogs/confirm.js",
    "content": "import DOMPurify from \"dompurify\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\n/**\n * Confirm dialog box\n * @param {string} titleText Title text\n * @param {string} [message] Alert message\n * @param {boolean} [isHTML] Whether the message is HTML\n * @returns {Promise<boolean>}\n */\nfunction confirm(titleText, message, isHTML) {\n\treturn new Promise((resolve) => {\n\t\tif (!message && titleText) {\n\t\t\tmessage = titleText;\n\t\t\ttitleText = \"\";\n\t\t}\n\n\t\tconst titleSpan = tag(\"strong\", {\n\t\t\tclassName: \"title\",\n\t\t\ttextContent: titleText,\n\t\t});\n\t\tconst messageSpan = tag(\"span\", {\n\t\t\tclassName: \"message scroll\",\n\t\t\tinnerHTML: isHTML ? DOMPurify.sanitize(message) : undefined,\n\t\t\ttextContent: isHTML ? undefined : message,\n\t\t});\n\t\tconst okBtn = tag(\"button\", {\n\t\t\ttextContent: strings.ok,\n\t\t\tonclick: function () {\n\t\t\t\thide();\n\t\t\t\tresolve(true);\n\t\t\t},\n\t\t});\n\t\tconst cancelBtn = tag(\"button\", {\n\t\t\ttextContent: strings.cancel,\n\t\t\tonclick: function () {\n\t\t\t\thide();\n\t\t\t\tresolve(false);\n\t\t\t},\n\t\t});\n\t\tconst confirmDiv = tag(\"div\", {\n\t\t\tclassName: \"prompt confirm\",\n\t\t\tchildren: [\n\t\t\t\ttitleSpan,\n\t\t\t\tmessageSpan,\n\t\t\t\ttag(\"div\", {\n\t\t\t\t\tclassName: \"button-container\",\n\t\t\t\t\tchildren: [cancelBtn, okBtn],\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\t\tconst mask = tag(\"span\", {\n\t\t\tclassName: \"mask\",\n\t\t});\n\n\t\tactionStack.push({\n\t\t\tid: \"confirm\",\n\t\t\taction: hideAlert,\n\t\t});\n\n\t\tapp.append(confirmDiv, mask);\n\t\trestoreTheme(true);\n\n\t\tfunction hideAlert() {\n\t\t\tconfirmDiv.classList.add(\"hide\");\n\t\t\trestoreTheme();\n\t\t\tsetTimeout(() => {\n\t\t\t\tapp.removeChild(confirmDiv);\n\t\t\t\tapp.removeChild(mask);\n\t\t\t}, 300);\n\t\t}\n\n\t\tfunction hide() {\n\t\t\tactionStack.remove(\"confirm\");\n\t\t\thideAlert();\n\t\t}\n\t});\n}\n\nexport default confirm;\n"
  },
  {
    "path": "src/dialogs/loader.js",
    "content": "import DOMPurify from \"dompurify\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\nlet loaderIsImmortal = false;\nlet onCancelCallback = null;\nlet $currentDialog = null;\nlet $currentMask = null;\n\n/**\n * @typedef {object} LoaderOptions\n * @property {number} timeout Timeout in milliseconds after which the loader will be shown\n * @property {function():void} oncancel Callback function to be called when the loader is shown\n */\n\n/**\n * @typedef {object} Loader\n * @property {function(title:string):void} setTitle Sets the title of the loader\n * @property {function(message:string):void} setMessage Sets the message of the loader\n * @property {function():void} hide Hides the loader\n * @property {function():void} destroy Removes the loader from DOM permanently\n * @property {function():void} show Shows previously hidden loader\n */\n\n/**\n * Creates new loading dialog\n * @param {string} titleText Title text\n * @param {string} [message] Loading message\n * @param {LoaderOptions} [options] Loader options\n * @returns {Loader}\n */\nfunction create(titleText, message = \"\", options = {}) {\n\tif (!message && titleText) {\n\t\tmessage = titleText;\n\t\ttitleText = \"\";\n\t}\n\n\tconst $oldLoader = tag.get(\"#__loader\");\n\tconst $oldMask = tag.get(\"#__loader-mask\");\n\n\tif ($oldLoader) $oldLoader.remove();\n\n\tconst $message = Ref();\n\tconst $titleSpan = Ref();\n\n\tconst $mask = $oldMask || <span className=\"mask\" id=\"__loader-mask\"></span>;\n\tconst $dialog = $oldLoader || (\n\t\t<div className=\"prompt alert\" id=\"__loader\">\n\t\t\t<strong ref={$titleSpan} className=\"title\">\n\t\t\t\t{titleText}\n\t\t\t</strong>\n\t\t\t<span className=\"message loader\">\n\t\t\t\t<span className=\"loader\"></span>\n\t\t\t\t<div\n\t\t\t\t\tref={$message}\n\t\t\t\t\tclassName=\"message\"\n\t\t\t\t\tinnerHTML={DOMPurify.sanitize(message)}\n\t\t\t\t\tstyle={{ whiteSpace: \"pre-wrap\" }}\n\t\t\t\t></div>\n\t\t\t</span>\n\t\t</div>\n\t);\n\n\tconst { timeout, oncancel } = options;\n\tif (typeof oncancel === \"function\") {\n\t\tonCancelCallback = oncancel;\n\t}\n\n\tif (typeof timeout === \"number\") {\n\t\tsetTimeout(() => {\n\t\t\t$dialog.append(\n\t\t\t\t<div className=\"button-container\">\n\t\t\t\t\t<button onclick={destroy}>{strings.cancel}</button>\n\t\t\t\t</div>,\n\t\t\t);\n\t\t}, timeout);\n\t}\n\n\tif (!$oldLoader) {\n\t\tactionStack.freeze();\n\t\tdocument.body.append($dialog, $mask);\n\t\trestoreTheme(true);\n\t}\n\n\treturn {\n\t\tsetTitle(title) {\n\t\t\t$titleSpan.textContent = title;\n\t\t},\n\t\tsetMessage(message) {\n\t\t\t$message.innerHTML = DOMPurify.sanitize(message);\n\t\t},\n\t\thide,\n\t\tshow,\n\t\tdestroy,\n\t};\n}\n\n/**\n * Removes the loader from DOM permanently\n */\nfunction destroy() {\n\tconst loaderDiv = tag.get(\"#__loader\");\n\tconst mask = tag.get(\"#__loader-mask\");\n\trestoreTheme();\n\n\tif (!loaderDiv && !mask) {\n\t\tactionStack.unfreeze();\n\t\treturn;\n\t}\n\n\tloaderDiv?.classList.add(\"hide\");\n\tsetTimeout(() => {\n\t\tactionStack.unfreeze();\n\t\tif (loaderDiv?.isConnected) loaderDiv.remove();\n\t\tif (mask?.isConnected) mask.remove();\n\t\tonCancelCallback?.();\n\t}, 300);\n}\n\n/**\n * Hides the loading dialog box temporarily and can be restored using show method\n */\nfunction hide() {\n\tconst loaderDiv = tag.get(\"#__loader\");\n\tconst mask = tag.get(\"#__loader-mask\");\n\n\tif (loaderDiv) {\n\t\t$currentDialog = loaderDiv;\n\t\tloaderDiv.remove();\n\t}\n\tif (mask) {\n\t\t$currentMask = mask;\n\t\tmask.remove();\n\t}\n}\n\n/**\n * Shows previously hidden dialog box.\n */\nfunction show() {\n\tif ($currentDialog) {\n\t\tapp.append($currentDialog);\n\t\t$currentDialog = null;\n\t}\n\tif ($currentMask) {\n\t\tapp.append($currentMask);\n\t\t$currentMask = null;\n\t}\n}\n\n/**\n * Shows title loader\n * @param {boolean} [immortal] If true, the loader will not be removed automatically\n */\nfunction showTitleLoader(immortal = false) {\n\tif (typeof immortal === \"boolean\") {\n\t\tloaderIsImmortal = immortal;\n\t}\n\n\tsetTimeout(() => {\n\t\tapp.classList.remove(\"title-loading-hide\");\n\t\tapp.classList.add(\"title-loading\");\n\t}, 0);\n}\n\n/**\n * Removes title loader\n * @param {boolean} immortal If not true, the loader will not remove when immortal was true when it was created.\n * @returns\n */\nfunction removeTitleLoader(immortal = undefined) {\n\tif (typeof immortal === \"boolean\") {\n\t\tloaderIsImmortal = immortal;\n\t}\n\n\tif (loaderIsImmortal) return;\n\tsetTimeout(() => {\n\t\tapp.classList.add(\"title-loading-hide\");\n\t}, 0);\n}\n\nexport default {\n\tcreate,\n\tdestroy,\n\thide,\n\tshow,\n\tshowTitleLoader,\n\tremoveTitleLoader,\n};\n"
  },
  {
    "path": "src/dialogs/multiPrompt.js",
    "content": "import autosize from \"autosize\";\nimport Checkbox from \"components/checkbox\";\nimport inputhints from \"components/inputhints\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\nimport appSettings from \"lib/settings\";\nimport alert from \"./alert\";\n\n/**\n * @typedef {object} Input\n * @property {string} id Input id\n * @property {boolean} [required] Is required\n * @property {string} [type] Input type\n * @property {RegExp} [match] Input match\n * @property {string} [value] Input value\n * @property {string} [placeholder] Input placeholder\n * @property {string} [hints] Input hints\n * @property {string} [name] Input name\n * @property {boolean} [disabled] Is disabled\n * @property {function} [onclick] On click\n * @property {function} [onchange] On change\n * @property {boolean} [readOnly] Is read only\n * @property {boolean} [autofocus] Is autofocus\n * @property {boolean} [hidden] Is hidden\n */\n\n/**\n * Opens a multi prompt dialog\n * @param {string} message Message\n * @param {Array<Input|Array<Input>>} inputs Inputs\n * @param {String} help Help text\n * @returns {Promise<Strings>}\n */\nexport default function multiPrompt(message, inputs, help) {\n\treturn new Promise((resolve, reject) => {\n\t\tconst $title = tag(\"div\", {\n\t\t\tclassName: \"title\",\n\t\t\tchild: tag(\"span\", {\n\t\t\t\ttextContent: message,\n\t\t\t}),\n\t\t\tstyle: {\n\t\t\t\tjustifyContent: \"space-between\",\n\t\t\t},\n\t\t});\n\t\tconst $body = tag(\"div\", {\n\t\t\tclassName: \"message scroll\",\n\t\t\tstyle: {\n\t\t\t\tfontSize: \"1rem\",\n\t\t\t},\n\t\t});\n\t\tconst okBtn = tag(\"button\", {\n\t\t\ttype: \"submit\",\n\t\t\ttextContent: strings.ok,\n\t\t\tonclick: function (e) {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tconst inputAr = [...$body.getAll(\"input\")];\n\n\t\t\t\tfor (let $input of inputAr) {\n\t\t\t\t\tif ($input.isRequired && !$input.value) {\n\t\t\t\t\t\t$errorMessage.textContent = strings.required.capitalize();\n\t\t\t\t\t\tconst $sibling = $input.nextElementSibling;\n\t\t\t\t\t\tconst $parent = $input.parentElement;\n\t\t\t\t\t\tif ($sibling) $parent.insertBefore($errorMessage, $sibling);\n\t\t\t\t\t\telse $parent.append($errorMessage);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\thide();\n\t\t\t\tresolve(getValue());\n\t\t\t},\n\t\t});\n\t\tconst cancelBtn = tag(\"button\", {\n\t\t\ttextContent: strings.cancel,\n\t\t\ttype: \"button\",\n\t\t\tonclick: function () {\n\t\t\t\treject();\n\t\t\t\thide();\n\t\t\t},\n\t\t});\n\t\tconst $errorMessage = (\n\t\t\t<span className=\"error-msg\" style={{ display: \"block\" }} />\n\t\t);\n\t\tconst $mask = <span className=\"mask\" />;\n\t\tconst $promptDiv = tag(\"form\", {\n\t\t\taction: \"#\",\n\t\t\tclassName: \"prompt multi\",\n\t\t\tonsubmit: (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (!okBtn.disabled) {\n\t\t\t\t\tresolve(getValue());\n\t\t\t\t}\n\t\t\t},\n\t\t\tchildren: [\n\t\t\t\t$title,\n\t\t\t\t$body,\n\t\t\t\ttag(\"div\", {\n\t\t\t\t\tclassName: \"button-container\",\n\t\t\t\t\tchildren: [cancelBtn, okBtn],\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\n\t\tif (/^https?:/.test(help)) {\n\t\t\t$title.append(\n\t\t\t\ttag(\"a\", {\n\t\t\t\t\thref: help,\n\t\t\t\t\tclassName: \"icon help\",\n\t\t\t\t}),\n\t\t\t);\n\t\t} else if (typeof help === \"string\") {\n\t\t\t$title.append(\n\t\t\t\ttag(\"span\", {\n\t\t\t\t\tclassName: \"icon help\",\n\t\t\t\t\tonclick: () => {\n\t\t\t\t\t\talert(strings.info, help);\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\tinputs.map((input) => {\n\t\t\tif (Array.isArray(input)) createGroup(input);\n\t\t\telse $body.append(createInput(input));\n\t\t});\n\n\t\tactionStack.push({\n\t\t\tid: \"prompt\",\n\t\t\taction: hidePrompt,\n\t\t});\n\n\t\trestoreTheme(true);\n\t\tsystem.setInputType(\"NORMAL\");\n\t\tdocument.body.append($promptDiv, $mask);\n\t\tconst $focusEl = [...$body.getAll(\"input[autofocus]\")].pop();\n\t\tif ($focusEl) $focusEl.focus();\n\n\t\tfunction hidePrompt() {\n\t\t\t$promptDiv.classList.add(\"hide\");\n\t\t\trestoreTheme();\n\t\t\tsetTimeout(() => {\n\t\t\t\tif ($promptDiv.isConnected) $promptDiv.remove();\n\t\t\t\tif ($mask.isConnected) $mask.remove($mask);\n\t\t\t}, 300);\n\t\t}\n\n\t\tfunction hide() {\n\t\t\tactionStack.remove(\"prompt\");\n\t\t\tsystem.setInputType(appSettings.value.keyboardMode);\n\t\t\thidePrompt();\n\t\t}\n\n\t\tfunction getValue() {\n\t\t\tconst values = {};\n\t\t\tconst inputAr = [...$body.getAll(\"input\")];\n\t\t\tinputAr.map(($input) => {\n\t\t\t\tif ($input.type === \"checkbox\" || $input.type === \"radio\")\n\t\t\t\t\tvalues[$input.id] = $input.checked;\n\t\t\t\telse values[$input.id] = $input.value;\n\t\t\t});\n\n\t\t\treturn values;\n\t\t}\n\n\t\t/**\n\t\t * Creates a group of inputs\n\t\t * @param {Array<Input>} inputs Array of inputs\n\t\t */\n\t\tfunction createGroup(inputs) {\n\t\t\tconst $text = tag(\"span\", {\n\t\t\t\tclassName: \"hero\",\n\t\t\t});\n\t\t\tconst $group = tag(\"div\", {\n\t\t\t\tclassName: \"input-group\",\n\t\t\t\tchild: $text,\n\t\t\t});\n\n\t\t\tinputs.map((input) => {\n\t\t\t\tlet $input;\n\n\t\t\t\tif (typeof input === \"string\") {\n\t\t\t\t\t$text.textContent = input;\n\t\t\t\t} else {\n\t\t\t\t\t$input = createInput(input, true);\n\t\t\t\t\t$group.append($input);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t$body.append($group);\n\t\t}\n\n\t\t/**\n\t\t * Creates an input\n\t\t * @param {Input} input Input object\n\t\t * @param {boolean} group Whether the input is part of a group\n\t\t * @returns {HTMLInputElement|HTMLTextAreaElement}\n\t\t */\n\t\tfunction createInput(input, group = false) {\n\t\t\tconst {\n\t\t\t\tid,\n\t\t\t\trequired,\n\t\t\t\ttype,\n\t\t\t\tmatch,\n\t\t\t\tvalue,\n\t\t\t\tplaceholder,\n\t\t\t\thints,\n\t\t\t\tname,\n\t\t\t\tdisabled,\n\t\t\t\tonclick,\n\t\t\t\tonchange,\n\t\t\t\treadOnly,\n\t\t\t\tautofocus,\n\t\t\t\thidden,\n\t\t\t} = input;\n\n\t\t\tconst inputType = type === \"textarea\" ? \"textarea\" : \"input\";\n\t\t\tlet _type = type === \"filename\" ? \"text\" : type || \"text\";\n\n\t\t\tlet $input;\n\n\t\t\tif (_type === \"checkbox\" || _type === \"radio\") {\n\t\t\t\t$input = Checkbox(placeholder, value, name, id, type);\n\n\t\t\t\tif (!group) {\n\t\t\t\t\t$input.style.marginTop = \"1rem\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$input = tag(inputType, {\n\t\t\t\t\tid,\n\t\t\t\t\tplaceholder,\n\t\t\t\t\tvalue: value,\n\t\t\t\t\tclassName: \"input\",\n\t\t\t\t\tisRequired: required,\n\t\t\t\t\treadOnly,\n\t\t\t\t\tautofocus,\n\t\t\t\t\thidden,\n\t\t\t\t});\n\n\t\t\t\tif (value) {\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t$input.scrollLeft = $input.scrollWidth;\n\t\t\t\t\t}, 0);\n\t\t\t\t}\n\n\t\t\t\tif (disabled) $input.disabled = true;\n\t\t\t\tif (hints) inputhints($input, hints);\n\n\t\t\t\tif (inputType === \"textarea\") {\n\t\t\t\t\t$input.rows = 1;\n\t\t\t\t\t$input.inputMode = _type;\n\t\t\t\t\tautosize($input);\n\t\t\t\t} else {\n\t\t\t\t\t$input.type = _type;\n\t\t\t\t}\n\n\t\t\t\t$input.oninput = function () {\n\t\t\t\t\tif (match && !match.test(this.value)) {\n\t\t\t\t\t\tconst $parent = this.parentElement;\n\t\t\t\t\t\tif ($input.nextElementSibling) {\n\t\t\t\t\t\t\t$parent.insertBefore($errorMessage, $input.nextElementSibling);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$parent.append($errorMessage);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$errorMessage.textContent = strings[\"invalid value\"];\n\t\t\t\t\t\tokBtn.disabled = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tokBtn.disabled = false;\n\t\t\t\t\t\t$errorMessage.textContent = \"\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t$input.onfocus = function () {\n\t\t\t\t\tthis.select();\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tObject.defineProperty($input, \"prompt\", {\n\t\t\t\tvalue: { $body, hide },\n\t\t\t});\n\n\t\t\tObject.defineProperty($input, \"setError\", {\n\t\t\t\tvalue(message) {\n\t\t\t\t\tif (!message) {\n\t\t\t\t\t\t$errorMessage.textContent = \"\";\n\t\t\t\t\t\tokBtn.disabled = false;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst $parent = this.parentElement;\n\t\t\t\t\tif ($input.nextElementSibling) {\n\t\t\t\t\t\t$parent.insertBefore($errorMessage, $input.nextElementSibling);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$parent.append($errorMessage);\n\t\t\t\t\t}\n\t\t\t\t\t$errorMessage.textContent = message;\n\t\t\t\t\tokBtn.disabled = !!message;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tif (onclick) $input.onclick = onclick.bind($input);\n\t\t\tif (onchange) $input.onchange = onchange.bind($input);\n\n\t\t\treturn $input;\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "src/dialogs/prompt.js",
    "content": "import autosize from \"autosize\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\nimport appSettings from \"lib/settings\";\n\n/**\n * @typedef {Object} PromptOptions\n * @property {RegExp} [match]\n * @property {boolean} [required]\n * @property {string} [placeholder]\n * @property {boolean} [capitalize] - If true, the first letter of the input will be capitalized\n * @property {(any)=>boolean} [test]\n */\n\n/**\n * Opens a prompt dialog\n * @param {string} message\n * @param {string} defaultValue\n * @param {\"textarea\"|\"text\"|\"number\"|\"tel\"|\"search\"|\"email\"|\"url\"} type\n * @param {PromptOptions} options\n * @returns {Promise<string|number|null>} Returns null if cancelled\n */\n\nexport default function prompt(\n\tmessage,\n\tdefaultValue,\n\ttype = \"text\",\n\toptions = {},\n) {\n\t// CodeMirror doesn't use commands.exec like ACE, so we store a reference to the editor\n\t// to potentially disable keymaps if needed in the future\n\tconst editor = editorManager.editor;\n\tconst { capitalize = true } = options;\n\n\treturn new Promise((resolve) => {\n\t\tconst inputType = type === \"textarea\" ? \"textarea\" : \"input\";\n\t\ttype = type === \"filename\" ? \"text\" : type;\n\n\t\tconst messageSpan = tag(\"span\", {\n\t\t\ttextContent: message,\n\t\t\tclassName: \"message scroll\",\n\t\t});\n\t\tconst input = tag(inputType, {\n\t\t\tvalue: defaultValue,\n\t\t\tclassName: \"input\",\n\t\t\tplaceholder: options.placeholder,\n\t\t\tautocapitalize: capitalize ? \"on\" : \"off\",\n\t\t});\n\t\tconst okBtn = tag(\"button\", {\n\t\t\ttype: \"submit\",\n\t\t\ttextContent: strings.ok,\n\t\t\tdisabled: !defaultValue,\n\t\t\tonclick: function () {\n\t\t\t\tif (options.required && !input.value) {\n\t\t\t\t\terrorMessage.textContent = strings.required;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\thide();\n\t\t\t\tlet { value } = input;\n\t\t\t\tif (type === \"number\") value = +value;\n\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t});\n\t\tconst cancelBtn = tag(\"button\", {\n\t\t\ttextContent: strings.cancel,\n\t\t\ttype: \"button\",\n\t\t\tonclick: function () {\n\t\t\t\thide();\n\t\t\t\tresolve(null);\n\t\t\t},\n\t\t});\n\t\tconst errorMessage = tag(\"span\", {\n\t\t\tclassName: \"error-msg\",\n\t\t});\n\t\tconst promptDiv = tag(\"form\", {\n\t\t\taction: \"#\",\n\t\t\tclassName: \"prompt\",\n\t\t\tonsubmit: (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (!okBtn.disabled) {\n\t\t\t\t\tresolve(input.value);\n\t\t\t\t}\n\t\t\t},\n\t\t\tchildren: [\n\t\t\t\tmessageSpan,\n\t\t\t\tinput,\n\t\t\t\terrorMessage,\n\t\t\t\ttag(\"div\", {\n\t\t\t\t\tclassName: \"button-container\",\n\t\t\t\t\tchildren: [cancelBtn, okBtn],\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\t\tconst mask = tag(\"span\", {\n\t\t\tclassName: \"mask\",\n\t\t});\n\n\t\tif (inputType === \"textarea\") {\n\t\t\tinput.rows = 1;\n\t\t\tinput.inputMode = type;\n\t\t} else {\n\t\t\tinput.type = type;\n\t\t\tif (type === \"number\") {\n\t\t\t\tinput.step = \"any\";\n\t\t\t}\n\t\t}\n\n\t\tinput.oninput = function () {\n\t\t\tconst { match, test } = options;\n\t\t\tlet isValid = true;\n\n\t\t\tif (match) {\n\t\t\t\tisValid = match.test(input.value);\n\t\t\t}\n\n\t\t\tif (test) {\n\t\t\t\tisValid = test(input.value);\n\t\t\t}\n\n\t\t\tif (!isValid) {\n\t\t\t\tokBtn.disabled = true;\n\t\t\t\terrorMessage.textContent = strings[\"invalid value\"];\n\t\t\t} else {\n\t\t\t\tokBtn.disabled = false;\n\t\t\t\terrorMessage.textContent = \"\";\n\t\t\t}\n\t\t};\n\n\t\tinput.onfocus = function () {\n\t\t\tthis.select();\n\t\t};\n\n\t\tactionStack.push({\n\t\t\tid: \"prompt\",\n\t\t\taction: hidePrompt,\n\t\t});\n\n\t\tsystem.setInputType(\"NORMAL\");\n\t\trestoreTheme(true);\n\t\tapp.append(promptDiv, mask);\n\t\tinput.focus();\n\t\tif (input.value) {\n\t\t\ttry {\n\t\t\t\tconst col = input.value.length;\n\t\t\t\tinput.setSelectionRange(col, col);\n\t\t\t} catch (error) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t\tif (inputType === \"textarea\") autosize(input);\n\n\t\tfunction hidePrompt() {\n\t\t\tpromptDiv.classList.add(\"hide\");\n\t\t\trestoreTheme();\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (promptDiv.isConnected) promptDiv.remove();\n\t\t\t\tif (mask.isConnected) mask.remove();\n\t\t\t}, 300);\n\t\t}\n\n\t\tfunction hide() {\n\t\t\t// CodeMirror keymaps are handled differently - no need to restore commands.exec\n\t\t\tactionStack.remove(\"prompt\");\n\t\t\tsystem.setInputType(appSettings.value.keyboardMode);\n\t\t\thidePrompt();\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "src/dialogs/rateBox.js",
    "content": "import constants from \"lib/constants\";\nimport template from \"views/rating.hbs\";\nimport box from \"./box\";\n\nfunction rateBox() {\n\tconst $box = box(\"Did you like the app?\", template, strings.cancel).onclick(\n\t\tonInteract,\n\t);\n\n\tfunction onInteract(e) {\n\t\t/**\n\t\t * @type {HTMLSpanElement}\n\t\t */\n\t\tconst $el = e.target;\n\t\tif (!$el) return;\n\t\tlet val = $el.getAttribute(\"value\");\n\t\tif (val) val = Number.parseInt(val);\n\t\tconst siblings = $el.parentElement.children;\n\t\tconst len = siblings.length;\n\t\tfor (let i = 0; i < len; ++i) {\n\t\t\tconst star = siblings[i];\n\t\t\tstar.classList.remove(\"stargrade\", \"star_outline\");\n\t\t\tif (i < val) star.classList.add(\"stargrade\");\n\t\t\telse star.classList.add(\"star_outline\");\n\t\t}\n\n\t\tsetTimeout(() => {\n\t\t\tif (val === 5) {\n\t\t\t\tsystem.openInBrowser(\n\t\t\t\t\t`https://play.google.com/store/apps/details?id=${BuildInfo.packageName}`,\n\t\t\t\t);\n\t\t\t\tlocalStorage.dontAskForRating = true;\n\t\t\t} else {\n\t\t\t\tconst stars = getStars(val);\n\t\t\t\tconst subject = \"feedback - Acode editor\";\n\t\t\t\tconst textBody = stars + \"</br>%0A\" + getFeedbackBody(\"</br>%0A\");\n\t\t\t\tconst email = constants.FEEDBACK_EMAIL;\n\t\t\t\tsystem.openInBrowser(\n\t\t\t\t\t`mailto:${email}?subject=${subject}&body=${textBody}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}, 100);\n\n\t\t$box.hide();\n\t}\n}\n\n/**\n * Gets body for feedback email\n * @param {String} eol\n * @returns\n */\nfunction getFeedbackBody(eol) {\n\tconst buildInfo = window.BuildInfo || {};\n\tconst device = window.device || {};\n\treturn (\n\t\t\"Version: \" +\n\t\t`${buildInfo.version} (${buildInfo.versionCode})` +\n\t\teol +\n\t\t\"Device: \" +\n\t\t(device.model || \"\") +\n\t\teol +\n\t\t\"Manufacturer: \" +\n\t\t(device.manufacturer || \"\") +\n\t\teol +\n\t\t\"Android version: \" +\n\t\tdevice.version +\n\t\teol +\n\t\t\"Info: \"\n\t);\n}\n\n/**\n *\n * @param {number} num\n */\nfunction getStars(num) {\n\tlet star = num;\n\tlet noStar = 5 - num;\n\tlet str = \"\";\n\n\twhile (star--) str += \"★\";\n\twhile (noStar--) str += \"☆\";\n\n\treturn str;\n}\n\nexport default rateBox;\n"
  },
  {
    "path": "src/dialogs/select.js",
    "content": "import Checkbox from \"components/checkbox\";\nimport tile from \"components/tile\";\nimport DOMPurify from \"dompurify\";\nimport actionStack from \"lib/actionStack\";\nimport restoreTheme from \"lib/restoreTheme\";\n\n/**\n * @typedef {object} SelectOptions\n * @property {boolean} [hideOnSelect]\n * @property {boolean} [textTransform]\n * @property {string} [default]\n * @property {function():void} [onCancel]\n * @property {function():void} [onHide]\n */\n\n/**\n * @typedef {object} SelectItem\n * @property {string} [value]\n * @property {string} [text]\n * @property {string} [icon]\n * @property {boolean} [disabled]\n * @property {string} [letters]\n * @property {boolean} [checkbox]\n * @property {HTMLElement} [tailElement]\n * @property {function(Event):void} [ontailclick]\n */\n\n/**\n * Create a select dialog\n * @param {string} title Title of the select\n * @param {string | string[] | SelectItem} items Object or [value, text, icon, disable?, letters?, checkbox?] or String\n * @param {SelectOptions | boolean} options options or rejectOnCancel\n * @returns {Promise<string>}\n */\nfunction select(title, items, options = {}) {\n\tlet rejectOnCancel = false;\n\tif (typeof options === \"boolean\") {\n\t\trejectOnCancel = options;\n\t\toptions = {};\n\t}\n\n\treturn new Promise((res, rej) => {\n\t\tconst { textTransform = false, hideOnSelect = true } = options;\n\t\tlet $defaultVal;\n\n\t\t// elements\n\t\tconst $mask = <span className=\"mask\" onclick={cancel}></span>;\n\t\tconst $list = tag(\"ul\", {\n\t\t\tclassName: `scroll${!textTransform ? \" no-text-transform\" : \"\"}`,\n\t\t});\n\t\tconst $titleSpan = title ? (\n\t\t\t<strong className=\"title\">{title}</strong>\n\t\t) : null;\n\t\tconst $select = (\n\t\t\t<div className=\"prompt select\">\n\t\t\t\t{$titleSpan ? [$titleSpan, $list] : $list}\n\t\t\t</div>\n\t\t);\n\n\t\t// Track tail click handlers for cleanup\n\t\tconst tailClickHandlers = new Map();\n\n\t\titems.map((item) => {\n\t\t\tlet lead,\n\t\t\t\ttail = null,\n\t\t\t\titemOptions = {\n\t\t\t\t\tvalue: null,\n\t\t\t\t\ttext: null,\n\t\t\t\t\ticon: null,\n\t\t\t\t\tdisabled: false,\n\t\t\t\t\tletters: \"\",\n\t\t\t\t\tcheckbox: null,\n\t\t\t\t\ttailElement: null,\n\t\t\t\t\tontailclick: null,\n\t\t\t\t};\n\n\t\t\t// init item options\n\t\t\tif (typeof item === \"object\") {\n\t\t\t\tif (Array.isArray(item)) {\n\t\t\t\t\t// This format does NOT support custom tail or handlers so pass object :)\n\t\t\t\t\tObject.keys(itemOptions).forEach(\n\t\t\t\t\t\t(key, i) => (itemOptions[key] = item[i]),\n\t\t\t\t\t);\n\n\t\t\t\t\titem.map((o, i) => {\n\t\t\t\t\t\tif (typeof o === \"boolean\" && i > 1) itemOptions.disabled = !o;\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\titemOptions = Object.assign({}, itemOptions, item);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\titemOptions.value = item;\n\t\t\t\titemOptions.text = item;\n\t\t\t}\n\n\t\t\t// handle icon (lead)\n\t\t\tif (itemOptions.icon) {\n\t\t\t\tif (itemOptions.icon === \"letters\" && !!itemOptions.letters) {\n\t\t\t\t\tlead = (\n\t\t\t\t\t\t<i className=\"icon letters\" data-letters={itemOptions.letters}></i>\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tlead = <i className={`icon ${itemOptions.icon}`}></i>;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// handle tail (checkbox or custom element)\n\t\t\tif (itemOptions.tailElement) {\n\t\t\t\ttail = itemOptions.tailElement;\n\t\t\t} else if (itemOptions.checkbox != null) {\n\t\t\t\ttail = Checkbox({\n\t\t\t\t\tchecked: itemOptions.checkbox,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst $item = tile({\n\t\t\t\tlead,\n\t\t\t\ttail,\n\t\t\t\ttext: (\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"text\"\n\t\t\t\t\t\tinnerHTML={DOMPurify.sanitize(itemOptions.text)}\n\t\t\t\t\t></span>\n\t\t\t\t),\n\t\t\t});\n\n\t\t\t$item.tabIndex = \"0\";\n\t\t\tif (itemOptions.disabled) $item.classList.add(\"disabled\");\n\t\t\tif (options.default === itemOptions.value) {\n\t\t\t\t$item.classList.add(\"selected\");\n\t\t\t\t$defaultVal = $item;\n\t\t\t}\n\n\t\t\t$item.onclick = function (e) {\n\t\t\t\t// Check if clicked element or any parent up to the item has data-action\n\t\t\t\tlet target = e.target;\n\t\t\t\twhile (target && target !== $item) {\n\t\t\t\t\tif (target.hasAttribute(\"data-action\")) {\n\t\t\t\t\t\t// Stop propagation and prevent default\n\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\ttarget = target.parentElement;\n\t\t\t\t}\n\n\t\t\t\tif (itemOptions.value === undefined) return;\n\t\t\t\tif (hideOnSelect) hide();\n\t\t\t\tres(itemOptions.value);\n\t\t\t};\n\n\t\t\t// Handle tail click event if a custom tail and handler are provided\n\t\t\tif (itemOptions.tailElement && itemOptions.ontailclick && tail) {\n\t\t\t\t// Apply the pointer-events: all directly to the tail element\n\t\t\t\ttail.style.pointerEvents = \"all\";\n\n\t\t\t\tconst tailClickHandler = function (e) {\n\t\t\t\t\te.stopPropagation();\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\titemOptions.ontailclick.call($item, e);\n\t\t\t\t};\n\n\t\t\t\ttail.addEventListener(\"click\", tailClickHandler);\n\t\t\t\ttailClickHandlers.set(tail, tailClickHandler);\n\t\t\t}\n\n\t\t\t$list.append($item);\n\t\t});\n\n\t\tactionStack.push({\n\t\t\tid: \"select\",\n\t\t\taction: cancel,\n\t\t});\n\n\t\tapp.append($select, $mask);\n\t\tif ($defaultVal) $defaultVal.scrollIntoView();\n\n\t\tconst $firstChild = $defaultVal || $list.firstChild;\n\t\tif ($firstChild && $firstChild.focus) $firstChild.focus();\n\t\trestoreTheme(true);\n\n\t\tfunction cancel() {\n\t\t\thide();\n\t\t\tif (typeof options.onCancel === \"function\") options.onCancel();\n\t\t\tif (rejectOnCancel) rej();\n\t\t}\n\n\t\tfunction hideSelect() {\n\t\t\t$select.classList.add(\"hide\");\n\t\t\trestoreTheme();\n\t\t\tsetTimeout(() => {\n\t\t\t\t$select.remove();\n\t\t\t\t$mask.remove();\n\t\t\t}, 300);\n\t\t}\n\n\t\tfunction hide() {\n\t\t\tif (typeof options.onHide === \"function\") options.onHide();\n\t\t\tactionStack.remove(\"select\");\n\t\t\thideSelect();\n\t\t\tlet listItems = [...$list.children];\n\t\t\tlistItems.map((item) => (item.onclick = null));\n\t\t\t// Clean up tail click handlers\n\t\t\ttailClickHandlers.forEach((handler, element) => {\n\t\t\t\telement.removeEventListener(\"click\", handler);\n\t\t\t});\n\t\t\ttailClickHandlers.clear();\n\t\t}\n\t});\n}\n\nexport default select;\n"
  },
  {
    "path": "src/dialogs/style.scss",
    "content": "@use \"../styles/mixins.scss\";\n\n.prompt {\n  position: fixed;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%) scale(1) translateZ(0);\n  z-index: 112;\n  height: fit-content;\n  max-height: 80vh;\n  width: 100vw;\n  max-width: 320px;\n  background-color: rgb(255, 255, 255);\n  background-color: var(--popup-background-color);\n  box-shadow: 0 0 16px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 0 16px var(--box-shadow-color);\n  border-radius: 2px !important;\n  border-radius: var(--popup-border-radius) !important;\n  overflow: hidden;\n\n  border: solid 1px transparent;\n  border: solid 1px var(--popup-border-color);\n\n  display: flex;\n  flex-direction: column;\n\n  &.box {\n    z-index: 113;\n  }\n\n  &.multi {\n    textarea:not(:first-of-type) {\n      margin-top: 20px;\n    }\n  }\n\n  &.confirm,\n  &.alert {\n    .title {\n      text-transform: uppercase;\n    }\n  }\n\n  &.select {\n    min-width: 220px;\n    width: fit-content;\n    max-width: 320px;\n\n    ul li :nth-child(2) {\n      display: block;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    .tile,\n    .title {\n      justify-content: center;\n    }\n\n    .no-text-transform {\n      li {\n        text-transform: initial !important;\n      }\n\n      .file {\n        background-position: center;\n      }\n    }\n  }\n\n  ul {\n    overflow-y: auto;\n    padding: 10px;\n    list-style: none;\n\n    li {\n      &.selected span {\n        color: rgb(169, 0, 0) !important;\n        color: var(--popup-active-color) !important;\n      }\n\n      &:not(:last-child) {\n        border-bottom: 0.02rem solid rgba(122, 122, 122, 0.227);\n        border-bottom: 0.02rem solid var(--border-color);\n      }\n\n      &:focus {\n        background-color: rgba($color: #000000, $alpha: 0.1);\n      }\n\n      height: 40px;\n      text-transform: uppercase;\n\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      color: rgb(37, 37, 37);\n      color: var(--popup-text-color);\n    }\n  }\n\n  &+.mask {\n    z-index: 111;\n    background-color: rgb(0, 0, 0);\n    opacity: 0.4;\n  }\n\n  &.hide {\n    transition: all 200ms ease-out;\n    transform: translate(-50%, -50%) scale(0.95) translateZ(0);\n    opacity: 0;\n\n    &+.mask {\n      opacity: 0;\n    }\n  }\n\n  .title {\n    display: flex;\n    align-items: center;\n    font-size: 1.25em;\n    color: rgb(37, 37, 37);\n    color: var(--secondary-text-color);\n    word-wrap: break-word;\n    overflow: auto;\n\n    &:not(:empty) {\n      min-height: 40px;\n      margin: 5px 10px 0 10px;\n    }\n  }\n\n  .message {\n    overflow: auto;\n    font-size: 0.9em;\n\n    &.color-picker {\n      .button-container {\n        margin: 0;\n      }\n    }\n\n    .picker_wrapper {\n      background-color: inherit;\n      box-shadow: none;\n      margin: auto;\n\n      .picker_selector {\n        border: solid 1px rgb(37, 37, 37);\n        border: solid 1px var(--popup-text-color);\n        box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.2);\n        box-shadow: 0 0 2px 0 var(--box-shadow-color);\n      }\n\n      .picker_hue {\n        .picker_selector {\n          border-radius: 0;\n          padding: 10px 2px;\n        }\n      }\n\n      .picker_alpha {\n        .picker_selector {\n          border-radius: 0;\n          padding: 2px 10px;\n        }\n      }\n\n      .picker_sample,\n      .picker_done {\n        display: none;\n      }\n\n      .picker_editor {\n        width: 100%;\n\n        input {\n          text-align: center;\n          background-color: rgb(0, 0, 0) !important;\n          mix-blend-mode: difference;\n          color: inherit;\n        }\n      }\n    }\n\n    img {\n      max-width: 100%;\n      max-height: 100%;\n      margin: auto;\n      display: flex;\n    }\n\n    &.loader {\n      display: flex;\n      align-items: center;\n\n      .loader {\n        @include mixins.circular-loader(30px);\n        margin: 0 10px;\n      }\n\n      .message {\n        display: flex;\n        align-items: center;\n        overflow: auto;\n        font-size: 0.9em;\n      }\n    }\n\n    .message,\n    &:not(.loader) {\n      color: #252525;\n      color: var(--popup-text-color);\n      padding: 10px;\n      min-height: 40px;\n      font-size: 1.2em;\n    }\n  }\n\n  .input-group {\n    display: flex;\n    min-height: 40px;\n    margin: 2.5px auto;\n    width: 100%;\n    flex-wrap: wrap;\n    max-width: 300px;\n\n    .hero {\n      display: flex;\n      align-items: center;\n      height: 40px;\n      width: 100%;\n      font-size: 1.2em;\n    }\n\n    .input-checkbox {\n      height: 40px;\n      width: 50%;\n    }\n  }\n\n  .input {\n    max-height: calc(100vh - 80px);\n    width: 100%;\n    max-width: 300px;\n    background-color: inherit;\n    border: none;\n    color: rgb(37, 37, 37);\n    color: var(--popup-text-color);\n    border-bottom: solid 1px currentColor;\n    text-indent: 0;\n    font-size: 1em;\n    margin: 2.5px auto;\n\n    &::placeholder {\n      opacity: 0.4;\n    }\n\n    &:focus {\n      border-bottom: solid 1px rgb(51, 153, 255);\n      border-bottom: solid 1px var(--active-color);\n    }\n  }\n\n  li {\n    .icon {\n      font-size: 0.85em;\n    }\n  }\n\n  .error-msg {\n    color: rgb(255, 185, 92) !important;\n    color: var(--error-text-color) !important;\n    height: fit-content;\n\n    &:not(:empty) {\n      margin: 10px auto 0 auto;\n    }\n  }\n\n  .button-container {\n    margin-top: 20px;\n  }\n}"
  },
  {
    "path": "src/fileSystem/externalFs.js",
    "content": "import loader from \"dialogs/loader\";\nimport { decode, encode } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nconst externalFs = {\n\tasync readFile(url) {\n\t\turl = await this.formatUri(url);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.read(url, (data) => resolve({ data }), reject);\n\t\t});\n\t},\n\n\tasync writeFile(filename, data) {\n\t\treturn new Promise(async (resolve, reject) => {\n\t\t\tsdcard.write(filename, data, resolve, reject);\n\t\t});\n\t},\n\n\tasync copy(src, dest) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.copy(src, dest, resolve, reject);\n\t\t});\n\t},\n\n\tasync move(src, dest) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.move(src, dest, resolve, reject);\n\t\t});\n\t},\n\n\tasync delete(name) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.delete(name, resolve, reject);\n\t\t});\n\t},\n\n\tasync createFile(parent, filename, data) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.createFile(\n\t\t\t\tparent,\n\t\t\t\tfilename,\n\t\t\t\tasync (res) => {\n\t\t\t\t\tif (data) {\n\t\t\t\t\t\tawait this.writeFile(res, data);\n\t\t\t\t\t}\n\t\t\t\t\tresolve(res);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\tasync createDir(parent, dirname) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.createDir(parent, dirname, resolve, reject);\n\t\t});\n\t},\n\n\tasync listDir(pathname) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.listDir(pathname, resolve, reject);\n\t\t});\n\t},\n\n\tasync renameFile(src, newname) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.rename(src, newname, resolve, reject);\n\t\t});\n\t},\n\n\tgetStorageAccessPermission(uuid, name) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tloader.destroy();\n\t\t\t}, 100);\n\t\t\tsdcard.getStorageAccessPermission(uuid, resolve, reject);\n\t\t});\n\t},\n\n\tlistStorages() {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.listStorages(resolve, reject);\n\t\t});\n\t},\n\n\tgetPath(uri, filename) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.getPath(uri, filename, resolve, reject);\n\t\t});\n\t},\n\n\tasync stats(uri) {\n\t\tconst storageList = helpers.parseJSON(localStorage.getItem(\"storageList\"));\n\n\t\tif (Array.isArray(storageList)) {\n\t\t\tconst storage = storageList.find((s) => s.uri === uri);\n\t\t\tif (storage) {\n\t\t\t\tconst stat = {\n\t\t\t\t\tsize: 0,\n\t\t\t\t\tname: storage.name,\n\t\t\t\t\ttype: \"dir\",\n\t\t\t\t\tcanRead: true,\n\t\t\t\t\tcanWrite: true,\n\t\t\t\t\tmodifiedDate: new Date(),\n\t\t\t\t\tisDirectory: true,\n\t\t\t\t\tisFile: false,\n\t\t\t\t\turl: uri,\n\t\t\t\t};\n\n\t\t\t\thelpers.defineDeprecatedProperty(\n\t\t\t\t\tstat,\n\t\t\t\t\t\"uri\",\n\t\t\t\t\tfunction () {\n\t\t\t\t\t\treturn this.url;\n\t\t\t\t\t},\n\t\t\t\t\tfunction (val) {\n\t\t\t\t\t\tthis.url = val;\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\treturn stat;\n\t\t\t}\n\t\t}\n\n\t\turi = await this.formatUri(uri);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.stats(\n\t\t\t\turi,\n\t\t\t\t(stats) => {\n\t\t\t\t\thelpers.defineDeprecatedProperty(\n\t\t\t\t\t\tstats,\n\t\t\t\t\t\t\"uri\",\n\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\treturn this.url;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfunction (val) {\n\t\t\t\t\t\t\tthis.url = val;\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tresolve(stats);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Format the virtual uri to a real uri\n\t * @param {string} uri\n\t * @returns\n\t */\n\tformatUri(uri) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsdcard.formatUri(uri, resolve, reject);\n\t\t});\n\t},\n\n\t/**\n\t * Test if url supports this file system\n\t * @param {string} url\n\t * @returns\n\t */\n\ttest(url) {\n\t\treturn /^content:/.test(url);\n\t},\n\n\tcreateFs,\n};\n\n/**\n * Initialize external file system\n * @param {string} url\n */\nfunction createFs(url) {\n\treturn {\n\t\tlsDir() {\n\t\t\treturn externalFs.listDir(url);\n\t\t},\n\t\tasync readFile(encoding) {\n\t\t\tlet { data } = await externalFs.readFile(url, encoding);\n\n\t\t\tif (encoding) {\n\t\t\t\tdata = await decode(data, encoding);\n\t\t\t}\n\n\t\t\treturn data;\n\t\t},\n\t\tasync writeFile(content, encoding) {\n\t\t\tif (typeof content === \"string\" && encoding) {\n\t\t\t\tcontent = await encode(content, encoding);\n\t\t\t}\n\t\t\treturn externalFs.writeFile(url, content);\n\t\t},\n\t\tcreateFile(name, data) {\n\t\t\tdata = data || \"\";\n\t\t\treturn externalFs.createFile(url, name, data);\n\t\t},\n\t\tcreateDirectory(name) {\n\t\t\treturn externalFs.createDir(url, name);\n\t\t},\n\t\tdelete() {\n\t\t\treturn externalFs.delete(url);\n\t\t},\n\t\tcopyTo(dest) {\n\t\t\treturn externalFs.copy(url, dest);\n\t\t},\n\t\tmoveTo(dest) {\n\t\t\tconst src = Url.dirname(url);\n\t\t\tif (Url.areSame(src, dest)) return Promise.resolve(url);\n\t\t\treturn externalFs.move(url, dest);\n\t\t},\n\t\trenameTo(newname) {\n\t\t\treturn externalFs.renameFile(url, newname);\n\t\t},\n\t\tasync exists() {\n\t\t\ttry {\n\t\t\t\tconst stats = await externalFs.stats(url);\n\t\t\t\treturn stats.exists;\n\t\t\t} catch (error) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t\tstat() {\n\t\t\treturn externalFs.stats(url);\n\t\t},\n\t};\n}\n\nexport default externalFs;\n"
  },
  {
    "path": "src/fileSystem/ftp.js",
    "content": "import settings from \"lib/settings\";\nimport mimeType from \"mime-types\";\nimport { decode, encode } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport Path from \"utils/Path\";\nimport Url from \"utils/Url\";\nimport internalFs from \"./internalFs\";\n\nclass FtpClient {\n\t#MAX_TRY = 3;\n\t#path = \"/\";\n\t#host;\n\t#username;\n\t#password;\n\t#port;\n\t#security;\n\t#mode;\n\t#conId;\n\t#stat;\n\t#origin;\n\t#try = 0;\n\n\tconstructor(\n\t\thost,\n\t\tusername = \"anonymous\",\n\t\tpassword = \"\",\n\t\tport = 21,\n\t\tsecurity = \"ftp\",\n\t\tmode = \"passive\",\n\t\tpath = \"/\",\n\t) {\n\t\tif (!host) {\n\t\t\tthrow new Error(\"host is required\");\n\t\t}\n\t\tthis.#host = host;\n\t\tthis.#username = username;\n\t\tthis.#password = password;\n\t\tthis.#port = port;\n\t\tthis.#security = security;\n\t\tthis.#mode = mode;\n\t\tthis.#path = path;\n\n\t\tthis.#origin = Url.formate({\n\t\t\tprotocol: \"ftp:\",\n\t\t\thostname: this.#host,\n\t\t\tpassword: this.#password,\n\t\t\tusername: this.#username,\n\t\t\tport: this.#port,\n\t\t\tquery: {\n\t\t\t\tsecurity: this.#security,\n\t\t\t\tmode: this.#mode,\n\t\t\t},\n\t\t});\n\t}\n\n\tconnect() {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.connect(\n\t\t\t\tthis.#host,\n\t\t\t\t+this.#port,\n\t\t\t\tthis.#username,\n\t\t\t\tthis.#password,\n\t\t\t\t{\n\t\t\t\t\tsecurityType: this.#security,\n\t\t\t\t\tconnectionMode: this.#mode,\n\t\t\t\t\tencoding: settings.value.defaultFileEncoding,\n\t\t\t\t},\n\t\t\t\t(conId) => {\n\t\t\t\t\tthis.#conId = conId;\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\tif (settings.value.retryRemoteFsAfterFail) {\n\t\t\t\t\t\tif (++this.#try > this.#MAX_TRY) {\n\t\t\t\t\t\t\tthis.#try = 0;\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.connect().then(resolve).catch(reject);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tsetPath(val) {\n\t\tthis.#path = val;\n\t}\n\n\tasync listDir() {\n\t\tawait this.#connectIfNotConnected();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.listDirectory(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#path,\n\t\t\t\t(list) => {\n\t\t\t\t\tresolve(\n\t\t\t\t\t\tlist.map((i) => {\n\t\t\t\t\t\t\ti.url = Url.join(this.#origin, i.url);\n\t\t\t\t\t\t\tif (i.isFile) {\n\t\t\t\t\t\t\t\ti.type = mimeType.lookup(i.name);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn i;\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Read file from ftp server\n\t * @returns\n\t */\n\tasync readFile() {\n\t\tawait this.#connectIfNotConnected();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.downloadFile(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#path,\n\t\t\t\tthis.#cacheFile,\n\t\t\t\tasync () => {\n\t\t\t\t\tconst data = await internalFs.readFile(this.#cacheFile);\n\t\t\t\t\tresolve(data);\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\treject(error);\n\t\t\t\t\tconsole.error(\"FTP readFile: \", error);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\t/**\n\t * Write file to ftp server\n\t * @param {string|ArrayBuffer} content\n\t * @returns\n\t */\n\tasync writeFile(content = \"\") {\n\t\tawait this.#connectIfNotConnected();\n\t\tconst localFile = this.#cacheFile;\n\t\tawait internalFs.writeFile(localFile, content, true, false);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.uploadFile(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#cacheFile,\n\t\t\t\tthis.#path,\n\t\t\t\t() => {\n\t\t\t\t\tresolve(Url.join(this.#origin, this.#path));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync createFile(name, content = \"\") {\n\t\tawait this.#connectIfNotConnected();\n\t\tconst localFile = this.#cacheFile;\n\t\tawait internalFs.writeFile(localFile, content, true, false);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.uploadFile(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#cacheFile,\n\t\t\t\tPath.join(this.#path, name),\n\t\t\t\tasync () => {\n\t\t\t\t\tresolve(Url.join(this.#origin, this.#path, name));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync createDir(name) {\n\t\tawait this.#connectIfNotConnected();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.createDirectory(\n\t\t\t\tthis.#conId,\n\t\t\t\tPath.join(this.#path, name),\n\t\t\t\tasync () => {\n\t\t\t\t\tresolve(Url.join(this.#origin, this.#path, name));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync delete() {\n\t\tawait this.#connectIfNotConnected();\n\t\tif (!this.#stat) {\n\t\t\tawait this.#getStat();\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet deleteOperation;\n\n\t\t\tif (this.#stat.isDirectory) {\n\t\t\t\tdeleteOperation = ftp.deleteDirectory;\n\t\t\t} else {\n\t\t\t\tdeleteOperation = ftp.deleteFile;\n\t\t\t}\n\n\t\t\tdeleteOperation(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#path,\n\t\t\t\t() => {\n\t\t\t\t\tresolve(Url.join(this.#origin, this.#path));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync rename(newName) {\n\t\tawait this.#connectIfNotConnected();\n\t\tconst path = Path.dirname(this.#path);\n\t\tconst newPath = Path.join(path, newName);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.rename(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#path,\n\t\t\t\tnewPath,\n\t\t\t\tasync () => {\n\t\t\t\t\tthis.#path = newPath;\n\t\t\t\t\tresolve(Url.join(this.#origin, newPath));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync moveTo(newPath) {\n\t\tnewPath = Path.join(newPath, Path.basename(this.#path));\n\t\tawait this.#connectIfNotConnected();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.rename(\n\t\t\t\tthis.#conId,\n\t\t\t\tthis.#path,\n\t\t\t\tnewPath,\n\t\t\t\tasync () => {\n\t\t\t\t\tthis.#path = newPath;\n\t\t\t\t\tresolve(Url.join(this.#origin, newPath));\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync exists() {\n\t\tawait this.#connectIfNotConnected();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.exists(this.#conId, this.#path, resolve, (error) => {\n\t\t\t\treject(error);\n\t\t\t\tconsole.error(\"FTP error: \", error);\n\t\t\t});\n\t\t});\n\t}\n\n\tasync stat() {\n\t\tif (this.#stat) return this.#stat;\n\t\tawait this.#connectIfNotConnected();\n\t\tawait this.#getStat();\n\t\treturn this.#stat;\n\t}\n\n\tasync copyTo() {\n\t\tthrow new Error(\"Not supported by FTP.\");\n\t}\n\n\tasync getWorkingDirectory() {\n\t\tawait this.#connectIfNotConnected();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.getWorkingDirectory(this.#conId, resolve, reject);\n\t\t});\n\t}\n\n\tget localName() {\n\t\treturn this.#cacheFile;\n\t}\n\n\tasync #getStat(url = this.#path) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.getStat(\n\t\t\t\tthis.#conId,\n\t\t\t\turl,\n\t\t\t\t(stat) => {\n\t\t\t\t\tthis.#stat = stat;\n\t\t\t\t\tthis.#stat.url = Url.join(this.#origin, url);\n\t\t\t\t\thelpers.defineDeprecatedProperty(\n\t\t\t\t\t\tthis.#stat,\n\t\t\t\t\t\t\"uri\",\n\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\treturn this.url;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfunction (val) {\n\t\t\t\t\t\t\tthis.url = val;\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tif (this.#stat.isFile) {\n\t\t\t\t\t\tthis.#stat.type = mimeType.lookup(this.#stat.name);\n\t\t\t\t\t}\n\t\t\t\t\tresolve(this.#stat);\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\tconsole.error(\"Error while getting stat\", err);\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tget #cacheFile() {\n\t\treturn Url.join(\n\t\t\tCACHE_STORAGE,\n\t\t\t\"ftp\" + Url.join(this.#origin, this.#path).hashCode(),\n\t\t);\n\t}\n\n\tasync #isConnected() {\n\t\tif (!this.#conId) return false;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tftp.isConnected(\n\t\t\t\tthis.#conId,\n\t\t\t\t(isConnected) => {\n\t\t\t\t\tresolve(isConnected);\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\treject(error);\n\t\t\t\t\tconsole.error(\"FTP isConnected: \", error);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tasync #connectIfNotConnected() {\n\t\tconst isConnected = await this.#isConnected();\n\t\tif (!isConnected) {\n\t\t\tawait this.connect();\n\t\t}\n\t}\n}\n\nexport default function Ftp(\n\tpath,\n\thost,\n\tport,\n\tusername,\n\tpassword,\n\tsecurity,\n\tmode,\n) {\n\treturn new FtpClient(path, host, port, username, password, security, mode);\n}\n\nFtp.fromUrl = (url) => {\n\tconst { username, password, hostname, pathname, port, query } =\n\t\tUrl.decodeUrl(url);\n\tconst { security, mode } = query;\n\tconst ftp = new FtpClient(\n\t\thostname,\n\t\tusername,\n\t\tpassword,\n\t\tport || 21,\n\t\tsecurity,\n\t\tmode,\n\t);\n\tftp.setPath(pathname);\n\n\treturn createFs(ftp);\n};\n\nFtp.test = (url) => /^ftp:/.test(url);\n\n/**\n * Create a fs like interface for the ftp client\n * @param {FtpClient} ftp\n * @returns\n */\nfunction createFs(ftp) {\n\treturn {\n\t\tlsDir() {\n\t\t\treturn ftp.listDir();\n\t\t},\n\t\tasync readFile(encoding) {\n\t\t\tconst { data } = await ftp.readFile();\n\t\t\tif (encoding) {\n\t\t\t\treturn decode(data, encoding);\n\t\t\t}\n\t\t\treturn data;\n\t\t},\n\t\tasync writeFile(content, encoding) {\n\t\t\tif (typeof content === \"string\" && encoding) {\n\t\t\t\tcontent = await encode(content, encoding);\n\t\t\t}\n\n\t\t\treturn ftp.writeFile(content);\n\t\t},\n\t\tcreateFile(name, data = \"\") {\n\t\t\treturn ftp.createFile(name, data);\n\t\t},\n\t\tcreateDirectory(name) {\n\t\t\treturn ftp.createDir(name);\n\t\t},\n\t\tdelete() {\n\t\t\treturn ftp.delete();\n\t\t},\n\t\tcopyTo(dest) {\n\t\t\tdest = Url.pathname(dest);\n\t\t\treturn ftp.copyTo(dest);\n\t\t},\n\t\tmoveTo(dest) {\n\t\t\tdest = Url.pathname(dest);\n\t\t\treturn ftp.moveTo(dest);\n\t\t},\n\t\trenameTo(newname) {\n\t\t\treturn ftp.rename(newname);\n\t\t},\n\t\texists() {\n\t\t\treturn ftp.exists();\n\t\t},\n\t\tstat() {\n\t\t\treturn ftp.stat();\n\t\t},\n\t\tget localName() {\n\t\t\treturn ftp.localName;\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "src/fileSystem/index.js",
    "content": "import ajax from \"@deadlyjack/ajax\";\nimport { decode } from \"utils/encodings\";\nimport Url from \"utils/Url\";\nimport externalFs from \"./externalFs\";\nimport Ftp from \"./ftp\";\nimport internalFs from \"./internalFs\";\nimport Sftp from \"./sftp\";\n\nconst fsList = [];\n\n/**\n * @typedef {Object} Stat\n * @property {string} name\n * @property {string} url\n * @property {string} uri - deprecated\n * @property {boolean} isFile\n * @property {boolean} isDirectory\n * @property {boolean} isLink\n * @property {number} size\n * @property {number} modifiedDate\n * @property {boolean} canRead\n * @property {boolean} canWrite\n */\n\n/**\n * @typedef {Object} File\n * @property {string} name\n * @property {string} url\n * @property {boolean} isFile\n * @property {boolean} isDirectory\n * @property {boolean} isLink\n */\n\n/**\n * @typedef {string|Blob|ArrayBuffer} FileContent\n * @typedef {Object} FileSystem\n * @property {() => Promise<File[]>} lsDir List directory\n * @property {() => Promise<void>} delete Delete file or directory\n * @property {() => Promise<boolean>} exists Check if file or directory exists\n * @property {() => Promise<Stat>} stat Get file or directory stat\n * @property {(encoding:string) => Promise<FileContent>} readFile Read file\n * @property {(data:FileContent, encoding: string) => Promise<void>} writeFile Write file content\n * @property {(name:string, data:FileContent) => Promise<string>} createFile Create file and return url of the created file\n * @property {(name:string) => Promise<string>} createDirectory Create directory and return url of the created directory\n * @property {(dest:string) => Promise<string>} copyTo Copy file or directory to destination\n * @property {(dest:string) => Promise<string>} moveTo Move file or directory to destination\n * @property {(newname:string) => Promise<string>} renameTo Rename file or directory\n */\n\n/**\n * Create a file system object from a url\n * @param {...string} url\n * @returns {FileSystem}\n */\nexport default function fsOperation(...url) {\n\tif (url.length > 1) {\n\t\turl = Url.join(...url);\n\t} else {\n\t\turl = url[0];\n\t}\n\treturn fsList.find((fs) => fs.test(url))?.fs(url);\n}\n\nfsOperation.extend = (test, fs) => {\n\tfsList.push({ test, fs });\n};\n\nfsOperation.remove = (test) => {\n\tconst index = fsList.findIndex((fs) => fs.test === test);\n\tif (index !== -1) {\n\t\tfsList.splice(index, 1);\n\t}\n};\n\nfsOperation.extend(Sftp.test, Sftp.fromUrl);\nfsOperation.extend(Ftp.test, Ftp.fromUrl);\nfsOperation.extend(internalFs.test, (url) => internalFs.createFs(url));\nfsOperation.extend(externalFs.test, (url) => externalFs.createFs(url));\n\nfsOperation.extend(\n\t(url) => /^https?:/.test(url),\n\t(url) => {\n\t\treturn {\n\t\t\tasync readFile(encoding, progress) {\n\t\t\t\tconst data = await ajax.get(url, {\n\t\t\t\t\tresponseType: \"arraybuffer\",\n\t\t\t\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\t\t\t\tonprogress: progress,\n\t\t\t\t});\n\n\t\t\t\tif (encoding) {\n\t\t\t\t\treturn await decode(data, encoding);\n\t\t\t\t}\n\n\t\t\t\treturn data;\n\t\t\t},\n\t\t\tasync writeFile(content, progress) {\n\t\t\t\treturn ajax.post(url, {\n\t\t\t\t\tdata: content,\n\t\t\t\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\t\t\t\tonprogress: progress,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\t},\n);\n"
  },
  {
    "path": "src/fileSystem/internalFs.js",
    "content": "import fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport { decode, encode } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nconst internalFs = {\n\t/**\n\t *\n\t * @param {string} url\n\t * @returns {Promise}\n\t */\n\tlistDir(url) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(url, success, reject);\n\n\t\t\tfunction success(fs) {\n\t\t\t\tconst reader = fs.createReader();\n\t\t\t\treader.readEntries(resolve, reject);\n\t\t\t}\n\t\t});\n\t},\n\n\t/**\n\t *\n\t * @param {string} filename\n\t * @param {any} data\n\t * @param {boolean} create If this property is true, and the requested file or\n\t * directory doesn't exist, the user agent should create it.\n\t * The default is false. The parent directory must already exist.\n\t * @param {boolean} exclusive If true, and the create option is also true,\n\t * the file must not exist prior to issuing the call.\n\t * Instead, it must be possible for it to be created newly at call time. The default is true.\n\t * @returns {Promise}\n\t */\n\twriteFile(filename, data, create = false, exclusive = true) {\n\t\texclusive = create ? exclusive : false;\n\t\tconst name = filename.split(\"/\").pop();\n\t\tconst dirname = Url.dirname(filename);\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\tdirname,\n\t\t\t\t(entry) => {\n\t\t\t\t\tentry.getFile(\n\t\t\t\t\t\tname,\n\t\t\t\t\t\t{ create, exclusive },\n\t\t\t\t\t\t(fileEntry) => {\n\t\t\t\t\t\t\tfileEntry.createWriter((file) => {\n\t\t\t\t\t\t\t\tfile.onwriteend = (res) => resolve(filename);\n\t\t\t\t\t\t\t\tfile.onerror = (err) => reject(err.target.error);\n\t\t\t\t\t\t\t\tfile.write(data);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t},\n\t\t\t\t\t\treject,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Delete a file or directory\n\t * @param {string} filename\n\t * @returns {Promise}\n\t */\n\tdelete(filename) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\tfilename,\n\t\t\t\t(entry) => {\n\t\t\t\t\tif (entry.isFile) {\n\t\t\t\t\t\tentry.remove(resolve, reject);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tentry.removeRecursively(resolve, reject);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Read a file\n\t * @param {string} filename\n\t * @param {string} encoding\n\t * @returns {Promise}\n\t */\n\treadFile(filename) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\tfilename,\n\t\t\t\t(fileEntry) => {\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\tconst url = fileEntry.toInternalURL();\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst data = await ajax({\n\t\t\t\t\t\t\t\turl: url,\n\t\t\t\t\t\t\t\tresponseType: \"arraybuffer\",\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tresolve({ data });\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tfileEntry.file((file) => {\n\t\t\t\t\t\t\t\tconst fileReader = new FileReader();\n\t\t\t\t\t\t\t\tfileReader.onerror = reject;\n\t\t\t\t\t\t\t\tfileReader.readAsArrayBuffer(file);\n\t\t\t\t\t\t\t\tfileReader.onloadend = () => {\n\t\t\t\t\t\t\t\t\tresolve({ data: fileReader.result });\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}, reject);\n\t\t\t\t\t\t}\n\t\t\t\t\t})();\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Rename a file or directory\n\t * @param {string} url\n\t * @param {string} newname\n\t * @returns {Promise}\n\t */\n\trenameFile(url, newname) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\turl,\n\t\t\t\t(fs) => {\n\t\t\t\t\tfs.getParent((parent) => {\n\t\t\t\t\t\tfs.moveTo(\n\t\t\t\t\t\t\tparent,\n\t\t\t\t\t\t\tnewname,\n\t\t\t\t\t\t\tasync (entry) => {\n\t\t\t\t\t\t\t\tconst newUrl = Url.join(Url.dirname(url), entry.name);\n\t\t\t\t\t\t\t\tresolve(newUrl);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\treject,\n\t\t\t\t\t\t);\n\t\t\t\t\t}, reject);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Create a directory\n\t * @param {string} path\n\t * @param {string} dirname\n\t * @returns {Promise}\n\t */\n\tcreateDir(path, dirname) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\tpath,\n\t\t\t\t(fs) => {\n\t\t\t\t\tfs.getDirectory(\n\t\t\t\t\t\tdirname,\n\t\t\t\t\t\t{ create: true },\n\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\tconst stats = await this.stats(Url.join(path, dirname));\n\t\t\t\t\t\t\tresolve(stats.url);\n\t\t\t\t\t\t},\n\t\t\t\t\t\treject,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Copy a file or directory to another location\n\t * @param {string} src\n\t * @param {string} dest\n\t * @returns {Promise<string>} The new location of the file or directory\n\t */\n\tcopy(src, dest) {\n\t\treturn moveOrCopy(\"copyTo\", src, dest);\n\t},\n\n\t/**\n\t * Move a file or directory to another location\n\t * @param {string} src\n\t * @param {string} dest\n\t * @returns {Promise<string>} The new location of the file or directory\n\t */\n\tmove(src, dest) {\n\t\treturn moveOrCopy(\"moveTo\", src, dest);\n\t},\n\n\t/**\n\t * Move or copy a file or directory to another location\n\t * @param {\"copyTO\"|\"moveTo\"} action\n\t * @param {string} src\n\t * @param {string} dest\n\t * @returns {Promise<string>} The new location of the file or directory\n\t */\n\tmoveOrCopy(action, src, dest) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\tthis.verify(src, dest)\n\t\t\t\t.then((res) => {\n\t\t\t\t\tconst { src, dest } = res;\n\n\t\t\t\t\tsrc[action](\n\t\t\t\t\t\tdest,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t(entry) => resolve(decodeURIComponent(entry.nativeURL)),\n\t\t\t\t\t\treject,\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.catch(reject);\n\t\t});\n\t},\n\n\t/**\n\t * Return the stats of a file or directory\n\t * @param {string} filename\n\t * @returns {object}\n\t */\n\tstats(filename) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\tfilename,\n\t\t\t\t(entry) => {\n\t\t\t\t\tsdcard.stats(\n\t\t\t\t\t\tentry.nativeURL,\n\t\t\t\t\t\t(stats) => {\n\t\t\t\t\t\t\thelpers.defineDeprecatedProperty(\n\t\t\t\t\t\t\t\tstats,\n\t\t\t\t\t\t\t\t\"uri\",\n\t\t\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t\t\treturn this.url;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tfunction (val) {\n\t\t\t\t\t\t\t\t\tthis.url = val;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tstats.url = filename;\n\t\t\t\t\t\t\tresolve(stats);\n\t\t\t\t\t\t},\n\t\t\t\t\t\treject,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Verify if a file or directory exists\n\t * @param {string} src\n\t * @param {string} dest\n\t * @returns {Promise<{src:Entry, dest:Entry}>}\n\t */\n\tverify(src, dest) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\tsrc,\n\t\t\t\t(srcEntry) => {\n\t\t\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\t\t\tdest,\n\t\t\t\t\t\t(destEntry) => {\n\t\t\t\t\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\t\t\t\t\tUrl.join(destEntry.nativeURL, srcEntry.name),\n\t\t\t\t\t\t\t\t(res) => {\n\t\t\t\t\t\t\t\t\treject({\n\t\t\t\t\t\t\t\t\t\tcode: 12,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\t\t\tif (err.code === 1) {\n\t\t\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\t\t\tsrc: srcEntry,\n\t\t\t\t\t\t\t\t\t\t\tdest: destEntry,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t},\n\t\t\t\t\t\treject,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Check if a file or directory exists\n\t * @param {Promise<Boolean>} url\n\t */\n\texists(url) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\treject = setMessage(reject);\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\turl,\n\t\t\t\t(entry) => {\n\t\t\t\t\tresolve(true);\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\tif (err.code === 1) resolve(false);\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t},\n\n\t/**\n\t * Test if url supports this file system\n\t * @param {string} url\n\t * @returns\n\t */\n\ttest(url) {\n\t\treturn /^file:/.test(url);\n\t},\n\n\tcreateFs,\n\tgetErrorMessage,\n};\n\nfunction setMessage(reject) {\n\treturn function (err) {\n\t\tif (err.code) {\n\t\t\tconst message = getErrorMessage(err.code);\n\t\t\terr.message = message;\n\t\t\treturn reject(err);\n\t\t}\n\t\treject(err);\n\t};\n}\n\n/**\n * Get error message for file error code\n * @param {number} code\n * @returns {string}\n */\nfunction getErrorMessage(code) {\n\tswitch (code) {\n\t\tcase 1:\n\t\t\treturn \"Path not found\";\n\t\tcase 2:\n\t\t\treturn \"Security error\";\n\t\tcase 3:\n\t\t\treturn \"Action aborted\";\n\t\tcase 4:\n\t\t\treturn \"File not readable\";\n\t\tcase 5:\n\t\t\treturn \"File encoding error\";\n\t\tcase 6:\n\t\t\treturn \"Modification not allowed\";\n\t\tcase 7:\n\t\t\treturn \"Invalid state\";\n\t\tcase 8:\n\t\t\treturn \"Syntax error\";\n\t\tcase 9:\n\t\t\treturn \"Invalid modification\";\n\t\tcase 10:\n\t\t\treturn \"Quota exceeded\";\n\t\tcase 11:\n\t\t\treturn \"Type mismatch\";\n\t\tcase 12:\n\t\t\treturn \"Path already exists\";\n\t\tdefault:\n\t\t\treturn \"Uncaught error\";\n\t}\n}\n\n/**\n * Initialize file system\n * @param {string} url\n * @this {object}\n */\nfunction createFs(url) {\n\treturn {\n\t\tasync lsDir() {\n\t\t\tconst files = [];\n\t\t\tconst entries = await internalFs.listDir(url);\n\n\t\t\tentries.map((entry) => {\n\t\t\t\tconst url = decodeURIComponent(entry.nativeURL);\n\t\t\t\tconst name = Url.basename(url);\n\t\t\t\tfiles.push({\n\t\t\t\t\tname,\n\t\t\t\t\turl,\n\t\t\t\t\tisDirectory: entry.isDirectory,\n\t\t\t\t\tisFile: entry.isFile,\n\t\t\t\t});\n\t\t\t});\n\n\t\t\treturn files;\n\t\t},\n\t\tasync readFile(encoding) {\n\t\t\tlet { data } = await internalFs.readFile(url, encoding);\n\n\t\t\tif (encoding) {\n\t\t\t\tdata = await decode(data, encoding);\n\t\t\t}\n\n\t\t\treturn data;\n\t\t},\n\t\tasync writeFile(content, encoding) {\n\t\t\tif (typeof content === \"string\" && encoding) {\n\t\t\t\tcontent = await encode(content, encoding);\n\t\t\t}\n\t\t\treturn internalFs.writeFile(url, content, false, false);\n\t\t},\n\t\tcreateFile(name, data) {\n\t\t\treturn internalFs.writeFile(Url.join(url, name), data || \"\", true, true);\n\t\t},\n\t\tcreateDirectory(name) {\n\t\t\treturn internalFs.createDir(url, name);\n\t\t},\n\t\tdelete() {\n\t\t\treturn internalFs.delete(url);\n\t\t},\n\t\tcopyTo(dest) {\n\t\t\treturn internalFs.moveOrCopy(\"copyTo\", url, dest);\n\t\t},\n\t\tmoveTo(dest) {\n\t\t\treturn internalFs.moveOrCopy(\"moveTo\", url, dest);\n\t\t},\n\t\tasync renameTo(newname) {\n\t\t\tconst name = Url.basename(url).toLowerCase();\n\n\t\t\tif (name === newname.toLowerCase()) {\n\t\t\t\tconst uuid = helpers.uuid();\n\t\t\t\tlet newUrl = await this.renameTo(uuid);\n\t\t\t\tnewUrl = await fsOperation(newUrl).renameTo(newname);\n\t\t\t\treturn newUrl;\n\t\t\t}\n\n\t\t\treturn internalFs.renameFile(url, newname);\n\t\t},\n\t\texists() {\n\t\t\treturn internalFs.exists(url);\n\t\t},\n\t\tstat() {\n\t\t\treturn internalFs.stats(url);\n\t\t},\n\t};\n}\n\nexport default internalFs;\n"
  },
  {
    "path": "src/fileSystem/sftp.js",
    "content": "import settings from \"lib/settings\";\nimport mimeType from \"mime-types\";\nimport { decode, encode } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport Path from \"utils/Path\";\nimport Url from \"utils/Url\";\nimport internalFs from \"./internalFs\";\n\nclass SftpClient {\n\t#MAX_TRY = 3;\n\t#hostname;\n\t#port;\n\t#username;\n\t#authenticationType;\n\t#password;\n\t#keyFile;\n\t#passPhrase;\n\t#base;\n\t#connectionID;\n\t#path;\n\t#stat;\n\t#retry = 0;\n\n\t/**\n\t *\n\t * @param {String} hostname\n\t * @param {Number} port\n\t * @param {String} username\n\t * @param {{password?: String, passPhrase?: String, keyFile?: String}} authentication\n\t */\n\tconstructor(hostname, port = 22, username, authentication) {\n\t\tthis.#hostname = hostname;\n\t\tthis.#port = port;\n\t\tthis.#username = username;\n\t\tthis.#authenticationType = !!authentication.keyFile ? \"key\" : \"password\";\n\t\tthis.#keyFile = authentication.keyFile;\n\t\tthis.#passPhrase = authentication.passPhrase;\n\t\tthis.#password = authentication.password;\n\t\tthis.#base = Url.formate({\n\t\t\tprotocol: \"sftp:\",\n\t\t\thostname: this.#hostname,\n\t\t\tport: this.#port,\n\t\t\tusername: this.#username,\n\t\t\tpassword: this.#password,\n\t\t\tquery: {\n\t\t\t\tpassPhrase: this.#passPhrase,\n\t\t\t\tkeyFile: this.#keyFile,\n\t\t\t},\n\t\t});\n\n\t\tthis.#connectionID = `${this.#username}@${this.#hostname}`;\n\t}\n\n\tsetPath(path) {\n\t\tthis.#path = path;\n\t}\n\n\t/**\n\t * List directory or get file info\n\t * @param {String} filename\n\t * @param {boolean} stat\n\t */\n\tlsDir(filename = this.#path) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected(async (connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst path = this.#safeName(filename);\n\n\t\t\t\t\tsftp.lsDir(\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\t(res) => {\n\t\t\t\t\t\t\tres.forEach((file) => {\n\t\t\t\t\t\t\t\tfile.url = Url.join(this.#base, file.url);\n\t\t\t\t\t\t\t\tfile.type = mimeType.lookup(filename);\n\t\t\t\t\t\t\t\tif (file.isLink) {\n\t\t\t\t\t\t\t\t\tfile.linkTarget = Url.join(this.#base, file.linkTarget);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tresolve(res);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\t/**\n\t *\n\t * @param {String} filename\n\t * @param {String} content\n\t */\n\tcreateFile(filename, content) {\n\t\tfilename = Path.join(this.#path, filename);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsftp.createFile(\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tcontent ? content : \"\",\n\t\t\t\t\t\tasync (_res) => {\n\t\t\t\t\t\t\tresolve(Url.join(this.#base, filename));\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t *\n\t * @param {String} dirname\n\t */\n\tcreateDir(dirname) {\n\t\tdirname = Path.join(this.#path, dirname);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tsftp.mkdir(\n\t\t\t\t\t\tthis.#safeName(dirname),\n\t\t\t\t\t\tasync (_res) => {\n\t\t\t\t\t\t\tresolve(Url.join(this.#base, this.#safeName(dirname)));\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Write to a file on server\n\t * @param {String|ArrayBuffer} content\n\t * @param {String} remotefile\n\t */\n\twriteFile(content, remotefile) {\n\t\tconst filename = remotefile || this.#path;\n\t\tconst localFilename = this.#getLocalname(filename);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tawait internalFs.writeFile(localFilename, content, true, false);\n\t\t\t\t\t\tconst remoteFile = this.#safeName(filename);\n\t\t\t\t\t\tsftp.putFile(remoteFile, localFilename, resolve, reject);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\t/**\n\t * Read the file from server\n\t */\n\treadFile() {\n\t\tconst filename = this.#path;\n\t\tconst localFilename = this.#getLocalname(filename);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tsftp.getFile(\n\t\t\t\t\t\tthis.#safeName(filename),\n\t\t\t\t\t\tlocalFilename,\n\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst data = await internalFs.readFile(localFilename);\n\t\t\t\t\t\t\t\tresolve(data);\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t});\n\t\t});\n\t}\n\n\tasync copyTo(dest) {\n\t\tconst src = this.#path;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst srcStat = await this.stat();\n\n\t\t\t\t\t\tif (srcStat.isDirectory) {\n\t\t\t\t\t\t\tawait this.#copyDirectory(src, dest);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tawait this.#copyFile(src, dest);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst finalPath = Path.join(dest, Path.basename(src));\n\t\t\t\t\t\tresolve(Url.join(this.#base, finalPath));\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\tasync #copyFile(src, dest) {\n\t\tconst destPath = Path.join(dest, Path.basename(src));\n\t\tconst tempFile = this.#getLocalname(src);\n\n\t\t// Download source file\n\t\tawait new Promise((resolve, reject) => {\n\t\t\tsftp.getFile(this.#safeName(src), tempFile, resolve, reject);\n\t\t});\n\n\t\t// Upload\n\t\tawait new Promise((resolve, reject) => {\n\t\t\tsftp.putFile(this.#safeName(destPath), tempFile, resolve, reject);\n\t\t});\n\n\t\t// Clean up temp file\n\t\ttry {\n\t\t\tawait internalFs.delete(tempFile);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to cleanup temp file:\", error);\n\t\t}\n\t}\n\n\tasync #copyDirectory(src, dest) {\n\t\t// Create destination directory\n\t\tconst destDir = Path.join(dest, Path.basename(src));\n\t\tawait new Promise((resolve, reject) => {\n\t\t\tsftp.mkdir(this.#safeName(destDir), resolve, reject);\n\t\t});\n\n\t\t// Get contents of source directory\n\t\tconst contents = await this.lsDir(src);\n\n\t\t// Copy all items\n\t\tfor (const item of contents) {\n\t\t\tconst itemSrc = Path.join(src, item.name);\n\t\t\tif (item.isDirectory) {\n\t\t\t\tawait this.#copyDirectory(itemSrc, destDir);\n\t\t\t} else {\n\t\t\t\tawait this.#copyFile(itemSrc, destDir);\n\t\t\t}\n\t\t}\n\t}\n\n\tmoveTo(dest) {\n\t\treturn this.rename(dest, true);\n\t}\n\n\t/**\n\t * Renames file and directory, it can also be use to move directory or file\n\t * @param {String} newname\n\t * @param {Boolean} move\n\t */\n\trename(newname, move) {\n\t\tconst src = this.#path;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tnewname = move ? newname : Path.join(Path.dirname(src), newname);\n\t\t\t\t\tsftp.rename(\n\t\t\t\t\t\tthis.#safeName(src),\n\t\t\t\t\t\tthis.#safeName(newname),\n\t\t\t\t\t\tasync (_res) => {\n\t\t\t\t\t\t\tconst url = move ? Url.join(newname, Url.basename(src)) : newname;\n\t\t\t\t\t\t\tresolve(Url.join(this.#base, url));\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\t/**\n\t * Delete file or directory\n\t */\n\tdelete() {\n\t\tconst filename = this.#path;\n\t\tconst fullFilename = Url.join(this.#base, filename);\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tawait this.#setStat();\n\t\t\t\t\tsftp.rm(\n\t\t\t\t\t\tthis.#safeName(filename),\n\t\t\t\t\t\tthis.#stat.isDirectory ? true : false,\n\t\t\t\t\t\tthis.#stat.isDirectory ? true : false,\n\t\t\t\t\t\t(_res) => {\n\t\t\t\t\t\t\tresolve(fullFilename);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\tpwd() {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected((connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tsftp.pwd(\n\t\t\t\t\t\t(res) => {\n\t\t\t\t\t\t\tresolve(res);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\tasync connect() {\n\t\tawait new Promise((resolve, reject) => {\n\t\t\tconst retry = (err) => {\n\t\t\t\tif (settings.value.retryRemoteFsAfterFail) {\n\t\t\t\t\tif (++this.#retry > this.#MAX_TRY) {\n\t\t\t\t\t\tthis.#retry = 0;\n\t\t\t\t\t\treject(err);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.connect().then(resolve).catch(reject);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treject(err);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (this.#authenticationType === \"key\") {\n\t\t\t\tsftp.connectUsingKeyFile(\n\t\t\t\t\tthis.#hostname,\n\t\t\t\t\tthis.#port,\n\t\t\t\t\tthis.#username,\n\t\t\t\t\tthis.#keyFile,\n\t\t\t\t\tthis.#passPhrase,\n\t\t\t\t\tresolve,\n\t\t\t\t\tretry,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsftp.connectUsingPassword(\n\t\t\t\tthis.#hostname,\n\t\t\t\tthis.#port,\n\t\t\t\tthis.#username,\n\t\t\t\tthis.#password,\n\t\t\t\tresolve,\n\t\t\t\tretry,\n\t\t\t);\n\t\t});\n\t}\n\n\tasync exists() {\n\t\treturn (await this.stat()).exists;\n\t}\n\n\tasync stat() {\n\t\tif (this.#stat) return this.#stat;\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsftp.isConnected(async (connectionID) => {\n\t\t\t\t(async () => {\n\t\t\t\t\tif (this.#notConnected(connectionID)) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait this.connect();\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\treject(error);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst path = this.#safeName(this.#path);\n\n\t\t\t\t\tsftp.stat(\n\t\t\t\t\t\tpath,\n\t\t\t\t\t\t(res) => {\n\t\t\t\t\t\t\tres.url = Url.join(this.#base, res.url);\n\t\t\t\t\t\t\tres.type = mimeType.lookup(path);\n\t\t\t\t\t\t\tif (res.isLink) {\n\t\t\t\t\t\t\t\tres.linkTarget = Url.join(this.#base, res.linkTarget);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\thelpers.defineDeprecatedProperty(\n\t\t\t\t\t\t\t\tres,\n\t\t\t\t\t\t\t\t\"uri\",\n\t\t\t\t\t\t\t\tfunction () {\n\t\t\t\t\t\t\t\t\treturn this.url;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tfunction (val) {\n\t\t\t\t\t\t\t\t\tthis.url = val;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tresolve(res);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t})();\n\t\t\t}, reject);\n\t\t});\n\t}\n\n\tget localName() {\n\t\treturn this.#getLocalname(this.#path);\n\t}\n\n\t/**\n\t *\n\t * @param {String} name\n\t */\n\t#safeName(name) {\n\t\tconst escapeCh = (str) => str.replace(/\\\\([\\s\\S])|([`\"])/g, \"\\\\$1$2\");\n\t\tconst ar = name.split(\"/\");\n\t\treturn ar.map((dirname) => escapeCh(dirname)).join(\"/\");\n\t}\n\n\t// #errorCodes(code, defaultMessage = strings[\"an error occurred\"]) {\n\t// \tswitch (code) {\n\t// \t\tcase 0:\n\t// \t\t\treturn strings[\"success\"];\n\t// \t\tcase 1:\n\t// \t\t\treturn strings[\"operation not permitted\"];\n\t// \t\tcase 2:\n\t// \t\t\treturn strings[\"no such file or directory\"];\n\t// \t\tcase 5:\n\t// \t\t\treturn strings[\"input/output error\"];\n\t// \t\tcase 13:\n\t// \t\t\treturn strings[\"permission denied\"];\n\t// \t\tcase 14:\n\t// \t\t\treturn strings[\"bad address\"];\n\t// \t\tcase 17:\n\t// \t\t\treturn strings[\"file exists\"];\n\t// \t\tcase 20:\n\t// \t\t\treturn strings[\"not a directory\"];\n\t// \t\tcase 21:\n\t// \t\t\treturn strings[\"is a directory\"];\n\t// \t\tcase 22:\n\t// \t\t\treturn strings[\"invalid argument\"];\n\t// \t\tcase 23:\n\t// \t\t\treturn strings[\"too many open files in system\"];\n\t// \t\tcase 24:\n\t// \t\t\treturn strings[\"too many open files\"];\n\t// \t\tcase 26:\n\t// \t\t\treturn strings[\"text file busy\"];\n\t// \t\tcase 27:\n\t// \t\t\treturn strings[\"file too large\"];\n\t// \t\tcase 28:\n\t// \t\t\treturn strings[\"no space left on device\"];\n\t// \t\tcase 30:\n\t// \t\t\treturn strings[\"read-only file system\"];\n\t// \t\tcase 37:\n\t// \t\t\treturn strings[\"too many users\"];\n\t// \t\tcase 110:\n\t// \t\t\treturn strings[\"connection timed out\"];\n\t// \t\tcase 111:\n\t// \t\t\treturn strings[\"connection refused\"];\n\t// \t\tcase 130:\n\t// \t\t\treturn strings[\"owner died\"];\n\n\t// \t\tdefault:\n\t// \t\t\treturn defaultMessage;\n\t// \t}\n\t// }\n\n\t/**\n\t *\n\t * @param {String} connectionID\n\t * @returns {Boolean}\n\t */\n\t#notConnected(connectionID) {\n\t\treturn !connectionID || connectionID !== this.#connectionID;\n\t}\n\n\t/**\n\t *\n\t * @param {String} filename\n\t * @returns {String}\n\t */\n\t#getLocalname(filename) {\n\t\treturn Url.join(\n\t\t\tCACHE_STORAGE,\n\t\t\t\"sftp\" + Url.join(this.#base, filename).hashCode(),\n\t\t);\n\t}\n\n\tasync #setStat() {\n\t\tif (!this.#stat) {\n\t\t\tthis.#stat = await this.stat();\n\t\t}\n\t}\n}\n\n/**\n *\n * @param {String} host\n * @param {Number} port\n * @param {String} username\n * @param {{password?: String, passPhrase?: String, keyFile?: String}} authentication\n */\nfunction Sftp(host, port, username, authentication) {\n\treturn new SftpClient(host, port, username, authentication);\n}\n\nSftp.fromUrl = (url) => {\n\tconst { username, password, hostname, pathname, port, query } =\n\t\tUrl.decodeUrl(url);\n\tconst { keyFile, passPhrase } = query;\n\n\tconst sftp = new SftpClient(hostname, port || 22, username, {\n\t\tpassword,\n\t\tkeyFile,\n\t\tpassPhrase,\n\t});\n\n\tsftp.setPath(pathname);\n\treturn createFs(sftp);\n};\n\nSftp.test = (url) => /^sftp:/.test(url);\n\n/**\n *\n * @param {SftpClient} sftp\n */\nfunction createFs(sftp) {\n\treturn {\n\t\tlsDir() {\n\t\t\treturn sftp.lsDir();\n\t\t},\n\t\tasync readFile(encoding) {\n\t\t\tconst { data } = await sftp.readFile();\n\n\t\t\tif (encoding) {\n\t\t\t\treturn decode(data, encoding);\n\t\t\t}\n\n\t\t\treturn data;\n\t\t},\n\t\tasync writeFile(content, encoding) {\n\t\t\tif (typeof content === \"string\" && encoding) {\n\t\t\t\tcontent = await encode(content, encoding);\n\t\t\t}\n\n\t\t\treturn sftp.writeFile(content, null);\n\t\t},\n\t\tcreateFile(name, data) {\n\t\t\treturn sftp.createFile(name, data);\n\t\t},\n\t\tcreateDirectory(name) {\n\t\t\treturn sftp.createDir(name);\n\t\t},\n\t\tdelete() {\n\t\t\treturn sftp.delete();\n\t\t},\n\t\tcopyTo(dest) {\n\t\t\tdest = Url.pathname(dest);\n\t\t\treturn sftp.copyTo(dest);\n\t\t},\n\t\tmoveTo(dest) {\n\t\t\tdest = Url.pathname(dest);\n\t\t\treturn sftp.moveTo(dest);\n\t\t},\n\t\trenameTo(newname) {\n\t\t\treturn sftp.rename(newname);\n\t\t},\n\t\texists() {\n\t\t\treturn sftp.exists();\n\t\t},\n\t\tstat() {\n\t\t\treturn sftp.stat();\n\t\t},\n\t\tget localName() {\n\t\t\treturn sftp.localName;\n\t\t},\n\t};\n}\n\nexport default Sftp;\n"
  },
  {
    "path": "src/handlers/editorFileTab.js",
    "content": "import constants from \"lib/constants\";\nimport settings from \"lib/settings\";\n\nconst opts = { passive: false };\n\n/**\n * Clone of tab being dragged\n * @type {HTMLDivElement}\n */\nlet $tabClone = null;\n/**\n * Selected tab element\n * @type {HTMLDivElement}\n */\nlet $tab = null;\n/**\n * Tab container element\n * @type {HTMLDivElement}\n */\nlet $parent = null;\n\nlet MAX_SCROLL = 0;\nlet MIN_SCROLL = 0;\n\n/**\n * Cached tab top position to avoid dom access\n * @type {number}\n */\nlet tabTop = 0;\n/**\n * Cached tab left position to avoid dom access\n * @type {number}\n */\nlet tabLeft = 0;\n/**\n * Stores the offset of tab from pointer\n * @type {number}\n */\nlet offsetX = 0;\n/**\n * Stores the offset of tab from pointer\n * @type {number}\n */\nlet offsetY = 0;\n/**\n * Caches the width of tab to avoid dom access\n * @type {number}\n */\nlet tabWidth = 0;\n/**\n * Caches the left position of parent to avoid dom access\n * @type {number}\n */\nlet parentLeft = 0;\n/**\n * Caches the right position of parent to avoid dom access\n * @type {number}\n */\nlet parentRight = 0;\n/**\n * Animation frame id\n * @type {number}\n */\nlet animationFrame = null;\n/**\n * @type {number}\n */\nlet prevScrollLeft = 0;\n\nconst MAX_SCROLL_SPEED = 4;\n\n/**\n * Handles file drag\n * @param {MouseEvent} e\n */\nexport default function startDrag(e) {\n\tconst { clientX, clientY } = getClientPos(e);\n\tconst { editor, activeFile } = editorManager;\n\n\tif (activeFile.focusedBefore) {\n\t\teditor.focus();\n\t}\n\n\tif (settings.value.vibrateOnTap) {\n\t\tnavigator.vibrate(constants.VIBRATION_TIME);\n\t}\n\n\t$tab = e.target;\n\t$parent = $tab.parentElement;\n\t$tabClone = $tab.cloneNode(true);\n\n\tconst rect = $tab.getBoundingClientRect();\n\tconst parentRect = $parent.getBoundingClientRect();\n\n\t/**\n\t * Setting offset of tab from pointer\n\t * this is used to set the position of tab when dragging\n\t * so tab moves with pointer but not snapped to top left corner\n\t * of the tab because setting translate will move the tab to\n\t * clientX, clientY position, it's like virtual transform origin.\n\t *\n\t * (rect.x, rect.y) is the position of the tab\n\t *     __________________\n\t *    |    * (pointer)   | clientY - rect.y\n\t *    |__________________|\n\t *    <----> clientX - rect.x\n\t */\n\toffsetX = clientX - rect.x;\n\toffsetY = clientY - rect.y;\n\n\ttabLeft = rect.left;\n\ttabWidth = rect.width;\n\tparentLeft = parentRect.left;\n\tparentRight = parentRect.right;\n\n\tMAX_SCROLL = $parent.scrollWidth - parentRect.width;\n\tMIN_SCROLL = 0;\n\n\t// setup the cloned tab\n\t$tabClone.classList.add(\"drag\");\n\t$tabClone.style.height = `${rect.height}px`;\n\t$tabClone.style.width = `${rect.width}px`;\n\t$tabClone.style.transform = `translate3d(${rect.x}px, ${rect.y}px, 0)`;\n\tapp.append($tabClone);\n\t$tab.click();\n\n\tdocument.addEventListener(\"mousemove\", onDrag, opts);\n\tdocument.addEventListener(\"touchmove\", onDrag, opts);\n\tdocument.addEventListener(\"mouseup\", releaseDrag, opts);\n\tdocument.addEventListener(\"touchend\", releaseDrag, opts);\n\tdocument.addEventListener(\"touchcancel\", releaseDrag, opts);\n\tdocument.addEventListener(\"mouseleave\", releaseDrag, opts);\n\n\tprevScrollLeft = $parent.scrollLeft;\n\t$parent.addEventListener(\"scroll\", preventDefaultScroll, opts);\n}\n\n/**\n * On mouse or touch move\n * @param {MouseEvent|TouchEvent} e\n * @returns\n */\nfunction onDrag(e) {\n\tif (e instanceof Event) {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\te.stopImmediatePropagation();\n\t}\n\n\tconst { clientX, clientY } = getClientPos(e);\n\n\ttabLeft = clientX - offsetX;\n\ttabTop = clientY - offsetY;\n\n\t$tabClone.style.transform = `translate3d(${tabLeft}px, ${tabTop}px, 0)`;\n\n\tif ($parent.scrollWidth === $parent.clientWidth) return;\n\n\tconst scroll = getScroll();\n\t// if can scroll and already scrolling return\n\t// or if can't scroll and not scrolling return\n\tif (!!scroll === !!animationFrame) return;\n\t// if can't scroll and scrolling clear interval\n\tif (!scroll && animationFrame) {\n\t\tcancelAnimationFrame(animationFrame);\n\t\tanimationFrame = null;\n\t\treturn;\n\t}\n\n\tscrollContainer();\n}\n\n/**\n * Cancels the drag\n * @param {MouseEvent} e\n */\nfunction releaseDrag(e) {\n\tconst { clientX, clientY } = getClientPos(e);\n\n\t/**@type {HTMLDivElement} target tab */\n\tconst $target = document.elementFromPoint(clientX, clientY);\n\n\tif (\n\t\t$parent.contains($target) && // target is in parent\n\t\t$target !== $tab && // target is not the tab\n\t\t!$tab.contains($target) // target is not a child of tab\n\t) {\n\t\t// get the target tab, if target is a child it will get the parent\n\t\tconst $targetTab = $target.closest(\".tile\");\n\n\t\tif ($targetTab) {\n\t\t\tconst rect = $targetTab.getBoundingClientRect();\n\t\t\tconst midX = rect.left + rect.width / 2;\n\t\t\tconst pointerX = tabLeft + tabWidth / 2;\n\t\t\tif (midX < pointerX) {\n\t\t\t\t// move right\n\t\t\t\tconst $nextSibling = $targetTab.nextElementSibling;\n\t\t\t\tif ($nextSibling) {\n\t\t\t\t\t$parent.insertBefore($tab, $nextSibling);\n\t\t\t\t} else {\n\t\t\t\t\t$parent.appendChild($tab);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$parent.insertBefore($tab, $targetTab);\n\t\t\t}\n\t\t\tupdateFileList($parent);\n\t\t}\n\t} else if (\n\t\t$target.tagName === \"INPUT\" ||\n\t\t$target.tagName === \"TEXTAREA\" ||\n\t\t$target.isContentEditable ||\n\t\t$target.closest(\".cm-editor\")\n\t) {\n\t\t// If released on an input area or CodeMirror editor\n\t\tconst filePath = editorManager.activeFile.uri;\n\t\tif (filePath) {\n\t\t\tif ($target.closest(\".cm-editor\")) {\n\t\t\t\tconst view = editorManager.editor;\n\t\t\t\tview.dispatch(view.state.replaceSelection(filePath));\n\t\t\t} else {\n\t\t\t\t$target.value += filePath;\n\t\t\t}\n\t\t}\n\t}\n\n\tcancelAnimationFrame(animationFrame);\n\t$tabClone.remove();\n\t$tabClone = null;\n\n\tdocument.removeEventListener(\"mousemove\", onDrag, opts);\n\tdocument.removeEventListener(\"touchmove\", onDrag, opts);\n\tdocument.removeEventListener(\"mouseup\", releaseDrag, opts);\n\tdocument.removeEventListener(\"touchend\", releaseDrag, opts);\n\tdocument.removeEventListener(\"touchcancel\", releaseDrag, opts);\n\tdocument.removeEventListener(\"mouseleave\", releaseDrag, opts);\n\n\t$parent.removeEventListener(\"scroll\", preventDefaultScroll);\n}\n\nfunction preventDefaultScroll() {\n\tthis.scrollLeft = prevScrollLeft;\n}\n\n/**\n * Scrolls the container using animation frame\n */\nfunction scrollContainer() {\n\treturn animate();\n\n\tfunction animate() {\n\t\tconst scroll = getScroll();\n\t\tif (!scroll) return;\n\t\tprevScrollLeft = $parent.scrollLeft += scroll;\n\t\tanimationFrame = requestAnimationFrame(animate);\n\t}\n}\n\n/**\n * Gets the client position from the event\n * @param {MouseEvent & TouchEvent} e\n * @returns {MouseEvent}\n */\nfunction getClientPos(e) {\n\tconst { touches, changedTouches } = e;\n\n\tlet { clientX = 0, clientY = 0 } = e;\n\n\tif (touches?.length) {\n\t\tconst [touch] = touches;\n\t\tclientX = touch.clientX;\n\t\tclientY = touch.clientY;\n\t} else if (changedTouches?.length) {\n\t\tconst [touch] = changedTouches;\n\t\tclientX = touch.clientX;\n\t\tclientY = touch.clientY;\n\t}\n\n\treturn { clientX, clientY };\n}\n\n/**\n * Update the position of the file list\n * @param {HTMLElement} $parent\n */\nfunction updateFileList($parent) {\n\tconst pinnedCount = editorManager.files.filter((file) => file.pinned).length;\n\tconst children = [...$parent.children];\n\tconst newFileList = [];\n\tfor (let el of children) {\n\t\tfor (let file of editorManager.files) {\n\t\t\tif (file.tab === el) {\n\t\t\t\tnewFileList.push(file);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\teditorManager.files = newFileList;\n\n\tconst draggedFile = newFileList.find((file) => file.tab === $tab);\n\tif (draggedFile) {\n\t\tconst draggedIndex = newFileList.indexOf(draggedFile);\n\t\tlet nextPinnedState;\n\n\t\tif (!draggedFile.pinned && draggedIndex < pinnedCount) {\n\t\t\tnextPinnedState = true;\n\t\t} else if (draggedFile.pinned && draggedIndex >= pinnedCount) {\n\t\t\tnextPinnedState = false;\n\t\t}\n\n\t\tif (nextPinnedState !== undefined) {\n\t\t\tdraggedFile.setPinnedState(nextPinnedState, { reorder: false });\n\t\t\tif (typeof editorManager.normalizePinnedTabOrder === \"function\") {\n\t\t\t\teditorManager.normalizePinnedTabOrder(editorManager.files);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Checks if the tab is going to scroll and returns the scroll value\n */\nfunction getScroll() {\n\tconst tabRight = tabLeft + tabWidth;\n\tconst scrollX = $parent.scrollLeft;\n\n\t/**@type {number} scroll value */\n\tlet scroll = 0;\n\n\t// tab right should be greater than parent right\n\tconst rightDiff = tabRight - parentRight;\n\t// tab left should be less than parent left\n\tconst leftDiff = parentLeft - tabLeft;\n\n\tconst scrollSpeed = (diff) => {\n\t\tconst ratio = diff / tabWidth;\n\t\treturn ratio * MAX_SCROLL_SPEED;\n\t};\n\n\tif (leftDiff > 0 && scrollX > MIN_SCROLL) {\n\t\tscroll = -scrollSpeed(leftDiff);\n\t} else if (rightDiff > 0 && scrollX < MAX_SCROLL) {\n\t\tscroll = scrollSpeed(rightDiff);\n\t}\n\n\treturn scroll;\n}\n"
  },
  {
    "path": "src/handlers/intent.js",
    "content": "import fsOperation from \"fileSystem\";\nimport openFile from \"lib/openFile\";\nimport helpers from \"utils/helpers\";\n\nconst handlers = [];\n/**\n * Queue to store intents that arrive before files are restored\n * @type {Array<{url: string, options: object}>}\n */\nconst pendingIntents = [];\n\n/**\n *\n * @param {Intent} intent\n */\nexport default async function HandleIntent(intent = {}) {\n\tconst type = intent.action.split(\".\").slice(-1)[0];\n\n\tif ([\"SEND\", \"VIEW\", \"EDIT\"].includes(type)) {\n\t\t/**@type {string} */\n\t\tconst url = intent.fileUri || intent.data;\n\t\tif (!url) return;\n\n\t\tif (url.startsWith(\"acode://\")) {\n\t\t\tconst path = url.replace(\"acode://\", \"\");\n\t\t\tconst [module, action, value] = path.split(\"/\");\n\n\t\t\tlet defaultPrevented = false;\n\t\t\tconst event = new IntentEvent(module, action, value);\n\t\t\tfor (const handler of handlers) {\n\t\t\t\thandler(event);\n\t\t\t\tif (event.defaultPrevented) defaultPrevented = true;\n\t\t\t\tif (event.propagationStopped) break;\n\t\t\t}\n\n\t\t\tif (defaultPrevented) return;\n\n\t\t\tif (module === \"plugin\") {\n\t\t\t\tconst { default: Plugin } = await import(\"pages/plugin\");\n\t\t\t\tconst installed = await fsOperation(PLUGIN_DIR, value).exists();\n\t\t\t\tPlugin({ id: value, installed, install: action === \"install\" });\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (sessionStorage.getItem(\"isfilesRestored\") === \"true\") {\n\t\t\tawait openFile(url, {\n\t\t\t\tmode: \"single\",\n\t\t\t\trender: true,\n\t\t\t});\n\t\t} else {\n\t\t\t// Store the intent for later processing when files are restored\n\t\t\tpendingIntents.push({\n\t\t\t\turl,\n\t\t\t\toptions: {\n\t\t\t\t\tmode: \"single\",\n\t\t\t\t\trender: true,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n}\n\nHandleIntent.onError = (error) => {\n\thelpers.error(error);\n};\n\nexport function addIntentHandler(handler) {\n\thandlers.push(handler);\n}\n\nexport function removeIntentHandler(handler) {\n\tconst index = handlers.indexOf(handler);\n\tif (index > -1) handlers.splice(index, 1);\n}\n\n/**\n * Process all pending intents that were queued before files were restored.\n * This function is called after isfilesRestored is set to true in main.js.\n * @returns {Promise<void>}\n */\nexport async function processPendingIntents() {\n\tif (sessionStorage.getItem(\"isfilesRestored\") !== \"true\") return;\n\n\t// Process all pending intents\n\twhile (pendingIntents.length > 0) {\n\t\tconst pendingIntent = pendingIntents.shift();\n\t\ttry {\n\t\t\tawait openFile(pendingIntent.url, pendingIntent.options);\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t}\n\t}\n}\n\nclass IntentEvent {\n\tmodule;\n\taction;\n\tvalue;\n\n\t#defaultPrevented = false;\n\t#propagationStopped = false;\n\n\t/**\n\t * Creates an instance of IntentEvent.\n\t * @param {string} module\n\t * @param {string} action\n\t * @param {string} value\n\t */\n\tconstructor(module, action, value) {\n\t\tthis.module = module;\n\t\tthis.action = action;\n\t\tthis.value = value;\n\t}\n\n\tpreventDefault() {\n\t\tthis.#defaultPrevented = true;\n\t}\n\n\tstopPropagation() {\n\t\tthis.#propagationStopped = true;\n\t}\n\n\tget defaultPrevented() {\n\t\treturn this.#defaultPrevented;\n\t}\n\n\tget propagationStopped() {\n\t\treturn this.#propagationStopped;\n\t}\n}\n"
  },
  {
    "path": "src/handlers/keyboard.js",
    "content": "import {\n\tgetSystemConfiguration,\n\tHARDKEYBOARDHIDDEN_NO,\n} from \"lib/systemConfiguration\";\nimport KeyboardEvent from \"utils/keyboardEvent\";\nimport windowResize from \"./windowResize\";\n\n/**\n * Keyboard event list\n * @typedef {'key'|'keyboardShow'|'keyboardHide'|'keyboardShowStart'|'keyboardHideStart'} KeyboardEventName\n */\n\n// Assuming that keyboard height is at least 200px\nlet MIN_KEYBOARD_HEIGHT = 100;\nconst event = {\n\tkey: [],\n\tkeyboardShow: [],\n\tkeyboardHide: [],\n\tkeyboardShowStart: [],\n\tkeyboardHideStart: [],\n};\n\nlet escKey = false;\nlet escResetTimeout = null;\nlet softKeyboardHeight = 0;\nlet windowHeight = window.innerHeight;\nlet currentWindowHeight = windowHeight;\n\nexport const keydownState = {\n\t/**\n\t * Get esc key state\n\t * @returns {boolean}\n\t */\n\tget esc() {\n\t\treturn escKey;\n\t},\n\t/**\n\t * Set esc key state\n\t * @param {boolean} val\n\t */\n\tset esc(val) {\n\t\tescKey = val;\n\t\tif (!val) return;\n\t\tclearTimeout(escResetTimeout);\n\t\tescResetTimeout = setTimeout(() => {\n\t\t\tescKey = false;\n\t\t}, 500);\n\t},\n};\n\n/**\n * Handles keyboard events\n * @param {KeyboardEvent} e\n */\nexport default function keyboardHandler(e) {\n\tconst $target = e.target;\n\tconst { key, ctrlKey, shiftKey, altKey, metaKey } = e;\n\n\tif (shouldIgnoreEditorShortcutTarget($target)) {\n\t\tkeydownState.esc = key === \"Escape\";\n\t\treturn;\n\t}\n\n\tif (!ctrlKey && !shiftKey && !altKey && !metaKey) return;\n\tif ([\"Control\", \"Alt\", \"Meta\", \"Shift\"].includes(key)) return;\n\n\tconst target = editorManager?.editor?.contentDOM;\n\tif (!target) return;\n\n\t// Physical keyboard events already reaching CodeMirror should not be\n\t// re-dispatched from the document listener.\n\tif ($target === target || (target.contains?.($target) ?? false)) return;\n\n\tconst event = KeyboardEvent(\"keydown\", {\n\t\tkey,\n\t\tctrlKey,\n\t\tshiftKey,\n\t\taltKey,\n\t\tmetaKey,\n\t});\n\ttarget?.dispatchEvent?.(event);\n}\n\n/**\n * Returns true when a keyboard event target should keep the shortcut local\n * instead of forwarding it into the editor.\n * @param {EventTarget | null} target\n * @returns {boolean}\n */\nfunction shouldIgnoreEditorShortcutTarget(target) {\n\tif (!(target instanceof Element)) return false;\n\n\treturn (\n\t\ttarget instanceof HTMLInputElement ||\n\t\ttarget instanceof HTMLTextAreaElement ||\n\t\ttarget instanceof HTMLSelectElement ||\n\t\ttarget.isContentEditable ||\n\t\t!!target.closest(\".prompt, #palette\")\n\t);\n}\n\ndocument.addEventListener(\"admob.banner.size\", async (event) => {\n\tconst { height } = event.size;\n\tMIN_KEYBOARD_HEIGHT = height + 10;\n});\n\nwindowResize.on(\"resizeStart\", async () => {\n\tconst { keyboardHeight, hardKeyboardHidden } = await getSystemConfiguration();\n\tconst externalKeyboard = hardKeyboardHidden === HARDKEYBOARDHIDDEN_NO;\n\n\tif (currentWindowHeight > window.innerHeight) {\n\t\t// height decreasing\n\t\tsoftKeyboardHeight =\n\t\t\tkeyboardHeight > MIN_KEYBOARD_HEIGHT ? keyboardHeight : 0;\n\t\tif (!externalKeyboard && softKeyboardHeight) {\n\t\t\temit(\"keyboardShowStart\");\n\t\t}\n\t} else if (currentWindowHeight < window.innerHeight) {\n\t\t// height increasing\n\t\tif (!externalKeyboard && softKeyboardHeight) {\n\t\t\temit(\"keyboardHideStart\");\n\t\t}\n\t}\n\n\tcurrentWindowHeight = window.innerHeight;\n});\n\nwindowResize.on(\"resize\", async () => {\n\tcurrentWindowHeight = window.innerHeight;\n\n\tif (currentWindowHeight > windowHeight) {\n\t\twindowHeight = currentWindowHeight;\n\t}\n\n\tconst { hardKeyboardHidden } = await getSystemConfiguration();\n\tconst externalKeyboard = hardKeyboardHidden === HARDKEYBOARDHIDDEN_NO;\n\n\tif (externalKeyboard || !softKeyboardHeight) return;\n\n\tconst keyboardHiddenYes = windowHeight <= window.innerHeight;\n\n\tif (keyboardHiddenYes) {\n\t\temit(\"keyboardHide\");\n\t} else {\n\t\temit(\"keyboardShow\");\n\t}\n\n\tfocusBlurEditor(keyboardHiddenYes);\n\tshowHideAd(keyboardHiddenYes);\n});\n\n/**\n * Add event listener for keyboard event.\n * @param {KeyboardEventName} eventName\n * @param {Function} callback\n * @returns\n */\nkeyboardHandler.on = (eventName, callback) => {\n\tif (!event[eventName]) return;\n\tevent[eventName].push(callback);\n};\n\n/**\n * Remove event listener for keyboard event.\n * @param {KeyboardEventName} eventName\n * @param {Function} callback\n * @returns\n */\nkeyboardHandler.off = (eventName, callback) => {\n\tif (!event[eventName]) return;\n\tevent[eventName] = event[eventName].filter((cb) => cb !== callback);\n};\n\n/**\n * Emit keyboard event.\n * @param {KeyboardEventName} eventName\n * @returns\n */\nfunction emit(eventName) {\n\tif (!event[eventName]) return;\n\tevent[eventName].forEach((cb) => cb());\n}\n\n/**\n * Focus the editor if keyboard is visible, blur it otherwise.\n * @param {boolean} keyboardHidden\n * @returns\n */\nfunction focusBlurEditor(keyboardHidden) {\n\tif (keyboardHidden) {\n\t\tdocument.activeElement?.blur();\n\t}\n}\n\n/**\n * Show ad if keyboard is hidden and ad is active, hide ad otherwise.\n * @param {boolean} keyboardHidden\n */\nfunction showHideAd(keyboardHidden) {\n\tconst bannerIsActive = !!window.ad?.active;\n\n\tif (!keyboardHidden && bannerIsActive) {\n\t\twindow.ad?.hide();\n\t} else if (bannerIsActive) {\n\t\twindow.ad?.show();\n\t}\n}\n"
  },
  {
    "path": "src/handlers/purchase.js",
    "content": "import helpers from \"utils/helpers\";\n\nexport default function purchaseListener(onpurchase, onerror) {\n\treturn [\n\t\t(purchases) => {\n\t\t\tconst [purchase] = purchases;\n\t\t\tif (purchase.purchaseState === iap.PURCHASE_STATE_PURCHASED) {\n\t\t\t\tif (!purchase.isAcknowledged) {\n\t\t\t\t\tiap.acknowledgePurchase(\n\t\t\t\t\t\tpurchase.purchaseToken,\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tonpurchase();\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(error) => {\n\t\t\t\t\t\t\tif (typeof onerror === \"function\") onerror(error);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tonpurchase();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst message =\n\t\t\t\tpurchase.purchaseState === iap.PURCHASE_STATE_PENDING\n\t\t\t\t\t? strings[\"purchase pending\"]\n\t\t\t\t\t: strings.failed;\n\n\t\t\thelpers.error(message);\n\t\t\tif (typeof onerror === \"function\") onerror(message);\n\t\t},\n\t\t(error) => {\n\t\t\tif (error === iap.ITEM_ALREADY_OWNED) {\n\t\t\t\tonpurchase();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet message =\n\t\t\t\terror === iap.USER_CANCELED ? strings.failed : strings.canceled;\n\n\t\t\tif (typeof onerror === \"function\") onerror(message);\n\t\t},\n\t];\n}\n"
  },
  {
    "path": "src/handlers/quickTools.js",
    "content": "import {\n\tfindNext as cmFindNext,\n\tfindPrevious as cmFindPrevious,\n\treplaceAll as cmReplaceAll,\n\treplaceNext as cmReplaceNext,\n\tgetSearchQuery,\n\tSearchQuery,\n\tsetSearchQuery,\n} from \"@codemirror/search\";\nimport { executeCommand } from \"cm/commandRegistry\";\nimport quickTools from \"components/quickTools\";\nimport actionStack from \"lib/actionStack\";\nimport searchHistory from \"lib/searchHistory\";\nimport appSettings from \"lib/settings\";\nimport searchSettings from \"settings/searchSettings\";\nimport KeyboardEvent from \"utils/keyboardEvent\";\n\n/**@type {HTMLInputElement | HTMLTextAreaElement} */\nlet input;\n\nconst state = {\n\tshift: false,\n\talt: false,\n\tctrl: false,\n\tmeta: false,\n};\n\nconst events = {\n\tshift: [],\n\talt: [],\n\tctrl: [],\n\tmeta: [],\n};\n\nfunction getRefValue(ref) {\n\tif (!ref) return \"\";\n\tconst direct = ref.value;\n\tif (typeof direct === \"string\") return direct;\n\tif (typeof direct === \"number\") return String(direct);\n\tif (ref.el) {\n\t\tconst elValue = ref.el.value;\n\t\tif (typeof elValue === \"string\") return elValue;\n\t\tif (typeof elValue === \"number\") return String(elValue);\n\t}\n\treturn \"\";\n}\n\nfunction setRefValue(ref, value) {\n\tif (!ref) return;\n\tconst normalized = typeof value === \"string\" ? value : String(value ?? \"\");\n\tif (ref.el) ref.el.value = normalized;\n\tref.value = normalized;\n}\n\nfunction applySearchQuery(editor, searchValue, replaceValue) {\n\tif (!editor) return null;\n\tconst options = appSettings?.value?.search ?? {};\n\tconst queryConfig = {\n\t\tsearch: String(searchValue ?? \"\"),\n\t\tcaseSensitive: !!options.caseSensitive,\n\t\tregexp: !!options.regExp,\n\t\twholeWord: !!options.wholeWord,\n\t};\n\tif (replaceValue !== undefined) {\n\t\tqueryConfig.replace = String(replaceValue ?? \"\");\n\t}\n\tconst query = new SearchQuery(queryConfig);\n\teditor.dispatch({ effects: setSearchQuery.of(query) });\n\treturn query;\n}\n\nfunction clearSearchQuery(editor) {\n\tif (!editor) return;\n\teditor.dispatch({\n\t\teffects: setSearchQuery.of(new SearchQuery({ search: \"\" })),\n\t});\n}\n\nfunction getSelectedText(editor) {\n\tif (!editor) return \"\";\n\tif (typeof editor.getSelectedText === \"function\") {\n\t\ttry {\n\t\t\treturn editor.getSelectedText() ?? \"\";\n\t\t} catch (_) {\n\t\t\t// fall back to CodeMirror state\n\t\t}\n\t}\n\ttry {\n\t\tconst { state } = editor;\n\t\tif (!state) return \"\";\n\t\tconst { from, to } = state.selection.main ?? {};\n\t\tif (typeof from !== \"number\" || typeof to !== \"number\") return \"\";\n\t\tif (from === to) return \"\";\n\t\treturn state.sliceDoc(from, to);\n\t} catch (_) {\n\t\treturn \"\";\n\t}\n}\n\nfunction selectionMatchesQuery(editor, query) {\n\ttry {\n\t\tif (!editor || !query || !query.valid || !query.search) return false;\n\t\tconst range = editor.state?.selection?.main;\n\t\tif (!range || range.from === range.to) return false;\n\t\tconst cursor = query.getCursor(editor.state.doc, range.from, range.to);\n\t\tcursor.next();\n\t\treturn (\n\t\t\t!cursor.done &&\n\t\t\tcursor.value.from === range.from &&\n\t\t\tcursor.value.to === range.to\n\t\t);\n\t} catch (_) {\n\t\treturn false;\n\t}\n}\n\n/**\n * @typedef { 'shift' | 'alt' | 'ctrl' | 'meta' } QuickToolsEvent\n * @typedef {(value: boolean)=>void} QuickToolsEventListener\n */\n\nquickTools.$input.addEventListener(\"input\", (e) => {\n\tconst key = e.target.value.toUpperCase();\n\tquickTools.$input.value = \"\";\n\tif (!key || key.length > 1) return;\n\tconst keyCombination = getKeys({ key });\n\n\tif (\n\t\tkeyCombination.shiftKey &&\n\t\t!keyCombination.ctrlKey &&\n\t\t!keyCombination.altKey &&\n\t\t!keyCombination.metaKey\n\t) {\n\t\tresetKeys();\n\t\tinsertText(shiftKeyMapping(key));\n\t\treturn;\n\t}\n\n\tconst event = KeyboardEvent(\"keydown\", keyCombination);\n\tinput = input || editorManager.editor.contentDOM;\n\n\tresetKeys();\n\tinput.dispatchEvent(event);\n});\n\nquickTools.$input.addEventListener(\"keydown\", (e) => {\n\tconst { keyCode, key, which } = e;\n\tconst keyCombination = getKeys({ keyCode, key, which });\n\n\tif (\n\t\t![\"ArrowRight\", \"ArrowLeft\", \"ArrowUp\", \"ArrowDown\"].includes(\n\t\t\tkeyCombination.key,\n\t\t)\n\t)\n\t\treturn;\n\te.preventDefault();\n\n\tconst event = KeyboardEvent(\"keydown\", keyCombination);\n\tif (input && input !== quickTools.$input) {\n\t\tinput.dispatchEvent(event);\n\t} else {\n\t\t// Otherwise fallback to editor view content\n\t\teditorManager.editor.contentDOM.dispatchEvent(event);\n\t}\n});\n\nappSettings.on(\"update:quicktoolsItems:after\", () => {\n\tsetTimeout(() => {\n\t\tif (actionStack.has(\"search-bar\")) return;\n\t\tconst { $footer, $row1, $row2 } = quickTools;\n\t\tconst height = getFooterHeight();\n\t\t$footer.content = [$row1, $row2].slice(0, height);\n\t}, 100);\n});\n\nlet historyNavigationInitialized = false;\n// Initialize history navigation\nfunction setupHistoryNavigation() {\n\tif (historyNavigationInitialized) return;\n\thistoryNavigationInitialized = true;\n\tconst { $searchInput, $replaceInput } = quickTools;\n\n\t// Search input history navigation\n\tif ($searchInput.el) {\n\t\t$searchInput.el.addEventListener(\"keydown\", (e) => {\n\t\t\tif ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === \"f\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst { editor, activeFile } = editorManager;\n\t\t\t\teditor.focus();\n\t\t\t\tactionStack.get(\"search-bar\")?.action();\n\t\t\t} else if (e.key === \"ArrowUp\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst newValue = searchHistory.navigateSearchUp($searchInput.el.value);\n\t\t\t\t$searchInput.el.value = newValue;\n\t\t\t\t// Trigger search\n\t\t\t\tfind(0, false);\n\t\t\t} else if (e.key === \"ArrowDown\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst newValue = searchHistory.navigateSearchDown(\n\t\t\t\t\t$searchInput.el.value,\n\t\t\t\t);\n\t\t\t\t$searchInput.el.value = newValue;\n\t\t\t\t// Trigger search\n\t\t\t\tfind(0, false);\n\t\t\t} else if (e.key === \"Enter\" || e.key === \"Escape\") {\n\t\t\t\t// Reset navigation on enter or escape\n\t\t\t\tsearchHistory.resetSearchNavigation();\n\t\t\t}\n\t\t});\n\n\t\t// Reset navigation when user starts typing\n\t\t$searchInput.el.addEventListener(\"input\", () => {\n\t\t\tsearchHistory.resetSearchNavigation();\n\t\t});\n\t}\n\n\t// Replace input history navigation\n\tif ($replaceInput.el) {\n\t\t$replaceInput.el.addEventListener(\"keydown\", (e) => {\n\t\t\tif (e.key === \"ArrowUp\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst newValue = searchHistory.navigateReplaceUp(\n\t\t\t\t\t$replaceInput.el.value,\n\t\t\t\t);\n\t\t\t\t$replaceInput.el.value = newValue;\n\t\t\t} else if (e.key === \"ArrowDown\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst newValue = searchHistory.navigateReplaceDown(\n\t\t\t\t\t$replaceInput.el.value,\n\t\t\t\t);\n\t\t\t\t$replaceInput.el.value = newValue;\n\t\t\t} else if (e.key === \"Enter\" || e.key === \"Escape\") {\n\t\t\t\t// Reset navigation on enter or escape\n\t\t\t\tsearchHistory.resetReplaceNavigation();\n\t\t\t}\n\t\t});\n\n\t\t// Reset navigation when user starts typing\n\t\t$replaceInput.el.addEventListener(\"input\", () => {\n\t\t\tsearchHistory.resetReplaceNavigation();\n\t\t});\n\t}\n}\n\nexport const key = {\n\tget shift() {\n\t\treturn state.shift;\n\t},\n\tget alt() {\n\t\treturn state.alt;\n\t},\n\tget ctrl() {\n\t\treturn state.ctrl;\n\t},\n\tget meta() {\n\t\treturn state.meta;\n\t},\n\t/**\n\t * Add listener when key changes\n\t * @param {QuickToolsEvent} event QuickTools event\n\t * @param {QuickToolsEventListener} callback Callback to call when key changes\n\t */\n\ton(event, callback) {\n\t\tevents[event].push(callback);\n\t},\n\t/**\n\t * Remove listener\n\t * @param {QuickToolsEvent} event QuickTools event\n\t * @param {QuickToolsEventListener} callback Callback to remove\n\t */\n\toff(event, callback) {\n\t\tevents[event] = events[event].filter((cb) => cb !== callback);\n\t},\n};\n\n/**\n * Performs quick actions\n * @param {string} action Action to perform\n * @param {string} value Value for the action\n * @returns {boolean} Whether the action was performed\n */\nexport default function actions(action, value) {\n\tconst { editor } = editorManager;\n\tconst { $input, $replaceInput } = quickTools;\n\n\tif (Object.keys(state).includes(action)) {\n\t\tsetInput();\n\t\tvalue = !state[action];\n\t\tstate[action] = value;\n\t\tevents[action].forEach((cb) => cb(value));\n\t\tif (Object.values(state).includes(true)) {\n\t\t\t$input.focus();\n\t\t} else if (input) {\n\t\t\tinput.focus();\n\t\t} else {\n\t\t\t$input.blur();\n\t\t}\n\n\t\treturn value;\n\t}\n\n\tswitch (action) {\n\t\tcase \"insert\":\n\t\t\treturn insertText(value);\n\n\t\tcase \"command\": {\n\t\t\tconst commandName =\n\t\t\t\ttypeof value === \"string\" ? value : String(value ?? \"\");\n\t\t\tif (!commandName) return false;\n\t\t\treturn executeCommand(commandName, editor);\n\t\t}\n\n\t\tcase \"key\": {\n\t\t\tvalue = Number.parseInt(value, 10);\n\t\t\tconst event = KeyboardEvent(\"keydown\", getKeys({ keyCode: value }));\n\t\t\tif (value > 40 && value < 37) {\n\t\t\t\tresetKeys();\n\t\t\t}\n\t\t\tsetInput();\n\t\t\tinput.dispatchEvent(event);\n\t\t\treturn true;\n\t\t}\n\n\t\tcase \"search\":\n\t\t\ttoggleSearch();\n\t\t\treturn actionStack.has(\"search-bar\");\n\n\t\tcase \"toggle\":\n\t\t\ttoggle();\n\t\t\treturn true;\n\n\t\tcase \"set-height\":\n\t\t\tif (typeof value === \"object\") {\n\t\t\t\tsetHeight(value.height, value.save);\n\t\t\t} else {\n\t\t\t\tsetHeight(value);\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase \"search-prev\":\n\t\t\tif (quickTools.$searchInput.el.value) {\n\t\t\t\tsearchHistory.addToHistory(quickTools.$searchInput.el.value);\n\t\t\t}\n\t\t\tfind(1, true);\n\t\t\treturn true;\n\n\t\tcase \"search-next\":\n\t\t\tif (quickTools.$searchInput.el.value) {\n\t\t\t\tsearchHistory.addToHistory(quickTools.$searchInput.el.value);\n\t\t\t}\n\t\t\tfind(1, false);\n\t\t\treturn true;\n\n\t\tcase \"search-settings\":\n\t\t\tsearchSettings().show();\n\t\t\treturn true;\n\n\t\tcase \"search-replace\":\n\t\t\tif ($replaceInput.value) {\n\t\t\t\tsearchHistory.addToHistory($replaceInput.value);\n\t\t\t}\n\t\t\tif (editor) {\n\t\t\t\tconst replaceValue = getRefValue($replaceInput);\n\t\t\t\tconst query = applySearchQuery(\n\t\t\t\t\teditor,\n\t\t\t\t\tgetRefValue(quickTools.$searchInput),\n\t\t\t\t\treplaceValue,\n\t\t\t\t);\n\t\t\t\tif (query && query.search && query.valid) {\n\t\t\t\t\tcmReplaceNext(editor);\n\t\t\t\t}\n\t\t\t\tupdateSearchState();\n\t\t\t}\n\t\t\treturn true;\n\n\t\tcase \"search-replace-all\":\n\t\t\tif ($replaceInput.value) {\n\t\t\t\tsearchHistory.addToHistory($replaceInput.value);\n\t\t\t}\n\t\t\tif (editor) {\n\t\t\t\tconst replaceValue = getRefValue($replaceInput);\n\t\t\t\tconst query = applySearchQuery(\n\t\t\t\t\teditor,\n\t\t\t\t\tgetRefValue(quickTools.$searchInput),\n\t\t\t\t\treplaceValue,\n\t\t\t\t);\n\t\t\t\tif (query && query.search && query.valid) {\n\t\t\t\t\tcmReplaceAll(editor);\n\t\t\t\t}\n\t\t\t}\n\t\t\tupdateSearchState();\n\t\t\treturn true;\n\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\nfunction setInput() {\n\tconst terminalInput = getActiveTerminalInput();\n\tif (terminalInput) {\n\t\tinput = terminalInput;\n\t\treturn;\n\t}\n\n\tconst { activeElement } = document;\n\tif (\n\t\t!activeElement ||\n\t\tactiveElement === quickTools.$input ||\n\t\tactiveElement === document.body\n\t)\n\t\treturn;\n\tinput = activeElement;\n}\n\nfunction toggleSearch() {\n\tconst $footer = quickTools.$footer;\n\tconst $searchRow1 = quickTools.$searchRow1;\n\tconst $searchRow2 = quickTools.$searchRow2;\n\tconst $searchInput = quickTools.$searchInput.el;\n\tconst $toggler = quickTools.$toggler;\n\tconst { editor } = editorManager;\n\tconst selectedText = getSelectedText(editor);\n\n\tif (!$footer.contains($searchRow1)) {\n\t\tconst { className } = quickTools.$toggler;\n\t\tconst $content = [...$footer.children];\n\t\tconst footerHeight = getFooterHeight();\n\n\t\t$toggler.className = \"floating icon clearclose\";\n\t\t$footer.content = [$searchRow1, $searchRow2];\n\t\tsetRefValue($searchInput, selectedText || \"\");\n\n\t\t$searchInput.oninput = function () {\n\t\t\tfind(0, false);\n\t\t};\n\n\t\t$searchInput.onsearch = function () {\n\t\t\tif (this.value) {\n\t\t\t\tsearchHistory.addToHistory(this.value);\n\t\t\t\tfind(1, false);\n\t\t\t} else {\n\t\t\t\tfind(0, false);\n\t\t\t}\n\t\t};\n\n\t\t// Setup history navigation for search inputs\n\t\tsetupHistoryNavigation();\n\n\t\tsetFooterHeight(2);\n\t\tfind(0, false);\n\n\t\tactionStack.push({\n\t\t\tid: \"search-bar\",\n\t\t\taction: () => {\n\t\t\t\tremoveSearch();\n\t\t\t\t$footer.content = $content;\n\t\t\t\t$toggler.className = className;\n\t\t\t\tsetFooterHeight(footerHeight);\n\t\t\t},\n\t\t});\n\t} else {\n\t\tconst inputValue = getRefValue($searchInput);\n\t\tif (inputValue !== selectedText) {\n\t\t\tsetRefValue($searchInput, selectedText || \"\");\n\t\t\tfind(0, false);\n\t\t\treturn;\n\t\t}\n\n\t\tactionStack.get(\"search-bar\").action();\n\t}\n\n\t$searchInput.focus();\n}\n\nfunction toggle() {\n\t// if search is active, remove it\n\tconst searchBar = actionStack.get(\"search-bar\");\n\tif (searchBar?.action) {\n\t\tsearchBar.action();\n\t\treturn;\n\t}\n\n\tconst $footer = quickTools.$footer;\n\tconst $row1 = quickTools.$row1;\n\tconst $row2 = quickTools.$row2;\n\n\tif (!$footer.contains($row1)) {\n\t\tsetHeight();\n\t} else if (!$footer.contains($row2)) {\n\t\tsetHeight(2);\n\t} else {\n\t\tsetHeight(0);\n\t}\n\tfocusEditor();\n}\n\nfunction setHeight(height = 1, save = true) {\n\tconst { $footer, $row1, $row2 } = quickTools;\n\tconst { editor, activeFile } = editorManager;\n\n\t// If active file has hideQuickTools, force height to 0 and don't save\n\tif (activeFile?.hideQuickTools) {\n\t\theight = 0;\n\t\tsave = false;\n\t}\n\n\tsetFooterHeight(height);\n\tif (save) {\n\t\tappSettings.update({ quickTools: height }, false);\n\t}\n\n\tif (!height) {\n\t\t$row1.remove();\n\t\t$row2.remove();\n\t\treturn;\n\t}\n\n\tif (height >= 1) {\n\t\t$row1.style.scrollBehavior = \"unset\";\n\t\t$footer.append($row1);\n\t\t$row1.scrollLeft = Number.parseInt(\n\t\t\tlocalStorage.quickToolRow1ScrollLeft,\n\t\t\t10,\n\t\t);\n\t\t--height;\n\t}\n\n\tif (height >= 1) {\n\t\t$row2.style.scrollBehavior = \"unset\";\n\t\t$footer.append($row2);\n\t\t$row2.scrollLeft = Number.parseInt(\n\t\t\tlocalStorage.quickToolRow2ScrollLeft,\n\t\t\t10,\n\t\t);\n\t\t--height;\n\t}\n}\n\n/**\n * Removes search bar from footer\n */\nfunction removeSearch() {\n\tconst { $footer, $searchRow1, $searchRow2 } = quickTools;\n\n\tif (!$footer.contains($searchRow1)) return;\n\tactionStack.remove(\"search-bar\");\n\t$footer.removeAttribute(\"data-searching\");\n\t$searchRow1.remove();\n\t$searchRow2.remove();\n\n\t// Reset history navigation when search is closed\n\tsearchHistory.resetAllNavigation();\n\n\tconst { activeFile, editor } = editorManager;\n\n\t// Check if current tab is a terminal\n\tif (\n\t\tactiveFile &&\n\t\tactiveFile.type === \"terminal\" &&\n\t\tactiveFile.terminalComponent\n\t) {\n\t\tactiveFile.terminalComponent.searchAddon?.clearDecorations();\n\t\tactiveFile.terminalComponent.searchAddon?.clearActiveDecoration();\n\t\treturn;\n\t}\n\tclearSearchQuery(editor);\n\tfocusEditor();\n}\n\n/**\n * Finds the next/previous search result\n * @param {number} skip Number of search results to skip\n * @param {boolean} backward Whether to search backward\n */\nfunction find(skip, backward) {\n\tconst { $searchInput } = quickTools;\n\tconst { activeFile } = editorManager;\n\n\t// Check if current tab is a terminal\n\tif (\n\t\tactiveFile &&\n\t\tactiveFile.type === \"terminal\" &&\n\t\tactiveFile.terminalComponent\n\t) {\n\t\tactiveFile.terminalComponent.search($searchInput.value, skip, backward);\n\t} else {\n\t\tconst editor = editorManager.editor;\n\t\tconst searchValue = getRefValue($searchInput);\n\t\tconst query = applySearchQuery(editor, searchValue);\n\t\tif (!query || !query.search || !query.valid) {\n\t\t\tupdateSearchState();\n\t\t\treturn;\n\t\t}\n\n\t\tconst normalizedSkip = Number(skip) || 0;\n\t\tif (normalizedSkip === 0 && selectionMatchesQuery(editor, query)) {\n\t\t\tupdateSearchState();\n\t\t\treturn;\n\t\t}\n\t\tconst steps = Math.max(1, normalizedSkip);\n\t\tconst runCommand = backward ? cmFindPrevious : cmFindNext;\n\t\tfor (let i = 0; i < steps; ++i) {\n\t\t\tif (!runCommand(editor)) break;\n\t\t}\n\t}\n\n\tupdateSearchState();\n}\n\nfunction updateSearchState() {\n\tconst MAX_COUNT = 999;\n\tconst { activeFile, editor } = editorManager;\n\tconst { $searchPos, $searchTotal } = quickTools;\n\n\t// Check if current tab is a terminal\n\tif (activeFile && activeFile.type === \"terminal\") {\n\t\t// For terminal, we can't easily count all matches like in ACE editor\n\t\t// xterm search addon doesn't provide this information\n\t\t// So we just show a generic indicator\n\t\t$searchTotal.textContent = \"?\";\n\t\t$searchPos.textContent = \"?\";\n\t\treturn;\n\t}\n\tconst query = editor ? getSearchQuery(editor.state) : null;\n\tif (!query || !query.search || !query.valid) {\n\t\t$searchTotal.textContent = \"0\";\n\t\t$searchPos.textContent = \"0\";\n\t\treturn;\n\t}\n\n\tconst cursor = query.getCursor(editor.state.doc);\n\tlet total = 0;\n\tlet before = 0;\n\tlet limited = false;\n\tconst cursorPos = editor.state.selection.main.head;\n\tfor (cursor.next(); !cursor.done; cursor.next()) {\n\t\ttotal++;\n\t\tif (cursorPos >= cursor.value.from) {\n\t\t\tbefore = Math.min(total, MAX_COUNT);\n\t\t}\n\t\tif (total === MAX_COUNT) {\n\t\t\tcursor.next();\n\t\t\tlimited = !cursor.done;\n\t\t\tbreak;\n\t\t}\n\t}\n\t$searchTotal.textContent = limited ? \"999+\" : String(total);\n\t$searchPos.textContent = String(before);\n}\n\n/**\n * Sets the height of the footer\n * @param {number} height Height of the footer\n * @returns {void}\n */\nfunction setFooterHeight(height) {\n\tconst { $toggler, $footer, $searchRow1 } = quickTools;\n\tif (height) root.setAttribute(\"footer-height\", height);\n\telse root.removeAttribute(\"footer-height\");\n\n\tif ($toggler.classList.contains(\"clearclose\")) return;\n\n\tif (height > 1 && !$footer.contains($searchRow1)) {\n\t\t$toggler.classList.remove(\"keyboard_arrow_up\");\n\t\t$toggler.classList.add(\"keyboard_arrow_down\");\n\t} else {\n\t\t$toggler.classList.remove(\"keyboard_arrow_down\");\n\t\t$toggler.classList.add(\"keyboard_arrow_up\");\n\t}\n}\n\nfunction getFooterHeight() {\n\treturn Number.parseInt(root.getAttribute(\"footer-height\")) || 0;\n}\n\nfunction focusEditor() {\n\tconst { editor, activeFile } = editorManager;\n\tif (!activeFile?.focused) {\n\t\treturn;\n\t}\n\n\tif (activeFile.type === \"terminal\" && activeFile.terminalComponent) {\n\t\tactiveFile.terminalComponent.focus();\n\t\treturn;\n\t}\n\n\tif (editor) {\n\t\teditor.focus();\n\t}\n}\n\nfunction resetKeys() {\n\tstate.shift = false;\n\tevents.shift.forEach((cb) => cb(false));\n\tstate.alt = false;\n\tevents.alt.forEach((cb) => cb(false));\n\tstate.ctrl = false;\n\tevents.ctrl.forEach((cb) => cb(false));\n\tstate.meta = false;\n\tevents.meta.forEach((cb) => cb(false));\n\tinput?.focus?.();\n}\n\n/**\n * Gets the current state of the modifier keys\n * @param {object} key Key object\n * @param {int} [key.keyCode] Key code\n * @param {string} [key.key] Key\n * @returns {KeyboardEventInit}\n */\nexport function getKeys(key = {}) {\n\treturn {\n\t\t...key,\n\t\tshiftKey: state.shift,\n\t\taltKey: state.alt,\n\t\tctrlKey: state.ctrl,\n\t\tmetaKey: state.meta,\n\t};\n}\n\nfunction getActiveTerminalComponent() {\n\tconst { activeFile } = editorManager;\n\tif (activeFile?.type !== \"terminal\") return null;\n\treturn activeFile.terminalComponent || null;\n}\n\nfunction getActiveTerminalInput() {\n\treturn getActiveTerminalComponent()?.terminal?.textarea || null;\n}\n\nfunction insertText(value) {\n\tconst text = String(value ?? \"\");\n\tif (!text) return false;\n\n\tconst terminalComponent = getActiveTerminalComponent();\n\tif (terminalComponent?.terminal) {\n\t\tif (typeof terminalComponent.terminal.paste === \"function\") {\n\t\t\tterminalComponent.terminal.paste(text);\n\t\t\tterminalComponent.focus();\n\t\t\treturn true;\n\t\t}\n\n\t\tif (terminalComponent.serverMode && terminalComponent.isConnected) {\n\t\t\tterminalComponent.write(text);\n\t\t\tterminalComponent.focus();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tconst { editor } = editorManager;\n\treturn editor ? editor.insert(text) : false;\n}\n\nfunction shiftKeyMapping(char) {\n\tswitch (char) {\n\t\tcase \"1\":\n\t\t\treturn \"!\";\n\t\tcase \"2\":\n\t\t\treturn \"@\";\n\t\tcase \"3\":\n\t\t\treturn \"#\";\n\t\tcase \"4\":\n\t\t\treturn \"$\";\n\t\tcase \"5\":\n\t\t\treturn \"%\";\n\t\tcase \"6\":\n\t\t\treturn \"^\";\n\t\tcase \"7\":\n\t\t\treturn \"&\";\n\t\tcase \"8\":\n\t\t\treturn \"*\";\n\t\tcase \"9\":\n\t\t\treturn \"(\";\n\t\tcase \"0\":\n\t\t\treturn \")\";\n\t\tcase \"-\":\n\t\t\treturn \"_\";\n\t\tcase \"=\":\n\t\t\treturn \"+\";\n\t\tcase \"[\":\n\t\t\treturn \"{\";\n\t\tcase \"]\":\n\t\t\treturn \"}\";\n\t\tcase \"\\\\\":\n\t\t\treturn \"|\";\n\t\tcase \";\":\n\t\t\treturn \":\";\n\t\tcase \"'\":\n\t\t\treturn '\"';\n\t\tcase \",\":\n\t\t\treturn \"<\";\n\t\tcase \".\":\n\t\t\treturn \">\";\n\t\tcase \"/\":\n\t\t\treturn \"?\";\n\t\tdefault:\n\t\t\treturn char.toUpperCase();\n\t}\n}\n"
  },
  {
    "path": "src/handlers/quickToolsInit.js",
    "content": "import quickTools from \"components/quickTools\";\nimport constants from \"lib/constants\";\nimport appSettings from \"lib/settings\";\nimport actions, { key } from \"./quickTools\";\n\nconst CONTEXT_MENU_TIMEOUT = 500;\nconst MOVE_X_THRESHOLD = 50;\n\nlet time;\nlet moveX;\nlet movedX; // total moved x\nlet touchMoved;\nlet isClickMode;\nlet contextmenu;\nlet startTime;\nlet contextmenuTimeout;\nlet active = false; // is button already active\nlet slide = 0;\n\n/**@type {HTMLElement} */\nlet $row;\n/**@type {number} */\nlet timeout;\n/**@type {HTMLElement} */\nlet $touchstart;\n\nfunction reset() {\n\tmoveX = 0;\n\tmovedX = 0;\n\ttime = 300;\n\t$row = null;\n\t$touchstart = null;\n\tcontextmenu = false;\n\ttouchMoved = undefined;\n\tcontextmenuTimeout = null;\n\tactive = false;\n}\n\n/**\n * Initialize quick tools\n * @param {HTMLElement} $footer\n */\nexport default function init() {\n\tconst { $footer, $toggler, $input } = quickTools;\n\n\t$toggler.addEventListener(\"click\", (e) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\tactions(\"toggle\");\n\t});\n\n\tkey.on(\"shift\", (value) => {\n\t\tif (value) $footer.setAttribute(\"data-shift\", \"true\");\n\t\telse $footer.removeAttribute(\"data-shift\");\n\t});\n\n\tkey.on(\"ctrl\", (value) => {\n\t\tif (value) $footer.setAttribute(\"data-ctrl\", \"true\");\n\t\telse $footer.removeAttribute(\"data-ctrl\");\n\t});\n\n\tkey.on(\"alt\", (value) => {\n\t\tif (value) $footer.setAttribute(\"data-alt\", \"true\");\n\t\telse $footer.removeAttribute(\"data-alt\");\n\t});\n\n\tkey.on(\"meta\", (value) => {\n\t\tif (value) $footer.setAttribute(\"data-meta\", \"true\");\n\t\telse $footer.removeAttribute(\"data-meta\");\n\t});\n\n\teditorManager.on([\"file-content-changed\", \"switch-file\"], () => {\n\t\tif (editorManager.activeFile?.isUnsaved) {\n\t\t\t$footer.setAttribute(\"data-unsaved\", \"true\");\n\t\t} else {\n\t\t\t$footer.removeAttribute(\"data-unsaved\");\n\t\t}\n\t});\n\n\teditorManager.on(\"save-file\", () => {\n\t\t$footer.removeAttribute(\"data-unsaved\");\n\t});\n\n\troot.append($footer, $toggler);\n\tdocument.body.append($input);\n\tif (\n\t\tappSettings.value.quickToolsTriggerMode ===\n\t\tappSettings.QUICKTOOLS_TRIGGER_MODE_CLICK\n\t) {\n\t\tisClickMode = true;\n\t\t$footer.addEventListener(\"click\", onclick);\n\t\t$footer.addEventListener(\"contextmenu\", oncontextmenu, true);\n\t\t$footer.addEventListener(\"wheel\", onwheel, { passive: false });\n\t} else {\n\t\t$footer.addEventListener(\"touchstart\", touchstart);\n\t\t$footer.addEventListener(\"keydown\", touchstart);\n\t}\n\n\tappSettings.on(\"update:quickToolsTriggerMode\", (value) => {\n\t\tif (value === appSettings.QUICKTOOLS_TRIGGER_MODE_CLICK) {\n\t\t\t$footer.removeEventListener(\"touchstart\", touchstart);\n\t\t\t$footer.removeEventListener(\"keydown\", touchstart);\n\t\t\t$footer.addEventListener(\"contextmenu\", onclick, true);\n\t\t\t$footer.addEventListener(\"click\", onclick);\n\t\t\t$footer.addEventListener(\"wheel\", onwheel, { passive: false });\n\t\t} else {\n\t\t\t$footer.removeEventListener(\"contextmenu\", onclick, true);\n\t\t\t$footer.removeEventListener(\"click\", onclick);\n\t\t\t$footer.removeEventListener(\"wheel\", onwheel);\n\t\t\t$footer.addEventListener(\"keydown\", touchstart);\n\t\t\t$footer.addEventListener(\"touchstart\", touchstart);\n\t\t}\n\t});\n}\n\nfunction onwheel(e) {\n\te.preventDefault();\n\tconst $el = e.target;\n\tconst { $row1, $row2 } = quickTools;\n\tlet $row;\n\n\tif ($row1?.contains($el)) {\n\t\t$row = $row1;\n\t} else if ($row2?.contains($el)) {\n\t\t$row = $row2;\n\t}\n\n\tif ($row) {\n\t\t$row.scrollLeft += e.deltaY;\n\t}\n}\n\nfunction onclick(e) {\n\treset();\n\n\te.preventDefault();\n\te.stopPropagation();\n\tclick(e.target);\n\tclearTimeout(timeout);\n}\n\nfunction touchstart(e) {\n\treset();\n\n\tconst $el = e.target;\n\tif ($el instanceof HTMLInputElement) {\n\t\treturn;\n\t}\n\n\tstartTime = performance.now();\n\t$touchstart = $el;\n\te.preventDefault();\n\te.stopPropagation();\n\n\tif ($el.dataset.repeat === \"true\") {\n\t\tcontextmenuTimeout = setTimeout(() => {\n\t\t\tif (touchMoved) return;\n\t\t\tcontextmenu = true;\n\t\t\toncontextmenu(e);\n\t\t}, CONTEXT_MENU_TIMEOUT);\n\t}\n\n\tif ($el.classList.contains(\"active\")) {\n\t\tactive = true;\n\t} else {\n\t\t$el.classList.add(\"active\");\n\t}\n\n\tdocument.addEventListener(\"touchmove\", touchmove);\n\tdocument.addEventListener(\"keyup\", touchcancel);\n\tdocument.addEventListener(\"touchend\", touchend);\n\tdocument.addEventListener(\"touchcancel\", touchcancel);\n}\n\n/**\n * Event handler for touchmove event\n * @param {TouchEvent} e\n */\nfunction touchmove(e) {\n\tif (contextmenu || touchMoved === false) return;\n\n\tconst $el = e.target;\n\tconst { $row1, $row2 } = quickTools;\n\tconst { clientX } = e.touches[0];\n\n\tif (moveX === 0) {\n\t\tmoveX = clientX;\n\t\treturn;\n\t}\n\n\tconst diff = moveX - clientX;\n\tif (touchMoved === undefined) {\n\t\tif (Math.abs(diff) > appSettings.value.touchMoveThreshold) {\n\t\t\ttouchMoved = true;\n\t\t} else {\n\t\t\tif ($row) {\n\t\t\t\tconst movedX = $row.scrollLeft % $row.clientWidth;\n\t\t\t\t// $row.scrollBy(-movedX, 0);\n\t\t\t\t// scrollBy is not working on mobile\n\t\t\t\t$row.scrollLeft -= movedX;\n\t\t\t}\n\t\t\ttouchMoved = false;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tmovedX += diff;\n\n\tif (!$row) {\n\t\tif ($row1?.contains($el)) {\n\t\t\t$row = $row1;\n\t\t} else if ($row2?.contains($el)) {\n\t\t\t$row = $row2;\n\t\t}\n\n\t\tslide = Number.parseInt($row.scrollLeft / $row.clientWidth, 10);\n\t}\n\n\tif ($row) {\n\t\t$row.style.scrollBehavior = \"unset\";\n\t\t$row.scrollLeft += diff;\n\t}\n\n\tif (!active) $touchstart.classList.remove(\"active\");\n\tmoveX = clientX;\n}\n\n/**\n * Event handler for touchend event\n * @param {TouchEvent} e\n */\nfunction touchend(e) {\n\tconst { $row1 } = quickTools;\n\tconst $el = document.elementFromPoint(\n\t\te.changedTouches[0].clientX,\n\t\te.changedTouches[0].clientY,\n\t);\n\n\tif (touchMoved && $row) {\n\t\t$row.style.scrollBehavior = \"smooth\";\n\t\tlet scroll = 0;\n\t\tif (movedX < 0 && movedX < -MOVE_X_THRESHOLD) {\n\t\t\tscroll = (slide - 1) * $row.clientWidth;\n\t\t} else if (movedX > 0 && movedX > MOVE_X_THRESHOLD) {\n\t\t\tscroll = (slide + 1) * $row.clientWidth;\n\t\t} else {\n\t\t\tscroll = slide * $row.clientWidth;\n\t\t}\n\n\t\tif ($row === $row1) {\n\t\t\tlocalStorage.quickToolRow1ScrollLeft = scroll;\n\t\t} else {\n\t\t\tlocalStorage.quickToolRow2ScrollLeft = scroll;\n\t\t}\n\n\t\t$row.scrollLeft = scroll;\n\t\ttouchcancel(e);\n\n\t\tif ($el === $touchstart && performance.now() - startTime < 100) {\n\t\t\tclick($el);\n\t\t}\n\t\treturn;\n\t}\n\n\tif ($touchstart !== $el || contextmenu) {\n\t\ttouchcancel(e);\n\t\treturn;\n\t}\n\n\ttouchcancel(e);\n\tclick($el);\n}\n\n/**\n *\n * @param {TouchEvent} e\n */\nfunction touchcancel(e) {\n\tdocument.removeEventListener(\"keyup\", touchcancel);\n\tdocument.removeEventListener(\"touchend\", touchend);\n\tdocument.removeEventListener(\"touchcancel\", touchcancel);\n\tdocument.removeEventListener(\"touchmove\", touchmove);\n\tclearTimeout(timeout);\n\tclearTimeout(contextmenuTimeout);\n\tif (!active) $touchstart.classList.remove(\"active\");\n}\n\n/**\n * Handler for contextmenu event\n * @param {TouchEvent|MouseEvent} e\n */\nfunction oncontextmenu(e) {\n\tconst $el = e.target;\n\tconst { lock } = $el.dataset;\n\n\tif (lock === \"true\") {\n\t\treturn; // because button with lock=true is locked when clicked so contextmenu doesn't make sense\n\t}\n\n\tconst { editor, activeFile } = editorManager;\n\n\tif (isClickMode && appSettings.value.vibrateOnTap) {\n\t\tnavigator.vibrate(constants.VIBRATION_TIME_LONG);\n\t\t$el.classList.add(\"active\");\n\t}\n\n\tconst dispatchEventWithTimeout = () => {\n\t\tif (time > 50) {\n\t\t\ttime -= 10;\n\t\t}\n\t\tclick($el);\n\t\ttimeout = setTimeout(dispatchEventWithTimeout, time);\n\t};\n\n\tif (activeFile.focused) {\n\t\teditor.focus();\n\t}\n\tdispatchEventWithTimeout();\n}\n\n/**\n * Executes the action associated with the button\n * @param {HTMLElement} $el\n */\nfunction click($el) {\n\t$el.classList.add(\"click\");\n\tclearTimeout($el.dataset.timeout);\n\t$el.dataset.timeout = setTimeout(() => {\n\t\t$el.classList.remove(\"click\");\n\t}, 300);\n\tconst { action } = $el.dataset;\n\tif (!action) return;\n\n\tlet { value } = $el.dataset;\n\n\tif (!value) {\n\t\tvalue = $el.value;\n\t}\n\n\tactions(action, value);\n}\n"
  },
  {
    "path": "src/handlers/windowResize.js",
    "content": "let resizeTimeout;\n\n/**\n * @typedef {'resize'|'resizeStart'} ResizeEventName\n */\n\nconst event = {\n\tresize: [],\n\tresizeStart: [],\n};\n\n/**\n * *external keyboard*\n * -> config.hardKeyboardHidden = HARDKEYBOARDHIDDEN_NO\n * This means that external keyboard is connected. If external keyboard is connected,\n * we don't need to do anything.\n *\n * *floating keyboard is active*\n * -> No way to detect this.\n *\n * *keyboard is hidden*\n * -> If window height is smaller than earlier, keyboard is visible. and vice versa.\n * If keyboard is not visible, we need to blur the editor, and hide the ad.\n * Else we need to focus the editor, and show the ad.\n */\n\nexport default function windowResize() {\n\tif (!resizeTimeout) {\n\t\temit(\"resizeStart\");\n\t}\n\n\tclearTimeout(resizeTimeout);\n\tresizeTimeout = setTimeout(onResize, 100);\n}\n\n/**\n * Add event listener for window done resizing.\n * @param {ResizeEventName} eventName\n * @param {Function} callback\n * @returns\n */\nwindowResize.on = (eventName, callback) => {\n\tif (!event[eventName]) return;\n\tevent[eventName].push(callback);\n};\n\n/**\n * Remove event listener for window done resizing.\n * @param {ResizeEventName} eventName\n * @param {Function} callback\n * @returns\n */\nwindowResize.off = (eventName, callback) => {\n\tif (!event[eventName]) return;\n\tevent[eventName] = event[eventName].filter((cb) => cb !== callback);\n};\n\n/**\n * Timeout function for window done resizing.\n */\nfunction onResize() {\n\tresizeTimeout = null;\n\temit(\"resize\");\n}\n\n/**\n * Emit event\n * @param {ResizeEventName} eventName\n * @returns\n */\nfunction emit(eventName) {\n\tif (!event[eventName]) return;\n\tevent[eventName].forEach((cb) => cb());\n}\n"
  },
  {
    "path": "src/index.d.ts",
    "content": "type LanguageMap = { [key: string]: string };\ndeclare const strings: LanguageMap;\ndeclare const ASSETS_DIRECTORY: string;\ndeclare const DATA_STORAGE: string;\ndeclare const CACHE_STORAGE: string;\ndeclare const PLUGIN_DIR: string;\ndeclare const KEYBINDING_FILE: string;\ndeclare const IS_FREE_VERSION: string;\ndeclare const ANDROID_SDK_INT: number;\ndeclare const DOES_SUPPORT_THEME: boolean;\ndeclare const acode: object;\n\ninterface Window {\n\tASSETS_DIRECTORY: string;\n\tDATA_STORAGE: string;\n\tCACHE_STORAGE: string;\n\tPLUGIN_DIR: string;\n\tKEYBINDING_FILE: string;\n\tIS_FREE_VERSION: string;\n\tANDROID_SDK_INT: number;\n\tDOES_SUPPORT_THEME: boolean;\n\tacode: object;\n}\n\ninterface String {\n\t/**\n\t * Capitalize the first letter of a string\n\t */\n\tcapitalize(): string;\n\t/**\n\t * Generate a hash from a string\n\t */\n\thash(): string;\n}\n\ntype ExecutorCallback = (\n\ttype: \"stdout\" | \"stderr\" | \"exit\",\n\tdata: string,\n) => void;\n\ninterface Executor {\n\texecute: (command: string, alpine: boolean) => Promise<string>;\n\tstart: (\n\t\tcommand: string,\n\t\tcallback: ExecutorCallback,\n\t\talpine: boolean,\n\t) => Promise<string>;\n\twrite: (uuid: string, input: string) => Promise<void>;\n\tstop: (uuid: string) => Promise<void>;\n\tisRunning: (uuid: string) => Promise<boolean>;\n\t/** Move the executor service to the foreground (shows notification) */\n\tmoveToForeground: () => Promise<void>;\n\t/** Move the executor service to the background (hides notification) */\n\tmoveToBackground: () => Promise<void>;\n\t/** Stop the executor service completely */\n\tstopService: () => Promise<void>;\n\t/**\n\t * Background executor\n\t */\n\tBackgroundExecutor: Executor;\n}\n\ndeclare const Executor: Executor | undefined;\n\ninterface Window {\n\tExecutor?: Executor;\n\teditorManager?: EditorManager;\n}\n\ninterface EditorManager {\n\teditor?: import(\"@codemirror/view\").EditorView;\n\tisCodeMirror?: boolean;\n\tactiveFile?: AcodeFile;\n\tgetLspMetadata?: (file: AcodeFile) => LspFileMetadata | null;\n}\n\ninterface LspFileMetadata {\n\turi: string;\n\tlanguageId?: string;\n\tlanguageName?: string;\n\tview?: import(\"@codemirror/view\").EditorView;\n\tfile?: AcodeFile;\n\trootUri?: string;\n}\n\n/**\n * Acode file object\n */\ninterface AcodeFile {\n\turi?: string;\n\tname?: string;\n\tsession?: unknown;\n\tcacheFile?: string;\n\t[key: string]: unknown;\n}\n\n// Extend globalThis with Executor\ndeclare global {\n\tvar Executor: Executor | undefined;\n}\n"
  },
  {
    "path": "src/lang/ar-ye.json",
    "content": "{\n\t\"lang\": \"العربية\",\n\t\"about\": \"حول\",\n\t\"active files\": \"الملفات المفتوحة\",\n\t\"add path\": \"إضافة مسار\",\n\t\"alert\": \"تنبيه\",\n\t\"app theme\": \"سمة التطبيق\",\n\t\"autocorrect\": \"تفعيل التصحيح التلقائي؟\",\n\t\"autosave\": \"حفظ تلقائي\",\n\t\"cancel\": \"إلغاء\",\n\t\"change language\": \"تغيير اللغة\",\n\t\"choose color\": \"اختر لوناً\",\n\t\"clear\": \"مسح\",\n\t\"close app\": \"إغلاق التطبيق؟\",\n\t\"commit message\": \"رسالة الـ Commit\",\n\t\"console\": \"وحدة التحكم\",\n\t\"conflict error\": \"خطأ! يرجى الانتظار قبل القيام بـ commit آخر.\",\n\t\"copy\": \"نسخ\",\n\t\"create folder error\": \"فشل إنشاء مجلد جديد\",\n\t\"cut\": \"قص\",\n\t\"delete\": \"حذف\",\n\t\"dependencies\": \"التبعيات\",\n\t\"delay\": \"الوقت بالمللي ثانية\",\n\t\"editor settings\": \"إعدادات المحرر\",\n\t\"editor theme\": \"سمة المحرر\",\n\t\"enter file name\": \"أدخل اسم الملف\",\n\t\"enter folder name\": \"أدخل اسم المجلد\",\n\t\"empty folder message\": \"مجلد فارغ\",\n\t\"enter line number\": \"أدخل رقم السطر\",\n\t\"error\": \"خطأ\",\n\t\"failed\": \"فشل\",\n\t\"file already exists\": \"الملف موجود مسبقاً\",\n\t\"file already exists force\": \"الملف موجود مسبقاً، هل تريد الاستبدال؟\",\n\t\"file changed\": \"تغير الملف، هل تريد إعادة التحميل؟\",\n\t\"file deleted\": \"تم الحذف بنجاح\",\n\t\"file is not supported\": \"الملف غير مدعوم\",\n\t\"file not supported\": \"نوع الملف غير مدعوم\",\n\t\"file too large\": \"الملف كبير جداً. أقصى حجم مسموح به هو {size}\",\n\t\"file renamed\": \"تمت إعادة التسمية بنجاح\",\n\t\"file saved\": \"تم الحفظ بنجاح\",\n\t\"folder added\": \"تمت إضافة المجلد\",\n\t\"folder already added\": \"المجلد مضاف مسبقاً!\",\n\t\"font size\": \"حجم الخط\",\n\t\"goto\": \"الذهاب إلى السطر\",\n\t\"icons definition\": \"تعريف الأيقونات\",\n\t\"info\": \"معلومات\",\n\t\"invalid value\": \"قيمة غير صالحة\",\n\t\"language changed\": \"تم تغيير اللغة بنجاح\",\n\t\"linting\": \"فحص أخطاء السينتاكس\",\n\t\"logout\": \"تسجيل الخروج\",\n\t\"loading\": \"جارٍ التحميل...\",\n\t\"my profile\": \"ملفي الشخصي\",\n\t\"new file\": \"ملف جديد\",\n\t\"new folder\": \"مجلد جديد\",\n\t\"no\": \"لا\",\n\t\"no editor message\": \"افتح أو أنشئ ملفاً أو مجلداً من القائمة\",\n\t\"not set\": \"غير محدد\",\n\t\"unsaved files close app\": \"هناك ملفات غير محفوظة. هل أنت متأكد من الخروج؟\",\n\t\"notice\": \"ملاحظة\",\n\t\"open file\": \"فتح ملف\",\n\t\"open files and folders\": \"فتح ملفات ومجلدات\",\n\t\"open folder\": \"فتح مجلد\",\n\t\"open recent\": \"الملفات الأخيرة\",\n\t\"ok\": \"حسناً\",\n\t\"overwrite\": \"استبدال\",\n\t\"paste\": \"لصق\",\n\t\"preview mode\": \"وضعية المعاينة\",\n\t\"read only file\": \"فشل حفظ الملف لأنه للقراءة فقط. حاول حفظه باسم آخر.\",\n\t\"reload\": \"إعادة تحميل\",\n\t\"rename\": \"إعادة تسمية\",\n\t\"replace\": \"استبدال\",\n\t\"required\": \"هذا الحقل مطلوب.\",\n\t\"run your web app\": \"تشغيل تطبيق الويب\",\n\t\"save\": \"حفظ\",\n\t\"saving\": \"جارٍ الحفظ...\",\n\t\"save as\": \"حفظ باسم\",\n\t\"save file to run\": \"يرجى حفظ الملف لتتمكن من تشغيله في المتصفح!\",\n\t\"search\": \"بحث\",\n\t\"see logs and errors\": \"عرض السجلات والأخطاء\",\n\t\"select folder\": \"تحديد مجلد\",\n\t\"settings\": \"الإعدادات\",\n\t\"settings saved\": \"تم حفظ الإعدادات\",\n\t\"show line numbers\": \"عرض أرقام الأسطر\",\n\t\"show hidden files\": \"عرض الملفات المخفية\",\n\t\"show spaces\": \"عرض المسافات\",\n\t\"soft tab\": \"تبويب سلس\",\n\t\"sort by name\": \"الترتيب حسب الاسم؟\",\n\t\"success\": \"نجاح\",\n\t\"tab size\": \"حجم التبويبات\",\n\t\"text wrap\": \"التفاف النص\",\n\t\"theme\": \"السمة\",\n\t\"unable to delete file\": \"عذراً، فشل حذف الملف\",\n\t\"unable to open file\": \"عذراً، فشل فتح الملف\",\n\t\"unable to open folder\": \"عذراً، فشل فتح المجلد\",\n\t\"unable to save file\": \"عذراً، فشل حفظ الملف\",\n\t\"unable to rename\": \"عذراً، فشلت إعادة التسمية\",\n\t\"unsaved file\": \"هذا الملف لم يحفظ، هل تريد الإغلاق على أي حال؟\",\n\t\"warning\": \"تحذير\",\n\t\"use emmet\": \"استخدام Emmet\",\n\t\"use quick tools\": \"استخدام الأدوات السريعة\",\n\t\"yes\": \"نعم\",\n\t\"encoding\": \"ترميز النص\",\n\t\"syntax highlighting\": \"تمييز الصيغة (Syntax Highlighting)\",\n\t\"read only\": \"للقراءة فقط\",\n\t\"select all\": \"تحديد الكل\",\n\t\"select branch\": \"حدد فرعاً (Branch)\",\n\t\"create new branch\": \"إنشاء فرع جديد\",\n\t\"use branch\": \"استخدام الفرع\",\n\t\"new branch\": \"فرع جديد\",\n\t\"branch\": \"فرع\",\n\t\"key bindings\": \"اختصارات لوحة المفاتيح\",\n\t\"edit\": \"تعديل\",\n\t\"reset\": \"إعادة ضبط\",\n\t\"color\": \"اللون\",\n\t\"select word\": \"تحديد كلمة\",\n\t\"quick tools\": \"الأدوات السريعة\",\n\t\"select\": \"اختيار\",\n\t\"editor font\": \"خط المحرر\",\n\t\"new project\": \"مشروع جديد\",\n\t\"format\": \"تنسيق\",\n\t\"project name\": \"اسم المشروع\",\n\t\"unsupported device\": \"جهازك لا يدعم السمات.\",\n\t\"vibrate on tap\": \"الاهتزاز عند اللمس\",\n\t\"copy command is not supported by ftp.\": \"أمر النسخ غير مدعوم عبر FTP.\",\n\t\"support title\": \"دعم Acode\",\n\t\"fullscreen\": \"شاشة كاملة\",\n\t\"animation\": \"الرسوم المتحركة\",\n\t\"backup\": \"نسخ احتياطي\",\n\t\"restore\": \"استعادة\",\n\t\"backup successful\": \"تم النسخ الاحتياطي بنجاح\",\n\t\"invalid backup file\": \"ملف نسخ احتياطي غير صالح\",\n\t\"live autocompletion\": \"إكمال تلقائي مباشر\",\n\t\"file properties\": \"خصائص الملف\",\n\t\"path\": \"المسار\",\n\t\"type\": \"النوع\",\n\t\"word count\": \"عدد الكلمات\",\n\t\"line count\": \"عدد الأسطر\",\n\t\"last modified\": \"آخر تعديل\",\n\t\"size\": \"الحجم\",\n\t\"share\": \"مشاركة\",\n\t\"show print margin\": \"إظهار هامش الطباعة\",\n\t\"login\": \"تسجيل الدخول\",\n\t\"scrollbar size\": \"حجم شريط التمرير\",\n\t\"cursor controller size\": \"حجم وحدة التحكم بالمؤشر\",\n\t\"none\": \"بلا\",\n\t\"small\": \"صغير\",\n\t\"large\": \"كبير\",\n\t\"floating button\": \"الزر العائم\",\n\t\"confirm on exit\": \"تأكيد عند الخروج\",\n\t\"show console\": \"إظهار وحدة التحكم\",\n\t\"image\": \"صورة\",\n\t\"insert file\": \"إدراج ملف\",\n\t\"insert color\": \"إدراج لون\",\n\t\"powersave mode warning\": \"قم بإيقاف وضع توفير الطاقة للمعاينة في متصفح خارجي.\",\n\t\"exit\": \"خروج\",\n\t\"custom\": \"مخصص\",\n\t\"reset warning\": \"هل أنت متأكد من إعادة تعيين السمة؟\",\n\t\"theme type\": \"نوع السمة\",\n\t\"light\": \"فاتح\",\n\t\"dark\": \"داكن\",\n\t\"file browser\": \"متصفح الملفات\",\n\t\"operation not permitted\": \"العملية غير مسموح بها\",\n\t\"no such file or directory\": \"لا يوجد ملف أو مجلد بهذا الاسم\",\n\t\"input/output error\": \"خطأ في الإدخال/الإخراج\",\n\t\"permission denied\": \"تم رفض الإذن\",\n\t\"bad address\": \"عنوان غير صالح\",\n\t\"file exists\": \"الملف موجود بالفعل\",\n\t\"not a directory\": \"ليس مجلداً\",\n\t\"is a directory\": \"هذا مجلد\",\n\t\"invalid argument\": \"معامل غير صالح\",\n\t\"too many open files in system\": \"عدد كبير جداً من الملفات المفتوحة في النظام\",\n\t\"too many open files\": \"عدد كبير جداً من الملفات المفتوحة\",\n\t\"text file busy\": \"الملف النصي قيد الاستخدام\",\n\t\"no space left on device\": \"لا توجد مساحة تخزين كافية على الجهاز\",\n\t\"read-only file system\": \"نظام الملفات للقراءة فقط\",\n\t\"file name too long\": \"اسم الملف طويل جداً\",\n\t\"too many users\": \"عدد كبير جداً من المستخدمين\",\n\t\"connection timed out\": \"انتهت مهلة الاتصال\",\n\t\"connection refused\": \"تم رفض الاتصال\",\n\t\"owner died\": \"توقف المالك (Owner died)\",\n\t\"an error occurred\": \"حدث خطأ ما\",\n\t\"add ftp\": \"إضافة اتصال FTP\",\n\t\"add sftp\": \"إضافة اتصال SFTP\",\n\t\"save file\": \"حفظ الملف\",\n\t\"save file as\": \"حفظ الملف باسم\",\n\t\"files\": \"الملفات\",\n\t\"help\": \"مساعدة\",\n\t\"file has been deleted\": \"تم حذف الملف {file}!\",\n\t\"feature not available\": \"هذه الميزة متاحة فقط في النسخة المدفوعة.\",\n\t\"deleted file\": \"ملف محذوف\",\n\t\"line height\": \"ارتفاع السطر\",\n\t\"preview info\": \"لتشغيل الملف النشط، انقر مطولاً على أيقونة التشغيل.\",\n\t\"manage all files\": \"امنح التطبيق إذن إدارة جميع الملفات من الإعدادات لتتمكن من التحرير بسهولة.\",\n\t\"close file\": \"إغلاق الملف\",\n\t\"reset connections\": \"إعادة تعيين الاتصالات\",\n\t\"check file changes\": \"التحقق من تغييرات الملف\",\n\t\"open in browser\": \"فتح في المتصفح\",\n\t\"desktop mode\": \"وضع سطح المكتب\",\n\t\"toggle console\": \"تبديل وحدة التحكم\",\n\t\"new line mode\": \"وضع السطر الجديد\",\n\t\"add a storage\": \"إضافة وحدة تخزين\",\n\t\"rate acode\": \"تقييم Acode\",\n\t\"support\": \"الدعم\",\n\t\"downloading file\": \"جاري تنزيل {file}...\",\n\t\"downloading...\": \"جاري التنزيل...\",\n\t\"folder name\": \"اسم المجلد\",\n\t\"keyboard mode\": \"وضع لوحة المفاتيح\",\n\t\"normal\": \"عادي\",\n\t\"app settings\": \"إعدادات التطبيق\",\n\t\"disable in-app-browser caching\": \"تعطيل التخزين المؤقت للمتصفح الداخلي\",\n\t\"copied to clipboard\": \"تم النسخ إلى الحافظة\",\n\t\"remember opened files\": \"تذكر الملفات المفتوحة\",\n\t\"remember opened folders\": \"تذكر المجلدات المفتوحة\",\n\t\"no suggestions\": \"بدون اقتراحات\",\n\t\"no suggestions aggressive\": \"بدون اقتراحات (صارم)\",\n\t\"install\": \"تثبيت\",\n\t\"installing\": \"جاري التثبيت...\",\n\t\"plugins\": \"الإضافات\",\n\t\"recently used\": \"المستخدمة مؤخراً\",\n\t\"update\": \"تحديث\",\n\t\"uninstall\": \"إلغاء التثبيت\",\n\t\"download acode pro\": \"تنزيل Acode Pro\",\n\t\"loading plugins\": \"جاري تحميل الإضافات...\",\n\t\"faqs\": \"الأسئلة الشائعة\",\n\t\"feedback\": \"ملاحظات\",\n\t\"header\": \"الرأس\",\n\t\"sidebar\": \"الشريط الجانبي\",\n\t\"inapp\": \"داخل التطبيق\",\n\t\"browser\": \"المتصفح\",\n\t\"diagonal scrolling\": \"تمرير قطري\",\n\t\"reverse scrolling\": \"تمرير عكسي\",\n\t\"formatter\": \"منسق الكود\",\n\t\"format on save\": \"تنسيق عند الحفظ\",\n\t\"remove ads\": \"إزالة الإعلانات\",\n\t\"fast\": \"سريع\",\n\t\"slow\": \"بطيء\",\n\t\"scroll settings\": \"إعدادات التمرير\",\n\t\"scroll speed\": \"سرعة التمرير\",\n\t\"loading...\": \"جاري التحميل...\",\n\t\"no plugins found\": \"لم يتم العثور على إضافات\",\n\t\"name\": \"الاسم\",\n\t\"username\": \"اسم المستخدم\",\n\t\"optional\": \"اختياري\",\n\t\"hostname\": \"اسم المضيف (Hostname)\",\n\t\"password\": \"كلمة المرور\",\n\t\"security type\": \"نوع الأمان\",\n\t\"connection mode\": \"وضع الاتصال\",\n\t\"port\": \"المنفذ\",\n\t\"key file\": \"ملف المفتاح\",\n\t\"select key file\": \"اختر ملف المفتاح\",\n\t\"passphrase\": \"عبارة المرور\",\n\t\"connecting...\": \"جاري الاتصال...\",\n\t\"type filename\": \"اكتب اسم الملف\",\n\t\"unable to load files\": \"تعذر تحميل الملفات\",\n\t\"preview port\": \"منفذ المعاينة\",\n\t\"find file\": \"البحث عن ملف\",\n\t\"system\": \"النظام\",\n\t\"please select a formatter\": \"يرجى اختيار منسق كود\",\n\t\"case sensitive\": \"حساس لحالة الأحرف\",\n\t\"regular expression\": \"تعبير نمطي (Regex)\",\n\t\"whole word\": \"كلمة بالكامل\",\n\t\"edit with\": \"تحرير بواسطة\",\n\t\"open with\": \"فتح بواسطة\",\n\t\"no app found to handle this file\": \"لم يتم العثور على تطبيق لفتح هذا الملف\",\n\t\"restore default settings\": \"استعادة الإعدادات الافتراضية\",\n\t\"server port\": \"منفذ الخادم\",\n\t\"preview settings\": \"إعدادات المعاينة\",\n\t\"preview settings note\": \"إذا اختلف منفذ المعاينة عن منفذ الخادم، فلن يقوم التطبيق بتشغيل الخادم وسيفتح بدلاً من ذلك الرابط في المتصفح. هذا مفيد عند تشغيل خادم خارجي.\",\n\t\"backup/restore note\": \"سيتم فقط نسخ الإعدادات، السمات المخصصة، الإضافات المثبتة، واختصارات المفاتيح. لن يتم نسخ اتصالات FTP/SFTP أو حالة التطبيق.\",\n\t\"host\": \"المضيف\",\n\t\"retry ftp/sftp when fail\": \"إعادة المحاولة عند فشل FTP/SFTP\",\n\t\"more\": \"المزيد\",\n\t\"thank you :)\": \"شكراً لك :)\",\n\t\"purchase pending\": \"الشراء معلق\",\n\t\"cancelled\": \"ملغي\",\n\t\"local\": \"محلي\",\n\t\"remote\": \"عن بعد\",\n\t\"show console toggler\": \"إظهار زر تبديل وحدة التحكم\",\n\t\"binary file\": \"هذا الملف يحتوي على بيانات ثنائية، هل تريد فتحه؟\",\n\t\"relative line numbers\": \"أرقام أسطر نسبية\",\n\t\"elastic tabstops\": \"علامات تبويب مرنة (Elastic)\",\n\t\"line based rtl switching\": \"تبديل اتجاه RTL لكل سطر\",\n\t\"hard wrap\": \"التفاف صارم\",\n\t\"spellcheck\": \"تدقيق إملائي\",\n\t\"wrap method\": \"طريقة الالتفاف\",\n\t\"use textarea for ime\": \"استخدام Textarea لدعم IME\",\n\t\"invalid plugin\": \"إضافة غير صالحة\",\n\t\"type command\": \"اكتب أمراً\",\n\t\"plugin\": \"إضافة\",\n\t\"quicktools trigger mode\": \"نمط تشغيل الأدوات السريعة\",\n\t\"print margin\": \"هامش الطباعة\",\n\t\"touch move threshold\": \"حساسية حركة اللمس\",\n\t\"info-retryremotefsafterfail\": \"إعادة محاولة اتصال FTP/SFTP تلقائياً عند الفشل.\",\n\t\"info-fullscreen\": \"إخفاء شريط العنوان في الشاشة الرئيسية.\",\n\t\"info-checkfiles\": \"التحقق من تغييرات الملفات عندما يكون التطبيق في الخلفية.\",\n\t\"info-console\": \"اختر وحدة تحكم JavaScript. Legacy هي الافتراضية، و Eruda هي وحدة تحكم خارجية.\",\n\t\"info-keyboardmode\": \"وضع لوحة المفاتيح لإدخال النص؛ خيار 'بدون اقتراحات' سيخفي الاقتراحات والتصحيح التلقائي.\",\n\t\"info-rememberfiles\": \"تذكر الملفات المفتوحة عند إغلاق التطبيق.\",\n\t\"info-rememberfolders\": \"تذكر المجلدات المفتوحة عند إغلاق التطبيق.\",\n\t\"info-floatingbutton\": \"إظهار أو إخفاء زر الأدوات السريعة العائم.\",\n\t\"info-openfilelistpos\": \"مكان عرض قائمة الملفات النشطة.\",\n\t\"info-touchmovethreshold\": \"إذا كانت حساسية اللمس عالية جداً، زد هذه القيمة لتجنب التحريك العرضي.\",\n\t\"info-scroll-settings\": \"تحتوي هذه الإعدادات على خيارات التمرير بما في ذلك التفاف النص.\",\n\t\"info-animation\": \"إذا شعرت ببطء في التطبيق، قم بتعطيل الرسوم المتحركة.\",\n\t\"info-quicktoolstriggermode\": \"غير هذه القيمة إذا كانت أزرار الأدوات السريعة لا تعمل بشكل صحيح.\",\n\t\"info-checkForAppUpdates\": \"التحقق من تحديثات التطبيق تلقائياً.\",\n\t\"info-quickTools\": \"إظهار أو إخفاء الأدوات السريعة.\",\n\t\"info-showHiddenFiles\": \"عرض الملفات والمجلدات المخفية (التي تبدأ بنقطة .).\",\n\t\"info-all_file_access\": \"تمكين الوصول إلى /sdcard و /storage في الطرفية.\",\n\t\"info-fontSize\": \"حجم الخط المستخدم لعرض النص.\",\n\t\"info-fontFamily\": \"عائلة الخط المستخدمة لعرض النص.\",\n\t\"info-theme\": \"سمة ألوان الطرفية.\",\n\t\"info-cursorStyle\": \"شكل المؤشر عند التركيز على الطرفية.\",\n\t\"info-cursorInactiveStyle\": \"شكل المؤشر عندما لا تكون الطرفية في حالة تركيز.\",\n\t\"info-fontWeight\": \"سماكة الخط المستخدم للنصوص غير العريضة.\",\n\t\"info-cursorBlink\": \"ما إذا كان المؤشر يومض أم لا.\",\n\t\"info-scrollback\": \"عدد الأسطر التي يتم الاحتفاظ بها في ذاكرة التمرير للخلف بالطرفية.\",\n\t\"info-tabStopWidth\": \"حجم مسافات التبويب (Tab) في الطرفية.\",\n\t\"info-letterSpacing\": \"المسافة بين الأحرف بالبكسل.\",\n\t\"info-imageSupport\": \"ما إذا كانت الصور مدعومة في الطرفية.\",\n\t\"info-fontLigatures\": \"ما إذا كانت ربطات الخط (Ligatures) مفعلة في الطرفية.\",\n\t\"info-confirmTabClose\": \"طلب تأكيد قبل إغلاق تبويبات الطرفية.\",\n\t\"info-backup\": \"إنشاء نسخة احتياطية لتثبيت الطرفية.\",\n\t\"info-restore\": \"استعادة نسخة احتياطية لتثبيت الطرفية.\",\n\t\"info-uninstall\": \"إلغاء تثبيت الطرفية.\",\n\t\"owned\": \"مملوك\",\n\t\"api_error\": \"خادم API معطل، يرجى المحاولة لاحقاً.\",\n\t\"installed\": \"مثبت\",\n\t\"all\": \"الكل\",\n\t\"medium\": \"متوسط\",\n\t\"refund\": \"استرداد الأموال\",\n\t\"product not available\": \"المنتج غير متوفر\",\n\t\"no-product-info\": \"هذا المنتج غير متوفر في بلدك حالياً، يرجى المحاولة لاحقاً.\",\n\t\"close\": \"إغلاق\",\n\t\"explore\": \"استكشاف\",\n\t\"key bindings updated\": \"تم تحديث اختصارات المفاتيح\",\n\t\"search in files\": \"البحث في الملفات\",\n\t\"exclude files\": \"استثناء الملفات\",\n\t\"include files\": \"تضمين الملفات\",\n\t\"search result\": \"وجد {matches} نتيجة في {files} ملفاً.\",\n\t\"invalid regex\": \"تعبير نمطي غير صالح: {message}.\",\n\t\"bottom\": \"أسفل\",\n\t\"save all\": \"حفظ الكل\",\n\t\"close all\": \"إغلاق الكل\",\n\t\"unsaved files warning\": \"بعض الملفات غير محفوظة. اضغط 'موافق' للاختيار أو 'إلغاء' للعودة.\",\n\t\"save all warning\": \"هل أنت متأكد من حفظ جميع الملفات والإغلاق؟ لا يمكن التراجع عن هذا الإجراء.\",\n\t\"save all changes warning\": \"هل أنت متأكد من حفظ جميع التغييرات؟\",\n\t\"close all warning\": \"هل أنت متأكد من إغلاق جميع الملفات؟ ستفقد أي تغييرات غير محفوظة.\",\n\t\"refresh\": \"تحديث\",\n\t\"shortcut buttons\": \"أزرار الاختصار\",\n\t\"no result\": \"لا توجد نتائج\",\n\t\"searching...\": \"جاري البحث...\",\n\t\"quicktools:ctrl-key\": \"مفتاح Control/Command\",\n\t\"quicktools:tab-key\": \"مفتاح Tab\",\n\t\"quicktools:shift-key\": \"مفتاح Shift\",\n\t\"quicktools:undo\": \"تراجع\",\n\t\"quicktools:redo\": \"إعادة\",\n\t\"quicktools:search\": \"البحث في الملف\",\n\t\"quicktools:save\": \"حفظ الملف\",\n\t\"quicktools:esc-key\": \"مفتاح Escape\",\n\t\"quicktools:curlybracket\": \"إدراج قوس متعرج { }\",\n\t\"quicktools:squarebracket\": \"إدراج قوس مربع [ ]\",\n\t\"quicktools:parentheses\": \"إدراج قوسين ( )\",\n\t\"quicktools:anglebracket\": \"إدراج قوس زاوية < >\",\n\t\"quicktools:left-arrow-key\": \"سهم يسار\",\n\t\"quicktools:right-arrow-key\": \"سهم يمين\",\n\t\"quicktools:up-arrow-key\": \"سهم لأعلى\",\n\t\"quicktools:down-arrow-key\": \"سهم لأسفل\",\n\t\"quicktools:moveline-up\": \"نقل السطر للأعلى\",\n\t\"quicktools:moveline-down\": \"نقل السطر للأسفل\",\n\t\"quicktools:copyline-up\": \"نسخ السطر للأعلى\",\n\t\"quicktools:copyline-down\": \"نسخ السطر للأسفل\",\n\t\"quicktools:semicolon\": \"إدراج فاصلة منقوطة ;\",\n\t\"quicktools:quotation\": \"إدراج علامة اقتباس \\\"\",\n\t\"quicktools:and\": \"إدراج رمز &\",\n\t\"quicktools:bar\": \"إدراج رمز الشريطة |\",\n\t\"quicktools:equal\": \"إدراج علامة =\",\n\t\"quicktools:slash\": \"إدراج شرطة مائلة /\",\n\t\"quicktools:exclamation\": \"إدراج علامة تعجب !\",\n\t\"quicktools:alt-key\": \"مفتاح Alt\",\n\t\"quicktools:meta-key\": \"مفتاح Windows/Meta\",\n\t\"info-quicktoolssettings\": \"خصص أزرار الاختصارات ومفاتيح لوحة التحكم في شريط الأدوات السريع أسفل المحرر.\",\n\t\"info-excludefolders\": \"استخدم النمط **/node_modules/** لتجاهل ملفات مجلد node_modules في القائمة والبحث.\",\n\t\"missed files\": \"تم فحص {count} ملفاً بعد بدء البحث ولن يتم تضمينها في النتائج.\",\n\t\"remove\": \"إزالة\",\n\t\"quicktools:command-palette\": \"لوحة الأوامر\",\n\t\"default file encoding\": \"ترميز الملف الافتراضي\",\n\t\"remove entry\": \"هل أنت متأكد من إزالة '{name}' من المسارات المحفوظة؟ لن يتم حذف المجلد الفعلي.\",\n\t\"delete entry\": \"تأكيد حذف '{name}'. لا يمكن التراجع عن هذا. هل ترغب في المتابعة؟\",\n\t\"change encoding\": \"إعادة فتح '{file}' بترميز '{encoding}'؟ ستفقد أي تغييرات غير محفوظة.\",\n\t\"reopen file\": \"هل أنت متأكد من إعادة فتح '{file}'؟ ستفقد التغييرات غير المحفوظة.\",\n\t\"plugin min version\": \"{name} متاح فقط في Acode إصدار {v-code} وما فوق. اضغط للتحديث.\",\n\t\"color preview\": \"معاينة اللون\",\n\t\"confirm\": \"تأكيد\",\n\t\"list files\": \"هل تريد سرد جميع الملفات في <strong>{name}</strong>؟ العدد الكبير قد يؤدي لتعطل التطبيق.\",\n\t\"problems\": \"المشكلات\",\n\t\"show side buttons\": \"إظهار الأزرار الجانبية\",\n\t\"bug_report\": \"إرسال تقرير عن خطأ\",\n\t\"verified publisher\": \"ناشر موثق\",\n\t\"most_downloaded\": \"الأكثر تنزيلاً\",\n\t\"newly_added\": \"أضيف حديثاً\",\n\t\"top_rated\": \"الأعلى تقييماً\",\n\t\"rename not supported\": \"إعادة التسمية غير مدعومة في مجلد Termux\",\n\t\"compress\": \"ضغط\",\n\t\"copy uri\": \"نسخ الرابط (URI)\",\n\t\"delete entries\": \"هل أنت متأكد من حذف {count} من العناصر؟\",\n\t\"deleting items\": \"جاري حذف {count} عنصراً...\",\n\t\"import project zip\": \"استيراد مشروع (zip)\",\n\t\"changelog\": \"سجل التغييرات\",\n\t\"notifications\": \"الإشعارات\",\n\t\"no_unread_notifications\": \"لا توجد إشعارات غير مقروءة\",\n\t\"should_use_current_file_for_preview\": \"استخدام الملف الحالي للمعاينة بدلاً من index.html\",\n\t\"fade fold widgets\": \"تلاشي عناصر الطي\",\n\t\"quicktools:home-key\": \"مفتاح Home\",\n\t\"quicktools:end-key\": \"مفتاح End\",\n\t\"quicktools:pageup-key\": \"مفتاح PageUp\",\n\t\"quicktools:pagedown-key\": \"مفتاح PageDown\",\n\t\"quicktools:delete-key\": \"مفتاح Delete\",\n\t\"quicktools:tilde\": \"إدراج رمز ~\",\n\t\"quicktools:backtick\": \"إدراج علامة `\",\n\t\"quicktools:hash\": \"إدراج رمز #\",\n\t\"quicktools:dollar\": \"إدراج رمز $\",\n\t\"quicktools:modulo\": \"إدراج رمز %\",\n\t\"quicktools:caret\": \"إدراج رمز ^\",\n\t\"plugin_enabled\": \"الإضافة مفعلة\",\n\t\"plugin_disabled\": \"الإضافة معطلة\",\n\t\"enable_plugin\": \"تفعيل هذه الإضافة\",\n\t\"disable_plugin\": \"تعطيل هذه الإضافة\",\n\t\"open_source\": \"مفتوح المصدر\",\n\t\"terminal settings\": \"إعدادات الطرفية\",\n\t\"font ligatures\": \"ربطات الخط (Ligatures)\",\n\t\"letter spacing\": \"تباعد الأحرف\",\n\t\"terminal:tab stop width\": \"عرض مسافة Tab\",\n\t\"terminal:scrollback\": \"أسطر ذاكرة التمرير\",\n\t\"terminal:cursor blink\": \"وميض المؤشر\",\n\t\"terminal:font weight\": \"سماكة الخط\",\n\t\"terminal:cursor inactive style\": \"شكل المؤشر غير النشط\",\n\t\"terminal:cursor style\": \"شكل المؤشر\",\n\t\"terminal:font family\": \"عائلة الخطوط\",\n\t\"terminal:convert eol\": \"تحويل EOL\",\n\t\"terminal:confirm tab close\": \"تأكيد إغلاق تبويب الطرفية\",\n\t\"terminal:image support\": \"دعم الصور\",\n\t\"terminal\": \"الطرفية\",\n\t\"allFileAccess\": \"الوصول لجميع الملفات\",\n\t\"fonts\": \"الخطوط\",\n\t\"sponsor\": \"دعم التطبيق\",\n\t\"downloads\": \"تنزيلات\",\n\t\"reviews\": \"مراجعات\",\n\t\"overview\": \"نظرة عامة\",\n\t\"contributors\": \"المساهمون\",\n\t\"quicktools:hyphen\": \"إدراج شرطة -\",\n\t\"check for app updates\": \"التحقق من تحديثات التطبيق\",\n\t\"prompt update check consent message\": \"يمكن لـ Acode التحقق من التحديثات الجديدة عند الاتصال بالإنترنت. هل تريد التفعيل؟\",\n\t\"keywords\": \"كلمات مفتاحية\",\n\t\"author\": \"المؤلف\",\n\t\"filtered by\": \"تصفية حسب\",\n\t\"clean install state\": \"حالة تثبيت نظيف\",\n\t\"backup created\": \"تم إنشاء النسخة الاحتياطية\",\n\t\"restore completed\": \"اكتملت الاستعادة\",\n\t\"restore will include\": \"سيتم استعادة التالي\",\n\t\"restore warning\": \"لا يمكن التراجع عن هذا الإجراء. هل تريد المتابعة؟\",\n\t\"reload to apply\": \"إعادة تحميل لتطبيق التغييرات؟\",\n\t\"reload app\": \"إعادة تحميل التطبيق\",\n\t\"preparing backup\": \"جاري تحضير النسخة الاحتياطية\",\n\t\"collecting settings\": \"تجميع الإعدادات\",\n\t\"collecting key bindings\": \"تجميع اختصارات المفاتيح\",\n\t\"collecting plugins\": \"تجميع معلومات الإضافات\",\n\t\"creating backup\": \"جاري إنشاء ملف النسخة الاحتياطية\",\n\t\"validating backup\": \"التحقق من صحة النسخة الاحتياطية\",\n\t\"restoring key bindings\": \"استعادة اختصارات المفاتيح\",\n\t\"restoring plugins\": \"استعادة الإضافات\",\n\t\"restoring settings\": \"استعادة الإعدادات\",\n\t\"legacy backup warning\": \"هذا تنسيق قديم للنسخ الاحتياطي، بعض الميزات قد تكون محدودة.\",\n\t\"checksum mismatch\": \"عدم تطابق المجموع التدقيقي (Checksum) - قد يكون الملف معدلاً أو تالفاً.\",\n\t\"plugin not found\": \"الإضافة غير موجودة في السجل\",\n\t\"paid plugin skipped\": \"إضافة مدفوعة - لم يتم العثور على عملية شراء\",\n\t\"source not found\": \"ملف المصدر لم يعد موجوداً\",\n\t\"restored\": \"تمت الاستعادة\",\n\t\"skipped\": \"تم التخطي\",\n\t\"backup not valid object\": \"ملف النسخة الاحتياطية ليس كائناً صالحاً\",\n\t\"backup no data\": \"لا توجد بيانات لاستعادتها في ملف النسخة الاحتياطية\",\n\t\"backup legacy warning\": \"هذا تنسيق قديم (v1). بعض الميزات قد تكون محدودة.\",\n\t\"backup missing metadata\": \"بيانات وصفية مفقودة - بعض المعلومات قد لا تتوفر.\",\n\t\"backup checksum mismatch\": \"عدم تطابق في المجموع التدقيقي. تابع بحذر.\",\n\t\"backup checksum verify failed\": \"تعذر التحقق من المجموع التدقيقي\",\n\t\"backup invalid settings\": \"تنسيق إعدادات غير صالح\",\n\t\"backup invalid keybindings\": \"تنسيق اختصارات مفاتيح غير صالح\",\n\t\"backup invalid plugins\": \"تنسيق إضافات مثبتة غير صالح\",\n\t\"issues found\": \"تم العثور على مشكلات\",\n\t\"error details\": \"تفاصيل الخطأ\",\n\t\"active tools\": \"الأدوات النشطة\",\n\t\"available tools\": \"الأدوات المتاحة\",\n\t\"recent\": \"الملفات الأخيرة\",\n\t\"command palette\": \"فتح لوحة الأوامر\",\n\t\"change theme\": \"تغيير السمة\",\n\t\"documentation\": \"الوثائق\",\n\t\"open in terminal\": \"فتح في الطرفية\",\n\t\"developer mode\": \"وضع المطور\",\n\t\"info-developermode\": \"تمكين أدوات المطور (Eruda) لتصحيح الإضافات ومعاينة حالة التطبيق. سيتم تفعيل المستكشف عند بدء التشغيل.\",\n\t\"developer mode enabled\": \"تم تفعيل وضع المطور. استخدم لوحة الأوامر لتبديل المستكشف (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"تم تعطيل وضع المطور\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/be-by.json",
    "content": "{\n\t\"lang\": \"Беларуская\",\n\t\"about\": \"Пра праграму\",\n\t\"active files\": \"Адкрытыя файлы\",\n\t\"alert\": \"Абвестка\",\n\t\"app theme\": \"Тэма праграмы\",\n\t\"autocorrect\": \"Уключыць аўтавыпраўленне?\",\n\t\"autosave\": \"Аўтазахаванне\",\n\t\"cancel\": \"Скасаваць\",\n\t\"change language\": \"Змяніць мову\",\n\t\"choose color\": \"Абраць колер\",\n\t\"clear\": \"ачысціць\",\n\t\"close app\": \"Закрыць праграму?\",\n\t\"commit message\": \"Зафіксаваць паведамленне\",\n\t\"console\": \"Кансоль\",\n\t\"conflict error\": \"Канфлікт! Калі ласка, пачакайце, перш чым зафіксаваць.\",\n\t\"copy\": \"Скапіяваць\",\n\t\"create folder error\": \"Выбачайце, не ўдалося стварыць новы каталог\",\n\t\"cut\": \"Выразаць\",\n\t\"delete\": \"Выдаліць\",\n\t\"dependencies\": \"Залежнасці\",\n\t\"delay\": \"Час у мілісекундах\",\n\t\"editor settings\": \"Налады рэдактара\",\n\t\"editor theme\": \"Тэма рэдактара\",\n\t\"enter file name\": \"Увядзіце назву файла\",\n\t\"enter folder name\": \"Увядзіце назву каталога\",\n\t\"empty folder message\": \"Пусты каталог\",\n\t\"enter line number\": \"Увядзіце нумар радка\",\n\t\"error\": \"Памылка\",\n\t\"failed\": \"Не ўдалося\",\n\t\"file already exists\": \"Файл ужо існуе\",\n\t\"file already exists force\": \"Файл ужо існуе. Перазапісаць?\",\n\t\"file changed\": \" быў зменены, перазагрузіць файл?\",\n\t\"file deleted\": \"Файл выдалены\",\n\t\"file is not supported\": \"Файл не падтрымліваецца\",\n\t\"file not supported\": \"Файлы гэтага тыпу не падтрымліваюцца.\",\n\t\"file too large\": \"Файл занадта вялікі. Максімальны памер {size}\",\n\t\"file renamed\": \"назва файла змененая\",\n\t\"file saved\": \"файл захаваны\",\n\t\"folder added\": \"каталог дададзены\",\n\t\"folder already added\": \"каталог ужо дададзены\",\n\t\"font size\": \"Памер шрыфту\",\n\t\"goto\": \"Перайсці да радка\",\n\t\"icons definition\": \"Азначэнне значкоў\",\n\t\"info\": \"інфармацыя\",\n\t\"invalid value\": \"Хібнае значэнне\",\n\t\"language changed\": \"мова паспяхова зменена\",\n\t\"linting\": \"Правяраць на сінтаксічныя памылкі\",\n\t\"logout\": \"Выйсці\",\n\t\"loading\": \"Загрузка\",\n\t\"my profile\": \"Мой профіль\",\n\t\"new file\": \"Новы файл\",\n\t\"new folder\": \"Новы каталог\",\n\t\"no\": \"Не\",\n\t\"no editor message\": \"Адкрыйце альбо стварыце новы файл і каталог з меню\",\n\t\"not set\": \"Не вызначана\",\n\t\"unsaved files close app\": \"Ёсць незахаваныя файлы. Закрыць праграму?\",\n\t\"notice\": \"Папярэджанне\",\n\t\"open file\": \"Адкрыць файл\",\n\t\"open files and folders\": \"Адкрытыя файлы і каталогі\",\n\t\"open folder\": \"Адкрыць каталог\",\n\t\"open recent\": \"Адкрыць нядаўнія\",\n\t\"ok\": \"добра\",\n\t\"overwrite\": \"Перазапісаць\",\n\t\"paste\": \"Уставіць\",\n\t\"preview mode\": \"Рэжым папярэдняга прагляду\",\n\t\"read only file\": \"Файл адкрыты толькі для чытання. Паспрабуйце \\\"Захаваць як\\\"\",\n\t\"reload\": \"Перазагрузіць\",\n\t\"rename\": \"Змяніць назву\",\n\t\"replace\": \"Замяніць\",\n\t\"required\": \"Гэтае поле абавязковае\",\n\t\"run your web app\": \"Запусціць сеціўную праграму\",\n\t\"save\": \"Захаваць\",\n\t\"saving\": \"Захаванне\",\n\t\"save as\": \"Захаваць як\",\n\t\"save file to run\": \"Захавайце файл для запуску ў браўзеры\",\n\t\"search\": \"Пошук\",\n\t\"see logs and errors\": \"Праглядзець журналы і памылкі\",\n\t\"select folder\": \"Абраць каталог\",\n\t\"settings\": \"Налады\",\n\t\"settings saved\": \"Налады захаваныя\",\n\t\"show line numbers\": \"Паказваць нумары радкоў\",\n\t\"show hidden files\": \"Паказваць схаваныя файлы\",\n\t\"show spaces\": \"Паказваць прагалы\",\n\t\"soft tab\": \"Мяккая табуляцыя\",\n\t\"sort by name\": \"Сартаваць па назве\",\n\t\"success\": \"Паспяхова\",\n\t\"tab size\": \"Памер табуляцыя\",\n\t\"text wrap\": \"Перанос тэксту\",\n\t\"theme\": \"Тэма\",\n\t\"unable to delete file\": \"немагчыма выдаліць файл\",\n\t\"unable to open file\": \"Выбачайце, файл немагчыма адкрыць\",\n\t\"unable to open folder\": \"Выбачайце, каталог немагчыма адкрыць\",\n\t\"unable to save file\": \"Выбачайце, файл немагчыма захаваць\",\n\t\"unable to rename\": \"Выбачайце, немагчыма змяніць назву\",\n\t\"unsaved file\": \"Файл не захаваны, усё адно закрыць?\",\n\t\"warning\": \"Папярэджанне\",\n\t\"use emmet\": \"Выкарыстоўваць emmet\",\n\t\"use quick tools\": \"Выкарыстоўваць хуткія інструменты\",\n\t\"yes\": \"Так\",\n\t\"encoding\": \"Кадаванне\",\n\t\"syntax highlighting\": \"Падсвятленне сінтаксісу\",\n\t\"read only\": \"Толькі чытанне\",\n\t\"select all\": \"Абраць усё\",\n\t\"select branch\": \"Абраць галіну\",\n\t\"create new branch\": \"Стварыць новую галіну\",\n\t\"use branch\": \"Выкарыстоўваць галіну\",\n\t\"new branch\": \"Новая галіна\",\n\t\"branch\": \"Галіна\",\n\t\"key bindings\": \"Прывязванне клавіш\",\n\t\"edit\": \"Рэдагаваць\",\n\t\"reset\": \"Скінуць\",\n\t\"color\": \"Колер\",\n\t\"select word\": \"Абраць слова\",\n\t\"quick tools\": \"Хуткія інструменты\",\n\t\"select\": \"Абраць\",\n\t\"editor font\": \"Шрыфт рэдактара\",\n\t\"new project\": \"Новы праект\",\n\t\"format\": \"Фарматаванне\",\n\t\"project name\": \"Назва праекта\",\n\t\"unsupported device\": \"Ваша прылада не падтрымлівае тэмы.\",\n\t\"vibrate on tap\": \"Вібрацыя пры націсканні\",\n\t\"copy command is not supported by ftp.\": \"Капіяванне не падтрымліваецца для FTP.\",\n\t\"support title\": \"Падтрымка Acode\",\n\t\"fullscreen\": \"Поўнаэкранны рэжым\",\n\t\"animation\": \"Анімацыя\",\n\t\"backup\": \"Рэзервовае капіяванне\",\n\t\"restore\": \"Аднаўленне\",\n\t\"backup successful\": \"Рэзервовая копія паспяхова створана\",\n\t\"invalid backup file\": \"Не ўдалося стварыць рэзервовую копію\",\n\t\"add path\": \"Дадаць шлях\",\n\t\"live autocompletion\": \"Імгненнае аўтазапаўненне\",\n\t\"file properties\": \"Уласцівасці файла\",\n\t\"path\": \"Шлях\",\n\t\"type\": \"Тып\",\n\t\"word count\": \"Колькасць слоў\",\n\t\"line count\": \"Колькасць радкоў\",\n\t\"last modified\": \"Апошняя змена\",\n\t\"size\": \"Памер\",\n\t\"share\": \"Абагуліць\",\n\t\"show print margin\": \"Паказаць поле друку\",\n\t\"login\": \"Лагін\",\n\t\"scrollbar size\": \"Памер паласы пракручвання\",\n\t\"cursor controller size\": \"Памер курсора\",\n\t\"none\": \"Няма\",\n\t\"small\": \"Маленькі\",\n\t\"large\": \"Вялікі\",\n\t\"floating button\": \"Выплыўная панэль кнопак\",\n\t\"confirm on exit\": \"Пацвярджэнне выхаду\",\n\t\"show console\": \"Паказваць кансоль\",\n\t\"image\": \"Выява\",\n\t\"insert file\": \"Уставіць файл\",\n\t\"insert color\": \"Уставіць колер\",\n\t\"powersave mode warning\": \"Для папярэдняга прагляду ў вонкавым браўзеры адключыце рэжым энергазберажэння.\",\n\t\"exit\": \"Выйсці\",\n\t\"custom\": \"Адвольна\",\n\t\"reset warning\": \"Сапраўды хочаце скінуць тэму?\",\n\t\"theme type\": \"Тып тэмы\",\n\t\"light\": \"Светлая\",\n\t\"dark\": \"Цёмная\",\n\t\"file browser\": \"Агляд файлаў\",\n\t\"operation not permitted\": \"Аперацыя забароненая\",\n\t\"no such file or directory\": \"Такі файл альбо каталог не існуе\",\n\t\"input/output error\": \"Памылка ўводу або вываду\",\n\t\"permission denied\": \"Адмоўлена ў доступе\",\n\t\"bad address\": \"Няправільны адрас\",\n\t\"file exists\": \"Файл існуе\",\n\t\"not a directory\": \"Гэта не каталог\",\n\t\"is a directory\": \"Гэта каталог\",\n\t\"invalid argument\": \"Хібны аргумент\",\n\t\"too many open files in system\": \"Занадта шмат адкрытых файлаў у сістэме\",\n\t\"too many open files\": \"Занадта шмат адкрытых файлаў\",\n\t\"text file busy\": \"Тэкставы файл заняты\",\n\t\"no space left on device\": \"На прыладзе скончылася вольнае месца\",\n\t\"read-only file system\": \"Файлавая сістэма даступная толькі для чытання\",\n\t\"file name too long\": \"Назва файла занадта доўгая\",\n\t\"too many users\": \"Занадта шмат карыстальнікаў\",\n\t\"connection timed out\": \"Час чакання злучэння скончыўся\",\n\t\"connection refused\": \"Злучэнне адкінута\",\n\t\"owner died\": \"Уладальнік знік\",\n\t\"an error occurred\": \"Адбылася памылка\",\n\t\"add ftp\": \"Дадаць FTP\",\n\t\"add sftp\": \"Дадаць SFTP\",\n\t\"save file\": \"Захаваць файл\",\n\t\"save file as\": \"Захаваць файл як\",\n\t\"files\": \"Файлы\",\n\t\"help\": \"Даведка\",\n\t\"file has been deleted\": \"{file} выдалены!\",\n\t\"feature not available\": \"Гэтая функцыя даступная толькі для ў платнай версіі праграмы.\",\n\t\"deleted file\": \"Выдалены файл\",\n\t\"line height\": \"Вышыня радка\",\n\t\"preview info\": \"Калі вы хочаце запусціць актыўны файл, націсніце і ўтрымлівайце значок прайгравання.\",\n\t\"manage all files\": \"Дазвольце Acode кіраваць усімі файламі ў наладах, каб праграма мела магчымасць рэдагаваць файлы.\",\n\t\"close file\": \"Закрыць файл\",\n\t\"reset connections\": \"Скінуць злучэнні\",\n\t\"check file changes\": \"Правяраць файлы на наяўнасць зменаў\",\n\t\"open in browser\": \"Адкрыць у браўзеры\",\n\t\"desktop mode\": \"Настольны рэжым\",\n\t\"toggle console\": \"Пераключэнне кансолі\",\n\t\"new line mode\": \"Рэжым новага радка\",\n\t\"add a storage\": \"Дадаць сховішча\",\n\t\"rate acode\": \"Ацаніць Acode\",\n\t\"support\": \"Падтрымка\",\n\t\"downloading file\": \"Спампоўванне {file}\",\n\t\"downloading...\": \"Спампоўванне...\",\n\t\"folder name\": \"Назва каталога\",\n\t\"keyboard mode\": \"Рэжым клавіятуры\",\n\t\"normal\": \"Звычайны\",\n\t\"app settings\": \"Налады праграмы\",\n\t\"disable in-app-browser caching\": \"Адключыць кэшаванне ў праграмным сродку агляду\",\n\t\"Should use Current File For preview instead of default (index.html)\": \"Для папярэдняга прагляду варта выкарыстоўваць бягучы файл замест прадвызначанага (index.html)\",\n\t\"copied to clipboard\": \"Скапіявана ў буфер абмену\",\n\t\"remember opened files\": \"Запамінаць адкрытыя файлы\",\n\t\"remember opened folders\": \"Запамінаць адкрытыя каталогі\",\n\t\"no suggestions\": \"Няма прапаноў\",\n\t\"no suggestions aggressive\": \"Без настойлівых прапаноў\",\n\t\"install\": \"Усталяваць\",\n\t\"installing\": \"Усталяванне...\",\n\t\"plugins\": \"Убудовы\",\n\t\"recently used\": \"Нядаўна выкарыстаныя\",\n\t\"update\": \"Абнавіць\",\n\t\"uninstall\": \"Выдаліць\",\n\t\"download acode pro\": \"Спампаваць Acode pro\",\n\t\"loading plugins\": \"Загрузка ўбудоў\",\n\t\"faqs\": \"Частыя пытанні\",\n\t\"feedback\": \"Зваротная сувязь\",\n\t\"header\": \"Загаловак\",\n\t\"sidebar\": \"Бакавая панэль\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Браўзер\",\n\t\"diagonal scrolling\": \"Дыяганальнае пракручванне\",\n\t\"reverse scrolling\": \"Адваротнае пракручванне\",\n\t\"formatter\": \"Сродкі фарматавання\",\n\t\"format on save\": \"Фарматаваць пры захаванні\",\n\t\"remove ads\": \"Выдаліць рэкламу\",\n\t\"fast\": \"Хутка\",\n\t\"slow\": \"Павольна\",\n\t\"scroll settings\": \"Налады пракручвання\",\n\t\"scroll speed\": \"Хуткасць пракручвання\",\n\t\"loading...\": \"Загрузка...\",\n\t\"no plugins found\": \"Не знойдзена ўбудоў\",\n\t\"name\": \"Назва\",\n\t\"username\": \"Імя карыстальніка\",\n\t\"optional\": \"неабавязкова\",\n\t\"hostname\": \"Назва хоста\",\n\t\"password\": \"Пароль\",\n\t\"security type\": \"Тып бяспекі\",\n\t\"connection mode\": \"Рэжым злучэння\",\n\t\"port\": \"Порт\",\n\t\"key file\": \"Файл ключа\",\n\t\"select key file\": \"Абраць файл ключа\",\n\t\"passphrase\": \"Парольная фраза\",\n\t\"connecting...\": \"Злучэнне...\",\n\t\"type filename\": \"Увядзіце назву ключа\",\n\t\"unable to load files\": \"Немагчыма загрузіць файлы\",\n\t\"preview port\": \"Порт папярэдняга прагляду\",\n\t\"find file\": \"Пошук файлаў\",\n\t\"system\": \"Сістэмны\",\n\t\"please select a formatter\": \"Абярыце сродак фарматавання\",\n\t\"case sensitive\": \"Зважаць на рэгістр\",\n\t\"regular expression\": \"Рэгулярны выраз\",\n\t\"whole word\": \"Цэлае слова\",\n\t\"edit with\": \"Рэдагаваць у\",\n\t\"open with\": \"Адкрыць у\",\n\t\"no app found to handle this file\": \"Няма праграмы для апрацоўвання гэтага файла\",\n\t\"restore default settings\": \"Аднавіць прадвызначаныя налады\",\n\t\"server port\": \"Порт сервера\",\n\t\"preview settings\": \"Налады папярэдняга прагляду\",\n\t\"preview settings note\": \"Калі порт папярэдняга прагляду і порт сервера адрозніваюцца, праграма не запусціць сервер, а адкрые ў браўзеры або ўбудаваным браўзеры https://<host>:<preview port>. Гэта карысна, калі вы працуеце з серверам у іншым месцы.\",\n\t\"backup/restore note\": \"Будуць стварацца рэзервовыя копіі вашых налад, тэмаў, усталяваных убудоў і прывязаных клавіш. Рэзервовая копія вашага FTP/SFTP або стану праграмы стварацца не будзе.\",\n\t\"host\": \"Хост\",\n\t\"retry ftp/sftp when fail\": \"Пры няўдачы паўтараць спробу падлучэння да ftp/sftp\",\n\t\"more\": \"Яшчэ\",\n\t\"thank you :)\": \"Дзякуй :)\",\n\t\"purchase pending\": \"чаканне куплі\",\n\t\"cancelled\": \"скасавана\",\n\t\"local\": \"Лакальны\",\n\t\"remote\": \"Адлеглы\",\n\t\"show console toggler\": \"Паказваць элемент пераключэння кансолі\",\n\t\"binary file\": \"Гэты файл змяшчае двайковыя даныя, хочаце адкрыць яго?\",\n\t\"relative line numbers\": \"Адносныя нумары радкоў\",\n\t\"elastic tabstops\": \"Эластычныя табулятары\",\n\t\"line based rtl switching\": \"Лінейнае пераключэнне RTL\",\n\t\"hard wrap\": \"Строгі перанос\",\n\t\"spellcheck\": \"Праверка правапісу\",\n\t\"wrap method\": \"Метад пераносу\",\n\t\"use textarea for ime\": \"Выкарыстоўваць тэкставае поле для IME\",\n\t\"invalid plugin\": \"Хібная ўбудова\",\n\t\"type command\": \"Увядзіце каманду\",\n\t\"plugin\": \"Убудова\",\n\t\"quicktools trigger mode\": \"Рэжым запуску хуткіх інструментаў\",\n\t\"print margin\": \"Поле друку\",\n\t\"touch move threshold\": \"Парог адчувальнасці сэнсара\",\n\t\"info-retryremotefsafterfail\": \"Пры няўдачы паўтараць спробу падлучэння да FTP/SFTP.\",\n\t\"info-fullscreen\": \"Схаваць радок загалоўка на галоўным экране.\",\n\t\"info-checkfiles\": \"Правяранне файла на наяўнасць зменаў, калі праграма працуе ў фонавым рэжыме.\",\n\t\"info-console\": \"Абярыце кансоль JavaScript. Legacy - прадвызначаная кансоль, eruda - старонняя кансоль.\",\n\t\"info-keyboardmode\": \"Рэжым клавіятуры для ўводу тэксту. \\\"Без прапаноў\\\" - не будзе прапаноў і аўтаматычнага выпраўлення. Калі параметр не працуе, паспрабуйце змяніць значэнне на \\\"Без настойлівых прапаноў\\\".\",\n\t\"info-rememberfiles\": \"Запамінаць адкрытыя файлы, калі праграма закрытая.\",\n\t\"info-rememberfolders\": \"Запамінаць адкрытыя каталогі, калі праграма закрытая.\",\n\t\"info-floatingbutton\": \"Паказаць або схаваць выплыўную кнопку хуткіх інструментаў.\",\n\t\"info-openfilelistpos\": \"Размяшчэнне спіса актыўных файлаў.\",\n\t\"info-touchmovethreshold\": \"Калі адчувальнасць сэнсара вашай прылады занадта высокая, вы можаце павялічыць гэтае значэнне, каб прадухіліць выпадковыя рухі.\",\n\t\"info-scroll-settings\": \"Гэтыя налады змяшчаюць налады пракручвання, уключаючы перанос тэксту.\",\n\t\"info-animation\": \"Калі ў праграме ёсць затрымліванне, адключыце анімацыю.\",\n\t\"info-quicktoolstriggermode\": \"Калі кнопка ў хуткіх інструментах не працуе, паспрабуйце змяніць гэтае значэнне.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Ва ўласнасці\",\n\t\"api_error\": \"Сервер API не працуе, паспрабуйце праз некаторы час.\",\n\t\"installed\": \"Усталявана\",\n\t\"all\": \"Усе\",\n\t\"medium\": \"Сярэдняя\",\n\t\"refund\": \"Вяртанне грошай\",\n\t\"product not available\": \"Прадукт недаступны\",\n\t\"no-product-info\": \"Гэты прадукт зараз недаступны ў вашай краіне, паўтарыце спробу пазней.\",\n\t\"close\": \"Закрыць\",\n\t\"explore\": \"Агляд\",\n\t\"key bindings updated\": \"Прывязванне клавіш абноўлена\",\n\t\"search in files\": \"Пошук у файлах\",\n\t\"exclude files\": \"Выключаныя файлы\",\n\t\"include files\": \"Уключаныя файлы\",\n\t\"search result\": \"{matches} вынік(аў) у {files} файле(ах).\",\n\t\"invalid regex\": \"Хібны рэгулярны выраз: {message}.\",\n\t\"bottom\": \"Уніз\",\n\t\"save all\": \"Захаваць усё\",\n\t\"close all\": \"Закрыць усе\",\n\t\"unsaved files warning\": \"Некаторыя файлы не былі захаваныя. Націсніце \\\"Добра\\\" і абярыце, што рабіць, або націсніце \\\"Скасаваць\\\", каб вярнуцца.\",\n\t\"save all warning\": \"Сапраўды хочаце захаваць усе файлы і закрыць? Гэтае дзеянне нельга скасаваць.\",\n\t\"save all changes warning\": \"Сапраўды хочаце захаваць усе файлы?\",\n\t\"close all warning\": \"Сапраўды хочаце закрыць усе файлы? Гэтае дзеянне нельга скасаваць, усе файлы страцяцца.\",\n\t\"refresh\": \"Абнавіць\",\n\t\"shortcut buttons\": \"Спалучэнні клавіш для кнопак\",\n\t\"no result\": \"Няма вынікаў\",\n\t\"searching...\": \"Пошук...\",\n\t\"quicktools:ctrl-key\": \"Control (Command)\",\n\t\"quicktools:tab-key\": \"Tab\",\n\t\"quicktools:shift-key\": \"Shift\",\n\t\"quicktools:undo\": \"Адрабіць\",\n\t\"quicktools:redo\": \"Паўтарыць\",\n\t\"quicktools:search\": \"Пошук у файле\",\n\t\"quicktools:save\": \"Захаваць файл\",\n\t\"quicktools:esc-key\": \"Escape\",\n\t\"quicktools:curlybracket\": \"Уставіць фігурную дужку\",\n\t\"quicktools:squarebracket\": \"Уставіць квадратную дужку\",\n\t\"quicktools:parentheses\": \"Уставіць круглую дужку\",\n\t\"quicktools:anglebracket\": \"Уставіць вуглавую дужку\",\n\t\"quicktools:left-arrow-key\": \"Стрэлка \\\"Улева\\\"\",\n\t\"quicktools:right-arrow-key\": \"Стрэлка \\\"Управа\\\"\",\n\t\"quicktools:up-arrow-key\": \"Стрэлка \\\"Уверх\\\"\",\n\t\"quicktools:down-arrow-key\": \"Стрэлка \\\"Уніз\\\"\",\n\t\"quicktools:moveline-up\": \"Перамясціць радок вышэй\",\n\t\"quicktools:moveline-down\": \"Перамясціць радок ніжэй\",\n\t\"quicktools:copyline-up\": \"Скапіяваць радок вышэй\",\n\t\"quicktools:copyline-down\": \"Скапіяваць радок ніжэй\",\n\t\"quicktools:semicolon\": \"Уставіць кропку з коскай\",\n\t\"quicktools:quotation\": \"Уставіць цытату\",\n\t\"quicktools:and\": \"Уставіць &\",\n\t\"quicktools:bar\": \"Уставіць вертыкальную рыску\",\n\t\"quicktools:equal\": \"Уставіць знак роўна\",\n\t\"quicktools:slash\": \"Уставіць касую рыску\",\n\t\"quicktools:exclamation\": \"Уставіць клічнік\",\n\t\"quicktools:alt-key\": \"Alt\",\n\t\"quicktools:meta-key\": \"Windows (Meta)\",\n\t\"info-quicktoolssettings\": \"Наладзьце спалучэнні клавішы і клавішы клавіятуры ў кантэйнеры хуткіх інструментаў пад рэдактарам, каб павысіць зручнасць працы.\",\n\t\"info-excludefolders\": \"Выкарыстоўвайце шаблон **/node_modules/**, каб ігнараваць усе файлы з каталога node_modules. Гэта выключыць файлы са спіса і пошуку файлаў.\",\n\t\"missed files\": \"Ад пачатку пошуку апрацавана {count} файлаў. Яны не будуць уключаны ў пошук.\",\n\t\"remove\": \"Выдаліць\",\n\t\"quicktools:command-palette\": \"Палітра каманд\",\n\t\"default file encoding\": \"Прадвызначанае кадаванне файлаў\",\n\t\"remove entry\": \"Сапраўды хочаце выдаліць \\\"{name}\\\" з захаваных шляхоў? Звярніце ўвагу, што сам шлях не выдаліцца.\",\n\t\"delete entry\": \"Пацвярджэнне выдалення: \\\"{name}\\\". Гэтае дзеянне нельга скасаваць. Працягнуць?\",\n\t\"change encoding\": \"Паўторна адкрыць \\\"{file}\\\" з кадаваннем \\\"{encoding}\\\"? Усе незахаваныя змены страцяцца. Хочаце працягнуць паўторнае адкрыццё?\",\n\t\"reopen file\": \"Сапраўды хочаце паўторна адкрыць \\\"{file}\\\"? Усе незахаваныя змены страцяцца.\",\n\t\"plugin min version\": \"{name} даступна толькі ў Acode - {v-code} і вышэй. Націсніце тут, каб абнавіць.\",\n\t\"color preview\": \"Папярэдні прагляд колеру\",\n\t\"confirm\": \"Пацвердзіць\",\n\t\"list files\": \"Пералічыць усе файлы ў <strong>{name}</strong>? Калі іх будзе занадта шмат, гэта можа прывесці да аварыйнага завяршэння праграмы.\",\n\t\"problems\": \"Праблемы\",\n\t\"show side buttons\": \"Паказваць бакавыя кнопкі\",\n\t\"bug_report\": \"Паведаміць пра хібу\",\n\t\"verified publisher\": \"Правераная асоба\",\n\t\"most_downloaded\": \"Найбольш спампоўваліся\",\n\t\"newly_added\": \"Нядаўна дададзеныя\",\n\t\"top_rated\": \"Найвышэйшы рэйтынг\",\n\t\"rename not supported\": \"Змена назвы ў каталозе termux не падтрымліваецца\",\n\t\"compress\": \"Запакаваць\",\n\t\"copy uri\": \"Скапіяваць URl\",\n\t\"delete entries\": \"Сапраўды хочаце выдаліць {count} элемент(аў)?\",\n\t\"deleting items\": \"Выдаленне {count} элемента(аў)...\",\n\t\"import project zip\": \"Імпартаваць праект(zip)\",\n\t\"changelog\": \"Журнал змен\",\n\t\"notifications\": \"Апавяшчэнні\",\n\t\"no_unread_notifications\": \"Няма непрачытаных апавяшчэнняў\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Спонсар\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/bn-bd.json",
    "content": "{\n\t\"lang\": \"বাংলা\",\n\t\"about\": \"সম্পর্কে\",\n\t\"active files\": \"সক্রিয় ফাইল\",\n\t\"alert\": \"সতর্কীকরণ\",\n\t\"app theme\": \"অ্যাপ থীম\",\n\t\"autocorrect\": \" অটো কারেক্ট সক্রীয় করুন?\",\n\t\"autosave\": \"অটো সংরক্ষণ\",\n\t\"cancel\": \"বাতিল করুন\",\n\t\"change language\": \"ভাষা বদলান\",\n\t\"choose color\": \"কালার পছন্দ করুন\",\n\t\"clear\": \"মুছে ফেলুন\",\n\t\"close app\": \"অ্যাপটি বন্ধ করুন?\",\n\t\"commit message\": \"কমিট মেসেজ\",\n\t\"console\": \"কনসোল\",\n\t\"conflict error\": \"কনফ্লিক্ট। আরেকটি কমিট করার আগে অপেক্ষা করুন। \",\n\t\"copy\": \" কপি\",\n\t\"create folder error\": \"দুঃখিত, ফোল্ডার তৈরী করতে অসমর্থ\",\n\t\"cut\": \"কাট\",\n\t\"delete\": \"ডিলেট\",\n\t\"dependencies\": \"নির্ভরতা\",\n\t\"delay\": \"মিলিসেকেন্ডে সময়\",\n\t\"editor settings\": \"সম্পাদক সেটিংস\",\n\t\"editor theme\": \"সম্পাদক থীম\",\n\t\"enter file name\": \"ফাইল এর নাম লিখুন\",\n\t\"enter folder name\": \"ফোল্ডার এর নাম লিখুন\",\n\t\"empty folder message\": \"ফাঁকা ফোল্ডার এর বার্তা\",\n\t\"enter line number\": \"লাইন নম্বর লিখুন\",\n\t\"error\": \"ত্রুটি\",\n\t\"failed\": \"ব্যার্থ\",\n\t\"file already exists\": \"ফাইলটি বিদ্যমান\",\n\t\"file already exists force\": \"ফাইলটি বিদ্যমান. ওভার রাইট?\",\n\t\"file changed\": \"ফাইলটি পরিবর্তিত হয়েছে, আবার লোড করুন?\",\n\t\"file deleted\": \"ফাইল ডিলেটেড \",\n\t\"file is not supported\": \"ফাইল অসমর্থিত\",\n\t\"file not supported\": \"এই ধরনের ফাইল অসমর্থিত\",\n\t\"file too large\": \"ফাইলটি তুলনামূকভাবে বড়। সর্বোচ্চ অনুমোদিত সাইজ হচ্ছে {size}\",\n\t\"file renamed\": \"ফাইল এর নাম পরিবর্তিত হয়েছে\",\n\t\"file saved\": \"ফাইল সংরক্ষিত\",\n\t\"folder added\": \"ফোল্ডার যোগ হয়েছে\",\n\t\"folder already added\": \"ফোল্ডারটি আগেই সংযোজিত\",\n\t\"font size\": \"ফন্টের আকার\",\n\t\"goto\": \"লাইনে যান\",\n\t\"icons definition\": \"প্রতীক বিবরন\",\n\t\"info\": \"তথ্য\",\n\t\"invalid value\": \"অকার্যকর মান\",\n\t\"language changed\": \"সফলভাবে ভাষা পরিবর্তন হয়েছে\",\n\t\"linting\": \"সিনট্যাক্স চেক করুন\",\n\t\"logout\": \"লগ আউট\",\n\t\"loading\": \"লোড হচ্ছে\",\n\t\"my profile\": \"আমার প্রোফাইল\",\n\t\"new file\": \"নতুন ফাইল\",\n\t\"new folder\": \"নতুন ফোল্ডার\",\n\t\"no\": \"না\",\n\t\"no editor message\": \"মেনু থেকে নতুন ফাইল বা ফোল্ডার খুলুন বা তৈরি করুন\",\n\t\"not set\": \"নির্দিষ্ট করা নেই\",\n\t\"unsaved files close app\": \"অসংরক্ষিত ফাইল বিদ্যমান। অ্যাপ বন্ধ করবেন?\",\n\t\"notice\": \"বিজ্ঞপ্তি\",\n\t\"open file\": \"ফাইল খুলুন\",\n\t\"open files and folders\": \"ফাইল এবং ফোল্ডার খুলুন\",\n\t\"open folder\": \"ফোল্ডার খুলুন\",\n\t\"open recent\": \"সাম্প্রতিক ফাইল\",\n\t\"ok\": \"ঠিক আছে\",\n\t\"overwrite\": \"ওভাররাইট\",\n\t\"paste\": \"পেস্ট\",\n\t\"preview mode\": \"প্রদর্শন মোড\",\n\t\"read only file\": \"রিড অনলি ফাইল সংরক্ষণে অসমর্থ। অনুগ্রপূর্বক সেইভ অ্যাস করুন\",\n\t\"reload\": \"পুনরায় লোড করুন\",\n\t\"rename\": \"পুণ: নামকরন\",\n\t\"replace\": \"প্রতিস্থাপন\",\n\t\"required\": \"এই ফিল্ডটি প্রয়োজনীয়\",\n\t\"run your web app\": \"আপনার ওয়েব অ্যাপ রান করুন\",\n\t\"save\": \"সংরক্ষণ করুন\",\n\t\"saving\": \"সংরক্ষিত হচ্ছে\",\n\t\"save as\": \"সংরক্ষণের ধরন\",\n\t\"save file to run\": \"অনুগ্রহ পূর্বক ব্রাউজারে রান করানোর আগে ফাইল টি সংরক্ষণ করুন\",\n\t\"search\": \"খুঁজুন\",\n\t\"see logs and errors\": \"logs এবং ভুল গুলো দেখুন\",\n\t\"select folder\": \"ফোল্ডার নির্বাচন করুন\",\n\t\"settings\": \"সেটিংস\",\n\t\"settings saved\": \"সেটিংস সংরক্ষিত হয়েছে\",\n\t\"show line numbers\": \"লাইন নাম্বার দেখান\",\n\t\"show hidden files\": \"লুকায়িত ফাইলগুলো দেখুন\",\n\t\"show spaces\": \"ফাকাগুলি দেখুন\",\n\t\"soft tab\": \"Soft tab\",\n\t\"sort by name\": \"নাম অনুসারে সাজান\",\n\t\"success\": \"সফল\",\n\t\"tab size\": \"ট্যাব আকার\",\n\t\"text wrap\": \"টেক্সট মোড়ান /wrap\",\n\t\"theme\": \"থীম\",\n\t\"unable to delete file\": \"ডিলেট করতে অসমর্থ\",\n\t\"unable to open file\": \"দুঃখিত, ফাইলটি খুলতে ব্যার্থ\",\n\t\"unable to open folder\": \"দুঃখিত, ফোল্ডার খুলতে ব্যার্থ\",\n\t\"unable to save file\": \"দুঃখিত, ফাইলটি সংরক্ষণে ব্যার্থ\",\n\t\"unable to rename\": \"দুঃখিত, পুন: নামকরনে ব্যার্থ\",\n\t\"unsaved file\": \"ফাইলটি সংরক্ষণ করা হইনি, তাও বন্ধ করবেন?\",\n\t\"warning\": \"সতর্ক বাণী\",\n\t\"use emmet\": \"emmet ব্যবহার করুন\",\n\t\"use quick tools\": \"quick tools ব্যবহার করুন\",\n\t\"yes\": \"হ্যা\",\n\t\"encoding\": \"টেক্সট এনকোডিং\",\n\t\"syntax highlighting\": \"সিনট্যাক্স হাইলাইট\",\n\t\"read only\": \"রিড অনলি\",\n\t\"select all\": \"সবটুকু নির্বাচন করুন\",\n\t\"select branch\": \"শাখা নির্বাচন করুন\",\n\t\"create new branch\": \"নতুন শাখা খুলুন\",\n\t\"use branch\": \"শাখা ব্যবহার করুন\",\n\t\"new branch\": \"নতুন শাখা\",\n\t\"branch\": \"শাখা\",\n\t\"key bindings\": \"কী বাইন্ডিংস\",\n\t\"edit\": \"সম্পাদন করুন\",\n\t\"reset\": \"রিসেট\",\n\t\"color\": \"রং\",\n\t\"select word\": \"শব্দ নির্বাচন করুন\",\n\t\"quick tools\": \"Quick tools\",\n\t\"select\": \"নির্বাচন\",\n\t\"editor font\": \"সম্পাদক ফন্ট\",\n\t\"new project\": \"নতুন প্রজেক্ট\",\n\t\"format\": \"সাজান\",\n\t\"project name\": \"প্রোজেক্ট এর নাম \",\n\t\"unsupported device\": \"আপনার ডিভাইসটি এই থিম সাপোর্ট করেনা।\",\n\t\"vibrate on tap\": \"Vibrate on tap\",\n\t\"copy command is not supported by ftp.\": \"কপি কমান্ড এফটিপি দ্বারা সমর্থিত নয়।\",\n\t\"support title\": \"Acode কে সমর্থন করুন\",\n\t\"fullscreen\": \"ফুলস্ক্রিন\",\n\t\"animation\": \"অ্যানিমেশন\",\n\t\"backup\": \"ব্যাকআপ\",\n\t\"restore\": \"restore\",\n\t\"backup successful\": \"ব্যাকআপ সফল\",\n\t\"invalid backup file\": \"অবৈধ ব্যাকআপ ফাইল\",\n\t\"add path\": \"পথ যোগ করুন\",\n\t\"live autocompletion\": \"লাইভ স্বয়ংক্রিয়-সম্পূর্ণকরন\",\n\t\"file properties\": \"ফাইলের বৈশিষ্ট্য\",\n\t\"path\": \"পথ\",\n\t\"type\": \"ধরন\",\n\t\"word count\": \"শব্দ গণনা\",\n\t\"line count\": \"লাইন গণনা\",\n\t\"last modified\": \"শেষ সংশোধন\",\n\t\"size\": \"আকার\",\n\t\"share\": \"শেয়ার করুন\",\n\t\"show print margin\": \"প্রিন্ট মার্জিন দেখাও\",\n\t\"login\": \"লগইন\",\n\t\"scrollbar size\": \"স্ক্রলবারের সাইজ\",\n\t\"cursor controller size\": \"কার্সর কন্ট্রলার আকার\",\n\t\"none\": \"none\",\n\t\"small\": \"ছোট\",\n\t\"large\": \"বড়\",\n\t\"floating button\": \"ভাসমান বাটোন\",\n\t\"confirm on exit\": \"প্রস্থান নিশ্চিত করুন\",\n\t\"show console\": \"কনসোল দেখান\",\n\t\"image\": \"ছবি\",\n\t\"insert file\": \"ফাইল যোগ করুন\",\n\t\"insert color\": \"রঙ যোগ করুন\",\n\t\"powersave mode warning\": \"এক্সটার্নাল ব্রাউজারে প্রিভিউ দেখতে পাওয়ার সেভিং মোড বন্ধ করুন।\",\n\t\"exit\": \"প্রস্থান করুন\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"আপনি কি নিশ্চিত যে আপনি থিম রিসেট করতে চান৷?\",\n\t\"theme type\": \"থিমের ধরণ\",\n\t\"light\": \"লাইট\",\n\t\"dark\": \"ডার্ক\",\n\t\"file browser\": \"ফাইল ব্রাউজার\",\n\t\"operation not permitted\": \"কার্যক্রম অনুমোদিত নয়\",\n\t\"no such file or directory\": \"এমন কোন ফাইল বা ডিরেক্টরি নেই\",\n\t\"input/output error\": \"ইনপুট/আউটপুট-এ ত্রুটি\",\n\t\"permission denied\": \"অনুমতি দেয়া হয় নি\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"ফাইল বিদ্যমান\",\n\t\"not a directory\": \"ডিরেক্টরি নয়\",\n\t\"is a directory\": \"একটি ডিরেক্টরি\",\n\t\"invalid argument\": \"অগ্রহণযোগ্য আর্গুমেন্ট\",\n\t\"too many open files in system\": \"সিস্টেমে অনেকগুলো ফাইল খোলা\",\n\t\"too many open files\": \"অনেক ফাইল খোলা আছে\",\n\t\"text file busy\": \"টেক্সট ফাইল ব্যাস্ত\",\n\t\"no space left on device\": \"ডিভাইসে কোন জায়গা অবশিষ্ট নেই\",\n\t\"read-only file system\": \"শুধুমাত্র পাঠযোগ্য ফাইল সিস্টেম\",\n\t\"file name too long\": \"ফাইলের নাম অনেক বড়\",\n\t\"too many users\": \"অনেক বেশি ব্যবহারকারী\",\n\t\"connection timed out\": \"সংযোগের সময় শেষ\",\n\t\"connection refused\": \"সংযোগ প্রত্যাখ্যান করা হয়েছে\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"একটি ত্রুটি ঘটেছে\",\n\t\"add ftp\": \"FTP সংযোগ করুন\",\n\t\"add sftp\": \"SFTP সংযোগ করুন\",\n\t\"save file\": \"ফাইলটি সেভ করুন\",\n\t\"save file as\": \"সেইভ ফাইল অ্যাস\",\n\t\"files\": \"ফাইলসমূহ\",\n\t\"help\": \"সাহায্য\",\n\t\"file has been deleted\": \"{file} ডিলিট করা হয়েছে!\",\n\t\"feature not available\": \"এই বৈশিষ্ট্যটি শুধুমাত্র অ্যাপটির অর্থপ্রদত্ত সংস্করণে উপলব্ধ।\",\n\t\"deleted file\": \"ডিলিট করা ফাইল\",\n\t\"line height\": \"লাইনের উচ্চতা\",\n\t\"preview info\": \"আপনি যদি সক্রিয় ফাইলটি চালাতে চান, তাহলে প্লে আইকনে আলতো চাপুন\",\n\t\"manage all files\": \"Acode কে আপনার ডিভাইসের ফাইলগুলিকে সহজেই এডিট করতে সেটিংসে সমস্ত ফাইল এডিট করার অনুমতি দিন৷\",\n\t\"close file\": \"ফাইল বন্ধ করুন\",\n\t\"reset connections\": \"কানেকশন রিসেট করুন\",\n\t\"check file changes\": \"ফাইলের পরিবর্তন চেক করুন\",\n\t\"open in browser\": \"ব্রাউজারে খুলুন\",\n\t\"desktop mode\": \"ডেক্সটপ মোড\",\n\t\"toggle console\": \"কনসোল চালু/বন্ধ করুন\",\n\t\"new line mode\": \"নিউ লাইন মোড\",\n\t\"add a storage\": \"স্টোরেজ যোগ করুন\",\n\t\"rate acode\": \"Acode-কে রেট করুন\",\n\t\"support\": \"সমর্থন\",\n\t\"downloading file\": \"{file} ডাউনলোড হচ্ছে\",\n\t\"downloading...\": \"ডাউনলোড হচ্ছে...\",\n\t\"folder name\": \"ফোল্ডারের-এর নাম\",\n\t\"keyboard mode\": \"কীবোর্ডের ধরন\",\n\t\"normal\": \"স্বাভাবিক\",\n\t\"app settings\": \"অ্যাপ সেটিংস\",\n\t\"disable in-app-browser caching\": \"ইন-অ্যাপ ব্রাউজারের ক্যাশ বন্ধ করুন\",\n\t\"copied to clipboard\": \"ক্লিপবোর্ডে কপি করা হয়েছে\",\n\t\"remember opened files\": \"খোলা ফাইলগুলো মনে রাখুন\",\n\t\"remember opened folders\": \"খোলা ফোল্ডারগুলো মনে রাখুন\",\n\t\"no suggestions\": \"কোনো পরামর্শ নেই\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"ইনস্টল করুন\",\n\t\"installing\": \" ইনস্টল করা হচ্ছে...\",\n\t\"plugins\": \"প্লাগইন\",\n\t\"recently used\": \"সম্প্রতি ব্যবহৃত\",\n\t\"update\": \"আপডেট করুন\",\n\t\"uninstall\": \"আনইনস্টল করুন\",\n\t\"download acode pro\": \"Acode pro ডাউনলোড করুন\",\n\t\"loading plugins\": \"প্লাগইন লোড হচ্ছে\",\n\t\"faqs\": \"প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী\",\n\t\"feedback\": \"প্রতিক্রিয়া\",\n\t\"header\": \"হেডার\",\n\t\"sidebar\": \"সাইডবার\",\n\t\"inapp\": \"অ্যাপে\",\n\t\"browser\": \"ব্রাউজার\",\n\t\"diagonal scrolling\": \"তির্যক স্ক্রোলিং\",\n\t\"reverse scrolling\": \"বিপরীত স্ক্রোলিং\",\n\t\"formatter\": \"ফরম্যাটার\",\n\t\"format on save\": \"সংরক্ষনের সাথে বিন্যাস করুন\",\n\t\"remove ads\": \"বিজ্ঞাপনগুলি সরান\",\n\t\"fast\": \"দ্রুত\",\n\t\"slow\": \"ধীর\",\n\t\"scroll settings\": \"স্ক্রোল সেটিংস\",\n\t\"scroll speed\": \"স্ক্রোল গতি\",\n\t\"loading...\": \"লোড হচ্ছে...\",\n\t\"no plugins found\": \"কোনও প্লাগইন পাওয়া যায়নি\",\n\t\"name\": \"নাম\",\n\t\"username\": \"ব্যবহারকারীর নাম\",\n\t\"optional\": \"ঐচ্ছিক\",\n\t\"hostname\": \"হোস্টের নাম\",\n\t\"password\": \"পাসওয়ার্ড\",\n\t\"security type\": \"নিরাপত্তার প্রকার\",\n\t\"connection mode\": \"সংযোগের প্রকার\",\n\t\"port\": \"পোর্ট\",\n\t\"key file\": \"কী ফাইল\",\n\t\"select key file\": \"কী ফাইল নির্বাচন করুন\",\n\t\"passphrase\": \"পাসফ্রেজ\",\n\t\"connecting...\": \"সংযুক্ত হচ্ছে...\",\n\t\"type filename\": \"ফাইলের নাম টাইপ করুন\",\n\t\"unable to load files\": \"ফাইল লোড করতে অক্ষম\",\n\t\"preview port\": \"প্রিভিউ পোর্ট\",\n\t\"find file\": \"ফাইলটি খুজুন\",\n\t\"system\": \"সিস্টেম\",\n\t\"please select a formatter\": \"একটি ফর্ম্যাটার নির্বাচন করুন৷\",\n\t\"case sensitive\": \"কেস সেন্সিটিভ\",\n\t\"regular expression\": \"রেগুলার এক্সপ্রেশন\",\n\t\"whole word\": \"পুরো শব্দ\",\n\t\"edit with\": \"এডিট উইথ\",\n\t\"open with\": \"ওপেন উইথ\",\n\t\"no app found to handle this file\": \"এই ফাইলটি খুলার জন্য কোনো অ্যাপ পাওয়া যায়নি\",\n\t\"restore default settings\": \"সেটিংটি পূর্বাবস্থায় ফিরিয়ে আনুন\",\n\t\"server port\": \"সার্ভার পোর্ট\",\n\t\"preview settings\": \"প্রিভিউ সেটিংস\",\n\t\"preview settings note\": \"যদি প্রিভিউ পোর্ট ও সার্ভার পোর্ট ভিন্ন হয়ে থাকে, অ্যাপ খুলবে না এর বদলে ব্রাউজারে অথবা অ্যাাপের ব্রাউজারে https://<host>:<preview port> খুলবে। এটা আপনি অন্য কোথাও সার্ভার চালিয়ে রাখলে ব্যবহার্যোগ্য।\",\n\t\"backup/restore note\": \"এটি শুধুমাত্র আপনার সেটিংস, কাস্টম থিম এবং কী বাইন্ডিং ব্যাকআপ করবে। এটি আপনার FPT/SFTP, GitHub প্রোফাইল ব্যাকআপ করবে না।\",\n\t\"host\": \"হোস্ট\",\n\t\"retry ftp/sftp when fail\": \"এফটিপি/এসএফটিপি ব্যর্থ হলে, পুনরায় চেষ্টা করুন\",\n\t\"more\": \"আরো\",\n\t\"thank you :)\": \"ধন্যবাদ :)\",\n\t\"purchase pending\": \"ক্রয় অপেক্ষারত\",\n\t\"cancelled\": \"বাতিল\",\n\t\"local\": \"স্থানীয়\",\n\t\"remote\": \"দূরবর্তী\",\n\t\"show console toggler\": \"কনসোল টগলার দেখান\",\n\t\"binary file\": \"এই ফাইলটিতে বাইনারি ডেটা রয়েছে, আপনি কি এটি খুলতে চান?\",\n\t\"relative line numbers\": \"আপেক্ষিক লাইন নম্বর\",\n\t\"elastic tabstops\": \"ইল্যাস্টিক ট্যাবস্টপ্‌স\",\n\t\"line based rtl switching\": \"লাইনভিত্তিক আরটিএল সুইচিং\",\n\t\"hard wrap\": \"হার্ড র‍্যাপ\",\n\t\"spellcheck\": \"বানান পরীক্ষণ\",\n\t\"wrap method\": \"র‍্যাপ ম্যাথড\",\n\t\"use textarea for ime\": \"আইএমই এর জন্য টেক্সএরিয়া ব্যবহার করুন\",\n\t\"invalid plugin\": \"অবৈধ প্লাগইন\",\n\t\"type command\": \"কমান্ড লিখুন\",\n\t\"plugin\": \"প্লাগইন\",\n\t\"quicktools trigger mode\": \"কুইক টুলস সক্রিয়ন মোড\",\n\t\"print margin\": \"প্রিন্ট মার্জিন\",\n\t\"touch move threshold\": \"টাচ মুভ সীমা\",\n\t\"info-retryremotefsafterfail\": \"এফটিপি/এসএফটিপি কানেকশন ব্যর্থ হলে, পুনরায় চেষ্টা করুন\",\n\t\"info-fullscreen\": \"হোম স্ক্রিনে টাইটেল-বার লুকান।\",\n\t\"info-checkfiles\": \"অ্যাপ ব্যকগ্রাউন্ডে থাকা অবস্থায় পরিবর্তন পরীক্ষা করুন।\",\n\t\"info-console\": \"জাভাস্ক্রিপ্ট কনসোল নির্বাচন করুন। ল্যাগেসি হচ্ছে সাধারন কনসোল , eruda হচ্ছে থার্ড পার্টি কনসোল\",\n\t\"info-keyboardmode\": \"টেক্সট ইনপুটের জন্য কীবোর্ড মোড। 'No suggestions' সক্রিয় হলে পরামর্শগুলো লুকানো হবে এবং অটো কারেক্ট কাজ করবে। যদি 'No suggestions' কাজ না করে, তবে মানটি 'No suggestions aggressive'-এ পরিবর্তন করে চেষ্টা করুন।\",\n\t\"info-rememberfiles\": \"অ্যাপ বন্ধ হলেও সক্রিয় ফাইলগুলো মনে রাখুন।\",\n\t\"info-rememberfolders\": \"অ্যাপ বন্ধ হলেও সক্রিয় ফোল্ডারগুলো মনে রাখুন।\",\n\t\"info-floatingbutton\": \"কুইক টুলস, ভাসমান বাটন দেখান অথবা লুকান।\",\n\t\"info-openfilelistpos\": \"সক্রিয় ফাইলের তালিকা যেখানে দেখানো হবে।\",\n\t\"info-touchmovethreshold\": \"যদি আপনার ডিভাইসের টাচ সেন্সিটিভিটি বেশি হয়ে থাকে, দূর্ঘটনাবসত স্থানান্তর বন্ধ করতে আপনি এই মানটি বাড়াতে পারেন।\",\n\t\"info-scroll-settings\": \"এই সেটিংস-এর মাঝে স্ক্রল সেটিংসের অন্তর্যুক্ত টেক্সট র‍্যাপ সেটিংস রয়েছে \",\n\t\"info-animation\": \"যদি অ্যাপটি ধীরে কাজ করে, অ্যানিমেশন বন্ধ করুন।\",\n\t\"info-quicktoolstriggermode\": \"যদি quick tools এর বাটনগুলো কাজ না করে, এখানের মানগুলো পরিবর্তন করে চেষ্টা করুন।\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API সার্ভার ডাউন, দয়া করে পুনরায় চেষ্টা করুন\",\n\t\"installed\": \"ইনস্টল করা হয়েছে\",\n\t\"all\": \"সব\",\n\t\"medium\": \"মধ্যম\",\n\t\"refund\": \"রিফান্ড\",\n\t\"product not available\": \"প্রোডাক্ট লভ্য নয়\",\n\t\"no-product-info\": \"এই প্রোডাক্টটি বর্তমানে আপনার দেশে উপলব্ধ নয়, পরে আবার চেষ্টা করুন।\",\n\t\"close\": \"বন্ধ\",\n\t\"explore\": \"এক্সপ্লোর\",\n\t\"key bindings updated\": \"কী বাইন্ডিংস আপডেট হয়েছে\",\n\t\"search in files\": \"ফাইলগুলোর মাঝে খুঁজুন\",\n\t\"exclude files\": \"ফাইল ছাঁটাই করুন\",\n\t\"include files\": \"ফাইল অন্তর্ভুক্ত করুন\",\n\t\"search result\": \"{files}টি ফাইলের মধ্যে {matches}টি ফলাফল পাওয়া গেছে।\",\n\t\"invalid regex\": \"অবৈধ রেগুলার এক্সপ্রেশন: {message}।\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"সব সংরক্ষন করুন\",\n\t\"close all\": \"সব বন্ধ করুন\",\n\t\"unsaved files warning\": \"কিছু ফাইল সংরক্ষিত নয়। 'ok' তে ক্লিক করে কি করা হবে নির্বাচন করুন অথবা 'cancel' চেপে পেছনে ফিরে যান।\",\n\t\"save all warning\": \"আপনি কি নিশ্চিতভাবে সকল ফাইল সংরক্ষন করে বন্ধ চান? এই কার্যক্রমটির ফলাফল বাতিল করা যাবে না।\",\n\t\"save all changes warning\": \"আপনি কি নিশ্চিতভাবে সকল ফাইল সংরক্ষন করতে চান?\",\n\t\"close all warning\": \"আপনি কি নিশ্চিতভাবে সকল ফাইল বন্ধ করতে চান? আপনার অসংরক্ষিত ফলাফলসমূহ হারিয়ে যাবে এবং ফেরানো সম্ভব হবে না।\",\n\t\"refresh\": \"রিফ্রেস\",\n\t\"shortcut buttons\": \"সর্টকাট বাটন\",\n\t\"no result\": \"কোনো ফলাফল নেই\",\n\t\"searching...\": \"খোঁজ চলছে...\",\n\t\"quicktools:ctrl-key\": \"কন্ট্রল/কমান্ড কী\",\n\t\"quicktools:tab-key\": \"ট্যাব কী\",\n\t\"quicktools:shift-key\": \"শিফট কী\",\n\t\"quicktools:undo\": \"আনডু\",\n\t\"quicktools:redo\": \"রিডু\",\n\t\"quicktools:search\": \"ফাইলের মাঝে খুঁজুন\",\n\t\"quicktools:save\": \"ফাইল সংরক্ষণ করুন\",\n\t\"quicktools:esc-key\": \"এসকেপ কী\",\n\t\"quicktools:curlybracket\": \"কার্লি ব্র্যাকেট যুক্ত করুন\",\n\t\"quicktools:squarebracket\": \"স্কয়ার ব্র্যাকেট যুক্ত করুন\",\n\t\"quicktools:parentheses\": \"বন্ধনী যুক্ত করুন\",\n\t\"quicktools:anglebracket\": \"এঙ্গেল ব্র্যাকেট যুক্ত করুন\",\n\t\"quicktools:left-arrow-key\": \"লেফট অ্যারো কী\",\n\t\"quicktools:right-arrow-key\": \"রাইট অ্যাারো কী\",\n\t\"quicktools:up-arrow-key\": \"আপ অ্যাারো কী\",\n\t\"quicktools:down-arrow-key\": \"ডাউন অ্যাারো কী\",\n\t\"quicktools:moveline-up\": \"লাইন উপরে সরান\",\n\t\"quicktools:moveline-down\": \"লাইন নিচে সরান\",\n\t\"quicktools:copyline-up\": \"লাইন উপরে কপি করুন\",\n\t\"quicktools:copyline-down\": \"লাইন নিচে কপি করুন\",\n\t\"quicktools:semicolon\": \"সেমিকোলোন যোগ করুন\",\n\t\"quicktools:quotation\": \"কোটেশন যোগ করুন\",\n\t\"quicktools:and\": \"অ্যাান্ড চিহ্ন যোগ করুন\",\n\t\"quicktools:bar\": \"বার চিহ্ন যুক্ত করুন\",\n\t\"quicktools:equal\": \"সমান চিহ্ন যোগ করুন\",\n\t\"quicktools:slash\": \"স্ল্যাশ চিহ্ন যুক্ত করুন\",\n\t\"quicktools:exclamation\": \"আশ্চর্যবোধক চিহ্ন যোগ করুন\",\n\t\"quicktools:alt-key\": \"আল্ট কী\",\n\t\"quicktools:meta-key\": \"উইন্ডোজ/মেটা কী\",\n\t\"info-quicktoolssettings\": \"সম্পাদকের নিচে Quicktools কন্টেইনারে শর্টকাট বাটন এবং কীবোর্ড কী কাস্টমাইজ করুন, যাতে কোডিং অভিজ্ঞতা উন্নত হয়।\",\n\t\"info-excludefolders\": \"node_modules ফোল্ডারের সব ফাইল উপেক্ষা করতে /node_modules/ প্যাটার্ন ব্যবহার করুন। এটি ফাইলগুলো তালিকাভুক্ত হতে বাধা দেবে এবং সার্চে অন্তর্ভুক্ত হবে না।\",\n\t\"missed files\": \"সার্চ শুরু হওয়ার পরে {count} ফাইল স্ক্যান করা হয়েছে এবং সার্চে অন্তর্ভুক্ত হবে না।\",\n\t\"remove\": \"মুছে ফেলুন\",\n\t\"quicktools:command-palette\": \"কমান্ড প্যালেট\",\n\t\"default file encoding\": \"ডিফল্ট ফাইল এনকোডিং\",\n\t\"remove entry\": \"আপনি কি নিশ্চিতভাবে '{name}' সংরক্ষিত পথ থেকে মুছে ফেলতে চান? মনে রাখবেন এটা মুছে ফেললেও পথটি মুছে যাবে না।\",\n\t\"delete entry\": \"মুছে ফেলা নিশ্চিত করুন: '{name}'। কার্যক্রম অসম্পাদিত করা সম্ভব হবে না। চালিয়ে যান?\",\n\t\"change encoding\": \"পুনরায় '{file}' ফাইলটি '{encoding}'-এ খুলুন? এই কার্যক্রমের মাধ্যমে অসংরক্ষিত পরিবর্তনগুলো হারিয়ে যাবে. আপনি কি নিশ্চিতভাবে পুনরায় খুলতে চান?\",\n\t\"reopen file\": \"আপনি কি নিশ্চিতভাবে '{file}' পুনরায় খুলতে চান? অসংরক্ষিত পরিবর্তনগুলো হারিয়ে যাবে।\",\n\t\"plugin min version\": \"{name} শুধমাত্র Acode - {v-code} কিংবা তার পরবর্তিতে লভ্য. আপডেট করতে এখানে ক্লিক করুন।\",\n\t\"color preview\": \"কালার প্রিভিউ\",\n\t\"confirm\": \"নিশ্চিত\",\n\t\"list files\": \"<strong>{name}</strong>- এর ভেতরের সকল ফাইল তালিকাভুক্ত করুন? অতিরিক্ত ফাইল অ্যাপটি ক্র্যাশ করতে পারে।\",\n\t\"problems\": \"সমস্যাগুলো\",\n\t\"show side buttons\": \"সাইড বাটনগুলো দেখান\",\n\t\"bug_report\": \"বাগ রিপোর্ট জমা দিন\",\n\t\"verified publisher\": \"ভেরিফায়েড প্রকাশক\",\n\t\"most_downloaded\": \"সর্বাধিক ডাউনলোড\",\n\t\"newly_added\": \"নতুন যোগ হয়েছে\",\n\t\"top_rated\": \"সর্বোচ্চ রেটিং\",\n\t\"rename not supported\": \"Termux ডিরেক্টরিতে নাম পরিবর্তন সমর্থিত নয়\",\n\t\"compress\": \"সংকুচিত করুন\",\n\t\"copy uri\": \"URI কপি করুন\",\n\t\"delete entries\": \"আপনি কি নিশ্চিত যে {count} আইটেম মুছে ফেলতে চান?\",\n\t\"deleting items\": \"{count} আইটেম মুছে ফেলা হচ্ছে...\",\n\t\"import project zip\": \"প্রোজেক্ট (zip) ইম্পোর্ট করুন\",\n\t\"changelog\": \"পরিবর্তনের তালিকা\",\n\t\"notifications\": \"নোটিফিকেশন\",\n\t\"no_unread_notifications\": \"কোনো অনপঠিত নোটিফিকেশন নেই\",\n\t\"should_use_current_file_for_preview\": \"ডিফল্ট (index.html) এর পরিবর্তে প্রিভিউ-এর জন্য বর্তমান ফাইল ব্যবহার করা উচিত কি?\",\n\t\"fade fold widgets\": \"ফোল্ড উইজেটস ফেড করুন\",\n\t\"quicktools:home-key\": \"হোম কী\",\n\t\"quicktools:end-key\": \"এন্ড কী\",\n\t\"quicktools:pageup-key\": \"পেজআপ কী\",\n\t\"quicktools:pagedown-key\": \"পেজডাউন কী\",\n\t\"quicktools:delete-key\": \"ডিলেট কী\",\n\t\"quicktools:tilde\": \"টিল্ডা চিহ্ন যোগ করুন\",\n\t\"quicktools:backtick\": \"ব্যাকটিক চিহ্ন যোগ করুন\",\n\t\"quicktools:hash\": \"হ্যাশ চিহ্ন যোগ করুন\",\n\t\"quicktools:dollar\": \"ডলার চিহ্ন যোগ করুন\",\n\t\"quicktools:modulo\": \"মডুলো/পারসেন্ট চিহ্ন যোগ করুন\",\n\t\"quicktools:caret\": \"ক্যারেট চিহ্ন যোগ করুন\",\n\t\"plugin_enabled\": \"প্লাগইন সক্রিয়\",\n\t\"plugin_disabled\": \"প্লাগইন নিষ্ক্রিয়\",\n\t\"enable_plugin\": \"এই প্লাগইন সক্রিয় করুন\",\n\t\"disable_plugin\": \"এই প্লাগইন নিষ্ক্রিয় করুন\",\n\t\"open_source\": \"ওপেন সোর্স\",\n\t\"terminal settings\": \"টার্মিনাল সেটিংস\",\n\t\"font ligatures\": \"ফন্ট লিগেচার\",\n\t\"letter spacing\": \"অক্ষরের ফাঁক\",\n\t\"terminal:tab stop width\": \"ট্যাব স্টপ প্রস্থ\",\n\t\"terminal:scrollback\": \"স্ক্রলব্যাক লাইন\",\n\t\"terminal:cursor blink\": \"কার্সর ব্লিঙ্ক\",\n\t\"terminal:font weight\": \"ফন্ট ওজন\",\n\t\"terminal:cursor inactive style\": \"নিষ্ক্রিয় কার্সর স্টাইল\",\n\t\"terminal:cursor style\": \"কার্সর স্টাইল\",\n\t\"terminal:font family\": \"ফন্ট ফ্যামিলি\",\n\t\"terminal:convert eol\": \"EOL রূপান্তর করুন\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"টার্মিনাল\",\n\t\"allFileAccess\": \"সকল ফাইল অ্যাক্সেস\",\n\t\"fonts\": \"ফন্ট\",\n\t\"sponsor\": \"স্পন্সর\",\n\t\"downloads\": \"ডাউনলোড\",\n\t\"reviews\": \"রিভিউ\",\n\t\"overview\": \"সংক্ষিপ্ত বিবরণ\",\n\t\"contributors\": \"অবদানকারী\",\n\t\"quicktools:hyphen\": \"হাইফেন যুক্ত করুন\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/cs-cz.json",
    "content": "{\n\t\"lang\": \"Čeština\",\n\t\"about\": \"O aplikaci\",\n\t\"active files\": \"Zobrazení aktivních souborů\",\n\t\"alert\": \"Upozornění\",\n\t\"app theme\": \"Motiv aplikace\",\n\t\"autocorrect\": \"Povolit automatické opravy?\",\n\t\"autosave\": \"Automatické ukládání\",\n\t\"cancel\": \"Zrušit\",\n\t\"change language\": \"Změnit jazyk\",\n\t\"choose color\": \"Vybrat barvu\",\n\t\"clear\": \"vymazat\",\n\t\"close app\": \"Zavřít aplikaci?\",\n\t\"commit message\": \"Zpráva pro commit\",\n\t\"console\": \"Konzole\",\n\t\"conflict error\": \"Commit konflikt! Počkejte prosím s dalším commitem.\",\n\t\"copy\": \"Kopírovat\",\n\t\"create folder error\": \"Omlouváme se, ale nelze vytvořit novou složku.\",\n\t\"cut\": \"Vyjmout\",\n\t\"delete\": \"Smazat\",\n\t\"dependencies\": \"Závislosti\",\n\t\"delay\": \"Čas v milisekundách\",\n\t\"editor settings\": \"Nastavení editoru\",\n\t\"editor theme\": \"Motiv editoru\",\n\t\"enter file name\": \"Zadejte název souboru\",\n\t\"enter folder name\": \"Zadejte název složky\",\n\t\"empty folder message\": \"Prázdná složka\",\n\t\"enter line number\": \"Zadejte číslo řádku\",\n\t\"error\": \"Chyba\",\n\t\"failed\": \"selhal\",\n\t\"file already exists\": \"Soubor již existuje\",\n\t\"file already exists force\": \"Soubor již existuje. Přepsat?\",\n\t\"file changed\": \" byl změněn, načíst soubor znovu?\",\n\t\"file deleted\": \"Soubor smazán\",\n\t\"file is not supported\": \"Soubor není podporován\",\n\t\"file not supported\": \"Tento typ souboru není podporován.\",\n\t\"file too large\": \"Soubor je příliš velký na zpracování. Maximální povolená velikost souboru je {size}\",\n\t\"file renamed\": \"soubor přejmenován\",\n\t\"file saved\": \"soubor uložen\",\n\t\"folder added\": \"složka přidána\",\n\t\"folder already added\": \"složka již byla přidána\",\n\t\"font size\": \"Velikost písma\",\n\t\"goto\": \"Přejít na řádek\",\n\t\"icons definition\": \"Definice ikon\",\n\t\"info\": \"Informace\",\n\t\"invalid value\": \"Neplatná hodnota\",\n\t\"language changed\": \"Jazyk byl úspěšně změněn\",\n\t\"linting\": \"Zkontrolujte syntaktickou chybu\",\n\t\"logout\": \"Odhlásit se\",\n\t\"loading\": \"Načítání\",\n\t\"my profile\": \"Můj profil\",\n\t\"new file\": \"Nový soubor\",\n\t\"new folder\": \"Nová složka\",\n\t\"no\": \"Ne\",\n\t\"no editor message\": \"Otevřít nebo vytvořit nový soubor a složku z nabídky\",\n\t\"not set\": \"Není nastaveno\",\n\t\"unsaved files close app\": \"Existují neuložené soubory. Chcete zavřít aplikaci?\",\n\t\"notice\": \"Upozornění\",\n\t\"open file\": \"Otevřít soubor\",\n\t\"open files and folders\": \"Otevřít soubory a složky\",\n\t\"open folder\": \"Otevřít složku\",\n\t\"open recent\": \"Otevřít nedávné\",\n\t\"ok\": \"OK\",\n\t\"overwrite\": \"Přepsat\",\n\t\"paste\": \"Vložit\",\n\t\"preview mode\": \"Režim náhledu\",\n\t\"read only file\": \"Nelze uložit soubor určený pouze pro čtení. Zkuste prosím uložit jako\",\n\t\"reload\": \"Znovu načíst\",\n\t\"rename\": \"Přejmenovat\",\n\t\"replace\": \"Nahradit\",\n\t\"required\": \"Toto pole je povinné\",\n\t\"run your web app\": \"Spusťte svou webovou aplikaci\",\n\t\"save\": \"Uložit\",\n\t\"saving\": \"Ukládání\",\n\t\"save as\": \"Uložit jako\",\n\t\"save file to run\": \"Uložte si tento soubor pro spuštění v prohlížeči\",\n\t\"search\": \"Vyhledávání\",\n\t\"see logs and errors\": \"Zobrazit protokoly a chyby\",\n\t\"select folder\": \"Vybrat složku\",\n\t\"settings\": \"Nastavení\",\n\t\"settings saved\": \"Nastavení uloženo\",\n\t\"show line numbers\": \"Zobrazit čísla řádků\",\n\t\"show hidden files\": \"Zobrazit skryté soubory\",\n\t\"show spaces\": \"Zobrazit mezery\",\n\t\"soft tab\": \"Měkký tabulátor\",\n\t\"sort by name\": \"Seřadit podle názvu\",\n\t\"success\": \"Úspěch\",\n\t\"tab size\": \"Velikost tabulátoru\",\n\t\"text wrap\": \"Zalamování textu / Zalamování slov\",\n\t\"theme\": \"Motiv\",\n\t\"unable to delete file\": \"nelze smazat soubor\",\n\t\"unable to open file\": \"Omlouváme se, soubor se nepodařilo otevřít\",\n\t\"unable to open folder\": \"Omlouváme se, složku se nepodařilo otevřít\",\n\t\"unable to save file\": \"Omlouváme se, soubor se nepodařilo uložit\",\n\t\"unable to rename\": \"Omlouváme se, přejmenování se nepodařilo\",\n\t\"unsaved file\": \"Tento soubor není uložen, přesto ho zavřít?\",\n\t\"warning\": \"Upozornění\",\n\t\"use emmet\": \"Použít emmet\",\n\t\"use quick tools\": \"Použít Rychlé nástroje\",\n\t\"yes\": \"Ano\",\n\t\"encoding\": \"Kódování textu\",\n\t\"syntax highlighting\": \"Zvýrazňování syntaxe\",\n\t\"read only\": \"Pouze pro čtení\",\n\t\"select all\": \"Vybrat vše\",\n\t\"select branch\": \"Vybrat větev\",\n\t\"create new branch\": \"Vytvořit novou větev\",\n\t\"use branch\": \"Použít větev\",\n\t\"new branch\": \"Nová větev\",\n\t\"branch\": \"Větev\",\n\t\"key bindings\": \"Klávesové zkratky\",\n\t\"edit\": \"Editovat\",\n\t\"reset\": \"Resetovat\",\n\t\"color\": \"Barva\",\n\t\"select word\": \"Vybrat slovo\",\n\t\"quick tools\": \"Rychlé nástroje\",\n\t\"select\": \"Vybrat\",\n\t\"editor font\": \"Písmo editoru\",\n\t\"new project\": \"Nový projekt\",\n\t\"format\": \"Formát\",\n\t\"project name\": \"Název projektu\",\n\t\"unsupported device\": \"Vaše zařízení nepodporuje motiv.\",\n\t\"vibrate on tap\": \"Vibrace při klepnutí\",\n\t\"copy command is not supported by ftp.\": \"Příkaz kopírování není FTP podporován.\",\n\t\"support title\": \"Podpora Acode\",\n\t\"fullscreen\": \"Celá obrazovka\",\n\t\"animation\": \"Animace\",\n\t\"backup\": \"Záloha\",\n\t\"restore\": \"Obnovit\",\n\t\"backup successful\": \"Zálohování bylo úspěšné\",\n\t\"invalid backup file\": \"Neplatný záložní soubor\",\n\t\"add path\": \"Přidat cestu\",\n\t\"live autocompletion\": \"Automatické doplňování v reálném čase\",\n\t\"file properties\": \"Vlastnosti souboru\",\n\t\"path\": \"Cesta\",\n\t\"type\": \"Typ\",\n\t\"word count\": \"Počet slov\",\n\t\"line count\": \"Počet řádků\",\n\t\"last modified\": \"Naposledy upraveno\",\n\t\"size\": \"Velikost\",\n\t\"share\": \"Sdílet\",\n\t\"show print margin\": \"Zobrazit okraj tisku\",\n\t\"login\": \"přihlášení\",\n\t\"scrollbar size\": \"Velikost posuvníku\",\n\t\"cursor controller size\": \"Velikost držadel u kurzoru\",\n\t\"none\": \"Žádná\",\n\t\"small\": \"Malá\",\n\t\"large\": \"Velká\",\n\t\"floating button\": \"Plovoucí tlačítko\",\n\t\"confirm on exit\": \"Potvrdit při zavření\",\n\t\"show console\": \"Zobrazit konzoli\",\n\t\"image\": \"Obrázek\",\n\t\"insert file\": \"Vložit soubor\",\n\t\"insert color\": \"Vložit barvu\",\n\t\"powersave mode warning\": \"Pro zobrazení náhledu v externím prohlížeči vypněte režim úspory energie.\",\n\t\"exit\": \"Konec\",\n\t\"custom\": \"Vlastní\",\n\t\"reset warning\": \"Jste si jisti, že chcete resetovat motiv?\",\n\t\"theme type\": \"Typ motivu\",\n\t\"light\": \"Světlý\",\n\t\"dark\": \"Tmavý\",\n\t\"file browser\": \"Prohlížeč souborů\",\n\t\"operation not permitted\": \"Operace není povolena\",\n\t\"no such file or directory\": \"Soubor nebo adresář neexistuje\",\n\t\"input/output error\": \"Chyba vstupu/výstupu\",\n\t\"permission denied\": \"Oprávnění zamítnuto\",\n\t\"bad address\": \"Špatná adresa\",\n\t\"file exists\": \"Soubor již existuje\",\n\t\"not a directory\": \"Není složka\",\n\t\"is a directory\": \"Je složka\",\n\t\"invalid argument\": \"Neplatný argument\",\n\t\"too many open files in system\": \"Příliš mnoho otevřených souborů v systému\",\n\t\"too many open files\": \"Příliš mnoho otevřených souborů\",\n\t\"text file busy\": \"Textový soubor je zaneprázdněn\",\n\t\"no space left on device\": \"Na zařízení nezbývá místo\",\n\t\"read-only file system\": \"Souborový systém pouze pro čtení\",\n\t\"file name too long\": \"Název souboru je příliš dlouhý\",\n\t\"too many users\": \"Příliš mnoho uživatelů\",\n\t\"connection timed out\": \"Časový limit připojení vypršel\",\n\t\"connection refused\": \"Spojení odmítnuto\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"Došlo k chybě\",\n\t\"add ftp\": \"Přidat FTP\",\n\t\"add sftp\": \"Přidat SFTP\",\n\t\"save file\": \"Uložit soubor\",\n\t\"save file as\": \"Uložit soubor jako\",\n\t\"files\": \"Soubory\",\n\t\"help\": \"Nápověda\",\n\t\"file has been deleted\": \"Soubor {file} byl smazán!\",\n\t\"feature not available\": \"Tato funkce je k dispozici pouze v placené verzi aplikace.\",\n\t\"deleted file\": \"Smazaný soubor\",\n\t\"line height\": \"Výška řádku\",\n\t\"preview info\": \"Pokud chcete spustit aktivní soubor, klepněte na a podržte ikonu přehrávání.\",\n\t\"manage all files\": \"Povolte editoru Acode správu všech souborů v nastavení, abyste mohli snadno upravovat soubory ve svém zařízení.\",\n\t\"close file\": \"Zavřít soubor\",\n\t\"reset connections\": \"Obnovit připojení\",\n\t\"check file changes\": \"Zkontrolovat změny v souboru\",\n\t\"open in browser\": \"Otevřít v prohlížeči\",\n\t\"desktop mode\": \"Režim plochy\",\n\t\"toggle console\": \"Přepnout konzoli\",\n\t\"new line mode\": \"Režim nového řádku\",\n\t\"add a storage\": \"Přidat úložiště\",\n\t\"rate acode\": \"Ohodnotit Acode\",\n\t\"support\": \"Podpora\",\n\t\"downloading file\": \"Stahování souboru {file}\",\n\t\"downloading...\": \"Stahování...\",\n\t\"folder name\": \"Název složky\",\n\t\"keyboard mode\": \"Režim klávesnice\",\n\t\"normal\": \"Normání\",\n\t\"app settings\": \"Nastavení aplikace\",\n\t\"disable in-app-browser caching\": \"Zakázat ukládání do mezipaměti v prohlížeči aplikace\",\n\t\"copied to clipboard\": \"Zkopírováno do schránky\",\n\t\"remember opened files\": \"Zapamatovat si otevřené soubory\",\n\t\"remember opened folders\": \"Zapamatovat si otevřené složky\",\n\t\"no suggestions\": \"Žádné návrhy\",\n\t\"no suggestions aggressive\": \"Žádné agresivní návrhy\",\n\t\"install\": \"Instalovat\",\n\t\"installing\": \"Instalace...\",\n\t\"plugins\": \"Pluginy\",\n\t\"recently used\": \"Nedávno použité\",\n\t\"update\": \"Aktualizovat\",\n\t\"uninstall\": \"Odinstalovat\",\n\t\"download acode pro\": \"Stáhnout Acode Pro\",\n\t\"loading plugins\": \"Načítání pluginů\",\n\t\"faqs\": \"Často kladené otázky\",\n\t\"feedback\": \"Zpětná vazba\",\n\t\"header\": \"Nahoře\",\n\t\"sidebar\": \"V bočním panelu\",\n\t\"inapp\": \"V aplikaci\",\n\t\"browser\": \"Prohlížeč\",\n\t\"diagonal scrolling\": \"Diagonální posouvání\",\n\t\"reverse scrolling\": \"Obrácené posouvání\",\n\t\"formatter\": \"Formátovač\",\n\t\"format on save\": \"Formátovat při ukládání\",\n\t\"remove ads\": \"Odstranit reklamy\",\n\t\"fast\": \"Rychle\",\n\t\"slow\": \"Pomalu\",\n\t\"scroll settings\": \"Nastavení posouvání\",\n\t\"scroll speed\": \"Rychlost posování\",\n\t\"loading...\": \"Načítání...\",\n\t\"no plugins found\": \"Nenalezeny žádné pluginy\",\n\t\"name\": \"Jméno\",\n\t\"username\": \"Uživatelské jméno\",\n\t\"optional\": \"volitelné\",\n\t\"hostname\": \"Název hostitele\",\n\t\"password\": \"Heslo\",\n\t\"security type\": \"Typ zabezpečení\",\n\t\"connection mode\": \"Režim připojení\",\n\t\"port\": \"port\",\n\t\"key file\": \"Soubor s klíčem\",\n\t\"select key file\": \"Vybrat soubor s klíčem\",\n\t\"passphrase\": \"Heslo\",\n\t\"connecting...\": \"Připojování...\",\n\t\"type filename\": \"Zadejte název souboru\",\n\t\"unable to load files\": \"Nelze načíst soubory\",\n\t\"preview port\": \"Port náhledu\",\n\t\"find file\": \"Najít soubor\",\n\t\"system\": \"Systém\",\n\t\"please select a formatter\": \"Prosím, vyberte formátovač\",\n\t\"case sensitive\": \"Rozlišovat velká a malá písmena\",\n\t\"regular expression\": \"Regulární výraz\",\n\t\"whole word\": \"Celé slovo\",\n\t\"edit with\": \"Editovat s\",\n\t\"open with\": \"Otevřít s\",\n\t\"no app found to handle this file\": \"Nebyla nalezena žádná aplikace pro zpracování tohoto souboru\",\n\t\"restore default settings\": \"Obnovit výchozí nastavení\",\n\t\"server port\": \"Port serveru\",\n\t\"preview settings\": \"Nastavení náhledu\",\n\t\"preview settings note\": \"Pokud se port náhledu a port serveru liší, aplikace nespustí server a místo toho otevře https://<host>:<preview port> v prohlížeči nebo v prohlížeči aplikace. To je užitečné, když provozujete server někde jinde.\",\n\t\"backup/restore note\": \"Zálohuje pouze vaše nastavení, vlastní šablonu, nainstalované pluginy a klávesové zkratky. Nezálohuje stav vašeho FTP/SFTP ani aplikace.\",\n\t\"host\": \"Hostite\",\n\t\"retry ftp/sftp when fail\": \"V případě selhání zkuste znovu ftp/sftp\",\n\t\"more\": \"Více\",\n\t\"thank you :)\": \"Děkuji :)\",\n\t\"purchase pending\": \"nákup čeká na vyřízení\",\n\t\"cancelled\": \"zrušeno\",\n\t\"local\": \"Lokální\",\n\t\"remote\": \"Vzdálený\",\n\t\"show console toggler\": \"Zobrazit přepínač konzole\",\n\t\"binary file\": \"Tento soubor obsahuje binární data, chcete ho otevřít?\",\n\t\"relative line numbers\": \"Relativní čísla řádků\",\n\t\"elastic tabstops\": \"Elastické záložky\",\n\t\"line based rtl switching\": \"Přepínání RTL na bázi řádku\",\n\t\"hard wrap\": \"Pevné zalomení\",\n\t\"spellcheck\": \"Kontrola pravopisu\",\n\t\"wrap method\": \"Metoda zalomení\",\n\t\"use textarea for ime\": \"Použít textovou oblast pro IME\",\n\t\"invalid plugin\": \"Neplatný plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Režim spouštění Rychlých nástrojů\",\n\t\"print margin\": \"Okraj tisku\",\n\t\"touch move threshold\": \"Nastavení citlivosti dotyku\",\n\t\"info-retryremotefsafterfail\": \"V případě selhání se znovu pokusit o připojení FTP/SFTP.\",\n\t\"info-fullscreen\": \"Skrýt titulní lištu na domovské obrazovce.\",\n\t\"info-checkfiles\": \"Kontrolovat změny souborů, když je aplikace na pozadí.\",\n\t\"info-console\": \"Vyberte konzoli JavaScriptu. Legacy je výchozí konzole, eruda je konzole třetí strany.\",\n\t\"info-keyboardmode\": \"Režim klávesnice pro zadávání textu, žádné návrhy skryjí návrhy a automatické opravy. Pokud možnost žádné návrhy nefunguje, zkuste změnit hodnotu na agresivní režim bez návrhů.\",\n\t\"info-rememberfiles\": \"Zapamatovat si otevřené soubory i po zavření aplikace.\",\n\t\"info-rememberfolders\": \"Zapamatovat si otevřené složky při zavření aplikace.\",\n\t\"info-floatingbutton\": \"Zobrazit nebo skrýt plovoucí tlačítko pro rychlé nástroje.\",\n\t\"info-openfilelistpos\": \"Kde zobrazit seznam aktivních souborů.\",\n\t\"info-touchmovethreshold\": \"Pokud je citlivost dotyku vašeho zařízení příliš vysoká, můžete tuto hodnotu zvýšit, abyste zabránili nechtěnému dotyku.\",\n\t\"info-scroll-settings\": \"Tato nastavení obsahují nastavení posouvání včetně zalamování textu.\",\n\t\"info-animation\": \"Pokud se aplikace zdá pomalá, vypněte animace.\",\n\t\"info-quicktoolstriggermode\": \"Pokud tlačítko v rychlých nástrojích nefunguje, zkuste tuto hodnotu změnit.\",\n\t\"info-checkForAppUpdates\": \"Automaticky kontrolovat aktualizace aplikace.\",\n\t\"info-quickTools\": \"Zobrazit nebo skrýt rychlé nástroje.\",\n\t\"info-showHiddenFiles\": \"Zobrazit skryté soubory a složky. (Začínající .)\",\n\t\"info-all_file_access\": \"Povolit přístup k /sdcard a /storage v terminálu.\",\n\t\"info-fontSize\": \"Velikost písma použitá k vykreslení textu.\",\n\t\"info-fontFamily\": \"Písmo použité k vykreslení textu.\",\n\t\"info-theme\": \"Barevný motiv terminálu.\",\n\t\"info-cursorStyle\": \"Styl kurzoru, když je terminál používán.\",\n\t\"info-cursorInactiveStyle\": \"Styl kurzoru, když terminál není používán.\",\n\t\"info-fontWeight\": \"Tloušťka písma použitá k vykreslení netučného textu.\",\n\t\"info-cursorBlink\": \"Zda kurzor bliká.\",\n\t\"info-scrollback\": \"Míra posunu zpět v terminálu. Posun zpět je počet řádků, které zůstanou zachovány při posunu řádků za počáteční zobrazovací oblast.\",\n\t\"info-tabStopWidth\": \"Velikost zarážek tabulace v terminálu.\",\n\t\"info-letterSpacing\": \"Mezery mezi znaky v pixelech.\",\n\t\"info-imageSupport\": \"Zda jsou v terminálu podporovány obrázky.\",\n\t\"info-fontLigatures\": \"Zda jsou v terminálu povoleny ligatury písem.\",\n\t\"info-confirmTabClose\": \"Před zavřením karet terminálu si vyžádat potvrzení.\",\n\t\"info-backup\": \"Vytvoří zálohu instalace terminálu.\",\n\t\"info-restore\": \"Obnoví zálohu instalace terminálu.\",\n\t\"info-uninstall\": \"Odinstaluje instalaci terminálu.\",\n\t\"owned\": \"Vlastněno\",\n\t\"api_error\": \"API server je nefunkční, zkuste to prosím později.\",\n\t\"installed\": \"Nainstalováno\",\n\t\"all\": \"Vše\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Vrácení peněz\",\n\t\"product not available\": \"Produkt není k dispozici\",\n\t\"no-product-info\": \"Tento produkt momentálně není ve vaší zemi k dispozici, zkuste to prosím znovu později.\",\n\t\"close\": \"Zavřít\",\n\t\"explore\": \"Prozkoumat\",\n\t\"key bindings updated\": \"Klávesové zkratky aktualizovány\",\n\t\"search in files\": \"Hledat v souborech\",\n\t\"exclude files\": \"Vyloučit soubory\",\n\t\"include files\": \"Zahrnout soubory\",\n\t\"search result\": \"hledání {matches} našlo {files} souborů.\",\n\t\"invalid regex\": \"Neplatný regulární výraz: {message}.\",\n\t\"bottom\": \"Dole\",\n\t\"save all\": \"Uložit vše\",\n\t\"close all\": \"Zavřít vše\",\n\t\"unsaved files warning\": \"Některé soubory se nepodařilo uložit. Klikněte na tlačítko „OK“ a vyberte, co chcete udělat, nebo se vraťte zpět tlačítkem „Zrušit“.\",\n\t\"save all warning\": \"Opravdu chcete uložit všechny soubory a zavřít? Tuto akci nelze vrátit zpět.\",\n\t\"save all changes warning\": \"Jste si jisti, že chcete uložit všechny soubory?\",\n\t\"close all warning\": \"Opravdu chcete zavřít všechny soubory? Ztratíte neuložené změny a tuto akci nelze vrátit zpět.\",\n\t\"refresh\": \"Obnovit\",\n\t\"shortcut buttons\": \"Zkratky\",\n\t\"no result\": \"Žádný výsledek\",\n\t\"searching...\": \"Hledání...\",\n\t\"quicktools:ctrl-key\": \"Klávesa Control/Command\",\n\t\"quicktools:tab-key\": \"Klávesa Tab\",\n\t\"quicktools:shift-key\": \"Klávesa Shift\",\n\t\"quicktools:undo\": \"Zpět\",\n\t\"quicktools:redo\": \"Znovu\",\n\t\"quicktools:search\": \"Hledat v souboru\",\n\t\"quicktools:save\": \"Uložit soubor\",\n\t\"quicktools:esc-key\": \"Klávesa Esc\",\n\t\"quicktools:curlybracket\": \"Vložit složenou závorku\",\n\t\"quicktools:squarebracket\": \"Vložit hranatou závorku\",\n\t\"quicktools:parentheses\": \"Vložit závorku\",\n\t\"quicktools:anglebracket\": \"Vložit ostrou závorku\",\n\t\"quicktools:left-arrow-key\": \"Šipka vlevo\",\n\t\"quicktools:right-arrow-key\": \"Šipka vpravo\",\n\t\"quicktools:up-arrow-key\": \"Šipka nahoru\",\n\t\"quicktools:down-arrow-key\": \"Šipka dolů\",\n\t\"quicktools:moveline-up\": \"Posunout řádek nahoru\",\n\t\"quicktools:moveline-down\": \"Posunout řádek dolů\",\n\t\"quicktools:copyline-up\": \"Kopírovat řádek nahoru\",\n\t\"quicktools:copyline-down\": \"Kopírovat řádek dolů\",\n\t\"quicktools:semicolon\": \"Vložit středník\",\n\t\"quicktools:quotation\": \"Vložit citaci\",\n\t\"quicktools:and\": \"Vložit & symbol\",\n\t\"quicktools:bar\": \"Vložit | symbol \",\n\t\"quicktools:equal\": \"Vložit symbol =\",\n\t\"quicktools:slash\": \"Vložit symbol /\",\n\t\"quicktools:exclamation\": \"Vložit vykřičník\",\n\t\"quicktools:alt-key\": \"Klávesa Alt\",\n\t\"quicktools:meta-key\": \"Klávesa Windows/Meta\",\n\t\"info-quicktoolssettings\": \"V rychlých nástrojích pod editorem si můžete přizpůsobit tlačítka a klávesové zkratky.\",\n\t\"info-excludefolders\": \"Použijte vzor **/node_modules/** pro ignorování všech souborů ze složky node_modules. Tím se soubory vyloučí ze seznamu a také zabrání jejich zahrnutí do vyhledávání souborů.\",\n\t\"missed files\": \"Po zahájení vyhledávání bylo naskenováno {count} souborů, které nebudou zahrnuty do vyhledávání.\",\n\t\"remove\": \"Odstranit\",\n\t\"quicktools:command-palette\": \"Paleta příkazů\",\n\t\"default file encoding\": \"Výchozí kódování souborů\",\n\t\"remove entry\": \"Opravdu chcete odstranit '{name}' z uložených cest? Upozorňujeme, že jeho odstraněním se samotná cesta neodstraní.\",\n\t\"delete entry\": \"Potvrdit smazání: '{name}'. Tuto akci nelze vrátit zpět. Pokračovat?\",\n\t\"change encoding\": \"Znovu otevřít soubor '{file}' s kódováním '{encoding}'? Tato akce povede ke ztrátě všech neuložených změn provedených v souboru. Chcete pokračovat v opětovném otevření?\",\n\t\"reopen file\": \"Opravdu chcete znovu otevřít soubor '{file}'? Veškeré neuložené změny budou ztraceny.\",\n\t\"plugin min version\": \"{name} je k dispozici pouze v Acode - {v-code} a vyšších verzích. Klikněte zde pro aktualizaci.\",\n\t\"color preview\": \"Náhled barev\",\n\t\"confirm\": \"Potvrdit\",\n\t\"list files\": \"Zobrazit všechny soubory v <strong>{name</strong>? Příliš mnoho souborů může způsobit pád aplikace.\",\n\t\"problems\": \"Problémy\",\n\t\"show side buttons\": \"Zobrazit boční tlačítka\",\n\t\"bug_report\": \"Odeslat hlášení o chybě\",\n\t\"verified publisher\": \"Ověřený vydavatel\",\n\t\"most_downloaded\": \"Nejvíce stahované\",\n\t\"newly_added\": \"Nově přidané\",\n\t\"top_rated\": \"Nejlépe hodnocené\",\n\t\"rename not supported\": \"Přejmenování adresáře termux není podporováno.\",\n\t\"compress\": \"Komprimovat\",\n\t\"copy uri\": \"Kopírovat Uri\",\n\t\"delete entries\": \"Jste si jisti, že chcete smazat {count} položek?\",\n\t\"deleting items\": \"Mazání položek ({count})...\",\n\t\"import project zip\": \"Importovat projekt (zip)\",\n\t\"changelog\": \"Protokol změn\",\n\t\"notifications\": \"Oznámení\",\n\t\"no_unread_notifications\": \"Žádná nepřečtená oznámení\",\n\t\"should_use_current_file_for_preview\": \"Pro náhled by se měl použít aktuální soubor místo výchozího (index.html).\",\n\t\"fade fold widgets\": \"Widgety s prolínáním a skládáním\",\n\t\"quicktools:home-key\": \"Klávesa Home\",\n\t\"quicktools:end-key\": \"Klávesa End\",\n\t\"quicktools:pageup-key\": \"Klávesa PageUp\",\n\t\"quicktools:pagedown-key\": \"Klávesa PageDown\",\n\t\"quicktools:delete-key\": \"Klávesa Delete\",\n\t\"quicktools:tilde\": \"Vložit symbol ~\",\n\t\"quicktools:backtick\": \"Vložit symbol `\",\n\t\"quicktools:hash\": \"Vložit symbol #\",\n\t\"quicktools:dollar\": \"Vložit symbol $\",\n\t\"quicktools:modulo\": \"Vložit symbol %\",\n\t\"quicktools:caret\": \"Vložit symbol ^\",\n\t\"plugin_enabled\": \"Plugin je povolen\",\n\t\"plugin_disabled\": \"Plugin je zakázán\",\n\t\"enable_plugin\": \"Povolit tento plugin\",\n\t\"disable_plugin\": \"Zakázat tento plugin\",\n\t\"open_source\": \"Otevřený zdrojový kód\",\n\t\"terminal settings\": \"Nastavení terminálu\",\n\t\"font ligatures\": \"Ligatury písma\",\n\t\"letter spacing\": \"Mezera mezi písmeny\",\n\t\"terminal:tab stop width\": \"Šířka zarážky tabulátoru\",\n\t\"terminal:scrollback\": \"Řádky pro posun zpět\",\n\t\"terminal:cursor blink\": \"Blikání kurzoru\",\n\t\"terminal:font weight\": \"Tloušťka písma\",\n\t\"terminal:cursor inactive style\": \"Styl neaktivního kurzoru\",\n\t\"terminal:cursor style\": \"Styl kurzoru\",\n\t\"terminal:font family\": \"Písma\",\n\t\"terminal:convert eol\": \"Převést EOL\",\n\t\"terminal:confirm tab close\": \"Potvrzení zavření karty terminálu\",\n\t\"terminal:image support\": \"Podpora obrázků\",\n\t\"terminal\": \"Terminál\",\n\t\"allFileAccess\": \"Přístup ke všem souborům\",\n\t\"fonts\": \"Fonty\",\n\t\"sponsor\": \"Sponzor\",\n\t\"downloads\": \"stahování\",\n\t\"reviews\": \"recenze\",\n\t\"overview\": \"Přehled\",\n\t\"contributors\": \"Přispěvatelé\",\n\t\"quicktools:hyphen\": \"Vložit symbol -\",\n\t\"check for app updates\": \"Zkontrolovat aktualizace aplikace\",\n\t\"prompt update check consent message\": \"Acode může zkontrolovat nové aktualizace aplikace, když jste online. Povolit kontroly aktualizací?\",\n\t\"keywords\": \"Klíčová slova\",\n\t\"author\": \"Autor\",\n\t\"filtered by\": \"Filtrováno podle\",\n\t\"clean install state\": \"Vymazat stav instalace\",\n\t\"backup created\": \"Záloha vytvořena\",\n\t\"restore completed\": \"Obnovení dokončeno\",\n\t\"restore will include\": \"Tím se obnoví\",\n\t\"restore warning\": \"Tuto akci nelze vrátit zpět. Pokračovat?\",\n\t\"reload to apply\": \"Znovu načíst pro použití změn?\",\n\t\"reload app\": \"Znovu načíst aplikaci\",\n\t\"preparing backup\": \"Příprava zálohy\",\n\t\"collecting settings\": \"Shromažďování informací o nastavení\",\n\t\"collecting key bindings\": \"Shromažďování informací o klávesových zkratkách\",\n\t\"collecting plugins\": \"Shromažďování informací o pluginech\",\n\t\"creating backup\": \"Vytváření zálohy\",\n\t\"validating backup\": \"Ověřování zálohy\",\n\t\"restoring key bindings\": \"Obnovení klávesových zkratek\",\n\t\"restoring plugins\": \"Obnovení pluginů\",\n\t\"restoring settings\": \"Obnovení nastavení\",\n\t\"legacy backup warning\": \"Toto je starší formát zálohy. Některé funkce mohou být omezené.\",\n\t\"checksum mismatch\": \"Chybný kontrolní součet – záložní soubor mohl být upraven nebo poškozen.\",\n\t\"plugin not found\": \"Plugin nebyl nalezen v registru\",\n\t\"paid plugin skipped\": \"Placený plugin - nebyl koupen\",\n\t\"source not found\": \"Zdrojový soubor již neexistuje.\",\n\t\"restored\": \"Obnoveno\",\n\t\"skipped\": \"Přeskočeno\",\n\t\"backup not valid object\": \"Záložní soubor není platný.\",\n\t\"backup no data\": \"Záložní soubor neobsahuje žádná data k obnovení\",\n\t\"backup legacy warning\": \"Toto je starší formát zálohy (v1). Některé funkce mohou být omezené.\",\n\t\"backup missing metadata\": \"Chybí zálohovací metadata – některé informace nemusí být k dispozici\",\n\t\"backup checksum mismatch\": \"Chybný kontrolní součet – záložní soubor mohl být upraven nebo poškozen. Postupujte opatrně.\",\n\t\"backup checksum verify failed\": \"Nepodařilo se ověřit kontrolní součet\",\n\t\"backup invalid settings\": \"Neplatný formát nastavení\",\n\t\"backup invalid keybindings\": \"Neplatný formát klávesových zkratek\",\n\t\"backup invalid plugins\": \"Neplatný formát instalovaných pluginů\",\n\t\"issues found\": \"Nalezené problémy\",\n\t\"error details\": \"Podrobnosti o chybě\",\n\t\"active tools\": \"Aktivní nástroje\",\n\t\"available tools\": \"Dostupné nástroje\",\n\t\"recent\": \"Nedávné soubory\",\n\t\"command palette\": \"Otevřít paletu příkazů\",\n\t\"change theme\": \"Změnit motiv\",\n\t\"documentation\": \"Dokumentace\",\n\t\"open in terminal\": \"Otevřít v terminálu\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/de-de.json",
    "content": "{\n\t\"lang\": \"Deutsch\",\n\t\"about\": \"über\",\n\t\"active files\": \"Aktive Dateien\",\n\t\"alert\": \"Warnung\",\n\t\"app theme\": \"App-Design\",\n\t\"autocorrect\": \"Autokorrektur aktivieren?\",\n\t\"autosave\": \"Automatisch speichern\",\n\t\"cancel\": \"Abbrechen\",\n\t\"change language\": \"Sprache wechseln\",\n\t\"choose color\": \"Farbe auswählen\",\n\t\"clear\": \"Löschen\",\n\t\"close app\": \"Anwendung schließen?\",\n\t\"commit message\": \"Meldung bestätigen\",\n\t\"console\": \"Konsole\",\n\t\"conflict error\": \"Konflikt! Bitte warten Sie vor dem nächsten Commit.\",\n\t\"copy\": \"Kopieren\",\n\t\"create folder error\": \"Entschuldigung, Ordner kann nicht angelegt werden\",\n\t\"cut\": \"Ausschneiden\",\n\t\"delete\": \"Löschen\",\n\t\"dependencies\": \"Abhängigkeiten\",\n\t\"delay\": \"Zeit in Millisekunden\",\n\t\"editor settings\": \"Editor-Einstellungen\",\n\t\"editor theme\": \"Editordesign\",\n\t\"enter file name\": \"Dateinamen eingeben\",\n\t\"enter folder name\": \"Ordnernamen eingeben\",\n\t\"empty folder message\": \"Leerer Ordner\",\n\t\"enter line number\": \"Zeilennummer eingeben\",\n\t\"error\": \"Fehler\",\n\t\"failed\": \"Fehlgeschlagen\",\n\t\"file already exists\": \"Datei existiert bereits\",\n\t\"file already exists force\": \"Datei existiert bereits. Überschreiben?\",\n\t\"file changed\": \" wurde verändert, Datei neu laden?\",\n\t\"file deleted\": \"Datei gelöscht\",\n\t\"file is not supported\": \"Datei wird nicht unterstützt\",\n\t\"file not supported\": \"Dieser Dateityp wird nicht unterstützt.\",\n\t\"file too large\": \"Datei ist zu groß zur Bearbeitung. Maximal erlaubte Größe ist {size}\",\n\t\"file renamed\": \"Datei umbenannt\",\n\t\"file saved\": \"Datei gespeichert\",\n\t\"folder added\": \"Ordner hinzugefügt\",\n\t\"folder already added\": \"Ordner bereits hinzugefügt\",\n\t\"font size\": \"Schriftgröße\",\n\t\"goto\": \"Gehe zur Zeile\",\n\t\"icons definition\": \"Icon-Definitionen\",\n\t\"info\": \"Information\",\n\t\"invalid value\": \"Ungültiger Wert\",\n\t\"language changed\": \"Sprache wurde erfolgreich gewechselt\",\n\t\"linting\": \"Syntaxprüfung fehlerhaft\",\n\t\"logout\": \"Abmelden\",\n\t\"loading\": \"Laden\",\n\t\"my profile\": \"Mein Profil\",\n\t\"new file\": \"Neue Datei\",\n\t\"new folder\": \"Neuer Ordner\",\n\t\"no\": \"Nein\",\n\t\"no editor message\": \"Datei oder Ordner über das Menü öffnen oder erstellen\",\n\t\"not set\": \"Nicht konfiguriert\",\n\t\"unsaved files close app\": \"Es sind nicht gespeicherte Dateien vorhanden. Anwendung beenden?\",\n\t\"notice\": \"Hinweis\",\n\t\"open file\": \"Datei öffnen\",\n\t\"open files and folders\": \"Dateien und Ordner öffnen\",\n\t\"open folder\": \"Ordner öffnen\",\n\t\"open recent\": \"Letzte Dateien öffnen\",\n\t\"ok\": \"OK\",\n\t\"overwrite\": \"Überschreiben\",\n\t\"paste\": \"Einfügen\",\n\t\"preview mode\": \"Vorschaumodus\",\n\t\"read only file\": \"Datei kann im Lesemodus nicht gespeichert werden. Versuchen Sie Speichern unter\",\n\t\"reload\": \"Neu laden\",\n\t\"rename\": \"Umbenennen\",\n\t\"replace\": \"Ersetzen\",\n\t\"required\": \"Dieses Feld ist notwendig\",\n\t\"run your web app\": \"Ihre Webanwendung starten\",\n\t\"save\": \"Speichern\",\n\t\"saving\": \"Speichern\",\n\t\"save as\": \"Speichern als\",\n\t\"save file to run\": \"Zum Starten im Browser Datei bitte speichern\",\n\t\"search\": \"Suche\",\n\t\"see logs and errors\": \"Log und Fehler ansehen\",\n\t\"select folder\": \"Ordner wählen\",\n\t\"settings\": \"Einstellungen\",\n\t\"settings saved\": \"Einstellungen gespeichert\",\n\t\"show line numbers\": \"Zeilennummern anzeigen\",\n\t\"show hidden files\": \"Versteckte Dateien anzeigen\",\n\t\"show spaces\": \"Leerzeichen anzeigen\",\n\t\"soft tab\": \"Weiche Tabs\",\n\t\"sort by name\": \"Nach Namen sortieren\",\n\t\"success\": \"Erfolg\",\n\t\"tab size\": \"Tab-Größe\",\n\t\"text wrap\": \"Textumbruch / Zeilenumbruch\",\n\t\"theme\": \"Design\",\n\t\"unable to delete file\": \"Datei kann nicht gelöscht werden\",\n\t\"unable to open file\": \"Entschuldigung, Datei kann nicht geöffnet werden\",\n\t\"unable to open folder\": \"Entschuldigung, Ordner kann nicht geöffnet werden\",\n\t\"unable to save file\": \"Entschuldigung, Datei kann nicht gespeichert werden\",\n\t\"unable to rename\": \"Entschuldigung, Umbenennen nicht möglich\",\n\t\"unsaved file\": \"Datei ist nicht gespeichert, trotzdem schließen?\",\n\t\"warning\": \"Warnung\",\n\t\"use emmet\": \"Emmet benutzen\",\n\t\"use quick tools\": \"Schnelltools benutzen\",\n\t\"yes\": \"Ja\",\n\t\"encoding\": \"Textcodierung\",\n\t\"syntax highlighting\": \"Syntaxhervorhebung\",\n\t\"read only\": \"Nur Lesen\",\n\t\"select all\": \"Alles auswählen\",\n\t\"select branch\": \"Branch wählen\",\n\t\"create new branch\": \"Neuen Branch erzeugen\",\n\t\"use branch\": \"Benutzer-Branch\",\n\t\"new branch\": \"Neuer Branch\",\n\t\"branch\": \"Branch\",\n\t\"key bindings\": \"Tastenbelegung\",\n\t\"edit\": \"Bearbeiten\",\n\t\"reset\": \"Zurücksetzen\",\n\t\"color\": \"Farbe\",\n\t\"select word\": \"Wort auswählen\",\n\t\"quick tools\": \"Schnelltools\",\n\t\"select\": \"Auswählen\",\n\t\"editor font\": \"Editor-Schriftart\",\n\t\"new project\": \"Neues Projekt\",\n\t\"format\": \"Format\",\n\t\"project name\": \"Projektname\",\n\t\"unsupported device\": \"Ihr Gerät unterstützt keine Designs.\",\n\t\"vibrate on tap\": \"Vibrieren beim Tippen\",\n\t\"copy command is not supported by ftp.\": \"Kopierkommando wird bei FTP nicht unterstützt.\",\n\t\"support title\": \"Acode unterstützen\",\n\t\"fullscreen\": \"Vollbildmodus\",\n\t\"animation\": \"Startanimation\",\n\t\"backup\": \"Sicherung\",\n\t\"restore\": \"Wiederherstellung\",\n\t\"backup successful\": \"Sicherung erfolgreich\",\n\t\"invalid backup file\": \"Ungültige Sicherungsdatei\",\n\t\"add path\": \"Pfad hinzufügen\",\n\t\"live autocompletion\": \"Direkte Autovervollständigung\",\n\t\"file properties\": \"Dateieigenschaften\",\n\t\"path\": \"Pfad\",\n\t\"type\": \"Typ\",\n\t\"word count\": \"Wortanzahl\",\n\t\"line count\": \"Zeilenanzahl\",\n\t\"last modified\": \"Zuletzt geändert\",\n\t\"size\": \"Größe\",\n\t\"share\": \"Freigabe\",\n\t\"show print margin\": \"Druckrand anzeigen\",\n\t\"login\": \"Anmelden\",\n\t\"scrollbar size\": \"Scrollbar-Größe\",\n\t\"cursor controller size\": \"Cursor-Marker-Größe\",\n\t\"none\": \"Keiner\",\n\t\"small\": \"Klein\",\n\t\"large\": \"Groß\",\n\t\"floating button\": \"Schwebende Schaltfläche\",\n\t\"confirm on exit\": \"Bestätigung beim Beenden\",\n\t\"show console\": \"Konsole anzeigen\",\n\t\"image\": \"Bild\",\n\t\"insert file\": \"Datei einfügen\",\n\t\"insert color\": \"Farbe einfügen\",\n\t\"powersave mode warning\": \"Energiesparmodus für eine Vorschau im externen Browser abschalten.\",\n\t\"exit\": \"Verlassen\",\n\t\"custom\": \"Benutzerdefiniert\",\n\t\"reset warning\": \"Wollen Sie wirklich das Design zurücksetzen?\",\n\t\"theme type\": \"Designtyp\",\n\t\"light\": \"Hell\",\n\t\"dark\": \"Dunkel\",\n\t\"file browser\": \"Datei-Browser\",\n\t\"operation not permitted\": \"Operation nicht zulässig\",\n\t\"no such file or directory\": \"Keine Datei oder kein Verzeichnis\",\n\t\"input/output error\": \"Ein-/Ausgabe-Fehler\",\n\t\"permission denied\": \"Zugriff verweigert\",\n\t\"bad address\": \"Unzulässige Adresse\",\n\t\"file exists\": \"Datei existiert bereits\",\n\t\"not a directory\": \"Ist kein Verzeichnis\",\n\t\"is a directory\": \"Ist ein Verzeichnis\",\n\t\"invalid argument\": \"Ungültiges Argument\",\n\t\"too many open files in system\": \"Zu viele geöffnete Dateien im System\",\n\t\"too many open files\": \"Zu viele geöffnete Dateien\",\n\t\"text file busy\": \"Textdatei in Benutzung\",\n\t\"no space left on device\": \"Kein Speicherplatz mehr auf diesem Gerät\",\n\t\"read-only file system\": \"Dateisystem ist schreibgeschützt\",\n\t\"file name too long\": \"Dateiname zu lang\",\n\t\"too many users\": \"Zu viele Benutzer\",\n\t\"connection timed out\": \"Verbindungszeit ist abgelaufen\",\n\t\"connection refused\": \"Verbindung verweigert\",\n\t\"owner died\": \"Besitzer existiert nicht\",\n\t\"an error occurred\": \"Es ist ein Fehler aufgetreten\",\n\t\"add ftp\": \"FTP hinzufügen\",\n\t\"add sftp\": \"SFTP hinzufügen\",\n\t\"save file\": \"Datei speichern\",\n\t\"save file as\": \"Datei speichern als\",\n\t\"files\": \"Dateien\",\n\t\"help\": \"Hilfe\",\n\t\"file has been deleted\": \"{file} wurde gelöscht!\",\n\t\"feature not available\": \"Diese Funktion ist nur in der Bezahlversion der App verfügbar.\",\n\t\"deleted file\": \"Gelöschte Datei\",\n\t\"line height\": \"Zeilenhöhe\",\n\t\"preview info\": \"Wenn Sie eine aktive Datei starten möchten, tippen und halten Sie das Wiedergabe-Symbol.\",\n\t\"manage all files\": \"Erlauben Sie Acode, alle Dateien in den Einstellungen zu verwalten, um Dateien auf Ihrem Gerät einfach zu bearbeiten.\",\n\t\"close file\": \"Datei schließen\",\n\t\"reset connections\": \"Verbindungen zurücksetzen\",\n\t\"check file changes\": \"Dateiänderungen prüfen\",\n\t\"open in browser\": \"Im Browser öffnen\",\n\t\"desktop mode\": \"Desktop-Modus\",\n\t\"toggle console\": \"Konsole umschalten\",\n\t\"new line mode\": \"Zeilenumbruchmodus\",\n\t\"add a storage\": \"Speicher hinzufügen\",\n\t\"rate acode\": \"Acode bewerten\",\n\t\"support\": \"Unterstützung\",\n\t\"downloading file\": \"{file} herunterladen\",\n\t\"downloading...\": \"Herunterladen ...\",\n\t\"folder name\": \"Ordnername\",\n\t\"keyboard mode\": \"Tastaturmodus\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App-Einstellungen\",\n\t\"disable in-app-browser caching\": \"In-App-Browser-Cache abschalten\",\n\t\"copied to clipboard\": \"In die Zwischenablage kopiert\",\n\t\"remember opened files\": \"Geöffnete Dateien merken\",\n\t\"remember opened folders\": \"Geöffnete Ordner merken\",\n\t\"no suggestions\": \"Keine Vorschläge\",\n\t\"no suggestions aggressive\": \"Keine aufdringlichen Vorschläge\",\n\t\"install\": \"Installation\",\n\t\"installing\": \"Installieren ...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Kürzlich verwendet\",\n\t\"update\": \"Aktualisierung\",\n\t\"uninstall\": \"Deinstallation\",\n\t\"download acode pro\": \"Acode Pro herunterladen\",\n\t\"loading plugins\": \"Plugins laden\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Rückmeldung\",\n\t\"header\": \"Kopfzeile\",\n\t\"sidebar\": \"Seitenleiste\",\n\t\"inapp\": \"App-intern\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonales Scrollen\",\n\t\"reverse scrolling\": \"Umgekehrtes Scrollen\",\n\t\"formatter\": \"Formatierer\",\n\t\"format on save\": \"Beim Speichern formatieren\",\n\t\"remove ads\": \"Anzeigen entfernen\",\n\t\"fast\": \"Schnell\",\n\t\"slow\": \"Langsam\",\n\t\"scroll settings\": \"Scroll-Einstellungen\",\n\t\"scroll speed\": \"Scroll-Geschwindigkeit\",\n\t\"loading...\": \"Laden ...\",\n\t\"no plugins found\": \"Keine Plugins gefunden\",\n\t\"name\": \"Name\",\n\t\"username\": \"Benutzername\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Passwort\",\n\t\"security type\": \"Sicherheitstyp\",\n\t\"connection mode\": \"Verbindungsmodus\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Schlüsseldatei\",\n\t\"select key file\": \"Schlüsseldatei wählen\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Verbinden ...\",\n\t\"type filename\": \"Dateinamen angeben\",\n\t\"unable to load files\": \"Dateien können nicht geladen werden\",\n\t\"preview port\": \"Vorschau-Port\",\n\t\"find file\": \"Datei suchen\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Bitte wählen Sie eine Formatierung\",\n\t\"case sensitive\": \"Groß-/Kleinschreibung beachten\",\n\t\"regular expression\": \"Regulärer Ausdruck\",\n\t\"whole word\": \"Gesamtes Wort\",\n\t\"edit with\": \"Bearbeiten mit\",\n\t\"open with\": \"Öffnen mit\",\n\t\"no app found to handle this file\": \"Keine App gefunden, um diese Datei zu verarbeiten\",\n\t\"restore default settings\": \"Standardeinstellungen wiederherstellen\",\n\t\"server port\": \"Server-Port\",\n\t\"preview settings\": \"Vorschau-Einstellungen\",\n\t\"preview settings note\": \"Wenn sich Vorschau-Port und Server-Port unterscheiden, startet die App keinen Server, sondern öffnet stattdessen https://<Host>:<Vorschau-Port> im Browser oder App-Browser. Dies ist nützlich, um einen Server an einem anderen Ort zu betreiben.\",\n\t\"backup/restore note\": \"Es werden nur Ihre Einstellungen, benutzerdefinierte Designs, installierte Plugins und Tastenbelegungen gesichert. FTP/SFTP- oder GitHub-Profile werden nicht gesichert.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"FTP/SFTP bei Fehler wiederholen\",\n\t\"more\": \"Mehr\",\n\t\"thank you :)\": \"Dankeschön :)\",\n\t\"purchase pending\": \"Kauf ausstehend\",\n\t\"cancelled\": \"abgebrochen\",\n\t\"local\": \"Lokal\",\n\t\"remote\": \"Entfernt\",\n\t\"show console toggler\": \"Konsolen-Umschalter anzeigen\",\n\t\"binary file\": \"Diese Datei enthält Binärdaten, möchten Sie sie wirklich öffnen?\",\n\t\"relative line numbers\": \"Relative Zeilennummern\",\n\t\"elastic tabstops\": \"Elastische Tabulatoren\",\n\t\"line based rtl switching\": \"Zeilenbasierte RTL-Umschaltung\",\n\t\"hard wrap\": \"Harter Umbruch\",\n\t\"spellcheck\": \"Rechtschreibprüfung\",\n\t\"wrap method\": \"Umbruchmethode\",\n\t\"use textarea for ime\": \"Textbereich für IME benutzen\",\n\t\"invalid plugin\": \"Ungültiges Plugin\",\n\t\"type command\": \"Befehl eingeben\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Schnelltools-Auslösemodus\",\n\t\"print margin\": \"Druckrand\",\n\t\"touch move threshold\": \"Schwellenwert für Berührungsbewegung\",\n\t\"info-retryremotefsafterfail\": \"FTP/SFTP-Verbindung bei Fehler erneut versuchen.\",\n\t\"info-fullscreen\": \"Titelzeile auf der Startseite verstecken.\",\n\t\"info-checkfiles\": \"Dateiänderungen überprüfen, wenn sich die App im Hintergrund befindet.\",\n\t\"info-console\": \"JavaScript-Konsole wählen. Legacy ist die Standardkonsole, Eruda ist die Konsole eines Drittanbieters.\",\n\t\"info-keyboardmode\": \"Tastaturmodus für Texteingaben. 'Keine Vorschläge' blendet Vorschläge und automatische Korrektur aus. Wenn 'Keine Vorschläge' nicht funktioniert, versuchen Sie, den Wert auf 'Keine aufdringlichen Vorschläge' zu ändern.\",\n\t\"info-rememberfiles\": \"Geöffnete Dateien merken, wenn die Anwendung geschlossen wird.\",\n\t\"info-rememberfolders\": \"Geöffnete Ordner merken, wenn die Anwendung geschlossen wird.\",\n\t\"info-floatingbutton\": \"Schwebende Schaltfläche für Schnelltools ein- oder ausblenden.\",\n\t\"info-openfilelistpos\": \"Wo soll die Liste der aktiven Dateien angezeigt werden.\",\n\t\"info-touchmovethreshold\": \"Wenn die Berührungsempfindlichkeit des Gerätes zu hoch ist, können Sie diesen Wert erhöhen, um versehentliche Berührungen zu verhindern.\",\n\t\"info-scroll-settings\": \"Diese Einstellungen steuern Scrollen und Textumbruch.\",\n\t\"info-animation\": \"Wenn die Anwendung träge reagiert, deaktivieren Sie die Animation.\",\n\t\"info-quicktoolstriggermode\": \"Wenn die Schaltfläche in den Schnelltools nicht funktioniert, versuchen Sie, diesen Wert zu ändern.\",\n\t\"info-checkForAppUpdates\": \"Automatisch nach App-Updates suchen.\",\n\t\"info-quickTools\": \"Schnelltools ein- oder ausblenden.\",\n\t\"info-showHiddenFiles\": \"Versteckte Dateien und Ordner anzeigen. (Diese beginnen mit einem .)\",\n\t\"info-all_file_access\": \"Zugriff auf /sdcard und /storage im Terminal aktivieren.\",\n\t\"info-fontSize\": \"Die Schriftgröße, die zum Rendern von Text verwendet wird.\",\n\t\"info-fontFamily\": \"Die Schriftfamilie, die zum Rendern von Text verwendet wird.\",\n\t\"info-theme\": \"Das Farbdesign des Terminals.\",\n\t\"info-cursorStyle\": \"Der Cursorstil, wenn das Terminal fokussiert ist.\",\n\t\"info-cursorInactiveStyle\": \"Der Cursorstil, wenn das Terminal nicht im Fokus ist.\",\n\t\"info-fontWeight\": \"Die Schriftstärke, die zum Rendern von nicht fettem Text verwendet wird.\",\n\t\"info-cursorBlink\": \"Erlaubt das Blinken des Cursors.\",\n\t\"info-scrollback\": \"Die Anzahl der Zeilen, die beim Zurückblättern erhalten bleiben, wenn über den anfänglichen Anzeigebereich hinaus gescrollt wird.\",\n\t\"info-tabStopWidth\": \"Die Größe der Tabulatoren im Terminal.\",\n\t\"info-letterSpacing\": \"Der Abstand zwischen den Zeichen in ganzen Pixeln.\",\n\t\"info-imageSupport\": \"Unterstützung von Bildern im Terminal.\",\n\t\"info-fontLigatures\": \"Unterstützung von Ligaturen in der Schriftart im Terminal.\",\n\t\"info-confirmTabClose\": \"Bestätigung beim Schließen von Terminal-Tabs.\",\n\t\"info-backup\": \"Erstellt eine Sicherungskopie der Terminal-Installation.\",\n\t\"info-restore\": \"Stellt eine Sicherung der Terminal-Installation wieder her.\",\n\t\"info-uninstall\": \"Deinstalliert die Terminal-Installation.\",\n\t\"owned\": \"Eigene\",\n\t\"api_error\": \"API-Server nicht verfügbar, bitte nach kurzer Zeit nochmal versuchen.\",\n\t\"installed\": \"Installiert\",\n\t\"all\": \"Alle\",\n\t\"medium\": \"Mittel\",\n\t\"refund\": \"Erstattung\",\n\t\"product not available\": \"Produkt nicht verfügbar\",\n\t\"no-product-info\": \"Dieses Produkt ist in Ihrem Land zur Zeit nicht verfügbar, bitte versuchen Sie es später noch einmal.\",\n\t\"close\": \"Schließen\",\n\t\"explore\": \"Erkunden\",\n\t\"key bindings updated\": \"Tastenbelegung aktualisiert\",\n\t\"search in files\": \"In Dateien suchen\",\n\t\"exclude files\": \"Dateien ausschließen\",\n\t\"include files\": \"Dateien einschließen\",\n\t\"search result\": \"{matches} Ergebnisse in {files} Dateien.\",\n\t\"invalid regex\": \"Ungültiger regulärer Ausdruck: {message}.\",\n\t\"bottom\": \"Unten\",\n\t\"save all\": \"Alle speichern\",\n\t\"close all\": \"Alle schließen\",\n\t\"unsaved files warning\": \"Einige Dateien sind nicht gespeichert. Drücken Sie 'OK', um eine Aktion zu wählen, oder 'Abbrechen', um zurückzugehen.\",\n\t\"save all warning\": \"Wollen Sie wirklich alle Dateien speichern und schließen? Diese Aktion kann nicht zurückgenommen werden.\",\n\t\"save all changes warning\": \"Sind Sie sicher, das Sie alle Dateien speichern wollen?\",\n\t\"close all warning\": \"Wollen Sie wirklich alle Dateien schließen? Sie verlieren alle nicht gespeicherten Änderungen und diese Aktion kann nicht zurückgenommen werden.\",\n\t\"refresh\": \"Aktualisieren\",\n\t\"shortcut buttons\": \"Schnellwahl-Schaltflächen\",\n\t\"no result\": \"Kein Ergebnis\",\n\t\"searching...\": \"Suche ...\",\n\t\"quicktools:ctrl-key\": \"Steuerungstaste\",\n\t\"quicktools:tab-key\": \"Tab-Taste\",\n\t\"quicktools:shift-key\": \"Umschalttaste\",\n\t\"quicktools:undo\": \"Rückgängig\",\n\t\"quicktools:redo\": \"Wiederholen\",\n\t\"quicktools:search\": \"In Dateien suchen\",\n\t\"quicktools:save\": \"Datei speichern\",\n\t\"quicktools:esc-key\": \"Escape-Taste\",\n\t\"quicktools:curlybracket\": \"Geschweifte Klammer einfügen\",\n\t\"quicktools:squarebracket\": \"Eckige Klammer einfügen\",\n\t\"quicktools:parentheses\": \"Klammer einfügen\",\n\t\"quicktools:anglebracket\": \"Spitze Klammer einfügen\",\n\t\"quicktools:left-arrow-key\": \"Pfeiltaste nach links\",\n\t\"quicktools:right-arrow-key\": \"Pfeiltaste nach rechts\",\n\t\"quicktools:up-arrow-key\": \"Pfeiltaste nach oben\",\n\t\"quicktools:down-arrow-key\": \"Pfeiltaste nach unten\",\n\t\"quicktools:moveline-up\": \"Zeile nach oben verschieben\",\n\t\"quicktools:moveline-down\": \"Zeile nach unten verschieben\",\n\t\"quicktools:copyline-up\": \"Zeile nach oben kopieren\",\n\t\"quicktools:copyline-down\": \"Zeile nach unten kopieren\",\n\t\"quicktools:semicolon\": \"Semikolon einfügen\",\n\t\"quicktools:quotation\": \"Zitat einfügen\",\n\t\"quicktools:and\": \"Und-Symbol einfügen\",\n\t\"quicktools:bar\": \"Balken-Symbol einfügen\",\n\t\"quicktools:equal\": \"Gleichheitszeichen einfügen\",\n\t\"quicktools:slash\": \"Schrägstrich einfügen\",\n\t\"quicktools:exclamation\": \"Ausrufezeichen einfügen\",\n\t\"quicktools:alt-key\": \"Alt-Taste\",\n\t\"quicktools:meta-key\": \"Windows-Taste\",\n\t\"info-quicktoolssettings\": \"Anpassen der Schnellwahl-Schaltflächen und -Tasten im Schnelltools-Container unterhalb des Editors, um Ihre Programmiererfahrung zu verbessern.\",\n\t\"info-excludefolders\": \"Benutzen Sie das Muster **/node_modules/**, um alle Dateien im Ordner node_modules auszuschließen. Dadurch werden die Dateien nicht aufgelistet und auch nicht in die Dateisuche einbezogen.\",\n\t\"missed files\": \"Nach Beginn der Suche wurden {count} Dateien gescannt und werden nicht in die Suche einbezogen.\",\n\t\"remove\": \"Entfernen\",\n\t\"quicktools:command-palette\": \"Befehlspalette\",\n\t\"default file encoding\": \"Standard-Dateicodierung\",\n\t\"remove entry\": \"Sind Sie sicher, das Sie '{name}' von den Speicherpfaden entfernen möchten? Bitte beachten Sie, dass der Pfad selbst nicht gelöscht wird.\",\n\t\"delete entry\": \"Löschen bestätigen: '{name}'. Diese Aktion kann nicht zurückgenommen werden. Fortfahren?\",\n\t\"change encoding\": \"Neuladen von '{file}' mit '{encoding}'-Codierung? Bei dieser Aktion gehen alle ungespeicherten Änderungen an dieser Datei verloren. Wollen Sie mit dem Neuladen fortfahren?\",\n\t\"reopen file\": \"Sind Sie sicher, dass Sie '{file}' erneut öffnen wollen? Alle ungespeicherten Änderungen werden verloren gehen.\",\n\t\"plugin min version\": \"{name} ist nur in Acode - {v-code} und neuer verfügbar. Klicken Sie hier zum Aktualisieren.\",\n\t\"color preview\": \"Farbvorschau\",\n\t\"confirm\": \"Bestätigen\",\n\t\"list files\": \"Alle Dateien in <strong>{name}</strong> auflisten? Zu viele Dateien können zum Absturz der App führen.\",\n\t\"problems\": \"Probleme\",\n\t\"show side buttons\": \"Seitenknöpfe anzeigen\",\n\t\"bug_report\": \"Einen Fehlerbericht übermitteln\",\n\t\"verified publisher\": \"Verifizierter Herausgeber\",\n\t\"most_downloaded\": \"Meist Heruntergeladene\",\n\t\"newly_added\": \"Neu Hinzugefügte\",\n\t\"top_rated\": \"Am besten Bewertete\",\n\t\"rename not supported\": \"Umbenennen des Termux-Verzeichnisses wird nicht unterstützt\",\n\t\"compress\": \"Komprimieren\",\n\t\"copy uri\": \"URI kopieren\",\n\t\"delete entries\": \"Sind Sie sicher, dass Sie {count} Einträge löschen wollen?\",\n\t\"deleting items\": \"Löschen von {count} Einträgen ...\",\n\t\"import project zip\": \"Projekt(-Zip) importieren\",\n\t\"changelog\": \"Änderungsprotokoll\",\n\t\"notifications\": \"Benachrichtigungen\",\n\t\"no_unread_notifications\": \"Keine ungelesenen Benachrichtigungen\",\n\t\"should_use_current_file_for_preview\": \"Aktuelle Datei für die Vorschau anstelle der Standardeinstellung (index.html) verwenden\",\n\t\"fade fold widgets\": \"Widgets falten ausblenden\",\n\t\"quicktools:home-key\": \"Pos 1-Taste\",\n\t\"quicktools:end-key\": \"Ende-Taste\",\n\t\"quicktools:pageup-key\": \"Bild auf-Taste\",\n\t\"quicktools:pagedown-key\": \"Bild ab-Taste\",\n\t\"quicktools:delete-key\": \"Entfernen-Taste\",\n\t\"quicktools:tilde\": \"Tilde einfügen\",\n\t\"quicktools:backtick\": \"Accent grave einfügen\",\n\t\"quicktools:hash\": \"Raute einfügen\",\n\t\"quicktools:dollar\": \"Dollar einfügen\",\n\t\"quicktools:modulo\": \"Prozentzeichen einfügen\",\n\t\"quicktools:caret\": \"Caret einfügen\",\n\t\"plugin_enabled\": \"Plugin aktiviert\",\n\t\"plugin_disabled\": \"Plugin deaktiviert\",\n\t\"enable_plugin\": \"Dieses Plugin aktivieren\",\n\t\"disable_plugin\": \"Dieses Plugin deaktivieren\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal-Einstellungen\",\n\t\"font ligatures\": \"Schriftligaturen\",\n\t\"letter spacing\": \"Zeichenabstand\",\n\t\"terminal:tab stop width\": \"Tabulatorbreite\",\n\t\"terminal:scrollback\": \"Zeilen zurückblättern\",\n\t\"terminal:cursor blink\": \"Blinkender Cursor\",\n\t\"terminal:font weight\": \"Schriftstärke\",\n\t\"terminal:cursor inactive style\": \"Cursorstil inaktiv\",\n\t\"terminal:cursor style\": \"Cursorstil\",\n\t\"terminal:font family\": \"Schriftfamilie\",\n\t\"terminal:convert eol\": \"Zeilenende konvertieren\",\n\t\"terminal:confirm tab close\": \"Terminal-Tab schließen bestätigen\",\n\t\"terminal:image support\": \"Bildunterstützung\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"Zugriff auf alle Dateien\",\n\t\"fonts\": \"Schriftarten\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"Downloads\",\n\t\"reviews\": \"Bewertungen\",\n\t\"overview\": \"Überblick\",\n\t\"contributors\": \"Mitwirkende\",\n\t\"quicktools:hyphen\": \"Bindestrich einfügen\",\n\t\"check for app updates\": \"Auf App-Updates prüfen\",\n\t\"prompt update check consent message\": \"Acode kann nach neuen App-Updates suchen, wenn Sie online sind. Update-Prüfungen aktivieren?\",\n\t\"keywords\": \"Schlüsselwörter\",\n\t\"author\": \"Autor\",\n\t\"filtered by\": \"Gefiltert nach\",\n\t\"clean install state\": \"Sauberer Installationsstatus\",\n\t\"backup created\": \"Sicherung erstellt\",\n\t\"restore completed\": \"Wiederherstellung abgeschlossen\",\n\t\"restore will include\": \"Dies wird wiederhergestellt\",\n\t\"restore warning\": \"Dieser Vorgang kann nicht rückgängig gemacht werden. Fortfahren?\",\n\t\"reload to apply\": \"Neu laden, um Änderungen zu übernehmen?\",\n\t\"reload app\": \"App neu laden\",\n\t\"preparing backup\": \"Sicherung vorbereiten\",\n\t\"collecting settings\": \"Einstellungen sammeln\",\n\t\"collecting key bindings\": \"Tastenbelegung sammeln\",\n\t\"collecting plugins\": \"Plugin-Informationen sammeln\",\n\t\"creating backup\": \"Sicherungsdatei erstellen\",\n\t\"validating backup\": \"Sicherung überprüfen\",\n\t\"restoring key bindings\": \"Tastenbelegung wiederherstellen\",\n\t\"restoring plugins\": \"Plugins wiederherstellen\",\n\t\"restoring settings\": \"Einstellungen wiederherstellen\",\n\t\"legacy backup warning\": \"Dies ist ein älteres Sicherungsformat. Einige Funktionen sind möglicherweise eingeschränkt.\",\n\t\"checksum mismatch\": \"Prüfsummenfehler – die Sicherungsdatei wurde möglicherweise geändert oder ist beschädigt.\",\n\t\"plugin not found\": \"Plugin in der Registrierung nicht gefunden\",\n\t\"paid plugin skipped\": \"Bezahltes Plugin – Kauf nicht gefunden\",\n\t\"source not found\": \"Quelldatei existiert nicht mehr\",\n\t\"restored\": \"Wiederhergestellt\",\n\t\"skipped\": \"Übersprungen\",\n\t\"backup not valid object\": \"Die Sicherungsdatei ist kein gültiges Objekt.\",\n\t\"backup no data\": \"Die Sicherungsdatei enthält keine wiederherzustellenden Daten.\",\n\t\"backup legacy warning\": \"Dies ist ein älteres Sicherungsformat (v1). Einige Funktionen sind möglicherweise eingeschränkt.\",\n\t\"backup missing metadata\": \"Fehlende Sicherungsmetadaten – einige Informationen sind möglicherweise nicht verfügbar.\",\n\t\"backup checksum mismatch\": \"Prüfsummenfehler – die Sicherungsdatei wurde möglicherweise geändert oder ist beschädigt. Gehen Sie vorsichtig vor.\",\n\t\"backup checksum verify failed\": \"Prüfsumme konnte nicht überprüft werden.\",\n\t\"backup invalid settings\": \"Ungültiges Einstellungsformat\",\n\t\"backup invalid keybindings\": \"Ungültiges Tastenbelegungsformat\",\n\t\"backup invalid plugins\": \"Ungültiges Format der installierten Plugins\",\n\t\"issues found\": \"Gefundene Probleme\",\n\t\"error details\": \"Fehlerdetails\",\n\t\"active tools\": \"Aktive Tools\",\n\t\"available tools\": \"Verfügbare Tools\",\n\t\"recent\": \"Zuletzt verwendete Dateien\",\n\t\"command palette\": \"Befehlspalette öffnen\",\n\t\"change theme\": \"Design ändern\",\n\t\"documentation\": \"Dokumentation\",\n\t\"open in terminal\": \"Im Terminal öffnen\",\n\t\"developer mode\": \"Entwicklermodus\",\n\t\"info-developermode\": \"Aktivieren Sie die Entwicklertools (Eruda) zum Debuggen von Plugins und Überprüfen des App-Status. Der Inspektor wird beim Start der App initialisiert.\",\n\t\"developer mode enabled\": \"Entwicklermodus aktiviert. Verwenden Sie die Befehlspalette, um den Inspektor umzuschalten (Strg+Umschalt+I).\",\n\t\"developer mode disabled\": \"Entwicklermodus deaktiviert\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/en-us.json",
    "content": "{\n\t\"lang\": \"English\",\n\t\"about\": \"About\",\n\t\"active files\": \"Active files\",\n\t\"alert\": \"Alert\",\n\t\"app theme\": \"App theme\",\n\t\"autocorrect\": \"Enable autocorrect?\",\n\t\"autosave\": \"Autosave\",\n\t\"cancel\": \"Cancel\",\n\t\"change language\": \"Change language\",\n\t\"choose color\": \"Choose color\",\n\t\"clear\": \"clear\",\n\t\"close app\": \"Close the application?\",\n\t\"commit message\": \"Commit message\",\n\t\"console\": \"Console\",\n\t\"conflict error\": \"Conflict! Please wait before another commit.\",\n\t\"copy\": \"Copy\",\n\t\"create folder error\": \"Sorry, unable create new folder\",\n\t\"cut\": \"Cut\",\n\t\"delete\": \"Delete\",\n\t\"dependencies\": \"Dependencies\",\n\t\"delay\": \"Time in milliseconds\",\n\t\"editor settings\": \"Editor settings\",\n\t\"editor theme\": \"Editor theme\",\n\t\"enter file name\": \"Enter file name\",\n\t\"enter folder name\": \"Enter folder name\",\n\t\"empty folder message\": \"Empty Folder\",\n\t\"enter line number\": \"Enter line number\",\n\t\"error\": \"Error\",\n\t\"failed\": \"Failed\",\n\t\"file already exists\": \"File already exists\",\n\t\"file already exists force\": \"File already exists. Overwrite?\",\n\t\"file changed\": \" has been changed, reload file?\",\n\t\"file deleted\": \"File deleted\",\n\t\"file is not supported\": \"File is not supported\",\n\t\"file not supported\": \"This file type is not supported.\",\n\t\"file too large\": \"File is to large to handle. Max file size allowed is {size}\",\n\t\"file renamed\": \"file renamed\",\n\t\"file saved\": \"file saved\",\n\t\"folder added\": \"folder added\",\n\t\"folder already added\": \"folder already added\",\n\t\"font size\": \"Font size\",\n\t\"goto\": \"Goto line\",\n\t\"icons definition\": \"Icons definition\",\n\t\"info\": \"Info\",\n\t\"invalid value\": \"Invalid value\",\n\t\"language changed\": \"language has been changed successfully\",\n\t\"linting\": \"Check syntax error\",\n\t\"logout\": \"Logout\",\n\t\"loading\": \"Loading\",\n\t\"my profile\": \"My profile\",\n\t\"new file\": \"New file\",\n\t\"new folder\": \"New Folder\",\n\t\"no\": \"No\",\n\t\"no editor message\": \"Open or create new file and folder from menu\",\n\t\"not set\": \"Not set\",\n\t\"unsaved files close app\": \"There are unsaved files. Close application?\",\n\t\"notice\": \"Notice\",\n\t\"open file\": \"Open file\",\n\t\"open files and folders\": \"Open files and folders\",\n\t\"open folder\": \"Open folder\",\n\t\"open recent\": \"Open recent\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"Overwrite\",\n\t\"paste\": \"Paste\",\n\t\"preview mode\": \"Preview mode\",\n\t\"read only file\": \"Cannot save read only file. Please try save as\",\n\t\"reload\": \"Reload\",\n\t\"rename\": \"Rename\",\n\t\"replace\": \"Replace\",\n\t\"required\": \"This field is required\",\n\t\"run your web app\": \"Run your web app\",\n\t\"save\": \"Save\",\n\t\"saving\": \"Saving\",\n\t\"save as\": \"Save as\",\n\t\"save file to run\": \"Please save this file to run in browser\",\n\t\"search\": \"Search\",\n\t\"see logs and errors\": \"See logs and errors\",\n\t\"select folder\": \"Select folder\",\n\t\"settings\": \"Settings\",\n\t\"settings saved\": \"Settings saved\",\n\t\"show line numbers\": \"Show line numbers\",\n\t\"show hidden files\": \"Show hidden files\",\n\t\"show spaces\": \"Show spaces\",\n\t\"soft tab\": \"Soft tab\",\n\t\"sort by name\": \"Sort by name\",\n\t\"success\": \"Success\",\n\t\"tab size\": \"Tab size\",\n\t\"text wrap\": \"Text wrap / Word wrap\",\n\t\"theme\": \"Theme\",\n\t\"unable to delete file\": \"unable to delete file\",\n\t\"unable to open file\": \"Sorry, unable to open file\",\n\t\"unable to open folder\": \"Sorry, unable to open folder\",\n\t\"unable to save file\": \"Sorry, unable to save file\",\n\t\"unable to rename\": \"Sorry, unable to rename\",\n\t\"unsaved file\": \"This file is not saved, close anyway?\",\n\t\"warning\": \"Warning\",\n\t\"use emmet\": \"Use emmet\",\n\t\"use quick tools\": \"Use quick tools\",\n\t\"yes\": \"Yes\",\n\t\"encoding\": \"Text encoding\",\n\t\"syntax highlighting\": \"Syntax highlighting\",\n\t\"read only\": \"Read only\",\n\t\"select all\": \"Select all\",\n\t\"select branch\": \"Select branch\",\n\t\"create new branch\": \"Create new branch\",\n\t\"use branch\": \"Use branch\",\n\t\"new branch\": \"New branch\",\n\t\"branch\": \"Branch\",\n\t\"key bindings\": \"Key bindings\",\n\t\"edit\": \"Edit\",\n\t\"reset\": \"Reset\",\n\t\"color\": \"Color\",\n\t\"select word\": \"Select word\",\n\t\"quick tools\": \"Quick tools\",\n\t\"select\": \"Select\",\n\t\"editor font\": \"Editor font\",\n\t\"new project\": \"New project\",\n\t\"format\": \"Format\",\n\t\"project name\": \"Project name\",\n\t\"unsupported device\": \"Your device does not support theme.\",\n\t\"vibrate on tap\": \"Vibrate on tap\",\n\t\"copy command is not supported by ftp.\": \"Copy command is not supported by FTP.\",\n\t\"support title\": \"Support Acode\",\n\t\"fullscreen\": \"Fullscreen\",\n\t\"animation\": \"Animation\",\n\t\"backup\": \"Backup\",\n\t\"restore\": \"Restore\",\n\t\"backup successful\": \"Backup successful\",\n\t\"invalid backup file\": \"Invalid backup file\",\n\t\"add path\": \"Add path\",\n\t\"live autocompletion\": \"Live autocompletion\",\n\t\"file properties\": \"File properties\",\n\t\"path\": \"Path\",\n\t\"type\": \"Type\",\n\t\"word count\": \"Word count\",\n\t\"line count\": \"Line count\",\n\t\"last modified\": \"Last modified\",\n\t\"size\": \"Size\",\n\t\"share\": \"Share\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"Login\",\n\t\"scrollbar size\": \"Scrollbar size\",\n\t\"cursor controller size\": \"Cursor controller size\",\n\t\"none\": \"None\",\n\t\"small\": \"Small\",\n\t\"large\": \"Large\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"Confirm on exit\",\n\t\"show console\": \"Show console\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"Turn off power saving mode to preview in external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"Custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"Light\",\n\t\"dark\": \"Dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme, installed plugins and key bindings. It will not backup your FTP/SFTP or App state.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails.\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"These settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"available tools\": \"Available tools\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\"\n}\n"
  },
  {
    "path": "src/lang/es-sv.json",
    "content": "{\n\t\"lang\": \"Español (by DouZerr)\",\n\t\"about\": \"Información\",\n\t\"active files\": \"Archivos Activos\",\n\t\"alert\": \"Alerta\",\n\t\"app theme\": \"Tema de App\",\n\t\"autocorrect\": \"¿Habilitar Autocorrección?\",\n\t\"autosave\": \"Guardar Automáticamente\",\n\t\"cancel\": \"Cancelar\",\n\t\"change language\": \"Cambiar Idioma\",\n\t\"choose color\": \"Elegir color\",\n\t\"clear\": \"Limpiar\",\n\t\"close app\": \"¿Cerrar la Aplicación?\",\n\t\"commit message\": \"Commitear mensaje\",\n\t\"console\": \"Consola\",\n\t\"conflict error\": \"¡Conflicto! Por favor espera antes de otro commit.\",\n\t\"copy\": \"Copiar\",\n\t\"create folder error\": \"Lo sentimos, no se puede crear una nueva carpeta\",\n\t\"cut\": \"Cortar\",\n\t\"delete\": \"Borrar\",\n\t\"dependencies\": \"Dependencias\",\n\t\"delay\": \"Tiempo en milisegundos\",\n\t\"editor settings\": \"Ajustes del Editor\",\n\t\"editor theme\": \"Tema del Editor\",\n\t\"enter file name\": \"Ingrese Nombre del Archivo\",\n\t\"enter folder name\": \"Ingrese Nombre de la Carpeta\",\n\t\"empty folder message\": \"Carpeta Vacía\",\n\t\"enter line number\": \"Ingrese Número de Línea\",\n\t\"error\": \"Error\",\n\t\"failed\": \"Ha Fallado\",\n\t\"file already exists\": \"El archivo ya existe\",\n\t\"file already exists force\": \"El archivo ya existe. ¿Sobrescribir?\",\n\t\"file changed\": \" ha sido cambiado, recargar archivo?\",\n\t\"file deleted\": \"Archivo Borrado\",\n\t\"file is not supported\": \"El archivo no es compatible\",\n\t\"file not supported\": \"Este tipo de archivo no es compatible.\",\n\t\"file too large\": \"El archivo es demasiado grande para manejarlo. El tamaño máximo de archivo permitido es {size}\",\n\t\"file renamed\": \"Archivo renombrado\",\n\t\"file saved\": \"Archivo guardado\",\n\t\"folder added\": \"Carpeta agregada\",\n\t\"folder already added\": \"Carpeta ya agregada\",\n\t\"font size\": \"Tamaño de Fuente\",\n\t\"goto\": \"Ir a la Línea\",\n\t\"icons definition\": \"Definición de iconos\",\n\t\"info\": \"info\",\n\t\"invalid value\": \"Valor Inválido\",\n\t\"language changed\": \"Idioma cambiado exitosamente\",\n\t\"linting\": \"Comprobar error de sintaxis\",\n\t\"logout\": \"Cerrar Sesión\",\n\t\"loading\": \"Cargando\",\n\t\"my profile\": \"Mi Perfil\",\n\t\"new file\": \"Nuevo Archivo\",\n\t\"new folder\": \"Nueva Carpeta\",\n\t\"no\": \"No\",\n\t\"no editor message\": \"Abrir o crear un nuevo archivo y carpeta desde el menú\",\n\t\"not set\": \"No Establecido\",\n\t\"unsaved files close app\": \"Existen archivos sin guardar.  ¿Cerrar la aplicación?\",\n\t\"notice\": \"Aviso\",\n\t\"open file\": \"Abrir Archivo\",\n\t\"open files and folders\": \"Abrir archivos y carpetas\",\n\t\"open folder\": \"Abrir Carpeta\",\n\t\"open recent\": \"Abrir Recientes\",\n\t\"ok\": \"Aceptar\",\n\t\"overwrite\": \"Sobrescribir\",\n\t\"paste\": \"Pegar\",\n\t\"preview mode\": \"Modo de vista previa\",\n\t\"read only file\": \"No se puede guardar un archivo de solo lectura. Por favor intenta guardar como\",\n\t\"reload\": \"Recargar\",\n\t\"rename\": \"Renombrar\",\n\t\"replace\": \"Reemplazar\",\n\t\"required\": \"Este campo es requerido\",\n\t\"run your web app\": \"Ejecute su aplicación web\",\n\t\"save\": \"Guardar\",\n\t\"saving\": \"Guardando\",\n\t\"save as\": \"Guardar como\",\n\t\"save file to run\": \"Guardar archivo para ejecutar en el navegador\",\n\t\"search\": \"Buscar\",\n\t\"see logs and errors\": \"Ver registros y errores\",\n\t\"select folder\": \"Seleccionar Carpeta\",\n\t\"settings\": \"Ajustes\",\n\t\"settings saved\": \"Ajustes guardados\",\n\t\"show line numbers\": \"Mostrar números de línea\",\n\t\"show hidden files\": \"Mostrar archivos ocultos\",\n\t\"show spaces\": \"Mostrar espacios\",\n\t\"soft tab\": \"Pestaña suave\",\n\t\"sort by name\": \"Ordenar por nombre\",\n\t\"success\": \"Éxito\",\n\t\"tab size\": \"Tamaño de pestaña\",\n\t\"text wrap\": \"Ajuste de línea\",\n\t\"theme\": \"Tema\",\n\t\"unable to delete file\": \"no se puede eliminar el archivo\",\n\t\"unable to open file\": \"Lo sentimos, no se puede abrir el archivo\",\n\t\"unable to open folder\": \"Lo sentimos, no se puede abrir la carpeta\",\n\t\"unable to save file\": \"Lo sentimos, no se puede guardar el archivo\",\n\t\"unable to rename\": \"Lo sentimos, no se puede cambiar el nombre\",\n\t\"unsaved file\": \"Este archivo no se guardado, ¿cerrar de todos modos?\",\n\t\"warning\": \"Advertencia\",\n\t\"use emmet\": \"Usar emmet\",\n\t\"use quick tools\": \"Usa herramientas rápidas\",\n\t\"yes\": \"Si\",\n\t\"encoding\": \"Codificación de Texto\",\n\t\"syntax highlighting\": \"Resaltado de sintaxis\",\n\t\"read only\": \"Solo lectura\",\n\t\"select all\": \"Seleccionar todo\",\n\t\"select branch\": \"Seleccione rama\",\n\t\"create new branch\": \"Crear nueva rama\",\n\t\"use branch\": \"Usar rama\",\n\t\"new branch\": \"Nueva rama\",\n\t\"branch\": \"Rama\",\n\t\"key bindings\": \"Atajos de teclado\",\n\t\"edit\": \"Editar\",\n\t\"reset\": \"Reiniciar\",\n\t\"color\": \"Color\",\n\t\"select word\": \"Seleccionar palabra\",\n\t\"quick tools\": \"Herramientas rápidas\",\n\t\"select\": \"Seleccionar\",\n\t\"editor font\": \"Fuente del editor\",\n\t\"new project\": \"Nuevo Proyecto\",\n\t\"format\": \"Formato\",\n\t\"project name\": \"Nombre del Proyecto\",\n\t\"unsupported device\": \"Su dispositivo no es compatible con el tema.\",\n\t\"vibrate on tap\": \"Vibrar al tocar\",\n\t\"copy command is not supported by ftp.\": \"Comando de copia no es compatible con FTP.\",\n\t\"support title\": \"Apoye Acode\",\n\t\"fullscreen\": \"pantalla completa\",\n\t\"animation\": \"animación\",\n\t\"backup\": \"respaldo\",\n\t\"restore\": \"restaurar\",\n\t\"backup successful\": \"Respaldo exitoso\",\n\t\"invalid backup file\": \"Archivo de respaldo inválido\",\n\t\"add path\": \"Añadir dirección\",\n\t\"live autocompletion\": \"Autocompletado en vivo\",\n\t\"file properties\": \"Propiedades del archivo\",\n\t\"path\": \"Dirección\",\n\t\"type\": \"Tipo\",\n\t\"word count\": \"Conteo de palabras\",\n\t\"line count\": \"Conteo de líneas\",\n\t\"last modified\": \"Última modificación\",\n\t\"size\": \"Tamaño\",\n\t\"share\": \"Compartir\",\n\t\"show print margin\": \"Mostrar margen de impresión\",\n\t\"login\": \"Iniciar Sesión\",\n\t\"scrollbar size\": \"Tamaño de barra de scroll\",\n\t\"cursor controller size\": \"Tamaño del controlador de cursor\",\n\t\"none\": \"ninguno\",\n\t\"small\": \"pequeño\",\n\t\"large\": \"grande\",\n\t\"floating button\": \"Botón flotante\",\n\t\"confirm on exit\": \"Confirmar al salir\",\n\t\"show console\": \"Mostrar consola\",\n\t\"image\": \"Imagen\",\n\t\"insert file\": \"Insertar archivo\",\n\t\"insert color\": \"Insertar color\",\n\t\"powersave mode warning\": \"Desactive el modo de ahorro de energía para obtener una vista previa en un navegador externo\",\n\t\"exit\": \"Salir\",\n\t\"custom\": \"personalizado\",\n\t\"reset warning\": \"¿Seguro que quieres restablecer el tema?\",\n\t\"theme type\": \"Tipo de tema\",\n\t\"light\": \"claro\",\n\t\"dark\": \"oscuro\",\n\t\"file browser\": \"Explorador de archivos\",\n\t\"operation not permitted\": \"Operación no permitida\",\n\t\"no such file or directory\": \"El fichero o directorio no existe\",\n\t\"input/output error\": \"Error de entrada/salida\",\n\t\"permission denied\": \"Permiso denegado\",\n\t\"bad address\": \"Dirección incorrecta\",\n\t\"file exists\": \"El archivo ya existe\",\n\t\"not a directory\": \"No es un directorio\",\n\t\"is a directory\": \"Es un directorio\",\n\t\"invalid argument\": \"Argumento no válido\",\n\t\"too many open files in system\": \"Demasiados archivos abiertos en el sistema\",\n\t\"too many open files\": \"Demasiados archivos abiertos\",\n\t\"text file busy\": \"Archivo de texto ocupado\",\n\t\"no space left on device\": \"No queda espacio en el dispositivo\",\n\t\"read-only file system\": \"Sistema de archivos de sólo lectura\",\n\t\"file name too long\": \"Nombre de archivo demasiado largo\",\n\t\"too many users\": \"Demasiados usuarios\",\n\t\"connection timed out\": \"Tiempo de conexión agotado\",\n\t\"connection refused\": \"Conexión denegada\",\n\t\"owner died\": \"El propietario murio\",\n\t\"an error occurred\": \"Ocurrió un error\",\n\t\"add ftp\": \"Añadir FTP\",\n\t\"add sftp\": \"Añadir SFTP\",\n\t\"save file\": \"Guardar el archivo\",\n\t\"save file as\": \"Guardar archivo como\",\n\t\"files\": \"Archivos\",\n\t\"help\": \"Ayuda\",\n\t\"file has been deleted\": \"{file} ha sido eliminado!\",\n\t\"feature not available\": \"Esta función solo está disponible en la versión de pago de la aplicación.\",\n\t\"deleted file\": \"Archivo eliminado\",\n\t\"line height\": \"Altura de la línea\",\n\t\"preview info\": \"Si desea ejecutar el archivo activo, toque y mantenga presionado el ícono de reproducción.\",\n\t\"manage all files\": \"Permita que Acode editor administre todos los archivos en la configuración para editar archivos en su dispositivo fácilmente.\",\n\t\"close file\": \"Cerrar el archivo\",\n\t\"reset connections\": \"Restablecer conexiones\",\n\t\"check file changes\": \"Comprobar cambios en el archivo\",\n\t\"open in browser\": \"Abrir en el navegador\",\n\t\"desktop mode\": \"Modo escritorio\",\n\t\"toggle console\": \"Abrir/Cerrar la consola\",\n\t\"new line mode\": \"Nuevo modo de línea\",\n\t\"add a storage\": \"Añadir un almacenamiento\",\n\t\"rate acode\": \"Califica Acode\",\n\t\"support\": \"Apoyar\",\n\t\"downloading file\": \"Descargando {file}\",\n\t\"downloading...\": \"Descargando...\",\n\t\"folder name\": \"Nombre de la carpeta\",\n\t\"keyboard mode\": \"Modo de teclado\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"Ajustes de aplicacion\",\n\t\"disable in-app-browser caching\": \"Desactivar el almacenamiento en caché de la aplicación en el navegador\",\n\t\"copied to clipboard\": \"Copiado al portapapeles\",\n\t\"remember opened files\": \"Recordar archivos abiertos\",\n\t\"remember opened folders\": \"Recordar carpetas abiertas\",\n\t\"no suggestions\": \"No hay sugerencias\",\n\t\"no suggestions aggressive\": \"Ninguna sugerencia, agresivo\",\n\t\"install\": \"Instalar\",\n\t\"installing\": \"Instalando...\",\n\t\"plugins\": \"Extensiones\",\n\t\"recently used\": \"Usado recientemente\",\n\t\"update\": \"Actualizar\",\n\t\"uninstall\": \"Desinstalar\",\n\t\"download acode pro\": \"Descargar Acode Pro\",\n\t\"loading plugins\": \"Cargando extensiones\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Comentarios\",\n\t\"header\": \"Cabecera\",\n\t\"sidebar\": \"Barra lateral\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Navegador\",\n\t\"diagonal scrolling\": \"Desplazamiento en diagonal\",\n\t\"reverse scrolling\": \"Desplazamiento inverso\",\n\t\"formatter\": \"Formateador\",\n\t\"format on save\": \"Formatear al guardar\",\n\t\"remove ads\": \"Eliminar anuncios\",\n\t\"fast\": \"Rápido\",\n\t\"slow\": \"Lento\",\n\t\"scroll settings\": \"Ajustes de desplazamiento\",\n\t\"scroll speed\": \"Velocidad de desplazamiento\",\n\t\"loading...\": \"Cargando...\",\n\t\"no plugins found\": \"No se han encontrado extensiones\",\n\t\"name\": \"Nombre\",\n\t\"username\": \"Usuario\",\n\t\"optional\": \"opcional\",\n\t\"hostname\": \"Nombre del host\",\n\t\"password\": \"Contraseña\",\n\t\"security type\": \"Tipo de seguridad\",\n\t\"connection mode\": \"Modo de conexión\",\n\t\"port\": \"Puerto\",\n\t\"key file\": \"Archivo de claves\",\n\t\"select key file\": \"Seleccionar archivo clave\",\n\t\"passphrase\": \"Frase de acceso\",\n\t\"connecting...\": \"Conectando...\",\n\t\"type filename\": \"Escriba el nombre del archivo\",\n\t\"unable to load files\": \"No se pueden cargar archivos\",\n\t\"preview port\": \"Puerto de previsualización\",\n\t\"find file\": \"Buscar archivo\",\n\t\"system\": \"Sistema\",\n\t\"please select a formatter\": \"Seleccione un formateador\",\n\t\"case sensitive\": \"Distingue entre mayúsculas y minúsculas\",\n\t\"regular expression\": \"Expresión regular\",\n\t\"whole word\": \"Palabra completa\",\n\t\"edit with\": \"Editar con\",\n\t\"open with\": \"Abrir con\",\n\t\"no app found to handle this file\": \"No se ha encontrado ninguna aplicación que gestione este archivo\",\n\t\"restore default settings\": \"Restablecer la configuración predeterminada\",\n\t\"server port\": \"Puerto del servidor\",\n\t\"preview settings\": \"Ajustes de previsualización\",\n\t\"preview settings note\": \"Si el puerto de previsualización y el puerto del servidor son diferentes, la aplicación no arrancará el servidor y en su lugar abrirá https://<host>:<puerto de previsualización> en el navegador o en el navegador de la aplicación. Esto es útil cuando se está ejecutando un servidor en otro lugar.\",\n\t\"backup/restore note\": \"Sólo hará una copia de seguridad de tu configuración, tema personalizado y atajos de teclado. No hará copia de seguridad de su FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Reintentar ftp/sftp cuando falla\",\n\t\"more\": \"Más\",\n\t\"thank you :)\": \"Gracias :)\",\n\t\"purchase pending\": \"compra pendiente\",\n\t\"cancelled\": \"cancelado\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remoto\",\n\t\"show console toggler\": \"Mostrar selector de consola\",\n\t\"binary file\": \"Este archivo contiene datos binarios, ¿desea abrirlo?\",\n\t\"relative line numbers\": \"Números de línea relativos\",\n\t\"elastic tabstops\": \"Tabuladores elásticos\",\n\t\"line based rtl switching\": \"Conmutación RTL basada en líneas\",\n\t\"hard wrap\": \"ajuste de línea rígido\",\n\t\"spellcheck\": \"Corrector ortográfico\",\n\t\"wrap method\": \"Método de ajuste de línea\",\n\t\"use textarea for ime\": \"Utilizar área de texto para IME\",\n\t\"invalid plugin\": \"Extensión inválida\",\n\t\"type command\": \"Escriba el comando\",\n\t\"plugin\": \"Extensión\",\n\t\"quicktools trigger mode\": \"Modo de activación de herramientas rápidas\",\n\t\"print margin\": \"Margen de impresión\",\n\t\"touch move threshold\": \"Umbral de movimiento táctil\",\n\t\"info-retryremotefsafterfail\": \"Reintentar la conexión FTP/SFTP cuando falle\",\n\t\"info-fullscreen\": \"Ocultar la barra de título en la pantalla de inicio.\",\n\t\"info-checkfiles\": \"Comprueba los cambios en los archivos cuando la aplicación esté en segundo plano.\",\n\t\"info-console\": \"Elija la consola JavaScript. Legacy es la consola por defecto, Eruda es una consola de terceros adicional.\",\n\t\"info-keyboardmode\": \"Modo de teclado para entrada de texto, 'sin sugerencias' ocultará las sugerencias y la autocorrección. Si 'sin sugerencias' no funciona, trate de cambiar el valor a 'ninguna sugerencia, agresivo'.\",\n\t\"info-rememberfiles\": \"Recordar los archivos abiertos al cerrar la aplicación.\",\n\t\"info-rememberfolders\": \"Recordar las carpetas abiertas al cerrar la aplicación.\",\n\t\"info-floatingbutton\": \"Mostrar u ocultar el botón flotante de herramientas rápidas.\",\n\t\"info-openfilelistpos\": \"Dónde mostrar la lista de archivos activos.\",\n\t\"info-touchmovethreshold\": \"Si la sensibilidad táctil de tu dispositivo es demasiado alta, puedes aumentar este valor para evitar movimientos táctiles accidentales.\",\n\t\"info-scroll-settings\": \"Esta configuración contiene los ajustes de desplazamiento, incluido el ajuste de línea de texto.\",\n\t\"info-animation\": \"Si la aplicación va lenta, desactiva la animación.\",\n\t\"info-quicktoolstriggermode\": \"Si el botón de las herramientas rápidas no funciona, intente cambiar este valor.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"En propiedad\",\n\t\"api_error\": \"El servidor API no funciona, inténtelo después de un rato.\",\n\t\"installed\": \"Instalado\",\n\t\"all\": \"Todo\",\n\t\"medium\": \"Medio\",\n\t\"refund\": \"Reembolso\",\n\t\"product not available\": \"Producto no disponible\",\n\t\"no-product-info\": \"Este producto no está disponible en su país en este momento, por favor inténtelo más tarde.\",\n\t\"close\": \"Cerrar\",\n\t\"explore\": \"Explorar\",\n\t\"key bindings updated\": \"Atajos de teclado actualizados\",\n\t\"search in files\": \"Buscar en archivos\",\n\t\"exclude files\": \"Excluir archivos\",\n\t\"include files\": \"Incluir archivos\",\n\t\"search result\": \"{matches} resulta en {files} archivos.\",\n\t\"invalid regex\": \"Expresión regular inválida: {message}.\",\n\t\"bottom\": \"Parte inferior\",\n\t\"save all\": \"Guardar todo\",\n\t\"close all\": \"Cerrar todo\",\n\t\"unsaved files warning\": \"Algunos archivos no se han guardado. Pulsa 'Aceptar' para decidir qué hacer o 'Cancelar' para volver atrás.\",\n\t\"save all warning\": \"¿Estás seguro de que quieres guardar todos los archivos y cerrar? Esta acción no se puede revertir.\",\n\t\"save all changes warning\": \"¿Estás seguro de que quieres guardar todos los archivos?\",\n\t\"close all warning\": \"¿Estás seguro de que quieres cerrar todos los archivos? Perderá los cambios no guardados y esta acción no se puede revertir.\",\n\t\"refresh\": \"Actualizar\",\n\t\"shortcut buttons\": \"Botones de acceso rápido\",\n\t\"no result\": \"Sin resultado\",\n\t\"searching...\": \"Buscando...\",\n\t\"quicktools:ctrl-key\": \"Tecla Control/Comando\",\n\t\"quicktools:tab-key\": \"Tecla Tabulador\",\n\t\"quicktools:shift-key\": \"Tecla Mayús\",\n\t\"quicktools:undo\": \"Deshacer\",\n\t\"quicktools:redo\": \"Rehacer\",\n\t\"quicktools:search\": \"Buscar en archivo\",\n\t\"quicktools:save\": \"Guardar archivo\",\n\t\"quicktools:esc-key\": \"Tecla Escape\",\n\t\"quicktools:curlybracket\": \"Insertar llave\",\n\t\"quicktools:squarebracket\": \"Insertar corchete\",\n\t\"quicktools:parentheses\": \"Insertar paréntesis\",\n\t\"quicktools:anglebracket\": \"Insertar signo mayor que/menor que\",\n\t\"quicktools:left-arrow-key\": \"Tecla flecha izquierda\",\n\t\"quicktools:right-arrow-key\": \"Tecla flecha derecha\",\n\t\"quicktools:up-arrow-key\": \"Tecla flecha arriba\",\n\t\"quicktools:down-arrow-key\": \"Tecla flecha abajo\",\n\t\"quicktools:moveline-up\": \"Mover la línea hacia arriba\",\n\t\"quicktools:moveline-down\": \"Mover la línea hacia abajo\",\n\t\"quicktools:copyline-up\": \"Copiar la línea hacia arriba\",\n\t\"quicktools:copyline-down\": \"Copiar la línea hacia abajo\",\n\t\"quicktools:semicolon\": \"Insertar punto y coma\",\n\t\"quicktools:quotation\": \"Insertar comillas\",\n\t\"quicktools:and\": \"Insertar símbolo ampersand\",\n\t\"quicktools:bar\": \"Insertar símbolo barra vertical\",\n\t\"quicktools:equal\": \"Insertar símbolo igual\",\n\t\"quicktools:slash\": \"Insertar símbolo barra oblicua\",\n\t\"quicktools:exclamation\": \"Insertar exclamación\",\n\t\"quicktools:alt-key\": \"Tecla Alt\",\n\t\"quicktools:meta-key\": \"Tecla Windows\",\n\t\"info-quicktoolssettings\": \"Personalice los botones de acceso rápido y las teclas del teclado en el contenedor de herramientas rápidas situado debajo del editor para mejorar su experiencia de codificación.\",\n\t\"info-excludefolders\": \"Utilice el patrón **/node_modules/** para ignorar todos los archivos de la carpeta node_modules. Esto excluirá los archivos de la lista y también evitará que se incluyan en las búsquedas de archivos.\",\n\t\"missed files\": \"Se han escaneado {count} archivos después de iniciarse la búsqueda y no se incluirán en ella.\",\n\t\"remove\": \"Eliminar\",\n\t\"quicktools:command-palette\": \"Paleta de comandos\",\n\t\"default file encoding\": \"Codificación de archivo por defecto\",\n\t\"remove entry\": \"¿Estás seguro de que quieres eliminar '{name}' de las rutas guardadas? Tenga en cuenta que al eliminarlo no se borrará la ruta en sí.\",\n\t\"delete entry\": \"Confirmar eliminación: '{name}'. Esta acción no se puede deshacer. ¿Continuar?\",\n\t\"change encoding\": \"¿Reabrir '{file}' con codificación '{encoding}'? Esta acción provocará la pérdida de cualquier cambio no guardado realizado en el archivo. ¿Desea continuar con la reapertura?\",\n\t\"reopen file\": \"¿Estás seguro de que quieres reabrir '{file}'? Cualquier cambio no guardado se perderá.\",\n\t\"plugin min version\": \"{name} sólo disponible en Acode - {v-code} y superior. Haga clic aquí para actualizar.\",\n\t\"color preview\": \"Vista previa del color\",\n\t\"confirm\": \"Confirmar\",\n\t\"list files\": \"¿Listar todos los archivos en <strong>{name}</strong>? Demasiados archivos pueden bloquear la aplicación.\",\n\t\"problems\": \"Problemas\",\n\t\"show side buttons\": \"Mostrar botones laterales\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Publicador verificado\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Patrocinador\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/fr-fr.json",
    "content": "{\n\t\"lang\": \"Français\",\n\t\"about\": \"À propos\",\n\t\"active files\": \"Fichiers ouverts\",\n\t\"alert\": \"Alerte\",\n\t\"app theme\": \"Thème de l'application\",\n\t\"autocorrect\": \"Activer la correction automatique ?\",\n\t\"autosave\": \"Sauvegarde automatique\",\n\t\"cancel\": \"Annuler\",\n\t\"change language\": \"Changer de langue\",\n\t\"choose color\": \"Choisir une couleur \",\n\t\"clear\": \"Effacer\",\n\t\"close app\": \"Fermer l'application ?\",\n\t\"commit message\": \"Message de commit\",\n\t\"console\": \"Console\",\n\t\"conflict error\": \"Conflit ! Veuillez attendre avant de procéder à un autre commit.\",\n\t\"copy\": \"Copier\",\n\t\"create folder error\": \"Désolé, impossible de créer un nouveau dossier\",\n\t\"cut\": \"Couper\",\n\t\"delete\": \"Effacer\",\n\t\"dependencies\": \"Dépendances\",\n\t\"delay\": \"Temps en millisecondes\",\n\t\"editor settings\": \"Paramètres de l'éditeur\",\n\t\"editor theme\": \"Thème de l'éditeur\",\n\t\"enter file name\": \"Entrer un nom de fichier\",\n\t\"enter folder name\": \"Entrer un nom de dossier\",\n\t\"empty folder message\": \"Dossier vide\",\n\t\"enter line number\": \"Entrer le numéro de ligne\",\n\t\"error\": \"Erreur\",\n\t\"failed\": \"Échec\",\n\t\"file already exists\": \"Le fichier existe déjà\",\n\t\"file already exists force\": \"Le fichier existe déjà. L'écraser ?\",\n\t\"file changed\": \" a été modifié, recharger le fichier ?\",\n\t\"file deleted\": \"Fichier effacé\",\n\t\"file is not supported\": \"Fichier non supporté\",\n\t\"file not supported\": \"Type de fichier non supporté.\",\n\t\"file too large\": \"Le fichier est trop volumineux. La taille maximale autorisée est {size}\",\n\t\"file renamed\": \"Fichier renommé\",\n\t\"file saved\": \"Fichier sauvegardé\",\n\t\"folder added\": \"Dossier ajouté\",\n\t\"folder already added\": \"Dossier déjà ajouté\",\n\t\"font size\": \"Taille de la police\",\n\t\"goto\": \"Aller à ligne\",\n\t\"icons definition\": \"Définition des icônes\",\n\t\"info\": \"Info\",\n\t\"invalid value\": \"Valeur invalide\",\n\t\"language changed\": \"La langue a été changée avec succès\",\n\t\"linting\": \"Vérifier les erreurs de syntaxe ?\",\n\t\"logout\": \"Se déconnecter\",\n\t\"loading\": \"Chargement\",\n\t\"my profile\": \"Mon profil\",\n\t\"new file\": \"Nouveau fichier\",\n\t\"new folder\": \"Nouveau dossier\",\n\t\"no\": \"Non\",\n\t\"no editor message\": \"Ouvrir ou créer un nouveau fichier ou dossier depuis le menu\",\n\t\"not set\": \"Non défini\",\n\t\"unsaved files close app\": \"Il existe des fichiers non sauvegardés. Fermer l'application ?\",\n\t\"notice\": \"Avis\",\n\t\"open file\": \"Ouvrir un fichier\",\n\t\"open files and folders\": \"Ouvrir les fichiers et dossiers\",\n\t\"open folder\": \"Ouvrir un dossier\",\n\t\"open recent\": \"Récent\",\n\t\"ok\": \"OK\",\n\t\"overwrite\": \"Écraser\",\n\t\"paste\": \"Coller\",\n\t\"preview mode\": \"Mode d'aperçu\",\n\t\"read only file\": \"Sauvegarde impossible, fichier en lecture seule. Essayez d'enregistrer sous un autre nom\",\n\t\"reload\": \"Recharger\",\n\t\"rename\": \"Renommer\",\n\t\"replace\": \"Remplacer\",\n\t\"required\": \"Champ requis\",\n\t\"run your web app\": \"Lancer votre application web\",\n\t\"save\": \"Enregistrer\",\n\t\"saving\": \"Enregistrement\",\n\t\"save as\": \"Enregistrer sous\",\n\t\"save file to run\": \"Veuillez enregistrer ce fichier pour l'exécuter dans le navigateur\",\n\t\"search\": \"Recherche\",\n\t\"see logs and errors\": \"Voir les journaux et les erreurs\",\n\t\"select folder\": \"Choisir un dossier\",\n\t\"settings\": \"Paramètres\",\n\t\"settings saved\": \"Paramètres enregistrés\",\n\t\"show line numbers\": \"Afficher les numéros de ligne\",\n\t\"show hidden files\": \"Afficher les fichiers cachés\",\n\t\"show spaces\": \"Afficher les espaces\",\n\t\"soft tab\": \"Tabulation légère\",\n\t\"sort by name\": \"Trier par nom\",\n\t\"success\": \"Succès\",\n\t\"tab size\": \"Taille de tabulation\",\n\t\"text wrap\": \"Lignes longues sur plusieurs lignes\",\n\t\"theme\": \"Thème\",\n\t\"unable to delete file\": \"Impossible de supprimer le fichier\",\n\t\"unable to open file\": \"Désolé, impossible d'ouvrir le fichier\",\n\t\"unable to open folder\": \"Désolé, impossible d'ouvrir le dossier\",\n\t\"unable to save file\": \"Désolé, impossible d'enregistrer le fichier\",\n\t\"unable to rename\": \"Désolé, impossible de renommer\",\n\t\"unsaved file\": \"Ce fichier n'a pas été sauvegardé, le fermer quand même ?\",\n\t\"warning\": \"Avertissement\",\n\t\"use emmet\": \"Utiliser emmet\",\n\t\"use quick tools\": \"Utiliser les outils rapides\",\n\t\"yes\": \"Oui\",\n\t\"encoding\": \"Encodage du texte\",\n\t\"syntax highlighting\": \"Coloration syntaxique\",\n\t\"read only\": \"Lecture seule\",\n\t\"select all\": \"Tout sélectionner\",\n\t\"select branch\": \"Sélectionner une branche\",\n\t\"create new branch\": \"Créer une nouvelle branche\",\n\t\"use branch\": \"Utiliser une branche\",\n\t\"new branch\": \"Nouvelle branche\",\n\t\"branch\": \"Branche\",\n\t\"key bindings\": \"Raccourcis\",\n\t\"edit\": \"Modifier\",\n\t\"reset\": \"Réinitialiser\",\n\t\"color\": \"Couleur\",\n\t\"select word\": \"Sélectionner un mot\",\n\t\"quick tools\": \"Outils rapides\",\n\t\"select\": \"Sélectionner\",\n\t\"editor font\": \"Police de l'éditeur\",\n\t\"new project\": \"Nouveau projet\",\n\t\"format\": \"Format\",\n\t\"project name\": \"Nom du projet\",\n\t\"unsupported device\": \"Votre appareil ne prend pas en charge ce thème.\",\n\t\"vibrate on tap\": \"Vibrer au toucher\",\n\t\"copy command is not supported by ftp.\": \"La copie n'est pas supportée en FTP.\",\n\t\"support title\": \"Soutenir Acode\",\n\t\"fullscreen\": \"Plein écran\",\n\t\"animation\": \"Animation\",\n\t\"backup\": \"Sauvegarder\",\n\t\"restore\": \"Restaurer\",\n\t\"backup successful\": \"Sauvegarde faite avec succès !\",\n\t\"invalid backup file\": \"Fichier de sauvegarde invalide\",\n\t\"add path\": \"Ajouter un chemin d'accès\",\n\t\"live autocompletion\": \"Correction automatique\",\n\t\"file properties\": \"Propriétés du fichier\",\n\t\"path\": \"Chemin\",\n\t\"type\": \"Type\",\n\t\"word count\": \"Nombre de mots\",\n\t\"line count\": \"Nombre de lignes\",\n\t\"last modified\": \"Modifié pour la dernière fois le\",\n\t\"size\": \"Taille\",\n\t\"share\": \"Partager\",\n\t\"show print margin\": \"Afficher les marges d'impression\",\n\t\"login\": \"Se connecter\",\n\t\"scrollbar size\": \"Taille de la barre de défilement\",\n\t\"cursor controller size\": \"Taille du curseur de contrôle\",\n\t\"none\": \"Aucun\",\n\t\"small\": \"Petit\",\n\t\"large\": \"Grand\",\n\t\"floating button\": \"Bouton flottant\",\n\t\"confirm on exit\": \"Confirmer pour quitter\",\n\t\"show console\": \"Afficher la console\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insérer un fichier\",\n\t\"insert color\": \"Insérer une couleur\",\n\t\"powersave mode warning\": \"Désactiver le mode d'économie d'énergie pour l'aperçu dans un navigateur externe.\",\n\t\"exit\": \"Quitter\",\n\t\"custom\": \"Personnalisé\",\n\t\"reset warning\": \"Voulez-vous vraiment réinitialiser le thème ?\",\n\t\"theme type\": \"Type de thème\",\n\t\"light\": \"Clair\",\n\t\"dark\": \"Sombre\",\n\t\"file browser\": \"Gestionnaire de fichiers\",\n\t\"operation not permitted\": \"Opération non autorisée\",\n\t\"no such file or directory\": \"Aucun fichier ou répertoire ne porte ce nom\",\n\t\"input/output error\": \"Erreur d'entrée/sortie\",\n\t\"permission denied\": \"Autorisation refusée\",\n\t\"bad address\": \"Mauvaise adresse\",\n\t\"file exists\": \"Le fichier existe\",\n\t\"not a directory\": \"N'est pas un répertoire\",\n\t\"is a directory\": \"Est un répertoire\",\n\t\"invalid argument\": \"Argument invalide\",\n\t\"too many open files in system\": \"Trop de fichiers ouverts dans le système\",\n\t\"too many open files\": \"Trop de fichiers ouverts\",\n\t\"text file busy\": \"Fichier texte occupé\",\n\t\"no space left on device\": \"Pas d'espace libre disponible sur le périphérique\",\n\t\"read-only file system\": \"Système de fichiers en lecture seule\",\n\t\"file name too long\": \"Nom de fichier trop long\",\n\t\"too many users\": \"Trop d'utilisateurs\",\n\t\"connection timed out\": \"La connexion a expiré\",\n\t\"connection refused\": \"Connexion rejetée\",\n\t\"owner died\": \"Le propriétaire a disparu\",\n\t\"an error occurred\": \"Une erreur s'est produite\",\n\t\"add ftp\": \"Ajouter FTP\",\n\t\"add sftp\": \"Ajouter SFTP\",\n\t\"save file\": \"Enregistrer le fichier\",\n\t\"save file as\": \"Enregistrer le fichier sous\",\n\t\"files\": \"Dossiers\",\n\t\"help\": \"Aide\",\n\t\"file has been deleted\": \"{file} a été supprimé !\",\n\t\"feature not available\": \"Cette fonctionnalité n'est disponible que dans la version payante de l'application.\",\n\t\"deleted file\": \"Fichier supprimé\",\n\t\"line height\": \"Hauteur de ligne\",\n\t\"preview info\": \"Si vous voulez exécuter le fichier actif, appuyez longuement sur l'icône de lecture.\",\n\t\"manage all files\": \"Autorisez l'éditeur Acode à gérer tous les fichiers dans les paramètres pour modifier facilement les fichiers sur votre appareil.\",\n\t\"close file\": \"Fermer le fichier\",\n\t\"reset connections\": \"Réinitialiser les connexions\",\n\t\"check file changes\": \"Vérifier les modifications du fichier\",\n\t\"open in browser\": \"Ouvrir dans le navigateur\",\n\t\"desktop mode\": \"Mode bureau\",\n\t\"toggle console\": \"Activer/désactiver la console\",\n\t\"new line mode\": \"Mode nouvelle ligne\",\n\t\"add a storage\": \"Ajouter un stockage\",\n\t\"rate acode\": \"Évaluer Acode\",\n\t\"support\": \"Soutenir\",\n\t\"downloading file\": \"Téléchargement de {file}\",\n\t\"downloading...\": \"Téléchargement...\",\n\t\"folder name\": \"Nom du dossier\",\n\t\"keyboard mode\": \"Mode de saisie\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"Paramètres de l'application\",\n\t\"disable in-app-browser caching\": \"Désactiver la mise en cache dans le navigateur de l'application\",\n\t\"copied to clipboard\": \"Copié dans le presse-papiers\",\n\t\"remember opened files\": \"Mémoriser les fichiers ouverts\",\n\t\"remember opened folders\": \"Mémoriser les dossiers ouverts\",\n\t\"no suggestions\": \"Aucune suggestion\",\n\t\"no suggestions aggressive\": \"Aucune suggestion agressive\",\n\t\"install\": \"Installer\",\n\t\"installing\": \"Installation...\",\n\t\"plugins\": \"Extensions\",\n\t\"recently used\": \"Récemment utilisé\",\n\t\"update\": \"Mise à jour\",\n\t\"uninstall\": \"Désinstaller\",\n\t\"download acode pro\": \"Télécharger Acode pro\",\n\t\"loading plugins\": \"Charger les extensions\",\n\t\"diagonal scrolling\": \"Défilement en diagonale\",\n\t\"reverse scrolling\": \"Défilement inversé\",\n\t\"formatter\": \"Formateur de code\",\n\t\"format on save\": \"Formater à l'enregistrement\",\n\t\"remove ads\": \"Supprimer les pubs\",\n\t\"faqs\": \"FAQ\",\n\t\"feedback\": \"Commentaires\",\n\t\"header\": \"Barre supérieure\",\n\t\"sidebar\": \"Barre latérale\",\n\t\"inapp\": \"Intégré\",\n\t\"browser\": \"Navigateur\",\n\t\"fast\": \"Rapide\",\n\t\"slow\": \"Lent\",\n\t\"scroll settings\": \"Paramètres du défilement\",\n\t\"scroll speed\": \"Vitesse du défilement\",\n\t\"loading...\": \"Chargement...\",\n\t\"no plugins found\": \"Aucune extension trouvée\",\n\t\"name\": \"Nom\",\n\t\"username\": \"Nom d'utilisateur\",\n\t\"optional\": \"optionel\",\n\t\"hostname\": \"Nom du serveur\",\n\t\"password\": \"Mot de passe\",\n\t\"security type\": \"Type de sécurité\",\n\t\"connection mode\": \"Mode de connexion\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Fichier de clé\",\n\t\"select key file\": \"Sélectionner le fichier de clé\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connection...\",\n\t\"type filename\": \"Entrer le nom du fichier\",\n\t\"unable to load files\": \"Impossible de charger les fichiers\",\n\t\"preview port\": \"Port pour l'aperçu\",\n\t\"find file\": \"Rechercher un fichier\",\n\t\"system\": \"Système\",\n\t\"please select a formatter\": \"Sélectionnez un formateur de code\",\n\t\"case sensitive\": \"Sensible à la casse\",\n\t\"regular expression\": \"Expression rationnelle\",\n\t\"whole word\": \"Mot entier\",\n\t\"edit with\": \"Modifier avec\",\n\t\"open with\": \"Ouvrir avec\",\n\t\"no app found to handle this file\": \"Aucune appli trouvée pour utiliser ce fichier\",\n\t\"restore default settings\": \"Rétablir les paramètres par défaut\",\n\t\"server port\": \"Port du serveur\",\n\t\"preview settings\": \"Paramètres des aperçus\",\n\t\"preview settings note\": \"Si le port d'aperçu et le port du serveur sont différents, l'appli ne démarrera pas le serveur. Au lieu de ça, elle ouvrira https://<serveur>:<port d'aperçu> dans le navigateur (externe ou intégré). C'est pratique si vous avez un serveur distant.\",\n\t\"backup/restore note\": \"Cela ne sauvegardera que vos paramètres, votre thème personnalisé et vos raccourcis clavier. Vos réglages FTP/SFTP ne seront pas sauvegardés.\",\n\t\"host\": \"Serveur\",\n\t\"retry ftp/sftp when fail\": \"Réessayer FTP/SFTP après échec\",\n\t\"more\": \"Plus\",\n\t\"thank you :)\": \"Merci :)\",\n\t\"purchase pending\": \"achat en cours\",\n\t\"cancelled\": \"annulé\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Distant\",\n\t\"show console toggler\": \"Afficher l'interrupteur de la console\",\n\t\"binary file\": \"Ce fichier contient des données binaires. Voulez-vous vraiment l'ouvrir ?\",\n\t\"relative line numbers\": \"Numéros de ligne relatifs\",\n\t\"elastic tabstops\": \"Taquets élastiques\",\n\t\"line based rtl switching\": \"Texte de droite à gauche par ligne\",\n\t\"hard wrap\": \"Retour à la ligne dur\",\n\t\"spellcheck\": \"Vérification de l'orthographe\",\n\t\"wrap method\": \"Gestion du débordement des lignes\",\n\t\"use textarea for ime\": \"Utiliser une textarea pour écrire\",\n\t\"invalid plugin\": \"Extension invalide\",\n\t\"type command\": \"Entrer une commande\",\n\t\"plugin\": \"Extension\",\n\t\"quicktools trigger mode\": \"Mode de déclenchement des outils rapides\",\n\t\"print margin\": \"Marge de droite\",\n\t\"touch move threshold\": \"Seuil de déplacement tactile\",\n\t\"info-retryremotefsafterfail\": \"Réessayer la connexion FTP/SFTP après échec.\",\n\t\"info-fullscreen\": \"Masquer la barre de titre dans l'écran d'accueil.\",\n\t\"info-checkfiles\": \"Vérifier si les fichiers ont été modifiés quand l'appli est passée à l'arrière-plan.\",\n\t\"info-console\": \"Choisir la console JavaScript. Legacy est la console par défaut, eruda est une console tierce.\",\n\t\"info-keyboardmode\": \"Mode du clavier pour la saisie. « Aucune suggestion » masque les suggestions et l'autocorrection. Si les suggestions ne fonctionnent pas, essayez « Aucune suggestion agressive ».\",\n\t\"info-rememberfiles\": \"Mémoriser les fichiers ouverts lorsque l'appli est fermée.\",\n\t\"info-rememberfolders\": \"Mémoriser les dossiers ouverts lorsque l'appli est fermée.\",\n\t\"info-floatingbutton\": \"Afficher ou masquer le bouton flottant des outils rapides.\",\n\t\"info-openfilelistpos\": \"Où afficher la liste des fichiers ouverts.\",\n\t\"info-touchmovethreshold\": \"Si la sensibilité tactile de votre appareil est trop élevée, vous pouvez augmenter cette valeur pour empêcher des déplacements accidentels.\",\n\t\"info-scroll-settings\": \"Ces paramètres permettent de configurer le défilement et la gestion des longues lignes.\",\n\t\"info-animation\": \"Si l'appli est lente, désactivez les animations.\",\n\t\"info-quicktoolstriggermode\": \"Si le bouton des outils rapides ne fonctionne pas, essayez de changer cette valeur.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Propriété\",\n\t\"api_error\": \"Serveur d'API éteint, veuillez réessayer plus tard.\",\n\t\"installed\": \"Installé\",\n\t\"all\": \"Tout\",\n\t\"medium\": \"Moyen\",\n\t\"refund\": \"Remboursement\",\n\t\"product not available\": \"Produit non disponible\",\n\t\"no-product-info\": \"Ce produit n'est pas disponible dans votre pays à l'heure actuelle, veuillez réessayer plus tard.\",\n\t\"close\": \"Fermer\",\n\t\"explore\": \"Explorer\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin activé\",\n\t\"plugin_disabled\": \"Plugin désactivé\",\n\t\"enable_plugin\": \"Activer ce plugin\",\n\t\"disable_plugin\": \"Désactiver ce plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Parrainer\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/he-il.json",
    "content": "{\n\t\"lang\": \"עברית\",\n\t\"about\": \"אודות\",\n\t\"active files\": \"קבצים פעילים\",\n\t\"alert\": \"התראה\",\n\t\"app theme\": \"עיצוב\",\n\t\"autocorrect\": \"להפעיל תיקון אוטומטי?\",\n\t\"autosave\": \"שמירה אוטומטית\",\n\t\"cancel\": \"ביטול\",\n\t\"change language\": \"שינוי שפה\",\n\t\"choose color\": \"בחירת צבע\",\n\t\"clear\": \"ניקוי\",\n\t\"close app\": \"לסגור את האפלקציה?\",\n\t\"commit message\": \"הודעת commit\",\n\t\"console\": \"קונסול\",\n\t\"conflict error\": \"התנגשות! אנא המתן לפני ביצוע commit נוסף.\",\n\t\"copy\": \"העתק\",\n\t\"create folder error\": \"מצטערים, לא הצלחנו ליצור תיקיה חדשה\",\n\t\"cut\": \"חתוך\",\n\t\"delete\": \"מחק\",\n\t\"dependencies\": \"תלויות\",\n\t\"delay\": \"זמן במילישניות\",\n\t\"editor settings\": \"הגדרות העורך\",\n\t\"editor theme\": \"ערוך עיצוב\",\n\t\"enter file name\": \"הקלד שם קובץ\",\n\t\"enter folder name\": \"הקלד שם תיקיה\",\n\t\"empty folder message\": \"תיקה ריקה\",\n\t\"enter line number\": \"הקלד מספר שורה\",\n\t\"error\": \"שגיאה\",\n\t\"failed\": \"נכשל\",\n\t\"file already exists\": \"קובץ כבר קיים\",\n\t\"file already exists force\": \"קובץ כבר קיים, לדרוס אותו?\",\n\t\"file changed\": \"הקובץ השתנה, לטעון את הקובץ המעודכן?\",\n\t\"file deleted\": \"קובץ נמחק\",\n\t\"file is not supported\": \"קובץ לא נתמך\",\n\t\"file not supported\": \"סוג קובץ זה אינו נתמך\",\n\t\"file too large\": \"קובץ גדול מידי, מקסימום גודל קובץ מותר {size}\",\n\t\"file renamed\": \"שם הקובץ השתנה\",\n\t\"file saved\": \"קובץ נשמר\",\n\t\"folder added\": \"תיקיה נוספה\",\n\t\"folder already added\": \"תיקיה כבר נוספה\",\n\t\"font size\": \"גודל גופן\",\n\t\"goto\": \"עבור לשורה\",\n\t\"icons definition\": \"הגדרת סמלים\",\n\t\"info\": \"מידע\",\n\t\"invalid value\": \"ערך לא חוקי\",\n\t\"language changed\": \"שפה שונתה בהצלחה\",\n\t\"linting\": \"בדיקת שגיאת תחביר\",\n\t\"logout\": \"התנתק\",\n\t\"loading\": \"טוען\",\n\t\"my profile\": \"הפרופיל שלי\",\n\t\"new file\": \"קובץ חדש\",\n\t\"new folder\": \"תיקיה חדשה\",\n\t\"no\": \"לא\",\n\t\"no editor message\": \"פתח או צור קובץ ותיקייה חדשים מהתפריט\",\n\t\"not set\": \"לא הוגדר\",\n\t\"unsaved files close app\": \"ישנם מספר קבצים שלא נשמרו, לסגור את האפליקציה?\",\n\t\"notice\": \"לידיעתך\",\n\t\"open file\": \"פתח קובץ\",\n\t\"open files and folders\": \"פתח קבצים ותקיות\",\n\t\"open folder\": \"פתח תיקיה\",\n\t\"open recent\": \"פתח אחרונים\",\n\t\"ok\": \"בסדר\",\n\t\"overwrite\": \"דריסה\",\n\t\"paste\": \"הדבק\",\n\t\"preview mode\": \"מצב תצוגה מקדימה\",\n\t\"read only file\": \"לא ניתן לשמור קובץ לקריאה בלבד נא לשמור כ\",\n\t\"reload\": \"טעינה מחדש\",\n\t\"rename\": \"שנה שם\",\n\t\"replace\": \"החלף\",\n\t\"required\": \"שם זה נדרש\",\n\t\"run your web app\": \"הפעל את אפליקציית האינטרנט שלך\",\n\t\"save\": \"שמור\",\n\t\"saving\": \"שומר\",\n\t\"save as\": \"שמור כ...\",\n\t\"save file to run\": \"נא לשמור את הקובץ כדי להריץ בדפדפן\",\n\t\"search\": \"חיפוש\",\n\t\"see logs and errors\": \"הצג יומנים ושגיאות\",\n\t\"select folder\": \"בחר תיקיה\",\n\t\"settings\": \"הגדרות\",\n\t\"settings saved\": \"הגדרות נשמרו\",\n\t\"show line numbers\": \"הצג מספרי שורה\",\n\t\"show hidden files\": \"הצגת קבצים מוסתרים\",\n\t\"show spaces\": \"הצגת רווחים\",\n\t\"soft tab\": \"לשונית רכה\",\n\t\"sort by name\": \"סדר לפי שם\",\n\t\"success\": \"הצליח\",\n\t\"tab size\": \"גודל טאב\",\n\t\"text wrap\": \"גלישת טקסט\",\n\t\"theme\": \"עיצוב\",\n\t\"unable to delete file\": \"לא ניתן למחוק קובץ\",\n\t\"unable to open file\": \"מצטערים, לא הצלחנו לפתוח את הקובץ\",\n\t\"unable to open folder\": \"מצטערים, לא הצלחנו לפתוח את התיקיה\",\n\t\"unable to save file\": \"מצטערים, לא הצלחנו לשמור את הקובץ\",\n\t\"unable to rename\": \"מצטערים, לא הצלחנו לשנות את שם הקובץ\",\n\t\"unsaved file\": \"הקובץ עדיין לא נשמר, לצאת בכל זאת?\",\n\t\"warning\": \"אזהרה\",\n\t\"use emmet\": \"השתמש ב- emmet\",\n\t\"use quick tools\": \"השתמש בכלים מהירים\",\n\t\"yes\": \"כן\",\n\t\"encoding\": \"קידוד טקסט\",\n\t\"syntax highlighting\": \"הדגשת תחביר\",\n\t\"read only\": \"קריאה בלבד\",\n\t\"select all\": \"בחר הכל\",\n\t\"select branch\": \"בחר בראנץ\",\n\t\"create new branch\": \"צור בראנץ חדש\",\n\t\"use branch\": \"השתמש בראנץ\",\n\t\"new branch\": \"בראנץ חדש\",\n\t\"branch\": \"בראנץ\",\n\t\"key bindings\": \"Key bindings\",\n\t\"edit\": \"ערוך\",\n\t\"reset\": \"איפוס\",\n\t\"color\": \"צבע\",\n\t\"select word\": \"בחר מילה\",\n\t\"quick tools\": \"כלים מהירים\",\n\t\"select\": \"בחר\",\n\t\"editor font\": \"עורך פונטים\",\n\t\"new project\": \"פרוייקט חדש\",\n\t\"format\": \"פורמט\",\n\t\"project name\": \"שם פרוייקט\",\n\t\"unsupported device\": \"המכשיר שלך לא תומך בעיצובים.\",\n\t\"vibrate on tap\": \"רטט במגע\",\n\t\"copy command is not supported by ftp.\": \"פקודת ההעתקה אינה נתמכת על ידי FTP.\",\n\t\"support title\": \"תמוך ב- Acode\",\n\t\"fullscreen\": \"מסך מלא\",\n\t\"animation\": \"אנימציה\",\n\t\"backup\": \"גיבוי\",\n\t\"restore\": \"שיחזור\",\n\t\"backup successful\": \"גיבוי הצליח\",\n\t\"invalid backup file\": \"קובץ גיבוי לא תקין\",\n\t\"add path\": \"הוסף נתיב\",\n\t\"live autocompletion\": \"השלמה אוטומטית בזמן אמת\",\n\t\"file properties\": \"מאפייני קובץ\",\n\t\"path\": \"נתיב\",\n\t\"type\": \"סוג\",\n\t\"word count\": \"ספירת מילים\",\n\t\"line count\": \"ספירת שורות\",\n\t\"last modified\": \"נערך לאחרונה\",\n\t\"size\": \"גודל\",\n\t\"share\": \"שיתוף\",\n\t\"show print margin\": \"הצג שולי הדפסה\",\n\t\"login\": \"התחברות\",\n\t\"scrollbar size\": \"גודל סרגל הגלילה\",\n\t\"cursor controller size\": \"גודל בקר הסמן\",\n\t\"none\": \"ללא\",\n\t\"small\": \"קטן\",\n\t\"large\": \"גדול\",\n\t\"floating button\": \"כפתור צף\",\n\t\"confirm on exit\": \"אמת יציאה\",\n\t\"show console\": \"הצג קונסולה\",\n\t\"image\": \"תמונה\",\n\t\"insert file\": \"הכנס קובץ\",\n\t\"insert color\": \"הכנס צבע\",\n\t\"powersave mode warning\": \"כבה את מצב חיסכון באנרגיה כדי להציג תצוגה מקדימה בדפדפן חיצוני.\",\n\t\"exit\": \"יציאה\",\n\t\"custom\": \"מותאם אישית\",\n\t\"reset warning\": \"האם אתה בטוח שברצונך לאפס את העיצוב?\",\n\t\"theme type\": \"סוג עיצוב\",\n\t\"light\": \"מואר\",\n\t\"dark\": \"כהה\",\n\t\"file browser\": \"עיון הקבצים\",\n\t\"operation not permitted\": \"פעולה אסורה\",\n\t\"no such file or directory\": \"לא נמצא קובץ או תיקיה כזו\",\n\t\"input/output error\": \"שגיאת קלט/פלט\",\n\t\"permission denied\": \"ההרשאה נדחתה\",\n\t\"bad address\": \"כתובת לא תקינה\",\n\t\"file exists\": \"קובץ קיים\",\n\t\"not a directory\": \"לא תיקיה\",\n\t\"is a directory\": \"תיקיה\",\n\t\"invalid argument\": \"ארגומנט לא חוקי\",\n\t\"too many open files in system\": \"יותר מידי קבצים פתוחים במכשיר\",\n\t\"too many open files\": \"יותר מידי קבצים פתוחים\",\n\t\"text file busy\": \"קובץ טקסט עסוק\",\n\t\"no space left on device\": \"לא נשאר אחסון במכשיר\",\n\t\"read-only file system\": \"קבצי מערכת לצפיה בלבד\",\n\t\"file name too long\": \"שם הקובץ ארוך מידי\",\n\t\"too many users\": \"יותר מידי משתמשים\",\n\t\"connection timed out\": \"תם הזמן שהוקצב לחיבור\",\n\t\"connection refused\": \"חיבור נדחה\",\n\t\"owner died\": \"הבעלים נפטר\",\n\t\"an error occurred\": \"אירעה שגיאה\",\n\t\"add ftp\": \"הוסף FTP\",\n\t\"add sftp\": \"הוסף SFTP\",\n\t\"save file\": \"שמור קובץ\",\n\t\"save file as\": \"שמור קובץ כ\",\n\t\"files\": \"קבצים\",\n\t\"help\": \"עזרה\",\n\t\"file has been deleted\": \"{file} נמחק!\",\n\t\"feature not available\": \"תכונה זו זמינה רק בגרסה בתשלום של האפליקציה.\",\n\t\"deleted file\": \"קובץ מחוק\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"אם ברצונך להפעיל את הקובץ הפעיל, לחץ והחזק את סמל ההפעלה.\",\n\t\"manage all files\": \"אפשר לעורך Acode לנהל את כל הקבצים שלך כדי לערוך  קבצים במכשיר שלך בקלות.\",\n\t\"close file\": \"סגור קובץ\",\n\t\"reset connections\": \"חיבורים אחרונים\",\n\t\"check file changes\": \"בדוק שינוי בקבצים\",\n\t\"open in browser\": \"פתח בדפדפן\",\n\t\"desktop mode\": \"מצב שולחן עבודה\",\n\t\"toggle console\": \"הפעלה/כיבוי קונסולה\",\n\t\"new line mode\": \"מצב שורה חדשה\",\n\t\"add a storage\": \"הוסף אחסון\",\n\t\"rate acode\": \"דרג את Acode\",\n\t\"support\": \"תמיכה\",\n\t\"downloading file\": \"מוריד את {file}\",\n\t\"downloading...\": \"מוריד...\",\n\t\"folder name\": \"שם  תיקיה\",\n\t\"keyboard mode\": \"מצב מקלדת\",\n\t\"normal\": \"רגיל\",\n\t\"app settings\": \"הגדרות האפליקציה\",\n\t\"disable in-app-browser caching\": \"השבתת אחסון במטמון בדפדפן בתוך האפליקציה\",\n\t\"Should use Current File For preview instead of default (index.html)\": \"יש להשתמש בקובץ הנוכחי לתצוגה מקדימה במקום ברירת המחדל (index.html)\",\n\t\"copied to clipboard\": \"הועתק\",\n\t\"remember opened files\": \"זכור קבצים שנפתחו\",\n\t\"remember opened folders\": \"זכור תקיות שנפתחו\",\n\t\"no suggestions\": \"אין הצעות\",\n\t\"no suggestions aggressive\": \"אין הצעות אגרסיביות\",\n\t\"install\": \"התקנה\",\n\t\"installing\": \"מתקין...\",\n\t\"plugins\": \"תוספים\",\n\t\"recently used\": \"בשימוש לאחרונה\",\n\t\"update\": \"עדכן\",\n\t\"uninstall\": \"הסר התקנה\",\n\t\"download acode pro\": \"מוריד Acode pro\",\n\t\"loading plugins\": \"טוען תוספים\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"משוב\",\n\t\"header\": \"כותרת\",\n\t\"sidebar\": \"סרגל צד\",\n\t\"inapp\": \"באפליקציה\",\n\t\"browser\": \"דפדפן\",\n\t\"diagonal scrolling\": \"גלילה אלכסונית\",\n\t\"reverse scrolling\": \"גלילה הפוכה\",\n\t\"formatter\": \"מעצב\",\n\t\"format on save\": \"עיצוב בעת שמירה\",\n\t\"remove ads\": \"הסר פרסומות\",\n\t\"fast\": \"מהיר\",\n\t\"slow\": \"איטי\",\n\t\"scroll settings\": \"הגדרות גלילה\",\n\t\"scroll speed\": \"מהירות גלילה\",\n\t\"loading...\": \"טוען...\",\n\t\"no plugins found\": \"לא נמצאו תוספים\",\n\t\"name\": \"שם\",\n\t\"username\": \"שם משתמש\",\n\t\"optional\": \"אפשרי\",\n\t\"hostname\": \"מארח\",\n\t\"password\": \"סיסמא\",\n\t\"security type\": \"סוג אבטחה\",\n\t\"connection mode\": \"סוג חיבור\",\n\t\"port\": \"פורט\",\n\t\"key file\": \"קובץ מפתח\",\n\t\"select key file\": \"בחר קובץ מפתח\",\n\t\"passphrase\": \"ביטוי סיסמה\",\n\t\"connecting...\": \"מתחבר...\",\n\t\"type filename\": \"שם סוג קובץ\",\n\t\"unable to load files\": \"לא הצלחנו לפתוח את הקובץ\",\n\t\"preview port\": \"פורט תצוגה מקדימה\",\n\t\"find file\": \"מצא קובץ\",\n\t\"system\": \"מערכת\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"תלוי רישיות\",\n\t\"regular expression\": \"ביטוי רגולרי\",\n\t\"whole word\": \"מילה שלמה\",\n\t\"edit with\": \"ערוך עם...\",\n\t\"open with\": \"פתח עם...\",\n\t\"no app found to handle this file\": \"לא נמצאה אפליקציה לפתיחת הקובץ הזה\",\n\t\"restore default settings\": \"שחזר הגדרות ברירת מחדל\",\n\t\"server port\": \"פורט שרת\",\n\t\"preview settings\": \"הגדרות תצוגה מקדימה\",\n\t\"preview settings note\": \"אם פורט התצוגה מקדימה ופורט השרת שונים, האפליקציה לא תפעיל את השרת ובמקום זאת תיפתח https://<host>:<preview port> בדפדפן או בדפדפן בתוך האפליקציה. זה שימושי כשאתה מפעיל שרת במקום אחר.\",\n\t\"backup/restore note\": \"זה יגבה רק את ההגדרות שלך, ערכת נושא מותאמת אישית, תוספים מותקנים וקישורי מקשים. זה לא יגבה את מצב ה-FTP/SFTP או האפליקציה שלך.\",\n\t\"host\": \"מארח\",\n\t\"retry ftp/sftp when fail\": \"נסה שוב כש- ftp/sftp נכשל\",\n\t\"more\": \"עוד\",\n\t\"thank you :)\": \"תודה לך :)\",\n\t\"purchase pending\": \"רכישה ממתינה\",\n\t\"cancelled\": \"בוטל\",\n\t\"local\": \"local\",\n\t\"remote\": \"מרוחק\",\n\t\"show console toggler\": \"הצג מתג קונסולה\",\n\t\"binary file\": \"קובץ זה מכיל נתונים בינאריים, האם ברצונך לפתוח אותו?\",\n\t\"relative line numbers\": \"מספרי שורות יחסיים\",\n\t\"elastic tabstops\": \"עצירות טאבים אלסטיות\",\n\t\"line based rtl switching\": \"מיתוג RTL מבוסס קו\",\n\t\"hard wrap\": \"עטיפה קשה\",\n\t\"spellcheck\": \"בדיקת איות\",\n\t\"wrap method\": \"שיטת עטיפת\",\n\t\"use textarea for ime\": \"השתמש באזור טקסט עבור IME\",\n\t\"invalid plugin\": \"תוסף לא חוקי\",\n\t\"type command\": \"הקלד פקודה\",\n\t\"plugin\": \"תוספים\",\n\t\"quicktools trigger mode\": \"מצב טריגר של כלים מהירים\",\n\t\"print margin\": \"שולי הדפסה\",\n\t\"touch move threshold\": \"סף תנועה במגע\",\n\t\"info-retryremotefsafterfail\": \"נסה שוב להתחבר ל FTP/SFTP אם נכשל.\",\n\t\"info-fullscreen\": \"הסתר את שורת הכותרת במסך הבית.\",\n\t\"info-checkfiles\": \"האזנה לשינוי קבצים כשאפלקציה ברקע.\",\n\t\"info-console\": \"בחר קונסולת JavaScript. קונסולת Legacy היא ברירת המחדל, Eruda היא קונסולת צד שלישי..\",\n\t\"info-keyboardmode\": \"מצב מקלדת להזנת טקסט, ללא הצעות יסתיר את ההצעות ותיקון אוטומטי יתבצע. אם ללא הצעות לא יעבוד, נסה לשנות את הערך ללא הצעות אגרסיביות..\",\n\t\"info-rememberfiles\": \"זכור קבצים פתוחים כאשר האפליקציה סגורה.\",\n\t\"info-rememberfolders\": \"זכור תיקיות פתוחות כאשר האפליקציה סגורה.\",\n\t\"info-floatingbutton\": \"הצג או הסתר את הכפתור הצף של הכלים המהירים.\",\n\t\"info-openfilelistpos\": \"היכן להציג את רשימת הקבצים הפעילים.\",\n\t\"info-touchmovethreshold\": \"אם רגישות המגע של המכשיר שלך גבוהה מידי, תוכל להגדיל ערך זה כדי למנוע תנועה מקרית של מגע.\",\n\t\"info-scroll-settings\": \"הגדרות אלה מכילות הגדרות גלילה כולל גלישת טקסט.\",\n\t\"info-animation\": \"אם האפליקציה מרגישה לאגית, השבת אנימציה.\",\n\t\"info-quicktoolstriggermode\": \"אם הכפתור בכלים המהירים אינו פועל, נסה לשנות ערך זה.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"בבעלות\",\n\t\"api_error\": \"שרת ה-API מושבת, אנא נסה שוב בעוד מספר דקות.\",\n\t\"installed\": \"מותקן\",\n\t\"all\": \"הכל\",\n\t\"medium\": \"בינוני\",\n\t\"refund\": \"החזר\",\n\t\"product not available\": \"מוצר לא זמין\",\n\t\"no-product-info\": \"מוצר זה אינו זמין במדינתך כרגע, נא לנסות פעם אחרת.\",\n\t\"close\": \"סגור\",\n\t\"explore\": \"חקור\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"חפש בקבצים\",\n\t\"exclude files\": \"החרגת קבצים\",\n\t\"include files\": \"הכללת קבצים\",\n\t\"search result\": \"{matches} תוצאות ב- {files} קבצים.\",\n\t\"invalid regex\": \"ביטוי רגולרי לא חוקי: {message}.\",\n\t\"bottom\": \"תחתית\",\n\t\"save all\": \"שמור הכל\",\n\t\"close all\": \"סגור הכל\",\n\t\"unsaved files warning\": \"חלק מהקבצים לא נשמרו. לחץ על 'אישור' ובחר מה לעשות או לחץ על 'ביטול' כדי לחזור אחורה.\",\n\t\"save all warning\": \"האם אתה בטוח שברצונך לשמור את כל הקבצים ולסגור? פעולה זו אינה ניתנת לביטול.\",\n\t\"save all changes warning\": \"האם אתה בטוח שברצונך לשמור את כל הקבצים?\",\n\t\"close all warning\": \"האם אתה בטוח שברצונך לסגור את כל הקבצים? שינויים שלא נשמרו יאבדו ולא יהיה ניתן לשחזר אותם.\",\n\t\"refresh\": \"רענן\",\n\t\"shortcut buttons\": \"כפתורי קיצור דרך\",\n\t\"no result\": \"אין תוצאות\",\n\t\"searching...\": \"מחפש...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab מקש\",\n\t\"quicktools:shift-key\": \"Shift מקש\",\n\t\"quicktools:undo\": \"בטל\",\n\t\"quicktools:redo\": \"בצע שוב\",\n\t\"quicktools:search\": \"חפש בקובץ\",\n\t\"quicktools:save\": \"שמור קובץ\",\n\t\"quicktools:esc-key\": \"Escape מקש\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow מקש\",\n\t\"quicktools:right-arrow-key\": \"Right arrow מקש\",\n\t\"quicktools:up-arrow-key\": \"Up arrow מקש\",\n\t\"quicktools:down-arrow-key\": \"Down arrow מקש\",\n\t\"quicktools:moveline-up\": \"הזזת שורה למעלה\",\n\t\"quicktools:moveline-down\": \"הזזת שורה למטה\",\n\t\"quicktools:copyline-up\": \"העתק שורה למעלה\",\n\t\"quicktools:copyline-down\": \"העתק שורה למעלה\",\n\t\"quicktools:semicolon\": \"הוסף פסיק\",\n\t\"quicktools:quotation\": \"הוסף סימן שאלה\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"הוסף סימן שווה\",\n\t\"quicktools:slash\": \"הוסף סימן אלכסון\",\n\t\"quicktools:exclamation\": \"הוסף סימן קריאה\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"התאם אישית לחצני קיצורי דרך ומקשי מקלדת בכלים המהירים שמתחת לעורך כדי לשפר את חוויית הקידוד שלך.\",\n\t\"info-excludefolders\": \"השתמשו בתבנית **/node_modules/** כדי להתעלם מכל הקבצים מהתיקייה node_modules. פעולה זו תמנע את הכללת הקבצים בחיפושי קבצים.\",\n\t\"missed files\": \"נסרקו {count} קבצים לאחר תחילת החיפוש ולא ייכללו בחיפוש.\",\n\t\"remove\": \"הסר\",\n\t\"quicktools:command-palette\": \"לוח פקודות\",\n\t\"default file encoding\": \"קידוד קובץ ברירת מחדל\",\n\t\"remove entry\": \"האם אתה בטוח שברצונך להסיר את '{name}' מהנתיבים השמורים? שים לב שהסרתו לא תמחק את הנתיב עצמו.\",\n\t\"delete entry\": \"אשר מחיקה: '{name}'. לא ניתן לבטל פעולה זו. להמשיך?\",\n\t\"change encoding\": \"לפתוח מחדש את '{file}' עם קידוד '{encoding}'? פעולה זו תגרום לאובדן כל השינויים שלא נשמרו בקובץ. האם ברצונך להמשיך בפתיחה מחדש?\",\n\t\"reopen file\": \"אתה בטוח שברצונך לפתוח מחדש את הקובץ '{file}'? כל השינויים שלא נשמרו ימחקו.\",\n\t\"plugin min version\": \"{name} זמין רק ב Acode - {v-code} ומעלה. לחץ פה לעידכון.\",\n\t\"color preview\": \"צבע תצוגה מקדימה\",\n\t\"confirm\": \"אישור\",\n\t\"list files\": \"רשימת כל הקבצים ב <strong>{name}</strong>? יותר מידי קבצים עלולים להוביל לקריסות.\",\n\t\"problems\": \"בעיות\",\n\t\"show side buttons\": \"הצג כפתורי צד\",\n\t\"bug_report\": \"דיווח באג\",\n\t\"verified publisher\": \"מפרסם מאומת\",\n\t\"most_downloaded\": \"הכי הרבה הורדות\",\n\t\"newly_added\": \"נוסף לאחרונה\",\n\t\"top_rated\": \"דירוג גבוהה\",\n\t\"rename not supported\": \"שינוי שם בספריית termux אינו נתמך\",\n\t\"compress\": \"דחוס\",\n\t\"copy uri\": \"העתק Uri\",\n\t\"delete entries\": \"אתה בטוח שברצונך למחוק {count} פריטים?\",\n\t\"deleting items\": \"מוחק {count} פריטים...\",\n\t\"import project zip\": \"ייבא פרוייקט(zip)\",\n\t\"changelog\": \"יומן שינויים\",\n\t\"notifications\": \"התראות\",\n\t\"no_unread_notifications\": \"אין התראות שלא נקראו\",\n\t\"should_use_current_file_for_preview\": \"יש להשתמש בקובץ הנוכחי לתצוגה מקדימה במקום ברירת המחדל (index.html)\",\n\t\"fade fold widgets\": \"ווידג'טים של קיפול דהייה\",\n\t\"quicktools:home-key\": \"Home מקש\",\n\t\"quicktools:end-key\": \"End מקש\",\n\t\"quicktools:pageup-key\": \"PageUp מקש\",\n\t\"quicktools:pagedown-key\": \"PageDown מקש\",\n\t\"quicktools:delete-key\": \"Delete מקש\",\n\t\"quicktools:tilde\": \"הוסף טילדה\",\n\t\"quicktools:backtick\": \"הוסף גרש הפוך\",\n\t\"quicktools:hash\": \"הוסף סמל גיבוב\",\n\t\"quicktools:dollar\": \"הוסף סימן דולר\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"הפלאגין הופעל\",\n\t\"plugin_disabled\": \"הפלאגין הושבת\",\n\t\"enable_plugin\": \"הפעל תוסף זה\",\n\t\"disable_plugin\": \"השבת תוסף זה\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"לָתֵת חָסוּת\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/hi-in.json",
    "content": "{\n\t\"lang\": \"हिंदी\",\n\t\"about\": \"एप्लीकेशन के बारे में\",\n\t\"active files\": \"सक्रिय फ़ाइलें\",\n\t\"alert\": \"चेतावनी\",\n\t\"app theme\": \"एप्लीकेशन का थीम\",\n\t\"autocorrect\": \"स्वत: सुधार सक्षम करें?\",\n\t\"autosave\": \"स्वरक्षण\",\n\t\"cancel\": \"रद्द करें\",\n\t\"change language\": \"भाषा बदलें\",\n\t\"choose color\": \"रंग चुनें\",\n\t\"create folder error\": \"क्षमा करें, नया फ़ोल्डर बनाने में असमर्थ\",\n\t\"clear\": \"साफ करें\",\n\t\"close app\": \"एप्लिकेशन बंद करें?\",\n\t\"commit message\": \"Commit message\",\n\t\"console\": \"कंसोल\",\n\t\"conflict error\": \"Conflict! Please wait before another commit.\",\n\t\"copy\": \"कापी\",\n\t\"cut\": \"कट\",\n\t\"delete\": \"इसे हटाएं\",\n\t\"dependencies\": \"निर्भरता\",\n\t\"delay\": \"मिलीसेकंड में समय\",\n\t\"editor settings\": \"एडिटर सेटिंग्स\",\n\t\"editor theme\": \"एडिटर का थीम\",\n\t\"enter file name\": \"फ़ाइल का नाम दर्ज करें\",\n\t\"enter folder name\": \"फ़ोल्डर का नाम दर्ज करें\",\n\t\"empty folder message\": \"खाली फ़ोल्डर\",\n\t\"enter line number\": \"लाइन नंबर दर्ज करें\",\n\t\"error\": \"एरर\",\n\t\"failed\": \"असफल\",\n\t\"file already exists\": \"फ़ाइल पहले से ही मौजूद है\",\n\t\"file already exists force\": \"फ़ाइल पहले से ही मौजूद है। ओवरराइट करें?\",\n\t\"file changed\": \" बदल दी गई है, फ़ाइल पुनः लोड करें?\",\n\t\"file deleted\": \"फ़ाइल डिलीट कर दि गई है\",\n\t\"file is not supported\": \"फ़ाइल समर्थित नहीं है\",\n\t\"file too large\": \"संभाल करने के लिए फ़ाइल बड़ी है। अधिकतम फ़ाइल आकार की अनुमति है {size}\",\n\t\"file renamed\": \"फ़ाइल का नाम बदल दिया गया है\",\n\t\"file saved\": \"फाइल सेव हो गया\",\n\t\"folder added\": \"फ़ोल्डर जोड़ा गया\",\n\t\"folder already added\": \"फ़ोल्डर पहले से ही जोड़ा गया\",\n\t\"goto\": \"गोटू लाइन\",\n\t\"icons definition\": \"आइकन स्पष्टीकरण\",\n\t\"info\": \"जानकारी\",\n\t\"invalid value\": \"अमान्य मूल्य\",\n\t\"language changed\": \"भाषा को सफलतापूर्वक बदल दिया गया है\",\n\t\"linting\": \"वाक्यविन्यास त्रुटि की जाँच करें\",\n\t\"logout\": \"लॉग आउट\",\n\t\"loading\": \"लोड हो रहा है\",\n\t\"my profile\": \"मेरी प्रोफाइल\",\n\t\"new file\": \"नई फ़ाइल\",\n\t\"new folder\": \"नया फोल्डर\",\n\t\"no editor message\": \"मेनू से नई फ़ाइल और फ़ोल्डर खोलें या बनाएँ\",\n\t\"no\": \"नहीं\",\n\t\"not set\": \"सेट नहीं है\",\n\t\"unsaved files close app\": \"बिना सेव की गई फ़ाइलें हैं। फिर भी एप्लिकेशन को बंद करें?\",\n\t\"notice\": \"नोटिस\",\n\t\"open file\": \"फ़ाइल खोलें\",\n\t\"open files and folders\": \"फ़ाइल और फ़ोल्डर खोलें\",\n\t\"open folder\": \"फ़ोल्डर खोलें\",\n\t\"open recent\": \"हाल ही का खोलें\",\n\t\"ok\": \"ठीक\",\n\t\"overwrite\": \"ओवरराइट करें\",\n\t\"paste\": \"पेस्ट\",\n\t\"preview mode\": \"पूर्वावलोकन मोड\",\n\t\"read only file\": \"यह फाइल सेव नहीं की सकती, किर्प्या इसे 'सेव एज' से सेव करे\",\n\t\"redo\": \"रीडू\",\n\t\"replace\": \"इससे बदलें\",\n\t\"reload\": \"रिलोड\",\n\t\"rename\": \"नाम बदलने\",\n\t\"required\": \"यह फ़ील्ड आवश्यक है\",\n\t\"run your web app\": \"अपना वेब ऐप चलाएं\",\n\t\"save\": \"सेव\",\n\t\"saving\": \"सेव हो रहा है\",\n\t\"save as\": \"सेव ऐज\",\n\t\"save file to run\": \"कृपया इस फाइल को ब्राउजर में चलाने के लिए सेव करें\",\n\t\"search\": \"खोज\",\n\t\"see logs and errors\": \"लॉग और त्रुटियों को दिखाएं\",\n\t\"select folder\": \"फोल्डर का चयन करें\",\n\t\"settings\": \"सेटिंग्स\",\n\t\"settings saved\": \"सेटिंग्स सेव हो गया\",\n\t\"show line numbers\": \"लाइन नंबर्स दिखाएं\",\n\t\"show hidden files\": \"छिपी फ़ाइलें दिखाएं\",\n\t\"show spaces\": \"रिक्त स्थान दिखाएं\",\n\t\"soft tab\": \"सॉफ्ट टैब\",\n\t\"sort by name\": \"नाम द्वारा क्रमबद्ध करें\",\n\t\"success\": \"सफल\",\n\t\"tab size\": \"टैब साइज\",\n\t\"text wrap\": \"टेक्स्ट व्रैप\",\n\t\"theme\": \"थीम\",\n\t\"unable to delete file\": \"फाइल डिलीट नहीं हो पा रहा है\",\n\t\"unable to open file\": \"क्षमा करें, फ़ाइल खोलने में असमर्थ\",\n\t\"unable to open folder\": \"क्षमा करें, फ़ोल्डर खोलने में असमर्थ\",\n\t\"unable to save file\": \"क्षमा करें, फ़ाइल सेव करने में असमर्थ\",\n\t\"unable to rename\": \"क्षमा करें, नाम बदलने में असमर्थ\",\n\t\"unsaved file\": \"यह फ़ाइल सेव नहीं गई है, फिर भी फ़ाइल बंद करें\",\n\t\"warning\": \"ध्यान दे\",\n\t\"use emmet\": \"इमेट का प्रयोग करें\",\n\t\"use quick tools\": \"त्वरित साधनों का उपयोग करें\",\n\t\"yes\": \"हाँ\",\n\t\"encoding\": \"टेक्स्ट एन्कोडिंग\",\n\t\"syntax highlighting\": \"सिंटेक्स हाइलाइटिंग\",\n\t\"read only\": \"केवल पढ़ने के लिए\",\n\t\"select all\": \"सेलेक्ट आल\",\n\t\"select branch\": \"शाखा का चयन करें\",\n\t\"create new branch\": \"नई शाखा बनाएं\",\n\t\"use branch\": \"शाखा का उपयोग करें\",\n\t\"new branch\": \"नई शाखा\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"की बिंडिंग्स\",\n\t\"edit\": \"संपादित करें\",\n\t\"reset\": \"रीसेट\",\n\t\"color\": \"रंग\",\n\t\"select word\": \"शब्द का चयन करें\",\n\t\"quick tools\": \"त्वरित उपकरण\",\n\t\"select\": \"चयन\",\n\t\"editor font\": \"एडिटर फ़ॉन्ट\",\n\t\"new project\": \"नया प्रोजेक्ट\",\n\t\"format\": \"फॉर्मेट\",\n\t\"project name\": \"प्रोजेक्ट का नाम\",\n\t\"unsupported device\": \"आपका डिवाइस थीम का समर्थन नहीं करता है।\",\n\t\"vibrate on tap\": \"टैप पर कंपन करें\",\n\t\"copy command is not supported by ftp.\": \"कॉपी कमांड एफ़टीपी द्वारा समर्थित नहीं है।\",\n\t\"support title\": \"Support Acode\",\n\t\"fullscreen\": \"पूर्ण स्क्रीन\",\n\t\"animation\": \"एनीमेशन\",\n\t\"backup\": \"बैकअप\",\n\t\"restore\": \"पुनर्स्थापित करना\",\n\t\"backup successful\": \"बैकअप सफल\",\n\t\"invalid backup file\": \"अमान्य बैकअप फ़ाइल\",\n\t\"add path\": \"पाथ जोड़ें\",\n\t\"live autocompletion\": \"लाइव स्वतः‑पूर्ण\",\n\t\"file properties\": \"फ़ाइल गुण\",\n\t\"path\": \"पथ\",\n\t\"type\": \"टाइप\",\n\t\"word count\": \"शब्द गणना\",\n\t\"line count\": \"लाइन काउंट\",\n\t\"last modified\": \"अंतिम बार संशोधित\",\n\t\"size\": \"आकार\",\n\t\"share\": \"शेयर\",\n\t\"show print margin\": \"प्रिंट मार्जिन दिखाएँ\",\n\t\"login\": \"लॉग इन करें\",\n\t\"scrollbar size\": \"स्क्रॉलबार का आकार\",\n\t\"cursor controller size\": \"कर्सर नियंत्रक आकार\",\n\t\"none\": \"कोई भी नहीं\",\n\t\"small\": \"छोटा\",\n\t\"large\": \"विशाल\",\n\t\"floating button\": \"फ्लोटिंग बटन\",\n\t\"confirm on exit\": \"बाहर निकलने पर पुष्टि करें\",\n\t\"show console\": \"कंसोल दिखाएँ\",\n\t\"image\": \"इमेज\",\n\t\"insert file\": \"फ़ाइल डालें\",\n\t\"insert color\": \"रंग डालें\",\n\t\"powersave mode warning\": \"बाहरी ब्राउज़र में पूर्वावलोकन करने के लिए पावर सेविंग मोड बंद करें।\",\n\t\"exit\": \"बाहर निकलें\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"क्या आप वाकई थीम रीसेट करना चाहते हैं?\",\n\t\"theme type\": \"थीम प्रकार\",\n\t\"light\": \"हल्का रंग\",\n\t\"dark\": \"गाढ़ा रंग\",\n\t\"file browser\": \"फ़ाइल ब्राउज़र\",\n\t\"file not supported\": \"यह फ़ाइल प्रकार समर्थित नहीं है।\",\n\t\"font size\": \"फॉण्ट साइज\",\n\t\"operation not permitted\": \"कार्रवाई की अनुमति नहीं\",\n\t\"no such file or directory\": \"ऐसी कोई फ़ाइल या डायरेक्टरी नहीं है\",\n\t\"input/output error\": \"इनपुट/आउटपुट त्रुटि\",\n\t\"permission denied\": \"अनुमति नहीं मिली\",\n\t\"bad address\": \"खराब पता\",\n\t\"file exists\": \"फ़ाइल मौजूद\",\n\t\"not a directory\": \"डायरेक्टरी नहीं है\",\n\t\"is a directory\": \"डायरेक्टरी है\",\n\t\"invalid argument\": \"अवैध तर्क\",\n\t\"too many open files in system\": \"सिस्टम में बहुत अधिक खुली फ़ाइलें\",\n\t\"too many open files\": \"बहुत अधिक खुली फ़ाइलें\",\n\t\"text file busy\": \"टेक्स्ट फ़ाइल व्यस्त\",\n\t\"no space left on device\": \"डिवाइस पर जगह समाप्त\",\n\t\"read-only file system\": \"रीड ओन्ली फ़ाइल सिस्टम\",\n\t\"file name too long\": \"फ़ाइल का नाम बहुत लंबा\",\n\t\"too many users\": \"बहुत अधिक उपयोगकर्ता\",\n\t\"connection timed out\": \"कनेक्शन का समय समाप्त\",\n\t\"connection refused\": \"कनेक्शन नहीं हो सका\",\n\t\"owner died\": \"मालिक मर गया\",\n\t\"an error occurred\": \"एक त्रुटि पाई गई\",\n\t\"add ftp\": \"FTP जोड़ें\",\n\t\"add sftp\": \"SFTP जोड़ें\",\n\t\"save file\": \"फ़ाइल सहेजें\",\n\t\"save file as\": \"फ़ाइल को इस नाम से सहेजें\",\n\t\"files\": \"फाइल्स\",\n\t\"help\": \"हेल्प\",\n\t\"file has been deleted\": \"{file} हटा दी गई है!\",\n\t\"feature not available\": \"यह सुविधा ऐप के केवल भुगतान किए गए संस्करण में उपलब्ध है।\",\n\t\"deleted file\": \"हटाई गई फ़ाइल\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"यदि आप सक्रिय फ़ाइल चलाना चाहते हैं, तो प्ले आइकन पर टैप करके रखें।\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"फ़ाइल बंद करें\",\n\t\"reset connections\": \"कनेक्शन रीसेट करें\",\n\t\"check file changes\": \"फ़ाइल परिवर्तनों की जाँच करें\",\n\t\"open in browser\": \"ब्राउज़र में खोलें\",\n\t\"desktop mode\": \"डेस्कटॉप मोड\",\n\t\"toggle console\": \"टॉगल कंसोल\",\n\t\"new line mode\": \"नई लाइन मोड\",\n\t\"add a storage\": \"एक स्टोरेज जोड़ें\",\n\t\"rate acode\": \"Acode को रेट करें\",\n\t\"support\": \"सहायता\",\n\t\"downloading file\": \"डौन्लोडिंग {file}\",\n\t\"downloading...\": \"डौन्लोडिंग...\",\n\t\"folder name\": \"फोल्डर का नाम\",\n\t\"keyboard mode\": \"कीबोर्ड मोड\",\n\t\"normal\": \"सामान्य\",\n\t\"app settings\": \"एप्लिकेशन सेटिंग\",\n\t\"disable in-app-browser caching\": \"इन-ऐप-ब्राउज़र कैशिंग बंद करें\",\n\t\"copied to clipboard\": \"क्लिपबोर्ड पर कॉपी किया गया\",\n\t\"remember opened files\": \"खोली गई फ़ाइलें याद रखें\",\n\t\"remember opened folders\": \"खोले गए फोल्डर याद रखें\",\n\t\"no suggestions\": \"कोई सुझाव नहीं\",\n\t\"no suggestions aggressive\": \"कोई सुझाव नहीं आक्रामक\",\n\t\"install\": \"इनस्टॉल\",\n\t\"installing\": \"इनस्टॉल हो रहा है...\",\n\t\"plugins\": \"प्लगिन्स\",\n\t\"recently used\": \"हाल ही में उपयोग किए गए\",\n\t\"update\": \"अपडेट\",\n\t\"uninstall\": \"हटाएँ\",\n\t\"download acode pro\": \"एकोड प्रो डाउनलोड करें\",\n\t\"loading plugins\": \"प्लगइन्स लोड हो रहा है\",\n\t\"faqs\": \"पूछे जाने वाले प्रश्न\",\n\t\"feedback\": \"प्रतिपुष्टि\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"साइडबार\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"ब्राउज़र\",\n\t\"diagonal scrolling\": \"विकर्ण स्क्रॉलिंग\",\n\t\"reverse scrolling\": \"रिवर्स स्क्रॉलिंग\",\n\t\"formatter\": \"फॉर्मेटर\",\n\t\"format on save\": \"सहेजने पर प्रारूपित करें\",\n\t\"remove ads\": \"विज्ञापन हटाएँ\",\n\t\"fast\": \"तेज़\",\n\t\"slow\": \"धीमा\",\n\t\"scroll settings\": \"स्क्रॉल सेटिंग्स\",\n\t\"scroll speed\": \"स्क्रोल गति\",\n\t\"loading...\": \"लोड हो रहा है...\",\n\t\"no plugins found\": \"कोई प्लगइन्स नहीं मिला\",\n\t\"name\": \"नाम\",\n\t\"username\": \"उपयोगकर्ता नाम\",\n\t\"optional\": \"वैकल्पिक\",\n\t\"hostname\": \"होस्ट नाम\",\n\t\"password\": \"पासवर्ड\",\n\t\"security type\": \"सुरक्षा प्रकार\",\n\t\"connection mode\": \"कनेक्शन मोड\",\n\t\"port\": \"पोर्ट\",\n\t\"key file\": \"की फाइल\",\n\t\"select key file\": \"की फ़ाइल का चयन करें\",\n\t\"passphrase\": \"पसफ्रेज़\",\n\t\"connecting...\": \"कनेक्टिंग...\",\n\t\"type filename\": \"फ़ाइल नाम लिखें\",\n\t\"unable to load files\": \"फ़ाइलें लोड करने में असमर्थ\",\n\t\"preview port\": \"प्रीव्यू पोर्ट\",\n\t\"find file\": \"फ़ाइल खोजें\",\n\t\"system\": \"सिस्टम\",\n\t\"please select a formatter\": \"कृपया एक फॉर्मेटर चुनें\",\n\t\"case sensitive\": \"केस सेंसिटिव\",\n\t\"regular expression\": \"रेगुलर एक्सप्रेशन\",\n\t\"whole word\": \"पूर्ण शब्द\",\n\t\"edit with\": \"के साथ संपादित करें\",\n\t\"open with\": \"के साथ खोलें\",\n\t\"no app found to handle this file\": \"इस फ़ाइल को संभालने के लिए कोई ऐप नहीं मिला\",\n\t\"restore default settings\": \"डिफ़ॉल्ट सेटिंग्स को पुनर्स्थापित करें\",\n\t\"server port\": \"सर्वर पोर्ट\",\n\t\"preview settings\": \"प्रीव्यू सेटिंग्स\",\n\t\"preview settings note\": \"यदि प्रीव्यू पोर्ट और सर्वर पोर्ट भिन्न हैं, तो ऐप सर्वर शुरू नहीं करेगा और इसके बजाय ब्राउज़र या इन-ऐप ब्राउज़र में https://<host>:<preview port> खोलेगा। यह तब उपयोगी है जब आप कहीं और सर्वर चला रहे हों।\",\n\t\"backup/restore note\": \"यह केवल आपकी सेटिंग्स, कस्टम थीम और की बाइंडिंग्स का बैकअप लेगा। यह आपके FPT/SFTP का बैकअप नहीं लेगा।\",\n\t\"host\": \"होस्ट\",\n\t\"retry ftp/sftp when fail\": \"विफल होने पर FTP/SFTP पुनः प्रयास करें\",\n\t\"more\": \"अधिक\",\n\t\"thank you :)\": \"धन्यवाद :)\",\n\t\"purchase pending\": \"खरीदारी लंबित\",\n\t\"cancelled\": \"रद्द\",\n\t\"local\": \"लोकल\",\n\t\"remote\": \"रिमोट\",\n\t\"show console toggler\": \"कंसोल टॉगलर दिखाएँ\",\n\t\"binary file\": \"इस फ़ाइल में बाइनरी डेटा है, क्या आप इसे खोलना चाहते हैं?\",\n\t\"relative line numbers\": \"सापेक्ष पंक्ति संख्या\",\n\t\"elastic tabstops\": \"इलास्टिक टैबस्टॉप्स\",\n\t\"line based rtl switching\": \"लाइन आधारित RTL स्विचिंग\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"कमांड पैलेट\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"रंग पूर्वावलोकन\",\n\t\"confirm\": \"पुष्टि करें\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"समस्याएं\",\n\t\"show side buttons\": \"साइड बटन दिखाएं\",\n\t\"bug_report\": \"बग रिपोर्ट सबमिट करें\",\n\t\"verified publisher\": \"सत्यापित प्रकाशक\",\n\t\"most_downloaded\": \"सर्वाधिक डाउनलोड\",\n\t\"newly_added\": \"नया नया़ा\",\n\t\"top_rated\": \"टॉप रेटेड\",\n\t\"rename not supported\": \"Termux डायरेक्टरी में रीनेम करना संभव नहीं है\",\n\t\"compress\": \"कम्प्रेस करें\",\n\t\"copy uri\": \"URI कॉपी करें\",\n\t\"delete entries\": \"क्या आप वाकई {count} आइटम हटाना चाहते हैं?\",\n\t\"deleting items\": \"हटाए जा रहे {count} आइटम...\",\n\t\"import project zip\": \"प्रोजेक्ट आयात करें (zip)\",\n\t\"changelog\": \"चेंज लॉग\",\n\t\"notifications\": \"सूचनाएँ\",\n\t\"no_unread_notifications\": \"कोई अवांछित सूचनाएँ नहीं\",\n\t\"should_use_current_file_for_preview\": \"डिफ़ॉल्ट (index.html) के बजाय पूर्वावलोकन के लिए वर्तमान फ़ाइल का उपयोग करना चाहिए\",\n\t\"fade fold widgets\": \"फेड फोल्ड विजेट्स\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"प्लगइन सक्रिय है\",\n\t\"plugin_disabled\": \"प्लगइन निष्क्रिय है\",\n\t\"enable_plugin\": \"इस प्लगइन को सक्षम करें\",\n\t\"disable_plugin\": \"इस प्लगइन को अक्षम करें\",\n\t\"open_source\": \"ओपन सोर्स\",\n\t\"terminal settings\": \"टर्मिनल सेटिंग्स\",\n\t\"font ligatures\": \"फॉन्ट लिगेचर्स\",\n\t\"letter spacing\": \"लेटर स्पेसिंग\",\n\t\"terminal:tab stop width\": \"टैब स्टॉप चौड़ाई\",\n\t\"terminal:scrollback\": \"स्क्रॉलबैक लाइन्स\",\n\t\"terminal:cursor blink\": \"कर्सर ब्लिंक\",\n\t\"terminal:font weight\": \"फ़ॉन्ट मोटाई\",\n\t\"terminal:cursor inactive style\": \"इनएक्टिव कर्सर स्टाइल\",\n\t\"terminal:cursor style\": \"कर्सर स्टाइल\",\n\t\"terminal:font family\": \"फ़ॉन्ट फैमिली\",\n\t\"terminal:convert eol\": \"EOL रूपांतरित करें\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"टर्मिनल\",\n\t\"allFileAccess\": \"ऑल फ़ाइल एक्सेस\",\n\t\"fonts\": \"फॉन्ट्स\",\n\t\"sponsor\": \"स्पॉन्सर\",\n\t\"downloads\": \"डाउनलोड\",\n\t\"reviews\": \"समीक्षाएँ\",\n\t\"overview\": \"ओवरव्यू\",\n\t\"contributors\": \"सहयोगी\",\n\t\"quicktools:hyphen\": \"हाइफ़न प्रतीक डालें\",\n\t\"check for app updates\": \"ऐप अपडेट की जांच करें\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/hu-hu.json",
    "content": "{\n\t\"lang\": \"Magyar\",\n\t\"about\": \"Névjegy\",\n\t\"active files\": \"Megnyitott fájlok\",\n\t\"alert\": \"Figyelmeztetés\",\n\t\"app theme\": \"Alkalmazás témája\",\n\t\"autocorrect\": \"Engedélyezi az automatikus javítást?\",\n\t\"autosave\": \"Automatikus mentés\",\n\t\"cancel\": \"Mégse\",\n\t\"change language\": \"Nyelv módosítása\",\n\t\"choose color\": \"Válasszon színt\",\n\t\"clear\": \"Tisztítás\",\n\t\"close app\": \"Biztosan bezárja az alkalmazást?\",\n\t\"commit message\": \"Véglegesítési üzenet\",\n\t\"console\": \"Konzol\",\n\t\"conflict error\": \"Ütközés! Várjon egy újabb véglegesítés előtt.\",\n\t\"copy\": \"Másolás\",\n\t\"create folder error\": \"Nem sikerült új mappát létrehozni\",\n\t\"cut\": \"Kivágás\",\n\t\"delete\": \"Törlés\",\n\t\"dependencies\": \"Függőségek\",\n\t\"delay\": \"Idő ezredmásodpercben\",\n\t\"editor settings\": \"Szerkesztő beállításai\",\n\t\"editor theme\": \"Szerkesztő témája\",\n\t\"enter file name\": \"Adja meg a fájl nevét\",\n\t\"enter folder name\": \"Adja meg a mappa nevét\",\n\t\"empty folder message\": \"Üres mappa\",\n\t\"enter line number\": \"Adja meg a sor számát\",\n\t\"error\": \"Hiba\",\n\t\"failed\": \"Nem sikerült\",\n\t\"file already exists\": \"A fájl már létezik\",\n\t\"file already exists force\": \"A fájl már létezik. Felülírja?\",\n\t\"file changed\": \" A fájl módosult, újratölti?\",\n\t\"file deleted\": \"Fájl törölve\",\n\t\"file is not supported\": \"A fájl nem támogatott\",\n\t\"file not supported\": \"Ez a fájltípus nem támogatott.\",\n\t\"file too large\": \"A fájl túl nagy a kezeléshez. A maximum fájlméret: {size}\",\n\t\"file renamed\": \"Fájl átnevezve\",\n\t\"file saved\": \"Fájl mentve\",\n\t\"folder added\": \"Mappa hozzáadva\",\n\t\"folder already added\": \"A mappa már hozzá van adva\",\n\t\"font size\": \"Betűméret\",\n\t\"goto\": \"Ugrás a sorhoz\",\n\t\"icons definition\": \"Ikonok definíciója\",\n\t\"info\": \"Információ\",\n\t\"invalid value\": \"Érvénytelen érték\",\n\t\"language changed\": \"A nyelv sikeresen módosítva lett\",\n\t\"linting\": \"Szintaxishiba ellenőrzése\",\n\t\"logout\": \"Kijelentkezés\",\n\t\"loading\": \"Betöltés\",\n\t\"my profile\": \"Saját profil\",\n\t\"new file\": \"Új fájl\",\n\t\"new folder\": \"Új mappa\",\n\t\"no\": \"Nem\",\n\t\"no editor message\": \"Nyisson meg vagy hozzon létre egy új fájlt vagy mappát a menüből\",\n\t\"not set\": \"Nincs beállítva\",\n\t\"unsaved files close app\": \"Mentetlen fájlok vannak megnyitva. Biztosan bezárja az alkalmazást?\",\n\t\"notice\": \"Megjegyzés\",\n\t\"open file\": \"Fájl megnyitása\",\n\t\"open files and folders\": \"Fájlok és mappák megnyitása\",\n\t\"open folder\": \"Mappa megnyitása\",\n\t\"open recent\": \"Legutóbbi megnyitása\",\n\t\"ok\": \"OK\",\n\t\"overwrite\": \"Felülírás\",\n\t\"paste\": \"Beillesztés\",\n\t\"preview mode\": \"Előnézeti mód\",\n\t\"read only file\": \"Nem menthet csak olvasható fájlt. Próbálja menteni másként\",\n\t\"reload\": \"Újratöltés\",\n\t\"rename\": \"Átnevezés\",\n\t\"replace\": \"Csere\",\n\t\"required\": \"Ez a mező kötelező\",\n\t\"run your web app\": \"Webalkalmazás futtatása\",\n\t\"save\": \"Mentés\",\n\t\"saving\": \"Mentés…\",\n\t\"save as\": \"Mentés másként\",\n\t\"save file to run\": \"Mentse el a fájlt a böngészőben való futtatáshoz\",\n\t\"search\": \"Keresés\",\n\t\"see logs and errors\": \"Naplók és hibák megjelenítése\",\n\t\"select folder\": \"Mappa kiválasztása\",\n\t\"settings\": \"Beállítások\",\n\t\"settings saved\": \"Beállítások mentve\",\n\t\"show line numbers\": \"Sorszámok megjelenítése\",\n\t\"show hidden files\": \"Rejtett fájlok megjelenítése\",\n\t\"show spaces\": \"Szóközök megjelenítése\",\n\t\"soft tab\": \"Szóköztabulátor\",\n\t\"sort by name\": \"Rendezés név szerint\",\n\t\"success\": \"Siker\",\n\t\"tab size\": \"Tabulátor mérete\",\n\t\"text wrap\": \"Szövegtördelés\",\n\t\"theme\": \"Téma\",\n\t\"unable to delete file\": \"Nem lehet törölni a fájlt\",\n\t\"unable to open file\": \"Nem lehet megnyitni a fájlt\",\n\t\"unable to open folder\": \"Nem lehet megnyitni a mappát\",\n\t\"unable to save file\": \"Nem lehet menteni a fájlt\",\n\t\"unable to rename\": \"Nem lehet átnevezni\",\n\t\"unsaved file\": \"A fájl nincs mentve, biztosan bezárja?\",\n\t\"warning\": \"Figyelmeztetés\",\n\t\"use emmet\": \"Emmet használata\",\n\t\"use quick tools\": \"Gyors-eszközök használata\",\n\t\"yes\": \"Igen\",\n\t\"encoding\": \"Szövegkódolás\",\n\t\"syntax highlighting\": \"Szintaxiskiemelés\",\n\t\"read only\": \"Csak olvasható\",\n\t\"select all\": \"Összes kijelölése\",\n\t\"select branch\": \"Ág kiválasztása\",\n\t\"create new branch\": \"Új ág létrehozása\",\n\t\"use branch\": \"Ág használata\",\n\t\"new branch\": \"Új ág\",\n\t\"branch\": \"Ág\",\n\t\"key bindings\": \"Billentyűparancsok\",\n\t\"edit\": \"Szerkesztés\",\n\t\"reset\": \"Visszaállítás\",\n\t\"color\": \"Szín\",\n\t\"select word\": \"Szó kiválasztása\",\n\t\"quick tools\": \"Gyors-eszközök\",\n\t\"select\": \"Kiválasztás\",\n\t\"editor font\": \"Szerkesztő betűtípusa\",\n\t\"new project\": \"Új projekt\",\n\t\"format\": \"Formátum\",\n\t\"project name\": \"Projekt neve\",\n\t\"unsupported device\": \"Ez az eszköz nem támogatja a témát.\",\n\t\"vibrate on tap\": \"Rezgés érintésre\",\n\t\"copy command is not supported by ftp.\": \"Az FTP nem támogatja a másolást.\",\n\t\"support title\": \"Támogatás\",\n\t\"fullscreen\": \"Teljes képernyő\",\n\t\"animation\": \"Animáció\",\n\t\"backup\": \"Biztonsági mentés\",\n\t\"restore\": \"Visszaállítás\",\n\t\"backup successful\": \"Sikeres biztonsági mentés\",\n\t\"invalid backup file\": \"Érvénytelen mentési fájl\",\n\t\"add path\": \"Útvonal hozzáadása\",\n\t\"live autocompletion\": \"Élő automatikus kiegészítés\",\n\t\"file properties\": \"Fájl tulajdonságai\",\n\t\"path\": \"Útvonal\",\n\t\"type\": \"Típus\",\n\t\"word count\": \"Szavak száma\",\n\t\"line count\": \"Sorok száma\",\n\t\"last modified\": \"Utoljára módosítva\",\n\t\"size\": \"Méret\",\n\t\"share\": \"Megosztás\",\n\t\"show print margin\": \"Nyomtatási margó megjelenítése\",\n\t\"login\": \"Bejelentkezés\",\n\t\"scrollbar size\": \"Görgetősáv mérete\",\n\t\"cursor controller size\": \"Kurzorirányító mérete\",\n\t\"none\": \"Egyik sem\",\n\t\"small\": \"Kicsi\",\n\t\"large\": \"Nagy\",\n\t\"floating button\": \"Lebegő gomb\",\n\t\"confirm on exit\": \"Megerősítés kérése kilépéskor\",\n\t\"show console\": \"Konzol megjelenítése\",\n\t\"image\": \"Kép\",\n\t\"insert file\": \"Kép beszúrása\",\n\t\"insert color\": \"Szín beszúrása\",\n\t\"powersave mode warning\": \"Kapcsolja ki az energiatakarékos üzemmódot a külső böngészőben történő előnézethez.\",\n\t\"exit\": \"Kilépés\",\n\t\"custom\": \"Egyedi\",\n\t\"reset warning\": \"Biztosan visszaállítja a témát?\",\n\t\"theme type\": \"Tématípus\",\n\t\"light\": \"Világos\",\n\t\"dark\": \"Sötét\",\n\t\"file browser\": \"Fájlböngésző\",\n\t\"operation not permitted\": \"A művelet nem engedélyezett\",\n\t\"no such file or directory\": \"Nincs ilyen fájl vagy könyvtár\",\n\t\"input/output error\": \"Be-/kimeneti hiba\",\n\t\"permission denied\": \"Hozzáférés megtagadva\",\n\t\"bad address\": \"Hibás cím\",\n\t\"file exists\": \"A fájl már létezik\",\n\t\"not a directory\": \"Nem egy könyvtár\",\n\t\"is a directory\": \"Ez egy könyvtár\",\n\t\"invalid argument\": \"Érvénytelen argumentum\",\n\t\"too many open files in system\": \"Túl sok a megnyitott fájl a rendszerben\",\n\t\"too many open files\": \"Túl sok a megnyitott fájl\",\n\t\"text file busy\": \"A szöveges fájl foglalt\",\n\t\"no space left on device\": \"Nincs több hely az eszközön\",\n\t\"read-only file system\": \"Csak olvasható fájlrendszer\",\n\t\"file name too long\": \"Túl hosszú fájlnév\",\n\t\"too many users\": \"Túl sok felhasználó\",\n\t\"connection timed out\": \"Lejárt kapcsolat\",\n\t\"connection refused\": \"Visszautasított kapcsolat\",\n\t\"owner died\": \"A tulajdonos meghalt\",\n\t\"an error occurred\": \"Hiba történt\",\n\t\"add ftp\": \"FTP hozzáadása\",\n\t\"add sftp\": \"SFTP hozzáadása\",\n\t\"save file\": \"Fájl mentése\",\n\t\"save file as\": \"Fájl mentése másként\",\n\t\"files\": \"Fájlok\",\n\t\"help\": \"Súgó\",\n\t\"file has been deleted\": \"A(z) {file} fájl törölve lett!\",\n\t\"feature not available\": \"Ez a funkció csak az alkalmazás fizetős verziójában érhető el.\",\n\t\"deleted file\": \"Törölt fájl\",\n\t\"line height\": \"Sormagasság\",\n\t\"preview info\": \"Ha az aktív fájlt szeretné futtatni, koppintson és tartsa lenyomva a lejátszás ikont.\",\n\t\"manage all files\": \"Engedélyezze az Acode szerkesztőnek az összes fájl kezelését a beállításokban, hogy könnyen szerkeszthesse a fájlokat az eszközén.\",\n\t\"close file\": \"Fájl bezárása\",\n\t\"reset connections\": \"Kapcsolatok visszaállítása\",\n\t\"check file changes\": \"Fájlmódosítások ellenőrzése\",\n\t\"open in browser\": \"Megnyitás böngészőben\",\n\t\"desktop mode\": \"Asztali mód\",\n\t\"toggle console\": \"Konzol átkapcsolása\",\n\t\"new line mode\": \"Új sor mód\",\n\t\"add a storage\": \"Tárhely hozzáadása\",\n\t\"rate acode\": \"Acode értékelése\",\n\t\"support\": \"Támogatás\",\n\t\"downloading file\": \"A(z) {file} fájl letöltése\",\n\t\"downloading...\": \"Letöltés…\",\n\t\"folder name\": \"Mappanév\",\n\t\"keyboard mode\": \"Billentyűzetmód\",\n\t\"normal\": \"Normál\",\n\t\"app settings\": \"Alkalmazás-beállítások\",\n\t\"disable in-app-browser caching\": \"Az alkalmazáson belüli böngésző gyorsítótárazásának letiltása\",\n\t\"copied to clipboard\": \"Vágólapra másolva\",\n\t\"remember opened files\": \"Megnyitott fájlok megjegyzése\",\n\t\"remember opened folders\": \"Megnyitott mappák megjegyzése\",\n\t\"no suggestions\": \"Javaslatok nélkül\",\n\t\"no suggestions aggressive\": \"Javaslatok nélkül (agresszív)\",\n\t\"install\": \"Telepítés\",\n\t\"installing\": \"Telepítés…\",\n\t\"plugins\": \"Bővítmények\",\n\t\"recently used\": \"Legutóbb használt\",\n\t\"update\": \"Frissítés\",\n\t\"uninstall\": \"Eltávolítás\",\n\t\"download acode pro\": \"Acode Pro letöltése\",\n\t\"loading plugins\": \"Bővítmények betöltése\",\n\t\"faqs\": \"GYIK\",\n\t\"feedback\": \"Visszajelzés\",\n\t\"header\": \"Fejléc\",\n\t\"sidebar\": \"Oldalsáv\",\n\t\"inapp\": \"Alkalmazáson belüli\",\n\t\"browser\": \"Böngésző\",\n\t\"diagonal scrolling\": \"Átlós görgetés\",\n\t\"reverse scrolling\": \"Fordított görgetés\",\n\t\"formatter\": \"Formátumkészítő\",\n\t\"format on save\": \"Formátum mentéskor\",\n\t\"remove ads\": \"Reklámok eltávolítása\",\n\t\"fast\": \"Gyors\",\n\t\"slow\": \"Lassú\",\n\t\"scroll settings\": \"Görgetési beállítások\",\n\t\"scroll speed\": \"Görgetési sebesség\",\n\t\"loading...\": \"Betöltés…\",\n\t\"no plugins found\": \"Nem található bővítmény\",\n\t\"name\": \"Név\",\n\t\"username\": \"Felhasználónév\",\n\t\"optional\": \"nem kötelező\",\n\t\"hostname\": \"Kiszolgálónév\",\n\t\"password\": \"Jelszó\",\n\t\"security type\": \"Biztonsági típus\",\n\t\"connection mode\": \"Kapcsolati mód\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Kulcsfájl\",\n\t\"select key file\": \"Kulcsfájl kiválasztása\",\n\t\"passphrase\": \"Jelmondat\",\n\t\"connecting...\": \"Kapcsolódás…\",\n\t\"type filename\": \"Fájlnév megadása\",\n\t\"unable to load files\": \"Nem sikerült betölteni a fájlokat\",\n\t\"preview port\": \"Port előnézete\",\n\t\"find file\": \"Fájl keresése\",\n\t\"system\": \"Rendszer\",\n\t\"please select a formatter\": \"Válasszon formátumkészítőt\",\n\t\"case sensitive\": \"Kis- és nagybetűk megkülönböztetése\",\n\t\"regular expression\": \"Reguláris kifejezések\",\n\t\"whole word\": \"Illesztés csak teljes szóra\",\n\t\"edit with\": \"Szerkesztés ezzel\",\n\t\"open with\": \"Megnyitás ezzel\",\n\t\"no app found to handle this file\": \"Nem található alkalmazás a fájl kezelésére\",\n\t\"restore default settings\": \"Alapértelmezett beállítások visszaállítása\",\n\t\"server port\": \"Kiszolgáló port\",\n\t\"preview settings\": \"Előnézet-beállítások\",\n\t\"preview settings note\": \"Ha az „Port előnézete” és a „Kiszolgáló portja” különbözik, az alkalmazás nem indítja el a kiszolgálót, hanem megnyitja a https://<host>:<preview port> címet a böngészőben vagy az alkalmazáson belüli böngészőben. Ez akkor hasznos, ha máshol futtatja a kiszolgálót.\",\n\t\"backup/restore note\": \"Ez csak a beállításokat, az egyéni témát és a billentyűparancsokat fogja menteni. Nem készít biztonsági mentést az FTP/SFTP-kről.\",\n\t\"host\": \"Kiszolgáló\",\n\t\"retry ftp/sftp when fail\": \"Újrapróbálkozás FTP/SFTP-sikertelenség esetén.\",\n\t\"more\": \"Több\",\n\t\"thank you :)\": \"Köszönöm! :)\",\n\t\"purchase pending\": \"Vásárlás folyamatban…\",\n\t\"cancelled\": \"Megszakítva\",\n\t\"local\": \"Helyi\",\n\t\"remote\": \"Távoli\",\n\t\"show console toggler\": \"Konzolkapcsoló megjelenítése\",\n\t\"binary file\": \"Ez a fájl bináris adatokat tartalmaz, biztosan meg akarja nyitni?\",\n\t\"relative line numbers\": \"Relatív sorszámok\",\n\t\"elastic tabstops\": \"Rugalmas tabulátor\",\n\t\"line based rtl switching\": \"Sor alapú RTL-váltás\",\n\t\"hard wrap\": \"Kemény törés\",\n\t\"spellcheck\": \"Helyesírás-ellenőrzés\",\n\t\"wrap method\": \"Tördelési módszer\",\n\t\"use textarea for ime\": \"Szövegterület használata az IME-hez\",\n\t\"invalid plugin\": \"Érvénytelen bővítmény\",\n\t\"type command\": \"Parancs beírása\",\n\t\"plugin\": \"Bővítmény\",\n\t\"quicktools trigger mode\": \"Gyors-eszközök aktiválási módja\",\n\t\"print margin\": \"Nyomtatási margó\",\n\t\"touch move threshold\": \"Érintéses mozgatás küszöbértéke\",\n\t\"info-retryremotefsafterfail\": \"Újrapróbálkozás FTP/SFTP-sikertelenség esetén\",\n\t\"info-fullscreen\": \"Címsor elrejtése a kezdőképernyőn.\",\n\t\"info-checkfiles\": \"Fájlmódosítások ellenőrzése, amikor az alkalmazás a háttérben van.\",\n\t\"info-console\": \"Válassza a JavaScript konzolt. A Legacy az alapértelmezett konzol, az Eruda egy harmadik fél konzolja.\",\n\t\"info-keyboardmode\": \"Billentyűzetmód a szövegbevitelhez, a „Javaslatok nélkül” elrejti a javaslatokat és az automatikus javítást. Ha a „Javaslatok nélkül” nem működik, módosítsa az értéket a következőre: „Javaslatok nélkül (agresszív)”.\",\n\t\"info-rememberfiles\": \"Megnyitott fájlok megjegyzése az alkalmazás bezárásakor.\",\n\t\"info-rememberfolders\": \"Megnyitott mappák megjegyzése az alkalmazás bezárásakor.\",\n\t\"info-floatingbutton\": \"Gyors-eszközök lebegő gombjának megjelenítése vagy elrejtése.\",\n\t\"info-openfilelistpos\": \"Hol jelenjen meg az aktív fájlok listája.\",\n\t\"info-touchmovethreshold\": \"Ha a készülék érintésérzékenysége túl magas, növelheti ezt az értéket, hogy megakadályozza a véletlen mozgatást.\",\n\t\"info-scroll-settings\": \"Ez a beállítás tartalmazza a görgetési beállításokat, beleértve a szövegtördelést is.\",\n\t\"info-animation\": \"Ha az alkalmazás késedelmesnek tűnik, tiltsa le az animációt.\",\n\t\"info-quicktoolstriggermode\": \"Ha a „Gyors-eszközök” gombja nem működik, akkor módosítsa ezt az értéket.\",\n\t\"info-checkForAppUpdates\": \"Alkalmazás-frissítések automatikus ellenőrzése.\",\n\t\"info-quickTools\": \"Gyors-eszközök megjelenítése vagy elrejtése.\",\n\t\"info-showHiddenFiles\": \"Rejtett fájlok és mappák megjelenítése (, amelyek nevei „.” ponttal kezdődnek)\",\n\t\"info-all_file_access\": \"Hozzáférés engedélyezése az „/sdcard” és „/storage” mappához a terminálban.\",\n\t\"info-fontSize\": \"Szöveg rendereléséhez használt betűméret.\",\n\t\"info-fontFamily\": \"Szöveg rendereléséhez használt betűtípus.\",\n\t\"info-theme\": \"Terminál színtémája.\",\n\t\"info-cursorStyle\": \"Kurzor stílusa, amikor a terminál van fókuszban.\",\n\t\"info-cursorInactiveStyle\": \"Kurzor stílusa, amikor nem a terminál van fókuszban.\",\n\t\"info-fontWeight\": \"Nem félkövér szöveg rendereléséhez használt betűvastagság.\",\n\t\"info-cursorBlink\": \"Független attól, hogy a kurzor villog-e.\",\n\t\"info-scrollback\": \"Sorok visszagörgetésének (előzmények) száma a terminálban. A visszagörgetés (előzmények) az a sormennyiség, amely megmarad, miután a sorok a kiinduló munkalapon túlra görgetődnek.\",\n\t\"info-tabStopWidth\": \"Tabulátor mérete a terminálban.\",\n\t\"info-letterSpacing\": \"Karakterek közötti térköz egész pixelekben megadva.\",\n\t\"info-imageSupport\": \"Független attól, hogy a terminál támogatja-e a képeket.\",\n\t\"info-fontLigatures\": \"Független attól, hogy a betűtípus-ligatúrák engedélyezve vannak-e a terminálban.\",\n\t\"info-confirmTabClose\": \"Megerősítés kérése a terminál lapjainak bezárása előtt.\",\n\t\"info-backup\": \"Biztonsági mentést készít a telepített terminálról.\",\n\t\"info-restore\": \"Visszaállít egy biztonsági mentést a telepített terminálról.\",\n\t\"info-uninstall\": \"Eltávolítja a jelenleg telepített terminált.\",\n\t\"owned\": \"Saját tulajdonú\",\n\t\"api_error\": \"Az API-kiszolgáló leállt, próbálja meg később.\",\n\t\"installed\": \"Telepített\",\n\t\"all\": \"Összes\",\n\t\"medium\": \"Közepes\",\n\t\"refund\": \"Visszatérítés\",\n\t\"product not available\": \"A termék nem érhető el\",\n\t\"no-product-info\": \"Ez a termék jelenleg nem érhető el az Ön országában, próbálja meg később.\",\n\t\"close\": \"Bezárás\",\n\t\"explore\": \"Felfedezés\",\n\t\"key bindings updated\": \"Billentyűparancsok frissítve\",\n\t\"search in files\": \"Keresés a fájlokban\",\n\t\"exclude files\": \"Fájlok kizárása\",\n\t\"include files\": \"Fájlok felvétele\",\n\t\"search result\": \"{matches} eredmény {files} fájlban.\",\n\t\"invalid regex\": \"Érvénytelen reguláris kifejezés: {message}.\",\n\t\"bottom\": \"Alul\",\n\t\"save all\": \"Összes mentése\",\n\t\"close all\": \"Összes bezárása\",\n\t\"unsaved files warning\": \"Egyes fájlok nem kerülnek mentésre. Koppintson az „OK” gombra, hogy mit tegyen, vagy koppintson a „Vissza” gombra a visszatéréshez.\",\n\t\"save all warning\": \"Biztosan el akarja menteni az összes fájlt és be akarja zárni? Ezt a műveletet nem lehet visszafordítani.\",\n\t\"save all changes warning\": \"Biztosan menti az összes fájlt?\",\n\t\"close all warning\": \"Biztosan bezárja az összes fájlt? Elveszíti a nem mentett módosításokat. Ez a művelet nem vonható vissza.\",\n\t\"refresh\": \"Frissítés\",\n\t\"shortcut buttons\": \"Gyors gombok\",\n\t\"no result\": \"Nincs eredmény\",\n\t\"searching...\": \"Keresés…\",\n\t\"quicktools:ctrl-key\": \"Ctrl-billentyű\",\n\t\"quicktools:tab-key\": \"Tabulátor-billentyű\",\n\t\"quicktools:shift-key\": \"Shift-billentyű\",\n\t\"quicktools:undo\": \"Visszavonás\",\n\t\"quicktools:redo\": \"Mégis\",\n\t\"quicktools:search\": \"Keresés fájlban\",\n\t\"quicktools:save\": \"Fájl mentése\",\n\t\"quicktools:esc-key\": \"Esc-billentyű\",\n\t\"quicktools:curlybracket\": \"Kapcsos zárójel beszúrása\",\n\t\"quicktools:squarebracket\": \"Szögletes zárójel beszúrása\",\n\t\"quicktools:parentheses\": \"Zárójel beszúrása\",\n\t\"quicktools:anglebracket\": \"Kúpos zárójel beszúrása\",\n\t\"quicktools:left-arrow-key\": \"Balra nyíl-billentyű\",\n\t\"quicktools:right-arrow-key\": \"Jobbra nyíl-billentyű\",\n\t\"quicktools:up-arrow-key\": \"Fel nyíl-billentyű\",\n\t\"quicktools:down-arrow-key\": \"Le nyíl-billentyű\",\n\t\"quicktools:moveline-up\": \"Sor mozgatása felfelé\",\n\t\"quicktools:moveline-down\": \"Sor mozgatása lefelé\",\n\t\"quicktools:copyline-up\": \"Sor másolása felfelé\",\n\t\"quicktools:copyline-down\": \"Sor másolása lefelé\",\n\t\"quicktools:semicolon\": \"Pontosvessző beszúrása\",\n\t\"quicktools:quotation\": \"Idézőjel beszúrása\",\n\t\"quicktools:and\": \"„ÉS” szimbólum beszúrása\",\n\t\"quicktools:bar\": \"Függőleges vonal beszúrása\",\n\t\"quicktools:equal\": \"Egyenlőségjel beszúrása\",\n\t\"quicktools:slash\": \"Perjel beszúrása\",\n\t\"quicktools:exclamation\": \"Felkiáltójel beszúrása\",\n\t\"quicktools:alt-key\": \"Alt-billentyű\",\n\t\"quicktools:meta-key\": \"Windows/Meta-billentyű\",\n\t\"info-quicktoolssettings\": \"Testre szabhatja a „Gyors-gombokat” és a billentyűket a szerkesztő alatti „Gyors-eszközök” tárolóban, hogy javítsa a kódolási élményt.\",\n\t\"info-excludefolders\": \"A **/node_modules/** minta használatával figyelmen kívül hagyhatja a node_modules mappában található összes fájlt. Ez kizárja a fájlokat a listából, és megakadályozza, hogy a fájlkeresésekben is szerepeljenek.\",\n\t\"missed files\": \"{count} fájl beolvasása a keresés megkezdése után, így nem lesznek benne a keresésben.\",\n\t\"remove\": \"Eltávolítás\",\n\t\"quicktools:command-palette\": \"Parancspaletta\",\n\t\"default file encoding\": \"Alapértelmezett fájlkódolás\",\n\t\"remove entry\": \"Biztosan el akarja távolítani a(z) „{name}” szót a mentett elérési utakból? Vegye figyelembe, hogy az eltávolítása nem törli magát az elérési utat.\",\n\t\"delete entry\": \"A(z) „{name}” törlésének megerősítése. Ez a művelet nem vonható vissza! Folytatja?\",\n\t\"change encoding\": \"A(z) „{file}” újranyitása „{encoding}” kódolással? Ez a művelet a fájlban végrehajtott, el nem mentett módosítások elvesztését eredményezi. Biztosan folytatja az újranyitást?\",\n\t\"reopen file\": \"Biztosan újranyitja a(z) „{file}” fájlt? Minden el nem mentett módosítás elvész.\",\n\t\"plugin min version\": \"A(z) „{name}” nevű bővítmény csak az Acode következő verziójától érhető el: {v-code}. Koppintson ide a frissítéshez.\",\n\t\"color preview\": \"Szín előnézete\",\n\t\"confirm\": \"Megerősítés\",\n\t\"list files\": \"Az összes fájl kilistázása itt: <strong>{name}</strong>? Túl sok fájl az alkalmazás összeomlását eredményezheti.\",\n\t\"problems\": \"Problémák\",\n\t\"show side buttons\": \"Oldalsó gombok megjelenítése\",\n\t\"bug_report\": \"Hibajelentés küldése\",\n\t\"verified publisher\": \"Hitelesített közzétevő\",\n\t\"most_downloaded\": \"Legtöbbször letöltött\",\n\t\"newly_added\": \"Újonnan hozzáadott\",\n\t\"top_rated\": \"Legjobbra értékelt\",\n\t\"rename not supported\": \"A Termux könyvtár átnevezése nem támogatott\",\n\t\"compress\": \"Tömörítés\",\n\t\"copy uri\": \"Uri másolása\",\n\t\"delete entries\": \"Biztosan töröl {count} elemet?\",\n\t\"deleting items\": \"{count} elem törlése…\",\n\t\"import project zip\": \"Projekt importálása zip-ből\",\n\t\"changelog\": \"Változáslista\",\n\t\"notifications\": \"Értesítések\",\n\t\"no_unread_notifications\": \"Nincsenek olvasatlan értesítések\",\n\t\"should_use_current_file_for_preview\": \"A jelenlegi fájl használata az előnézethez az alapértelmezett (index.html) helyett\",\n\t\"fade fold widgets\": \"Elhalványulás a modulok bezárásakor\",\n\t\"quicktools:home-key\": \"Ugrás az elejére-billentyű\",\n\t\"quicktools:end-key\": \"Ugrás a végére-billentyű\",\n\t\"quicktools:pageup-key\": \"Lapozás felfelé-billentyű\",\n\t\"quicktools:pagedown-key\": \"Lapozás lefelé-billentyű\",\n\t\"quicktools:delete-key\": \"Törlés-billentyű\",\n\t\"quicktools:tilde\": \"Hullámvonal beszúrása\",\n\t\"quicktools:backtick\": \"Fordított félidézőjel beszúrása\",\n\t\"quicktools:hash\": \"Számjel beszúrása\",\n\t\"quicktools:dollar\": \"Dollárjel beszúrása\",\n\t\"quicktools:modulo\": \"Százalékjel beszúrása\",\n\t\"quicktools:caret\": \"Hatványjel beszúrása\",\n\t\"plugin_enabled\": \"Bővítmény engedélyezve\",\n\t\"plugin_disabled\": \"Bővítmény letiltva\",\n\t\"enable_plugin\": \"Bővítmény engedélyezése\",\n\t\"disable_plugin\": \"Bővítmény letiltása\",\n\t\"open_source\": \"Nyílt forráskódú\",\n\t\"terminal settings\": \"Terminálbeállítások\",\n\t\"font ligatures\": \"Betűtípus-ligatúrák\",\n\t\"letter spacing\": \"Betűköz\",\n\t\"terminal:tab stop width\": \"Tabulátor szélessége\",\n\t\"terminal:scrollback\": \"Sorok visszagörgetése (előzmények)\",\n\t\"terminal:cursor blink\": \"Kurzor villogása\",\n\t\"terminal:font weight\": \"Betűvastagság\",\n\t\"terminal:cursor inactive style\": \"Inaktív kurzor stílusa\",\n\t\"terminal:cursor style\": \"Kurzor stílusa\",\n\t\"terminal:font family\": \"Betűtípuscsalád\",\n\t\"terminal:convert eol\": \"Sorvégződések átalakítása\",\n\t\"terminal:confirm tab close\": \"Megerősítés kérése a terminál lapjainak bezárásakor\",\n\t\"terminal:image support\": \"Képek támogatása\",\n\t\"terminal\": \"Terminál\",\n\t\"allFileAccess\": \"Hozzáférés az összes fájlhoz\",\n\t\"fonts\": \"Betűtípusok\",\n\t\"sponsor\": \"Szponzor\",\n\t\"downloads\": \"letöltés\",\n\t\"reviews\": \"megjegyzés\",\n\t\"overview\": \"Áttekintés\",\n\t\"contributors\": \"Közreműködők\",\n\t\"quicktools:hyphen\": \"Kötőjel beszúrása\",\n\t\"check for app updates\": \"Alkalmazásfrissítések ellenőrzése\",\n\t\"prompt update check consent message\": \"Internetkapcsolat esetén az Acode ellenőrizheti az új alkalmazásfrissítéseket. Engedélyezi a frissítések ellenőrzését?\",\n\t\"keywords\": \"Kulcsszavak\",\n\t\"author\": \"Szerző\",\n\t\"filtered by\": \"Szűrési szempont\",\n\t\"clean install state\": \"Tiszta telepítési állapot\",\n\t\"backup created\": \"Biztonsági mentés létrehozva\",\n\t\"restore completed\": \"Helyreállítás kész\",\n\t\"restore will include\": \"Ez helyreállítja\",\n\t\"restore warning\": \"Ez a művelet nem vonható vissza. Folytatja?\",\n\t\"reload to apply\": \"Újratölti az alkalmazást a változtatások érvényesítéséhez?\",\n\t\"reload app\": \"Alkalmazás újratöltése\",\n\t\"preparing backup\": \"Felkészülés a biztonsági mentésre\",\n\t\"collecting settings\": \"Beállítások gyűjtése\",\n\t\"collecting key bindings\": \"Billentyűparancsok gyűjtése\",\n\t\"collecting plugins\": \"Bővítményinformációk gyűjtése\",\n\t\"creating backup\": \"Biztonsági mentési fájl létrehozása\",\n\t\"validating backup\": \"Biztonsági mentés érvényesítése\",\n\t\"restoring key bindings\": \"Billentyűparancsok helyreállítása\",\n\t\"restoring plugins\": \"Bővítmények helyreállítása\",\n\t\"restoring settings\": \"Beállítások helyreállítása\",\n\t\"legacy backup warning\": \"Ez egy régebbi biztonsági mentési formátum. Egyes funkciók korlátozottak lehetnek.\",\n\t\"checksum mismatch\": \"Ellenőrzőösszeg-eltérés - a biztonsági mentés fájlja módosult vagy megsérült.\",\n\t\"plugin not found\": \"Nem található bővítmény a rendszerleíró adatbázisban\",\n\t\"paid plugin skipped\": \"Fizetős bővítmény - nem található vásárlás\",\n\t\"source not found\": \"Már nem létezik a forrásfájl\",\n\t\"restored\": \"Helyreállítva\",\n\t\"skipped\": \"Kihagyva\",\n\t\"backup not valid object\": \"Nem érvényes objektum a biztonsági mentési fájl\",\n\t\"backup no data\": \"Nem tartalmaz helyreállítandó adatokat a biztonsági mentési fájl.\",\n\t\"backup legacy warning\": \"Ez egy régebbi biztonsági mentési formátum (v1). Egyes funkciók korlátozottak lehetnek.\",\n\t\"backup missing metadata\": \"Hiányzó biztonsági mentési metaadatok - egyes információk nem érhetők el\",\n\t\"backup checksum mismatch\": \"Ellenőrzőösszeg-eltérés - a biztonsági mentés fájlja módosult vagy megsérült. Óvatosan járjon el.\",\n\t\"backup checksum verify failed\": \"Nem ellenőrizhető az ellenőrző összeg\",\n\t\"backup invalid settings\": \"Érvénytelen a beállítások formátuma\",\n\t\"backup invalid keybindings\": \"Érvénytelen a billentyűparancsok formátuma\",\n\t\"backup invalid plugins\": \"Érvénytelen a telepített bővítmények formátuma\",\n\t\"issues found\": \"Problémák találhatók\",\n\t\"error details\": \"Hiba részletei\",\n\t\"active tools\": \"Aktív eszközök\",\n\t\"available tools\": \"Elérhető eszközök\",\n\t\"recent\": \"Legutóbbi fájlok\",\n\t\"command palette\": \"Parancspaletta megnyitása\",\n\t\"change theme\": \"Téma módosítása\",\n\t\"documentation\": \"Dokumentáció\",\n\t\"open in terminal\": \"Megnyitás terminálban\",\n\t\"developer mode\": \"Fejlesztői mód\",\n\t\"info-developermode\": \"Engedélyezze a fejlesztői eszközöket (Eruda) a bővítmények hibakereséséhez és az alkalmazás állapotának megfigyeléséhez. A megfigyelő az alkalmazás indításakor előkészítődik.\",\n\t\"developer mode enabled\": \"Fejlesztői mód engedélyezve. A parancspaletta használatával kapcsolhatja be/ki a megfigyelőt (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Fejlesztői mód letiltva\",\n\t\"copy relative path\": \"Relatív elérési útvonal másolása\",\n\t\"shortcut request sent\": \"Parancsikon-kérelem megnyitva. A befejezéshez koppintson a hozzáadás gombra.\",\n\t\"add to home screen\": \"Hozzáadás a kezdőképernyőhöz\",\n\t\"pin shortcuts not supported\": \"Ez az eszköz nem támogatja a kezdőképernyő-parancsikonokat.\",\n\t\"save file before home shortcut\": \"A kezdőképernyőhöz való hozzáadás előtt mentse el a fájlt.\",\n\t\"terminal_required_message_for_lsp\": \"A Terminál nincs telepítve. Először telepítse a Terminált, hogy használni tudja az LSP-kiszolgálókat.\",\n\t\"shift click selection\": \"Shift + koppintás/kattintás a kiválasztáshoz\",\n\t\"earn ad-free time\": \"Reklámmentesség szerzése egy kis időre\",\n\t\"indent guides\": \"Behúzási segédvonalak\",\n\t\"language servers\": \"Nyelvi kiszolgálók\",\n\t\"lint gutter\": \"Szintaxisellenőrzési margó megjelenítése\",\n\t\"rainbow brackets\": \"Szivárványszínű zárójelek\",\n\t\"lsp-add-custom-server\": \"Egyéni kiszolgáló hozzáadása\",\n\t\"lsp-binary-args\": \"Bináris argumentumok (JSON-tömb)\",\n\t\"lsp-binary-command\": \"Bináris parancs\",\n\t\"lsp-binary-path-optional\": \"Bináris útvonal (nem kötelező)\",\n\t\"lsp-check-command-optional\": \"Ellenőrző parancs (nem kötelező felülbírálás)\",\n\t\"lsp-checking-installation-status\": \"Telepítési állapot ellenőrzése…\",\n\t\"lsp-configured\": \"Beállítva\",\n\t\"lsp-custom-server-added\": \"Egyéni kiszolgáló hozzáadva\",\n\t\"lsp-default\": \"Alapértelmezett\",\n\t\"lsp-details-line\": \"Részletek: {details}\",\n\t\"lsp-edit-initialization-options\": \"Előkészítési beállítások szerkesztése\",\n\t\"lsp-empty\": \"Üres\",\n\t\"lsp-enabled\": \"Engedélyezve\",\n\t\"lsp-error-add-server-failed\": \"Nem sikerült hozzáadni a kiszolgálót\",\n\t\"lsp-error-args-must-be-array\": \"Az argumentumoknak JSON-tömbnek kell lenniük\",\n\t\"lsp-error-binary-command-required\": \"A bináris parancs megadása kötelező\",\n\t\"lsp-error-language-id-required\": \"Legalább egy nyelvazonosító szükséges\",\n\t\"lsp-error-package-required\": \"Legalább egy csomag szükséges\",\n\t\"lsp-error-server-id-required\": \"Kiszolgálóazonosító szükséges\",\n\t\"lsp-feature-completion\": \"Kódkiegészítés\",\n\t\"lsp-feature-completion-info\": \"Automatikus kiegészítési javaslatok engedélyezése a kiszolgálótól.\",\n\t\"lsp-feature-diagnostics\": \"Diagnosztika\",\n\t\"lsp-feature-diagnostics-info\": \"Hibák és figyelmeztetések megjelenítése a nyelvi kiszolgálótól.\",\n\t\"lsp-feature-formatting\": \"Formázás\",\n\t\"lsp-feature-formatting-info\": \"Kódformázás engedélyezése a nyelvi kiszolgálótól.\",\n\t\"lsp-feature-hover\": \"Felugró információk\",\n\t\"lsp-feature-hover-info\": \"Típusinformációk és dokumentáció megjelenítése rámutatáskor.\",\n\t\"lsp-feature-inlay-hints\": \"Beágyazott tippek\",\n\t\"lsp-feature-inlay-hints-info\": \"Beágyazott típustippek megjelenítése a szerkesztőben.\",\n\t\"lsp-feature-signature\": \"Szignatúra-súgó\",\n\t\"lsp-feature-signature-info\": \"Függvényparaméter-tippek megjelenítése gépelés közben.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Előkészítési beállítások\",\n\t\"lsp-initialization-options-json\": \"Előkészítési beállítások (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Előkészítési beállítások frissítve\",\n\t\"lsp-install-command\": \"Telepítési parancs\",\n\t\"lsp-install-command-unavailable\": \"Nem érhető el a telepítési parancs\",\n\t\"lsp-install-info-check-failed\": \"Az Acode nem tudta ellenőrizni a telepítési állapotot.\",\n\t\"lsp-install-info-missing\": \"A nyelvi kiszolgáló nincs telepítve a terminálkörnyezetben.\",\n\t\"lsp-install-info-ready\": \"A nyelvi kiszolgáló telepítve van és készen áll.\",\n\t\"lsp-install-info-unknown\": \"A telepítési állapot automatikus ellenőrzése nem lehetséges.\",\n\t\"lsp-install-info-version-available\": \"A {version} verzió elérhető.\",\n\t\"lsp-install-method-apk\": \"APK-csomag\",\n\t\"lsp-install-method-cargo\": \"Cargo-crate\",\n\t\"lsp-install-method-manual\": \"Kézi bináris\",\n\t\"lsp-install-method-npm\": \"npm-csomag\",\n\t\"lsp-install-method-pip\": \"pip-csomag\",\n\t\"lsp-install-method-shell\": \"Egyéni parancsértelmező\",\n\t\"lsp-install-method-title\": \"Telepítési mód\",\n\t\"lsp-install-repair\": \"Telepítés / javítás\",\n\t\"lsp-installation-status\": \"Telepítési állapot\",\n\t\"lsp-installed\": \"Telepítve\",\n\t\"lsp-invalid-timeout\": \"Érvénytelen időtúllépési érték\",\n\t\"lsp-language-ids\": \"Nyelvazonosítók (vesszővel elválasztva)\",\n\t\"lsp-packages-prompt\": \"{method}-csomagok (vesszővel elválasztva)\",\n\t\"lsp-remove-installed-files\": \"Eltávolítja a(z) {server} telepített fájljait?\",\n\t\"lsp-server-disabled-toast\": \"Kiszolgáló letiltva\",\n\t\"lsp-server-enabled-toast\": \"Kiszolgáló engedélyezve\",\n\t\"lsp-server-id\": \"Kiszolgálóazonosító\",\n\t\"lsp-server-label\": \"Kiszolgálócímke\",\n\t\"lsp-server-not-found\": \"Nem található a kiszolgáló\",\n\t\"lsp-server-uninstalled\": \"Kiszolgáló eltávolítva\",\n\t\"lsp-startup-timeout\": \"Indítási időtúllépés\",\n\t\"lsp-startup-timeout-ms\": \"Indítási időtúllépés (ezredmásodperc)\",\n\t\"lsp-startup-timeout-set\": \"Indítási időtúllépés beállítva: {timeout} ms\",\n\t\"lsp-state-disabled\": \"letiltva\",\n\t\"lsp-state-enabled\": \"engedélyezve\",\n\t\"lsp-status-check-failed\": \"Nem sikerült az ellenőrzés\",\n\t\"lsp-status-installed\": \"Telepítve\",\n\t\"lsp-status-installed-version\": \"Telepítve ({version})\",\n\t\"lsp-status-line\": \"Állapot: {status}\",\n\t\"lsp-status-not-installed\": \"Nincs telepítve\",\n\t\"lsp-status-unknown\": \"Ismeretlen\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Nem érhető el az eltávolítási parancs\",\n\t\"lsp-uninstall-server\": \"Kiszolgáló eltávolítása\",\n\t\"lsp-update-command-optional\": \"Frissítési parancs (nem kötelező)\",\n\t\"lsp-update-command-unavailable\": \"Nem érhető el a frissítési parancs\",\n\t\"lsp-update-server\": \"Kiszolgáló frissítése\",\n\t\"lsp-version-line\": \"Verzió: {version}\",\n\t\"lsp-view-initialization-options\": \"Előkészítési beállítások megtekintése\",\n\t\"settings-category-about-acode\": \"Az Acode névjegye\",\n\t\"settings-category-advanced\": \"Speciális\",\n\t\"settings-category-assistance\": \"Segítségnyújtás\",\n\t\"settings-category-core\": \"Alapvető beállítások\",\n\t\"settings-category-cursor\": \"Kurzor\",\n\t\"settings-category-cursor-selection\": \"Kurzor és kijelölés\",\n\t\"settings-category-custom-servers\": \"Egyéni kiszolgálók\",\n\t\"settings-category-customization-tools\": \"Testreszabás és eszközök\",\n\t\"settings-category-display\": \"Megjelenítés\",\n\t\"settings-category-editing\": \"Szerkesztés\",\n\t\"settings-category-features\": \"Funkciók\",\n\t\"settings-category-files-sessions\": \"Fájlok és munkamenetek\",\n\t\"settings-category-fonts\": \"Betűtípusok\",\n\t\"settings-category-general\": \"Általános\",\n\t\"settings-category-guides-indicators\": \"Segédvonalak és jelzők\",\n\t\"settings-category-installation\": \"Telepítés\",\n\t\"settings-category-interface\": \"Felület\",\n\t\"settings-category-maintenance\": \"Karbantartás\",\n\t\"settings-category-permissions\": \"Engedélyek\",\n\t\"settings-category-preview\": \"Előnézet\",\n\t\"settings-category-scrolling\": \"Görgetés\",\n\t\"settings-category-server\": \"Kiszolgáló\",\n\t\"settings-category-servers\": \"Kiszolgálók\",\n\t\"settings-category-session\": \"Munkamenet\",\n\t\"settings-category-support-acode\": \"Az Acode támogatása\",\n\t\"settings-category-text-layout\": \"Szöveg és elrendezés\",\n\t\"settings-info-app-animation\": \"Alkalmazáson belüli átmeneti animációk vezérlése.\",\n\t\"settings-info-app-check-files\": \"Szerkesztők frissítése, ha a fájlok az Acode-on kívül módosulnak.\",\n\t\"settings-info-app-clean-install-state\": \"Az első lépések és a beállítási folyamatok által használt tárolt telepítési állapot törlése.\",\n\t\"settings-info-app-confirm-on-exit\": \"Megerősítés kérése az alkalmazás bezárása előtt.\",\n\t\"settings-info-app-console\": \"Válassza ki, melyik hibakereső konzol-integrációt használja az Acode.\",\n\t\"settings-info-app-default-file-encoding\": \"Alapértelmezett kódolás fájlok megnyitásakor vagy létrehozásakor.\",\n\t\"settings-info-app-exclude-folders\": \"Mappák és minták kihagyása kereséskor vagy beolvasáskor.\",\n\t\"settings-info-app-floating-button\": \"A lebegő gyorsművelet-gomb megjelenítése.\",\n\t\"settings-info-app-font-manager\": \"Alkalmazás-betűtípusok telepítése, kezelése vagy eltávolítása.\",\n\t\"settings-info-app-fullscreen\": \"Rendszerszintű állapotsor elrejtése az Acode használatakor.\",\n\t\"settings-info-app-keybindings\": \"A billentyűparancs-fájl szerkesztése vagy a gyorsbillentyűk alaphelyzetbe állítása.\",\n\t\"settings-info-app-keyboard-mode\": \"Válassza ki, hogyan viselkedjen a szoftveres billentyűzet szerkesztéskor.\",\n\t\"settings-info-app-language\": \"Válassza ki az alkalmazás nyelvét és a lefordított feliratokat.\",\n\t\"settings-info-app-open-file-list-position\": \"Válassza ki, hol jelenjen meg az aktív fájlok listája.\",\n\t\"settings-info-app-quick-tools-settings\": \"Gyorseszköz-parancsikonok átrendezése és testreszabása.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Válassza ki, hogyan nyíljanak meg a gyorseszközök érintésre.\",\n\t\"settings-info-app-remember-files\": \"Legutóbb megnyitott fájlok újbóli megnyitása.\",\n\t\"settings-info-app-remember-folders\": \"Az előző munkamenet mappáinak újbóli megnyitása.\",\n\t\"settings-info-app-retry-remote-fs\": \"Távoli fájlműveletek megismétlése sikertelen átvitel után.\",\n\t\"settings-info-app-side-buttons\": \"További műveletgombok megjelenítése a szerkesztő mellett.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Támogatói bejegyzés megjelenítése az oldalsávban.\",\n\t\"settings-info-app-touch-move-threshold\": \"Legkisebb elmozdulás az érintéses húzás érzékeléséhez.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Haptikus visszajelzés engedélyezése érintésekhez és vezérlőkhöz.\",\n\t\"settings-info-editor-autosave\": \"Módosítások automatikus mentése egy bizonyos késleltetés után.\",\n\t\"settings-info-editor-color-preview\": \"Színértékek beágyazott előnézete a szerkesztőben.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Kódblokk-összecsukó jelölők elhalványítása, amíg nincs rájuk szükség.\",\n\t\"settings-info-editor-font-family\": \"Válassza ki a szerkesztőben használt betűtípust.\",\n\t\"settings-info-editor-font-size\": \"Szerkesztő szövegméretének beállítása.\",\n\t\"settings-info-editor-format-on-save\": \"Formázó futtatása minden alkalommal, amikor egy fájlt mentenek.\",\n\t\"settings-info-editor-hard-wrap\": \"Valódi soremelések beszúrása a tisztán vizuális tördelés helyett.\",\n\t\"settings-info-editor-indent-guides\": \"Behúzási segédvonalak megjelenítése.\",\n\t\"settings-info-editor-line-height\": \"Sorok közötti függőleges távolság beállítása.\",\n\t\"settings-info-editor-line-numbers\": \"Sorok számának megjelenítése a margón.\",\n\t\"settings-info-editor-lint-gutter\": \"Diagnosztikai és szintaxisellenőrző jelölők megjelenítése a margón.\",\n\t\"settings-info-editor-live-autocomplete\": \"Javaslatok megjelenítése gépelés közben.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Összetartozó zárójelek színezése a beágyazási mélység alapján.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Távolság megjelenítése a jelenlegi sortól.\",\n\t\"settings-info-editor-rtl-text\": \"Jobbról balra haladó viselkedés váltása soronként.\",\n\t\"settings-info-editor-scroll-settings\": \"Görgetősáv méretének, sebességének és az ujjmozdulatok viselkedésének beállítása.\",\n\t\"settings-info-editor-shift-click-selection\": \"Kijelölés kiterjesztése a Shift + érintés vagy kattintás használatával.\",\n\t\"settings-info-editor-show-spaces\": \"Látható szóközjelölők megjelenítése.\",\n\t\"settings-info-editor-soft-tab\": \"Szóközök beszúrása tabulátorkarakterek helyett.\",\n\t\"settings-info-editor-tab-size\": \"Állítsa be, hány szóközt használjon egy tabulátorlépés.\",\n\t\"settings-info-editor-teardrop-size\": \"Kurzorfogantyú méretének beállítása az érintéses szerkesztéshez.\",\n\t\"settings-info-editor-text-wrap\": \"Hosszú sorok tördelése a szerkesztőben.\",\n\t\"settings-info-lsp-add-custom-server\": \"Egyéni nyelvi kiszolgáló regisztrálása telepítési, frissítési és indítási parancsokkal.\",\n\t\"settings-info-lsp-edit-init-options\": \"Előkészítési beállítások szerkesztése JSON-formátumban.\",\n\t\"settings-info-lsp-install-server\": \"Ezen nyelvi kiszolgáló telepítése vagy javítása.\",\n\t\"settings-info-lsp-server-enabled\": \"Ezen nyelvi kiszolgáló engedélyezése vagy letiltása.\",\n\t\"settings-info-lsp-startup-timeout\": \"Állítsa be, hogy az Acode mennyi ideig várjon a kiszolgáló elindulására.\",\n\t\"settings-info-lsp-uninstall-server\": \"A kiszolgálóhoz tartozó telepített csomagok vagy binárisok eltávolítása.\",\n\t\"settings-info-lsp-update-server\": \"Ezen nyelvi kiszolgáló frissítése, ha elérhető frissítési folyamat.\",\n\t\"settings-info-lsp-view-init-options\": \"Érvényes előkészítési beállítások megtekintése JSON-formátumban.\",\n\t\"settings-info-main-ad-rewards\": \"Reklámok megtekintése az ideiglenes reklámmentes hozzáférés feloldásához.\",\n\t\"settings-info-main-app-settings\": \"Nyelv, alkalmazás viselkedése és gyorselérési eszközök.\",\n\t\"settings-info-main-backup-restore\": \"Beállítások exportálása biztonsági mentésbe vagy azok későbbi visszaállítása.\",\n\t\"settings-info-main-changelog\": \"Legutóbbi frissítések és kiadási megjegyzések megtekintése.\",\n\t\"settings-info-main-edit-settings\": \"Nyers settings.json fájl közvetlen szerkesztése.\",\n\t\"settings-info-main-editor-settings\": \"Betűtípusok, tabulátorok, javaslatok és a szerkesztő megjelenítése.\",\n\t\"settings-info-main-formatter\": \"Válasszon formázót minden támogatott nyelvhez.\",\n\t\"settings-info-main-lsp-settings\": \"Nyelvi kiszolgálók és szerkesztő-intelligencia konfigurálása.\",\n\t\"settings-info-main-plugins\": \"Telepített bővítmények és elérhető műveleteik kezelése.\",\n\t\"settings-info-main-preview-settings\": \"Előnézeti mód, kiszolgálóportok és a böngésző viselkedése.\",\n\t\"settings-info-main-rateapp\": \"Az Acode értékelése a Google Play áruházban.\",\n\t\"settings-info-main-remove-ads\": \"Teljesen reklámmentes hozzáférés feloldása.\",\n\t\"settings-info-main-reset\": \"Az Acode visszaállítása az alapértelmezett konfigurációra.\",\n\t\"settings-info-main-sponsors\": \"Az Acode folyamatos fejlesztésének támogatása.\",\n\t\"settings-info-main-terminal-settings\": \"Terminál-téma, betűtípus, kurzor és munkamenet viselkedése.\",\n\t\"settings-info-main-theme\": \"Alkalmazás-téma, kontraszt és egyéni színek.\",\n\t\"settings-info-preview-disable-cache\": \"A tartalom mindig töltődjön be újra az alkalmazáson belüli böngészőben.\",\n\t\"settings-info-preview-host\": \"Az előnézeti webcím megnyitásakor használt gépnév.\",\n\t\"settings-info-preview-mode\": \"Válassza ki, hol nyíljon meg az előnézet az indításkor.\",\n\t\"settings-info-preview-preview-port\": \"Az élő előnézeti kiszolgáló által használt port.\",\n\t\"settings-info-preview-server-port\": \"Az alkalmazás belső kiszolgálója által használt port.\",\n\t\"settings-info-preview-show-console-toggler\": \"A konzol gombjának megjelenítése az előnézetben.\",\n\t\"settings-info-preview-use-current-file\": \"A jelenlegi fájl előnyben részesítése az előnézet indításakor.\",\n\t\"settings-info-terminal-convert-eol\": \"Sorvégek átalakítása a terminálkimenet beillesztésekor vagy megjelenítésekor.\",\n\t\"settings-note-formatter-settings\": \"Rendeljen formázót minden nyelvhez. További lehetőségek feloldásához telepítsen formázó bővítményeket.\",\n\t\"settings-note-lsp-settings\": \"A nyelvi kiszolgálók automatikus kiegészítést, diagnosztikát, felugró részleteket és egyebeket nyújtanak. Itt telepíthet, frissíthet vagy definiálhat egyéni kiszolgálókat. A felügyelt telepítők a terminál/proot környezetben futnak.\",\n\t\"search result label singular\": \"találat\",\n\t\"search result label plural\": \"találat\",\n\t\"pin tab\": \"Lap rögzítése\",\n\t\"unpin tab\": \"Lap rögzítésének megszüntetése\",\n\t\"pinned tab\": \"Rögzített lap\",\n\t\"unpin tab before closing\": \"Lap rögzítésének megszüntetése bezárás előtt.\",\n\t\"app font\": \"Alkalmazás betűtípusa\",\n\t\"settings-info-app-font-family\": \"Válassza ki az alkalmazás egész felületén használandó betűtípust.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (bináris parancs futtatása)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (kapcsolódás ws/wss webcímhez)\",\n\t\"lsp-websocket-url\": \"WebSocket webcím\",\n\t\"lsp-websocket-server-managed-externally\": \"Ezt a kiszolgálót külsőleg kezelik WebSocketen keresztül.\",\n\t\"lsp-error-websocket-url-invalid\": \"A WebSocket webcímnek ws:// vagy wss:// előtaggal kell kezdődnie\",\n\t\"lsp-error-websocket-url-required\": \"A WebSocket webcím megadása kötelező\",\n\t\"lsp-remove-custom-server\": \"Egyéni kiszolgáló eltávolítása\",\n\t\"lsp-remove-custom-server-confirm\": \"Eltávolítja a(z) {server} egyéni nyelvi kiszolgálót?\",\n\t\"lsp-custom-server-removed\": \"Egyéni kiszolgáló eltávolítva\",\n\t\"settings-info-lsp-remove-custom-server\": \"Eltávolítja ezt az egyéni nyelvi kiszolgálót az Acode alkalmazásból.\"\n}\n"
  },
  {
    "path": "src/lang/id-id.json",
    "content": "{\n\t\"lang\": \"Bahasa Indonesia\",\n\t\"about\": \"Tentang\",\n\t\"active files\": \"Berkas Aktif\",\n\t\"alert\": \"Peringatan\",\n\t\"app theme\": \"Tema Aplikasi\",\n\t\"autocorrect\": \"Aktifkan koreksi otomatis?\",\n\t\"autosave\": \"Simpan Otomatis\",\n\t\"cancel\": \"Batal\",\n\t\"change language\": \"Ubah Bahasa\",\n\t\"choose color\": \"Pilih warna\",\n\t\"clear\": \"Bersihkan\",\n\t\"close app\": \"Tutup aplikasi?\",\n\t\"commit message\": \"Pesan komit\",\n\t\"console\": \"Konsol\",\n\t\"conflict error\": \"Konflik! Mohon tunggu sebelum komit lainnya.\",\n\t\"copy\": \"Salin\",\n\t\"create folder error\": \"Maaf, tidak dapat membuat folder baru\",\n\t\"cut\": \"Potong\",\n\t\"delete\": \"Hapus\",\n\t\"dependencies\": \"Dependensi\",\n\t\"delay\": \"Waktu dalam milidetik\",\n\t\"editor settings\": \"Pengaturan Editor\",\n\t\"editor theme\": \"Tema Editor\",\n\t\"enter file name\": \"Masukkan nama berkas\",\n\t\"enter folder name\": \"Masukkan nama folder\",\n\t\"empty folder message\": \"Folder Kosong\",\n\t\"enter line number\": \"Masukkan nomor baris\",\n\t\"error\": \"Kesalahan\",\n\t\"failed\": \"Gagal\",\n\t\"file already exists\": \"Berkas sudah ada\",\n\t\"file already exists force\": \"Berkas sudah ada. Timpa?\",\n\t\"file changed\": \" telah diubah, muat ulang berkas?\",\n\t\"file deleted\": \"Berkas dihapus\",\n\t\"file is not supported\": \"Berkas tidak mendukung\",\n\t\"file not supported\": \"Jenis berkas ini tidak didukung.\",\n\t\"file too large\": \"Berkas terlalu besar untuk ditangani. Ukuran maksimal berkas yang diizinkan adalah {size}\",\n\t\"file renamed\": \"Berkas berganti nama\",\n\t\"file saved\": \"Berkas disimpan\",\n\t\"folder added\": \"Folder ditambahkan\",\n\t\"folder already added\": \"Folder sudab ditambahkan\",\n\t\"font size\": \"Ukuran Font\",\n\t\"goto\": \"Pergi ke baris\",\n\t\"icons definition\": \"Definisi ikon\",\n\t\"info\": \"Info\",\n\t\"invalid value\": \"Nilai tidak valid\",\n\t\"language changed\": \"Bahasa telah diubah dengan sukses\",\n\t\"linting\": \"Cek kesalahan sintaks\",\n\t\"logout\": \"Keluar\",\n\t\"loading\": \"Memuat\",\n\t\"my profile\": \"Profil saya\",\n\t\"new file\": \"Berkas baru\",\n\t\"new folder\": \"Folder baru\",\n\t\"no\": \"Tidak\",\n\t\"no editor message\": \"Buka atau buat berkas dan folder baru dari menu\",\n\t\"not set\": \"Tidak ada set\",\n\t\"unsaved files close app\": \"Ada berkas yang belum disimpan. Tutup Aplikasi?\",\n\t\"notice\": \"Pemberitahuan\",\n\t\"open file\": \"Buka berkas\",\n\t\"open files and folders\": \"Buka berkas dan folder\",\n\t\"open folder\": \"Buka folder\",\n\t\"open recent\": \"Buka baru-baru ini\",\n\t\"ok\": \"Oke\",\n\t\"overwrite\": \"Timpa\",\n\t\"paste\": \"Tempel\",\n\t\"preview mode\": \"Mode Pratinjau\",\n\t\"read only file\": \"Tidak dapat menyimpan berkas hanya baca. Silakan coba simpan sebagai\",\n\t\"reload\": \"Muat Ulang\",\n\t\"rename\": \"Ubah Nama\",\n\t\"replace\": \"Ganti\",\n\t\"required\": \"Bidang ini diperlukan\",\n\t\"run your web app\": \"Jalankan aplikasi web anda\",\n\t\"save\": \"Simpan\",\n\t\"saving\": \"Menyimpan\",\n\t\"save as\": \"Simpan sebagai\",\n\t\"save file to run\": \"Silakan simpan berkas ini untuk berjalan di peramban\",\n\t\"search\": \"Cari\",\n\t\"see logs and errors\": \"Lihat log dan kesalahan\",\n\t\"select folder\": \"Pilih folder\",\n\t\"settings\": \"Pengaturan\",\n\t\"settings saved\": \"Pengaturan disimpan\",\n\t\"show line numbers\": \"Tampilkan nomor baris\",\n\t\"show hidden files\": \"Tampilkan berkas tersembunyi\",\n\t\"show spaces\": \"Tampilkan spasi\",\n\t\"soft tab\": \"Tab lunak\",\n\t\"sort by name\": \"Urutkan berdasarkan nama\",\n\t\"success\": \"Sukses\",\n\t\"tab size\": \"Ukuran Tab\",\n\t\"text wrap\": \"Bungkus Teks\",\n\t\"theme\": \"Tema\",\n\t\"unable to delete file\": \"Tidak dapat menghapus berkas\",\n\t\"unable to open file\": \"Maaf, tidak dapat membuka berkas\",\n\t\"unable to open folder\": \"Maaf, tidak dapat membuka folder\",\n\t\"unable to save file\": \"Maaf, tidak dapat menyimpan berkas\",\n\t\"unable to rename\": \"Maaf, tidak dapat mengganti nama\",\n\t\"unsaved file\": \"Berkas ini tidak disimpan, tutup?\",\n\t\"warning\": \"Peringatan\",\n\t\"use emmet\": \"Gunakan emmet\",\n\t\"use quick tools\": \"Gunakan alat cepat\",\n\t\"yes\": \"Iya\",\n\t\"encoding\": \"Enkoding Teks\",\n\t\"syntax highlighting\": \"Penyorotan Sintaks\",\n\t\"read only\": \"Hanya baca\",\n\t\"select all\": \"Pilih semua\",\n\t\"select branch\": \"Pilih cabang\",\n\t\"create new branch\": \"Buat cabang baru\",\n\t\"use branch\": \"Gunakan cabang\",\n\t\"new branch\": \"Cabang baru\",\n\t\"branch\": \"Cabang\",\n\t\"key bindings\": \"Binding kunci\",\n\t\"edit\": \"Edit\",\n\t\"reset\": \"Atur ulang\",\n\t\"color\": \"Warna\",\n\t\"select word\": \"Pilih kata\",\n\t\"quick tools\": \"Alat cepat\",\n\t\"select\": \"Pilih\",\n\t\"editor font\": \"Font Editor\",\n\t\"new project\": \"Proyek baru\",\n\t\"format\": \"Format\",\n\t\"project name\": \"Nama proyek\",\n\t\"unsupported device\": \"Perangkat Anda tidak mendukung tema.\",\n\t\"vibrate on tap\": \"Bergetar pada ketuk\",\n\t\"copy command is not supported by ftp.\": \"Perintah Salin tidak didukung oleh FTP.\",\n\t\"support title\": \"Dukung Acode\",\n\t\"fullscreen\": \"Layar penuh\",\n\t\"animation\": \"Animasi\",\n\t\"backup\": \"Cadangkan\",\n\t\"restore\": \"Mengembalikan\",\n\t\"backup successful\": \"Berhasil membuat cadangan\",\n\t\"invalid backup file\": \"Berkas cadangan tidak valid\",\n\t\"add path\": \"Tambah jalur\",\n\t\"live autocompletion\": \"Penyelesaian otomatis langsung\",\n\t\"file properties\": \"Properti berkas\",\n\t\"path\": \"Jalur\",\n\t\"type\": \"Jenis\",\n\t\"word count\": \"Jumlah kata\",\n\t\"line count\": \"Jumlah baris\",\n\t\"last modified\": \"Modifikasi terakhir\",\n\t\"size\": \"Ukuran\",\n\t\"share\": \"Bagikan\",\n\t\"show print margin\": \"Tampilkan margin cetak\",\n\t\"login\": \"Masuk\",\n\t\"scrollbar size\": \"Ukuran bar gulir\",\n\t\"cursor controller size\": \"Ukuran pengontrol kursor\",\n\t\"none\": \"Tidak ada\",\n\t\"small\": \"Kecil\",\n\t\"large\": \"Besar\",\n\t\"floating button\": \"Tombol mengambang\",\n\t\"confirm on exit\": \"Konfirmasi saat keluar\",\n\t\"show console\": \"Tampilkan konsol\",\n\t\"image\": \"Gambar\",\n\t\"insert file\": \"Menyisipkan berkas\",\n\t\"insert color\": \"Menyisipkan warna\",\n\t\"powersave mode warning\": \"Matikan mode hemat daya untuk pratinjau di peramban eksternal.\",\n\t\"exit\": \"Keluar\",\n\t\"custom\": \"Kostum\",\n\t\"reset warning\": \"Apakah Anda yakin ingin mengatur ulang tema?\",\n\t\"theme type\": \"Jenis tema\",\n\t\"light\": \"Terang\",\n\t\"dark\": \"Gelap\",\n\t\"file browser\": \"Penjelajah Berkas\",\n\t\"operation not permitted\": \"Operasi tidak diizinkan\",\n\t\"no such file or directory\": \"Tidak ada berkas atau direktori seperti itu\",\n\t\"input/output error\": \"Kesalahan masukan/keluaran\",\n\t\"permission denied\": \"Izin ditolak\",\n\t\"bad address\": \"Alamat yang buruk\",\n\t\"file exists\": \"Berkas ada\",\n\t\"not a directory\": \"Bukan direktori\",\n\t\"is a directory\": \"Adalah direktori\",\n\t\"invalid argument\": \"Argumen tidak valid\",\n\t\"too many open files in system\": \"Terlalu banyak berkas terbuka dalam sistem\",\n\t\"too many open files\": \"Terlalu banyak berkas terbuka\",\n\t\"text file busy\": \"Berkas teks sibuk\",\n\t\"no space left on device\": \"Tidak ada ruang yang tersisa di perangkat\",\n\t\"read-only file system\": \"Sistem berkas hanya-baca\",\n\t\"file name too long\": \"Nama berkas terlalu panjang\",\n\t\"too many users\": \"Terlalu banyak pengguna\",\n\t\"connection timed out\": \"Waktu koneksi habis\",\n\t\"connection refused\": \"Koneksi ditolak\",\n\t\"owner died\": \"Pemilik mati\",\n\t\"an error occurred\": \"Terjadi kesalahan\",\n\t\"add ftp\": \"Tambahkan FTP\",\n\t\"add sftp\": \"Tambahkan SFTP\",\n\t\"save file\": \"Simpan berkas\",\n\t\"save file as\": \"Simpan berkas sebagai\",\n\t\"files\": \"Berkas\",\n\t\"help\": \"Bantuan\",\n\t\"file has been deleted\": \"{file} telah dihapus!\",\n\t\"feature not available\": \"Fitur ini hanya tersedia dalam versi berbayar dari aplikasi.\",\n\t\"deleted file\": \"Berkas yang dihapus\",\n\t\"line height\": \"Tinggi baris\",\n\t\"preview info\": \"Jika Anda ingin menjalankan berkas aktif, ketuk dan tahan ikon Putar.\",\n\t\"manage all files\": \"Izinkan Acode Editor untuk mengelola semua berkas dalam pengaturan untuk mengedit berkas di perangkat Anda dengan mudah.\",\n\t\"close file\": \"Tutup berkas\",\n\t\"reset connections\": \"Atur ulang koneksi\",\n\t\"check file changes\": \"Periksa perubahan berkas\",\n\t\"open in browser\": \"Buka di peramban\",\n\t\"desktop mode\": \"Mode Desktop\",\n\t\"toggle console\": \"Alihkan Konsol\",\n\t\"new line mode\": \"Mode baris baru\",\n\t\"add a storage\": \"Tambahkan penyimpanan\",\n\t\"rate acode\": \"Nilai Acode\",\n\t\"support\": \"Dukung\",\n\t\"downloading file\": \"Mengunduh {file}\",\n\t\"downloading...\": \"Mengunduh...\",\n\t\"folder name\": \"Nama Folder\",\n\t\"keyboard mode\": \"Mode Papan Ketik\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"Pengaturan Aplikasi\",\n\t\"disable in-app-browser caching\": \"Nonaktifkan caching peramban-dalam-aplikasi\",\n\t\"Should use Current File For preview instead of default (index.html)\": \"Harus menggunakan berkas saat ini untuk pratinjau alih-alih default (index.html)\",\n\t\"copied to clipboard\": \"Disalin ke clipboard\",\n\t\"remember opened files\": \"Ingat berkas yang dibuka\",\n\t\"remember opened folders\": \"Ingat folder yang dibuka\",\n\t\"no suggestions\": \"Tidak ada saran\",\n\t\"no suggestions aggressive\": \"Tidak ada saran yang agresif\",\n\t\"install\": \"Pasang\",\n\t\"installing\": \"Memasang...\",\n\t\"plugins\": \"Plugin\",\n\t\"recently used\": \"Baru - baru ini digunakan\",\n\t\"update\": \"Perbarui\",\n\t\"uninstall\": \"Copot pemasangan\",\n\t\"download acode pro\": \"Unduh Acode pro\",\n\t\"loading plugins\": \"Memuat plugin\",\n\t\"faqs\": \"FAQ\",\n\t\"feedback\": \"Umpan balik\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Bilah sisi\",\n\t\"inapp\": \"Dalam aplikasi\",\n\t\"browser\": \"Peramban\",\n\t\"diagonal scrolling\": \"Pengguliran diagonal\",\n\t\"reverse scrolling\": \"Pengguliran terbalik\",\n\t\"formatter\": \"Pemformat\",\n\t\"format on save\": \"Format pada simpan\",\n\t\"remove ads\": \"Hapus iklan\",\n\t\"fast\": \"Cepat\",\n\t\"slow\": \"Lambat\",\n\t\"scroll settings\": \"Pengaturan gulir\",\n\t\"scroll speed\": \"Kecepatan gulir\",\n\t\"loading...\": \"Memuat...\",\n\t\"no plugins found\": \"Tidak ditemukan plugin\",\n\t\"name\": \"Nama\",\n\t\"username\": \"Nama pengguna\",\n\t\"optional\": \"Opsional\",\n\t\"hostname\": \"Nama host\",\n\t\"password\": \"Kata sandi\",\n\t\"security type\": \"Jenis keamanan\",\n\t\"connection mode\": \"Mode koneksi\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Berkas kunci\",\n\t\"select key file\": \"Pilih berkas kunci\",\n\t\"passphrase\": \"Frasa sandi\",\n\t\"connecting...\": \"Menghubungkan...\",\n\t\"type filename\": \"Jenis nama berkas\",\n\t\"unable to load files\": \"Tidak dapat memuat berkas\",\n\t\"preview port\": \"Port pratinjau\",\n\t\"find file\": \"Temukan berkas\",\n\t\"system\": \"Sistem\",\n\t\"please select a formatter\": \"Mohon pilih pemformat\",\n\t\"case sensitive\": \"Sensitif huruf besar\",\n\t\"regular expression\": \"Ekspresi Regular\",\n\t\"whole word\": \"Seluruh kata\",\n\t\"edit with\": \"Edit dengan\",\n\t\"open with\": \"Buka dengan\",\n\t\"no app found to handle this file\": \"Tidak ditemukan aplikasi untuk menangani berkas ini\",\n\t\"restore default settings\": \"Kembalikan pengaturan default\",\n\t\"server port\": \"Port server\",\n\t\"preview settings\": \"Pengaturan pratinjau\",\n\t\"preview settings note\": \"Jika port pratinjau dan port server berbeda, aplikasi tidak akan memulai server dan sebaliknya akan membuka https://<host>:<pratinjau port> di peramban atau peramban-dalam-aplikasi. Ini berguna ketika Anda menjalankan server di tempat lain.\",\n\t\"backup/restore note\": \"Ini hanya akan membuat cadangan pengaturan Anda, tema khusus, plugin yang dipasang, dan binding kunci. Ini tidak akan membuat cadangan FTP/SFTP atau status aplikasi Anda.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Ulangi FTP/SFTP ketika gagal\",\n\t\"more\": \"Lebih banyak\",\n\t\"thank you :)\": \"Terima kasih :)\",\n\t\"purchase pending\": \"Pembelian Tertunda\",\n\t\"cancelled\": \"Dibatalkan\",\n\t\"local\": \"Lokal\",\n\t\"remote\": \"Jarak Jauh\",\n\t\"show console toggler\": \"Tampilkan pengalih konsol\",\n\t\"binary file\": \"Berkas ini berisi data biner, apakah Anda ingin membukanya?\",\n\t\"relative line numbers\": \"Nomor garis relatif\",\n\t\"elastic tabstops\": \"Tabstop elastis\",\n\t\"line based rtl switching\": \"Beralih RTL berbasis garis\",\n\t\"hard wrap\": \"Bungkus keras\",\n\t\"spellcheck\": \"Periksa ejaan\",\n\t\"wrap method\": \"Metode Bungkus\",\n\t\"use textarea for ime\": \"Gunakan textarea untuk IME\",\n\t\"invalid plugin\": \"Plugin Tidak Valid\",\n\t\"type command\": \"Ketik perintah\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Mode Pemicu Alat Cepat\",\n\t\"print margin\": \"Cetak margin\",\n\t\"touch move threshold\": \"Ambang pergerakan sentuh\",\n\t\"info-retryremotefsafterfail\": \"Ulangi koneksi FTP/SFTP ketika gagal.\",\n\t\"info-fullscreen\": \"Sembunyikan Bilah Judul di Layar Beranda.\",\n\t\"info-checkfiles\": \"Periksa perubahan berkas ketika aplikasi di latar belakang.\",\n\t\"info-console\": \"Pilih konsol JavaScript. Legacy adalah konsol default, Eruda adalah konsol pihak ketiga.\",\n\t\"info-keyboardmode\": \"Mode keyboard untuk input teks, tidak ada saran akan menyembunyikan saran dan koreksi otomatis. Jika tidak ada saran tidak berfungsi, cobalah untuk mengubah nilai ke tanpa saran yang agresif.\",\n\t\"info-rememberfiles\": \"Ingat berkas yang dibuka ketika aplikasi ditutup.\",\n\t\"info-rememberfolders\": \"Ingat folder yang dibuka ketika aplikasi ditutup.\",\n\t\"info-floatingbutton\": \"Tampilkan atau sembunyikan tombol apung Alat Cepat.\",\n\t\"info-openfilelistpos\": \"Di mana untuk menampilkan daftar berkas aktif.\",\n\t\"info-touchmovethreshold\": \"Jika sensitivitas sentuhan perangkat Anda terlalu tinggi, Anda dapat meningkatkan nilai ini untuk mencegah gerakan sentuh yang tidak disengaja.\",\n\t\"info-scroll-settings\": \"Pengaturan ini berisi pengaturan gulir termasuk bungkus teks.\",\n\t\"info-animation\": \"Jika aplikasi terasa lag, nonaktifkan animasi.\",\n\t\"info-quicktoolstriggermode\": \"Jika tombol dalam alat cepat tidak berfungsi, cobalah untuk mengubah nilai ini.\",\n\t\"info-checkForAppUpdates\": \"Periksa pembaruan aplikasi secara otomatis.\",\n\t\"info-quickTools\": \"Tampilkan atau sembunyikan alat cepat.\",\n\t\"info-showHiddenFiles\": \"Tampilkan berkas dan folder tersembunyi. (Berawal dengan .)\",\n\t\"info-all_file_access\": \"Aktifkan akses ke /sdcard dan /storage di terminal.\",\n\t\"info-fontSize\": \"Ukuran font yang digunakan untuk menampilkan teks.\",\n\t\"info-fontFamily\": \"Jenis huruf yang digunakan untuk menampilkan teks.\",\n\t\"info-theme\": \"Tema warna terminal.\",\n\t\"info-cursorStyle\": \"Gaya kursor saat terminal sedang aktif.\",\n\t\"info-cursorInactiveStyle\": \"Gaya kursor saat terminal tidak terfokus.\",\n\t\"info-fontWeight\": \"Ketebalan font yang digunakan untuk menampilkan teks tidak tebal.\",\n\t\"info-cursorBlink\": \"Apakah kursor berkedip.\",\n\t\"info-scrollback\": \"Jumlah scrollback di terminal. Scrollback adalah jumlah baris yang dipertahankan ketika baris digulirkan melampaui tampilan awal.\",\n\t\"info-tabStopWidth\": \"Ukuran tab berhenti di terminal.\",\n\t\"info-letterSpacing\": \"Jarak antar karakter dalam piksel penuh.\",\n\t\"info-imageSupport\": \"Apakah gambar didukung di terminal.\",\n\t\"info-fontLigatures\": \"Apakah ligatur font diaktifkan di terminal.\",\n\t\"info-confirmTabClose\": \"Mintalah konfirmasi sebelum menutup tab terminal.\",\n\t\"info-backup\": \"Membuat cadangan instalasi terminal.\",\n\t\"info-restore\": \"Mengembalikan cadangan instalasi terminal.\",\n\t\"info-uninstall\": \"Menghapus instalasi terminal.\",\n\t\"owned\": \"Dimiliki\",\n\t\"api_error\": \"API server turun, silakan coba setelah beberapa waktu.\",\n\t\"installed\": \"Terpasang\",\n\t\"all\": \"Semua\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Pengembalian dana\",\n\t\"product not available\": \"Produk tidak tersedia\",\n\t\"no-product-info\": \"Produk ini tidak tersedia di negara Anda saat ini, silakan coba lagi.\",\n\t\"close\": \"Tutup\",\n\t\"explore\": \"Jelajahi\",\n\t\"key bindings updated\": \"Binding kunci diperbarui\",\n\t\"search in files\": \"Cari dalam berkas\",\n\t\"exclude files\": \"Kecualikan berkas\",\n\t\"include files\": \"Sertakan berkas\",\n\t\"search result\": \"{matches} hasil dalam {files} berkas.\",\n\t\"invalid regex\": \"Ekspresi regular tidak valid: {message}.\",\n\t\"bottom\": \"Bawah\",\n\t\"save all\": \"Simpan semua\",\n\t\"close all\": \"Tutup semua\",\n\t\"unsaved files warning\": \"Beberapa berkas tidak disimpan. Klik 'Oke' pilih apa yang harus dilakukan atau tekan 'Batal' untuk kembali.\",\n\t\"save all warning\": \"Apakah Anda yakin ingin menyimpan semua berkas dan tutup? Tindakan ini tidak dapat dibalik.\",\n\t\"save all changes warning\": \"Apakah Anda yakin ingin menyimpan semua berkas?\",\n\t\"close all warning\": \"Apakah Anda yakin ingin menutup semua berkas? Anda akan kehilangan perubahan yang belum disimpan dan tindakan ini tidak dapat dibalik.\",\n\t\"refresh\": \"Segarkan\",\n\t\"shortcut buttons\": \"Tombol pintasan\",\n\t\"no result\": \"Tidak ada hasil\",\n\t\"searching...\": \"Mencari...\",\n\t\"quicktools:ctrl-key\": \"Kunci Control/Command\",\n\t\"quicktools:tab-key\": \"Kunci Tab\",\n\t\"quicktools:shift-key\": \"Kunci Shift\",\n\t\"quicktools:undo\": \"Membatalkan\",\n\t\"quicktools:redo\": \"Mengulangi\",\n\t\"quicktools:search\": \"Cari di berkas\",\n\t\"quicktools:save\": \"Simpan berkas\",\n\t\"quicktools:esc-key\": \"Kunci Escape\",\n\t\"quicktools:curlybracket\": \"Masukkan kurung kurawal\",\n\t\"quicktools:squarebracket\": \"Masukkan kurung persegi\",\n\t\"quicktools:parentheses\": \"Masukkan tanda kurung\",\n\t\"quicktools:anglebracket\": \"Masukkan kurung sudut\",\n\t\"quicktools:left-arrow-key\": \"Kunci panah kiri\",\n\t\"quicktools:right-arrow-key\": \"Kunci panah kanan\",\n\t\"quicktools:up-arrow-key\": \"Kunci panah atas\",\n\t\"quicktools:down-arrow-key\": \"Kunci panah bawah\",\n\t\"quicktools:moveline-up\": \"Pindahkan baris ke atas\",\n\t\"quicktools:moveline-down\": \"Pindahkan baris ke bawah\",\n\t\"quicktools:copyline-up\": \"Salin baris ke atas\",\n\t\"quicktools:copyline-down\": \"Salin baris ke bawah\",\n\t\"quicktools:semicolon\": \"Masukkan titik koma\",\n\t\"quicktools:quotation\": \"Masukkan kutipan\",\n\t\"quicktools:and\": \"Masukkan simbol dan\",\n\t\"quicktools:bar\": \"Masukkan simbol bar\",\n\t\"quicktools:equal\": \"Masukkan simbol sama dengan\",\n\t\"quicktools:slash\": \"Masukkan simbol slash\",\n\t\"quicktools:exclamation\": \"Masukkan tanda seru\",\n\t\"quicktools:alt-key\": \"Kunci Alt\",\n\t\"quicktools:meta-key\": \"Kunci Windows/Meta\",\n\t\"info-quicktoolssettings\": \"Kustomisasi tombol pintas dan tombol keyboard dalam wadah alat cepat di bawah editor untuk meningkatkan pengalaman pengkodean Anda.\",\n\t\"info-excludefolders\": \"Gunakan pola **/node_modules/** untuk mengabaikan semua berkas dari folder node_modules. Ini akan mengecualikan berkas dari terdaftar dan juga akan mencegah mereka dimasukkan dalam pencarian berkas.\",\n\t\"missed files\": \"Memindai {count} berkas setelah pencarian dimulai dan tidak akan dimasukkan dalam pencarian.\",\n\t\"remove\": \"Hapus\",\n\t\"quicktools:command-palette\": \"Palet perintah\",\n\t\"default file encoding\": \"Enkoding berkas default\",\n\t\"remove entry\": \"Apakah Anda yakin ingin menghapus '{name}' dari jalur yang disimpan? Harap dicatat bahwa menghapusnya tidak akan menghapus jalur itu sendiri.\",\n\t\"delete entry\": \"Konfirmasikan penghapusan: '{name}'. Tindakan ini tidak dapat diurungkan. Melanjutkan?\",\n\t\"change encoding\": \"Buka kembali '{file}' dengan enkoding '{encoding}'? Tindakan ini akan mengakibatkan hilangnya perubahan yang belum disimpan yang dilakukan pada berkas. Apakah Anda ingin melanjutkan pembukaan kembali?\",\n\t\"reopen file\": \"Apakah Anda yakin ingin membuka kembali '{file}'? Setiap perubahan yang belum disimpan akan hilang.\",\n\t\"plugin min version\": \"{name} hanya tersedia dalam Acode - {v-code} dan di atas. Klik di sini untuk memperbarui.\",\n\t\"color preview\": \"Pratinjau warna\",\n\t\"confirm\": \"Konfirmasi\",\n\t\"list files\": \"Daftar semua berkas dalam <strong>{name}</strong>? Terlalu banyak berkas dapat menyebabkan aplikasi rusak.\",\n\t\"problems\": \"Masalah\",\n\t\"show side buttons\": \"Tampilkan tombol samping\",\n\t\"bug_report\": \"Kirim Laporan Bug\",\n\t\"verified publisher\": \"Penerbit terverifikasi\",\n\t\"most_downloaded\": \"Paling Banyak Diunduh\",\n\t\"newly_added\": \"Baru Ditambahkan\",\n\t\"top_rated\": \"Peringkat Teratas\",\n\t\"rename not supported\": \"Mengganti nama pada direktori Termux tidak didukung\",\n\t\"compress\": \"Kompres\",\n\t\"copy uri\": \"Salin Uri\",\n\t\"delete entries\": \"Apakah Anda yakin ingin menghapus {count} item?\",\n\t\"deleting items\": \"menghapus {count} item...\",\n\t\"import project zip\": \"Impor Proyek (zip)\",\n\t\"changelog\": \"Catatan Perubahan\",\n\t\"notifications\": \"Notifikasi\",\n\t\"no_unread_notifications\": \"Tidak ada pemberitahuan yang belum dibaca\",\n\t\"should_use_current_file_for_preview\": \"Harus menggunakan berkas saat ini untuk pratinjau alih-alih default (index.html)\",\n\t\"fade fold widgets\": \"Widget Lipat Pudar\",\n\t\"quicktools:home-key\": \"Kunci Home\",\n\t\"quicktools:end-key\": \"Kunci End\",\n\t\"quicktools:pageup-key\": \"Kunci PageUp\",\n\t\"quicktools:pagedown-key\": \"Kunci PageDown\",\n\t\"quicktools:delete-key\": \"Kunci Delete\",\n\t\"quicktools:tilde\": \"Masukkan tanda gelombang\",\n\t\"quicktools:backtick\": \"Masukkan tanda kutip terbalik\",\n\t\"quicktools:hash\": \"Masukkan tanda pagar\",\n\t\"quicktools:dollar\": \"Masukkan simbol dolar\",\n\t\"quicktools:modulo\": \"Masukkan simbol modulus/persen\",\n\t\"quicktools:caret\": \"Masukkan tanda sisipan\",\n\t\"plugin_enabled\": \"Plugin diaktifkan\",\n\t\"plugin_disabled\": \"Plugin dinonaktifkan\",\n\t\"enable_plugin\": \"Aktifkan Plugin ini\",\n\t\"disable_plugin\": \"Nonaktifkan Plugin ini\",\n\t\"open_source\": \"Sumber Terbuka\",\n\t\"terminal settings\": \"Pengaturan Terminal\",\n\t\"font ligatures\": \"Ligatur Huruf\",\n\t\"letter spacing\": \"Jarak Antar Huruf\",\n\t\"terminal:tab stop width\": \"Lebar Hentian Tab\",\n\t\"terminal:scrollback\": \"Garis Gulir Balik\",\n\t\"terminal:cursor blink\": \"Kursor Berkedip\",\n\t\"terminal:font weight\": \"Berat Huruf\",\n\t\"terminal:cursor inactive style\": \"Gaya Kursor Tidak Aktif\",\n\t\"terminal:cursor style\": \"Gaya Kursor\",\n\t\"terminal:font family\": \"Keluarga Huruf\",\n\t\"terminal:convert eol\": \"Konversi Akhir Baris\",\n\t\"terminal:confirm tab close\": \"Konfirmasi penutupan tab terminal\",\n\t\"terminal:image support\": \"Dukungan gambar\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"Semua akses berkas\",\n\t\"fonts\": \"Huruf\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"Unduhan\",\n\t\"reviews\": \"Ulasan\",\n\t\"overview\": \"Ikhtisar\",\n\t\"contributors\": \"Kontributor\",\n\t\"quicktools:hyphen\": \"Masukkan simbol tanda hubung\",\n\t\"check for app updates\": \"Periksa pembaruan aplikasi\",\n\t\"prompt update check consent message\": \"Acode dapat memeriksa pembaruan aplikasi baru saat Anda online. Aktifkan pemeriksaan pembaruan?\",\n\t\"keywords\": \"Kata kunci\",\n\t\"author\": \"Pembuat\",\n\t\"filtered by\": \"Disaring oleh\",\n\t\"clean install state\": \"Bersihkan kondisi instal\",\n\t\"backup created\": \"Cadangan dibuat\",\n\t\"restore completed\": \"Pemulihan selesai\",\n\t\"restore will include\": \"Ini akan memulihkan\",\n\t\"restore warning\": \"Tindakan ini tidak dapat dibatalkan. Lanjutkan?\",\n\t\"reload to apply\": \"Muat ulang untuk menerapkan perubahan?\",\n\t\"reload app\": \"Muat ulang aplikasi\",\n\t\"preparing backup\": \"Mempersiapkan cadangan\",\n\t\"collecting settings\": \"Mengumpulkan pengaturan\",\n\t\"collecting key bindings\": \"Mengumpulkan binding kunci\",\n\t\"collecting plugins\": \"Mengumpulkan informasi plugin\",\n\t\"creating backup\": \"Membuat berkas cadangan\",\n\t\"validating backup\": \"Memvalidasi cadangan\",\n\t\"restoring key bindings\": \"Mengembalikan binding kunci\",\n\t\"restoring plugins\": \"Memulihkan plugin\",\n\t\"restoring settings\": \"Memulihkan pengaturan\",\n\t\"legacy backup warning\": \"Ini adalah format pencadangan lama. Beberapa fitur mungkin terbatas.\",\n\t\"checksum mismatch\": \"Ketidakcocokan checksum - berkas cadangan mungkin telah dimodifikasi atau rusak.\",\n\t\"plugin not found\": \"Plugin tidak ditemukan di registri.\",\n\t\"paid plugin skipped\": \"Plugin berbayar - pembelian tidak ditemukan\",\n\t\"source not found\": \"Berkas sumber tidak lagi ada.\",\n\t\"restored\": \"Dipulihkan\",\n\t\"skipped\": \"Dilewati\",\n\t\"backup not valid object\": \"Berkas cadangan bukan objek yang valid\",\n\t\"backup no data\": \"Berkas cadangan tidak berisi data untuk dipulihkan.\",\n\t\"backup legacy warning\": \"Ini adalah format pencadangan lama (v1). Beberapa fitur mungkin terbatas.\",\n\t\"backup missing metadata\": \"Metadata cadangan hilang - beberapa informasi mungkin tidak tersedia.\",\n\t\"backup checksum mismatch\": \"Ketidakcocokan checksum - berkas cadangan mungkin telah dimodifikasi atau rusak. Lanjutkan dengan hati-hati.\",\n\t\"backup checksum verify failed\": \"Checksum tidak dapat diverifikasi.\",\n\t\"backup invalid settings\": \"Format pengaturan tidak valid\",\n\t\"backup invalid keybindings\": \"Format keyBindings tidak valid\",\n\t\"backup invalid plugins\": \"Format installedPlugins tidak valid\",\n\t\"issues found\": \"Masalah ditemukan\",\n\t\"error details\": \"Detail kesalahan\",\n\t\"active tools\": \"Alat-alat yang aktif\",\n\t\"available tools\": \"Alat-alat yang tersedia\",\n\t\"recent\": \"Berkas terbaru\",\n\t\"command palette\": \"Buka Palet Perintah\",\n\t\"change theme\": \"Ganti tema\",\n\t\"documentation\": \"Dokumentasi\",\n\t\"open in terminal\": \"Buka di Terminal\",\n\t\"developer mode\": \"Mode Pengembang\",\n\t\"info-developermode\": \"Aktifkan alat pengembang (Eruda) untuk men-debug plugin dan memeriksa status aplikasi. Inspektor akan diinisialisasi saat aplikasi dimulai.\",\n\t\"developer mode enabled\": \"Mode pengembang diaktifkan. Gunakan palet perintah untuk mengaktifkan/menonaktifkan inspektor (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Mode pengembang dinonaktifkan\",\n\t\"copy relative path\": \"Salin jalur relatif\",\n\t\"shortcut request sent\": \"Permintaan pintasan dibuka. Tekan Tambah untuk menyelesaikan.\",\n\t\"add to home screen\": \"Tambah ke layar beranda\",\n\t\"pin shortcuts not supported\": \"Pintasan layar beranda tidak didukung di perangkat ini.\",\n\t\"save file before home shortcut\": \"Simpan berkas sebelum menambahkannya ke layar beranda.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal tidak terpasang. Mohon pasang Terminal terlebih dahulu untuk menggunakan server LSP.\",\n\t\"shift click selection\": \"Pemilihan Shift + tekan/klik\",\n\t\"earn ad-free time\": \"Ambil waktu bebas iklan\",\n\t\"indent guides\": \"Panduan indentasi\",\n\t\"language servers\": \"Server bahasa\",\n\t\"lint gutter\": \"Tampilkan gutter lint\",\n\t\"rainbow brackets\": \"Kurung pelangi\",\n\t\"lsp-add-custom-server\": \"Tambah server kostum\",\n\t\"lsp-binary-args\": \"Argumen binari (senarai JSON)\",\n\t\"lsp-binary-command\": \"Perintah binari\",\n\t\"lsp-binary-path-optional\": \"Jalur binari (opsional)\",\n\t\"lsp-check-command-optional\": \"Perintah periksa (opsional timpa)\",\n\t\"lsp-checking-installation-status\": \"Memeriksa status pemasangan...\",\n\t\"lsp-configured\": \"Dikonfigurasi\",\n\t\"lsp-custom-server-added\": \"Server kostum ditambahkan\",\n\t\"lsp-default\": \"Bawaan\",\n\t\"lsp-details-line\": \"Detail: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit opsi inisiasi\",\n\t\"lsp-empty\": \"Kosong\",\n\t\"lsp-enabled\": \"Diaktifkan\",\n\t\"lsp-error-add-server-failed\": \"Gagal menambahkan server\",\n\t\"lsp-error-args-must-be-array\": \"Argumen harus berupa sebuah senarai JSON\",\n\t\"lsp-error-binary-command-required\": \"Perintah binari diperlukan\",\n\t\"lsp-error-language-id-required\": \"Setidaknya satu ID bahasa diperlukan\",\n\t\"lsp-error-package-required\": \"Setidaknya satu paket diperlukan\",\n\t\"lsp-error-server-id-required\": \"ID server diperlukan\",\n\t\"lsp-feature-completion\": \"Penyelesaian kode\",\n\t\"lsp-feature-completion-info\": \"Aktifkan saran penyelesaian otomatis dari server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostik\",\n\t\"lsp-feature-diagnostics-info\": \"Tampilkan kesalahan dan peringatan dari server bahasa.\",\n\t\"lsp-feature-formatting\": \"Pemformatan\",\n\t\"lsp-feature-formatting-info\": \"Aktifkan pemformatan kode dari server.\",\n\t\"lsp-feature-hover\": \"Informasi saat kursor diarahkan\",\n\t\"lsp-feature-hover-info\": \"Tampilkan informasi tipe dan dokumentasi saat kursor diarahkan ke atasnya.\",\n\t\"lsp-feature-inlay-hints\": \"Petunjuk sisipan\",\n\t\"lsp-feature-inlay-hints-info\": \"Tampilkan petunjuk pengetikan sebaris di editor.\",\n\t\"lsp-feature-signature\": \"Bantuan parameter fungsi\",\n\t\"lsp-feature-signature-info\": \"Tampilkan petunjuk parameter fungsi saat mengetik.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Opsi inisiasi\",\n\t\"lsp-initialization-options-json\": \"Opsi inisiasi (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Opsi inisiasi diperbarui\",\n\t\"lsp-install-command\": \"Perintah pasang\",\n\t\"lsp-install-command-unavailable\": \"Perintah pasang tidak tersedia\",\n\t\"lsp-install-info-check-failed\": \"Acode tidak dapat menverifikasi status pemasangan.\",\n\t\"lsp-install-info-missing\": \"Server bahasa tidak terpasang di dalam lingkungan terminal.\",\n\t\"lsp-install-info-ready\": \"Server bahasa terpasang dan siap.\",\n\t\"lsp-install-info-unknown\": \"Status pemasangan tidak dapat diperiksa secara otomatis.\",\n\t\"lsp-install-info-version-available\": \"Versi {version} tersedia.\",\n\t\"lsp-install-method-apk\": \"Paket APK\",\n\t\"lsp-install-method-cargo\": \"Crate Cargo\",\n\t\"lsp-install-method-manual\": \"Binari manual\",\n\t\"lsp-install-method-npm\": \"Paket npm\",\n\t\"lsp-install-method-pip\": \"Paket pip\",\n\t\"lsp-install-method-shell\": \"Shell kostum\",\n\t\"lsp-install-method-title\": \"Metode pasang\",\n\t\"lsp-install-repair\": \"Pasang / perbaiki\",\n\t\"lsp-installation-status\": \"Status pemasangan\",\n\t\"lsp-installed\": \"Terpasang\",\n\t\"lsp-invalid-timeout\": \"Nilai timeout tidak valid\",\n\t\"lsp-language-ids\": \"ID bahasa (dipisah koma)\",\n\t\"lsp-packages-prompt\": \"{method} paket (dipisah koma)\",\n\t\"lsp-remove-installed-files\": \"Hapus berkas terpasang dari {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server dinonaktifkan\",\n\t\"lsp-server-enabled-toast\": \"Server diaktifkan\",\n\t\"lsp-server-id\": \"ID server\",\n\t\"lsp-server-label\": \"Label server\",\n\t\"lsp-server-not-found\": \"Server tidak ditemukan\",\n\t\"lsp-server-uninstalled\": \"Server dicopot pemasangannya\",\n\t\"lsp-startup-timeout\": \"Batas waktu mulai\",\n\t\"lsp-startup-timeout-ms\": \"Batas waktu mulai (milidetik)\",\n\t\"lsp-startup-timeout-set\": \"Batas waktu mulai diatur ke {timeout} ms\",\n\t\"lsp-state-disabled\": \"dinonaktifkan\",\n\t\"lsp-state-enabled\": \"diaktifkan\",\n\t\"lsp-status-check-failed\": \"Pemeriksaan gagal\",\n\t\"lsp-status-installed\": \"Terpasang\",\n\t\"lsp-status-installed-version\": \"Terpasang ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Tidak terpasang\",\n\t\"lsp-status-unknown\": \"Tidak diketahui\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Perintah copot pemasangan tidak tersedia\",\n\t\"lsp-uninstall-server\": \"Copot pemasangan server\",\n\t\"lsp-update-command-optional\": \"Perintah perbarui (opsional)\",\n\t\"lsp-update-command-unavailable\": \"Perintah perbarui tidak tersedia\",\n\t\"lsp-update-server\": \"Perbarui server\",\n\t\"lsp-version-line\": \"Versi: {version}\",\n\t\"lsp-view-initialization-options\": \"Lihat opsi inisiasi\",\n\t\"settings-category-about-acode\": \"Tentang Acode\",\n\t\"settings-category-advanced\": \"Lanjutan\",\n\t\"settings-category-assistance\": \"Asisten\",\n\t\"settings-category-core\": \"Pengaturan inti\",\n\t\"settings-category-cursor\": \"Kursor\",\n\t\"settings-category-cursor-selection\": \"Kursor & pemilihan\",\n\t\"settings-category-custom-servers\": \"Kostum server\",\n\t\"settings-category-customization-tools\": \"Kostumisasi & alat\",\n\t\"settings-category-display\": \"Tampilan\",\n\t\"settings-category-editing\": \"Mengedit\",\n\t\"settings-category-features\": \"Fitur\",\n\t\"settings-category-files-sessions\": \"Berkas & sesi\",\n\t\"settings-category-fonts\": \"Huruf\",\n\t\"settings-category-general\": \"Umum\",\n\t\"settings-category-guides-indicators\": \"Panduan & indikator\",\n\t\"settings-category-installation\": \"Pemasangan\",\n\t\"settings-category-interface\": \"Antarmuka\",\n\t\"settings-category-maintenance\": \"Pemeliharaan\",\n\t\"settings-category-permissions\": \"Izin\",\n\t\"settings-category-preview\": \"Pratinjau\",\n\t\"settings-category-scrolling\": \"Pengguliran\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Server\",\n\t\"settings-category-session\": \"Sesi\",\n\t\"settings-category-support-acode\": \"Dukung Acode\",\n\t\"settings-category-text-layout\": \"Teks & tampilan\",\n\t\"settings-info-app-animation\": \"Kendalikan animasi transisi di seluruh aplikasi.\",\n\t\"settings-info-app-check-files\": \"Segarkan ulang editor ketika berkas berubah di luar Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Hapus status instalasi yang tersimpan yang digunakan oleh alur orientasi dan pengaturan.\",\n\t\"settings-info-app-confirm-on-exit\": \"Tanyakan sebelum menutup aplikasi.\",\n\t\"settings-info-app-console\": \"Pilih mana integrasi konsol debug yang Acode gunakan.\",\n\t\"settings-info-app-default-file-encoding\": \"Enkoding bawaan ketika membuka atau membuat berkas.\",\n\t\"settings-info-app-exclude-folders\": \"Lewati folder dan pola ketika mencari atau memindai.\",\n\t\"settings-info-app-floating-button\": \"Tampilkan tombol aksi cepat yang melayang.\",\n\t\"settings-info-app-font-manager\": \"Pasang, kelola, atau menghapus huruf aplikasi.\",\n\t\"settings-info-app-fullscreen\": \"Sembunyikan bar status sistem ketika menggunakan Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit berkas binding kunci atau atur ulang pintasan.\",\n\t\"settings-info-app-keyboard-mode\": \"Pilih bagaimana papan ketik perangkat lunak berperilaku ketika mengedit.\",\n\t\"settings-info-app-language\": \"Pilih bahasa aplikasi dan label yang diterjemahkan.\",\n\t\"settings-info-app-open-file-list-position\": \"Pilih dimana daftar berkas aktif muncul.\",\n\t\"settings-info-app-quick-tools-settings\": \"Urutkan ulang dan kostumisaikan pintasan alat cepat.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Pilih bagaimana alat cepat terbuka saat ditekan atau disentuh.\",\n\t\"settings-info-app-remember-files\": \"Buka ulang berkas yang dibuka terakhir kali.\",\n\t\"settings-info-app-remember-folders\": \"Buka ulang folder dari sesi sebelumnya.\",\n\t\"settings-info-app-retry-remote-fs\": \"Ulangi lagi operasi berkas jarak jauh setelah transfer gagal.\",\n\t\"settings-info-app-side-buttons\": \"Tampilkan tombol aksi ekstra di samping editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Tampilkan entri sponsor di bar samping.\",\n\t\"settings-info-app-touch-move-threshold\": \"Pergerakan minimum sebelum penyeretan sentuh terdeteksi.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Aktifkan umpan balik getar untuk tekan dan kendali.\",\n\t\"settings-info-editor-autosave\": \"Simpan perubahan secara otomatis setelah jeda.\",\n\t\"settings-info-editor-color-preview\": \"Pratinjau nilai warna langsung di dalam editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Redupkan penanda lipatan hingga dibutuhkan.\",\n\t\"settings-info-editor-font-family\": \"Pilih tipe huruf yang digunakan di dalam editor.\",\n\t\"settings-info-editor-font-size\": \"Atur ukuran teks di editor.\",\n\t\"settings-info-editor-format-on-save\": \"Jalankan pemformat ketika sebuah berkas tersimpan.\",\n\t\"settings-info-editor-hard-wrap\": \"Masukkan pemotong baris asli daripada hanya membungkusnya secara visual.\",\n\t\"settings-info-editor-indent-guides\": \"Tampilkan baris panduan indentasi.\",\n\t\"settings-info-editor-line-height\": \"Atur jarak vertikal diantara baris.\",\n\t\"settings-info-editor-line-numbers\": \"Tampilkan nomor baris di dalam gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Tampilkan alat diagnostik dan penanda lint di gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Tampilkan saran ketika anda mengetik.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Warna menyamakan kurung dari kedalaman bersarang.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Tampilkan jarak dari baris saat ini.\",\n\t\"settings-info-editor-rtl-text\": \"Ganti perilaku kanan-ke-kiri setiap baris.\",\n\t\"settings-info-editor-scroll-settings\": \"Atur ukuran bar gulir, kecepatan, dan perilaku gestur.\",\n\t\"settings-info-editor-shift-click-selection\": \"Perluas pemilihan dengan Shift + tekan atau klik.\",\n\t\"settings-info-editor-show-spaces\": \"Tampilkan penanda spasi yang terlihat.\",\n\t\"settings-info-editor-soft-tab\": \"Masukkan spasi daripada karakter tab.\",\n\t\"settings-info-editor-tab-size\": \"Atur seberapa banyak spasi setiap langkah tab digunakan.\",\n\t\"settings-info-editor-teardrop-size\": \"Atur ukuran penangan kursor untuk pengeditan sentuh.\",\n\t\"settings-info-editor-text-wrap\": \"Bungkus baris panjang di dalam editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Daftarkan server bahasa kostum dengan perintah pasang, perbarui, dan luncur.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit opsi inisiasi sebagai JSON.\",\n\t\"settings-info-lsp-install-server\": \"Pasang atau perbaiki server bahasa ini.\",\n\t\"settings-info-lsp-server-enabled\": \"Aktifkan atau nonaktifkan server bahasa ini.\",\n\t\"settings-info-lsp-startup-timeout\": \"Atur seberapa lama Acode menunggu untuk server memulai.\",\n\t\"settings-info-lsp-uninstall-server\": \"Hapus paket terpasang atau binari dari server ini.\",\n\t\"settings-info-lsp-update-server\": \"Perbarui server bahasa ini jika alur pembaruan tersedia.\",\n\t\"settings-info-lsp-view-init-options\": \"Lihat opsi inisiasi efektif sebagai JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Tonton iklan untuk membuka akses bebas iklan sementara.\",\n\t\"settings-info-main-app-settings\": \"Bahasa, perilaku aplikasi, dan alat akses cepat.\",\n\t\"settings-info-main-backup-restore\": \"Ekspor pengaturan ke pencadangan atau atur ulang mereka nanti.\",\n\t\"settings-info-main-changelog\": \"Lihat perbaruan saat ini dan catatan rilis.\",\n\t\"settings-info-main-edit-settings\": \"Edit berkas mentah settings.json secara langsung.\",\n\t\"settings-info-main-editor-settings\": \"Huruf, tab, saran, dan tampilan editor.\",\n\t\"settings-info-main-formatter\": \"Pilih pemformat untuk setiap bahasa yang didukung.\",\n\t\"settings-info-main-lsp-settings\": \"Konfigurasi server bahasa dan kecerdasan editor.\",\n\t\"settings-info-main-plugins\": \"Kelola plugin terpasang dan aksi yang tersedia.\",\n\t\"settings-info-main-preview-settings\": \"Mode pratinjau, port server, dan perilaku peramban.\",\n\t\"settings-info-main-rateapp\": \"Nilai Acode di Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Buka akses permanen bebas iklan.\",\n\t\"settings-info-main-reset\": \"Atur ulang Acode ke konfigurasi bawaannya.\",\n\t\"settings-info-main-sponsors\": \"Mendukung pengembangan Acode yang berkelanjutan.\",\n\t\"settings-info-main-terminal-settings\": \"Tema Terminal, huruf, kursor, dan perilaku sesi.\",\n\t\"settings-info-main-theme\": \"Tema aplikasi, kontras, dan warna kostum.\",\n\t\"settings-info-preview-disable-cache\": \"Selalu muat ulang konten di dalam peramban-dalam-aplikasi.\",\n\t\"settings-info-preview-host\": \"Nama host yang digunakan ketika membuka URL pratinjau.\",\n\t\"settings-info-preview-mode\": \"Pilih dimana pratinjau dibuka ketika anda meluncurkannya.\",\n\t\"settings-info-preview-preview-port\": \"Port yang digunakan oleh server pratinjau langsung.\",\n\t\"settings-info-preview-server-port\": \"Port yang digunakan oleh server internal aplikasi.\",\n\t\"settings-info-preview-show-console-toggler\": \"Tampilkan tombol konsol di dalam pratinjau.\",\n\t\"settings-info-preview-use-current-file\": \"Utamakan berkas saat ini ketika memulai pratinjau.\",\n\t\"settings-info-terminal-convert-eol\": \"Konversikan akhiran baris saat menempelkan atau menampilkan output terminal.\",\n\t\"settings-note-formatter-settings\": \"Tetapkan pemformat untuk setiap bahasa. Pasang plugin pemformat untuk membuka lebih banyak opsi.\",\n\t\"settings-note-lsp-settings\": \"Server bahasa menambahkan fitur pelengkapan otomatis, diagnostik, detail saat kursor diarahkan ke teks, dan banyak lagi. Anda dapat memasang, memperbarui, atau menentukan server khusus di sini. Penginstal terkelola berjalan di dalam lingkungan terminal/proot.\",\n\t\"search result label singular\": \"hasil\",\n\t\"search result label plural\": \"hasil\",\n\t\"pin tab\": \"Sematkan tab\",\n\t\"unpin tab\": \"Lepas sematan tab\",\n\t\"pinned tab\": \"Tab yang tersematkan\",\n\t\"unpin tab before closing\": \"Lepas sematan tab sebelum menutupnya.\",\n\t\"app font\": \"Huruf Aplikasi\",\n\t\"settings-info-app-font-family\": \"Pilih huruf yang digunakan di seluruh antarmuka aplikasi.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (luncurkan perintah binari)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (hubungkan ke sebuah URL ws/wss)\",\n\t\"lsp-websocket-url\": \"URL WebSocket\",\n\t\"lsp-websocket-server-managed-externally\": \"Server ini dikelola secara eksternal lewat WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"URL WebSocket harus berawalan ws:// atau wss://\",\n\t\"lsp-error-websocket-url-required\": \"URL WebSocket diperlukan\",\n\t\"lsp-remove-custom-server\": \"Hapus server kostum\",\n\t\"lsp-remove-custom-server-confirm\": \"Hapus server bahasa kostum {server}?\",\n\t\"lsp-custom-server-removed\": \"Server kostum dihapus\",\n\t\"settings-info-lsp-remove-custom-server\": \"Hapus server bahasa kostum ini dari Acode.\"\n}\n"
  },
  {
    "path": "src/lang/ir-fa.json",
    "content": "{\n\t\"lang\": \"فارسی - ترجمه صفا صفری\",\n\t\"about\": \"درباره ما\",\n\t\"active files\": \"فایلهای فعال\",\n\t\"alert\": \"هشدار\",\n\t\"app theme\": \"تم برنامه\",\n\t\"autocorrect\": \"فعالسازی اصلاح خودکار؟\",\n\t\"autosave\": \"ذخیره خودکار\",\n\t\"cancel\": \"انصراف\",\n\t\"change language\": \"انتخاب زبان\",\n\t\"choose color\": \"انتخاب رنگ\",\n\t\"clear\": \"واضح\",\n\t\"close app\": \"خروج از برنامه؟\",\n\t\"commit message\": \"تایید پیام\",\n\t\"console\": \"خط فرمان\",\n\t\"conflict error\": \"Conflict! Please wait before another commit.\",\n\t\"copy\": \"کپی\",\n\t\"create folder error\": \"متأسفم ، قادر به ایجاد پوشه جدید نیستم\",\n\t\"cut\": \"برش\",\n\t\"delete\": \"حذف\",\n\t\"dependencies\": \"Dependencies\",\n\t\"delay\": \"زمان در واحد میلی ثانیه\",\n\t\"editor settings\": \"تنظیمات ویرایشگر\",\n\t\"editor theme\": \"تم ویرایشگر\",\n\t\"enter file name\": \"نام فایل را وارد کنید\",\n\t\"enter folder name\": \"نام پوشه را وارد کنید\",\n\t\"empty folder message\": \"پوشه خالی\",\n\t\"enter line number\": \"شماره خط را وارد کنید\",\n\t\"error\": \"خطا\",\n\t\"failed\": \"ناموفق\",\n\t\"file already exists\": \"فایل در حال حاضر موجود میباشد\",\n\t\"file already exists force\": \"فایل در حال حاضر موجود میباشد ، رونویسی کنم؟\",\n\t\"file changed\": \" تغییر یافت دوباره بارگزاری کنم؟\",\n\t\"file deleted\": \"فایل پاک شده\",\n\t\"file is not supported\": \"فایل پشتیبانی نمیشود\",\n\t\"file not supported\": \"این نوع فایل پشتیبانی نمیشود.\",\n\t\"file too large\": \"{size} فایل برای نمایش خیلی بزرگ است  حداکثر اندازه فایل مجاز\",\n\t\"file renamed\": \"نام فایل تغییر کرد\",\n\t\"file saved\": \"فایل ذخیره شد\",\n\t\"folder added\": \"فولدر ایجاد شد\",\n\t\"folder already added\": \"پوشه قبلاً اضافه شده است\",\n\t\"font size\": \"اندازه متن\",\n\t\"goto\": \"برو به خط\",\n\t\"icons definition\": \"تعریف آیکن ها\",\n\t\"info\": \"اطلاعات\",\n\t\"invalid value\": \"مقدار نامعتبر است\",\n\t\"language changed\": \"زبان برنامه با موفقیت تغییر یافت\",\n\t\"linting\": \"خطای علامتی را بررسی کنم\",\n\t\"logout\": \"خروج\",\n\t\"loading\": \"بارگذاری\",\n\t\"my profile\": \"پروفایل من\",\n\t\"new file\": \"فایل جدید\",\n\t\"new folder\": \"پوشه جدید\",\n\t\"no\": \"خیر\",\n\t\"no editor message\": \"فایل را انتخاب کنید و یا فایل و پوشه جدید را در فهرست ایجاد کنید\",\n\t\"not set\": \"تنظیم نشده\",\n\t\"unsaved files close app\": \"در اینجا فایل ذخیره نشده وجود دارد ، از برنامه خارج میشوید؟\",\n\t\"notice\": \"نکته\",\n\t\"open file\": \"باز کردن فایل\",\n\t\"open files and folders\": \"باز کردن فایل و پوشه\",\n\t\"open folder\": \"بازکردن پوشه\",\n\t\"open recent\": \"اخیراً را باز کنید\",\n\t\"ok\": \"تایید\",\n\t\"overwrite\": \"رونویسی\",\n\t\"paste\": \"چسباندن\",\n\t\"preview mode\": \"حالت پیش نمایش\",\n\t\"read only file\": \"نمیتوانم فایل های فقط خواندنی(read only) را ویرایش کنم. گزینه (ذخیره به عنوان) را امتحان کنید.\",\n\t\"redo defination\": \"پسرو\",\n\t\"reload\": \"بارگذاری مجدد\",\n\t\"rename\": \"تغییرنام\",\n\t\"replace\": \"جایگذاری\",\n\t\"required\": \"این فیلد لازم است\",\n\t\"run your web app\": \"اجرای برنامه وب\",\n\t\"save\": \"ذخیره\",\n\t\"saving\": \"در حال ذخیره\",\n\t\"save as\": \"ذخیره به عنوان\",\n\t\"save file to run\": \"لطفا این فایل را ذخیره کنید تا در مرورگر اجرا شود\",\n\t\"search\": \"جست و جو\",\n\t\"see logs and errors\": \"دیدن لاگ و خطا ها\",\n\t\"select folder\": \"انتخاب پوشه\",\n\t\"settings\": \"تنظیمات\",\n\t\"settings saved\": \"تنظیمات ذخیره شد\",\n\t\"show line numbers\": \"نمایش شماره خطوط\",\n\t\"show hidden files\": \"نمایش فایلهای مخفی\",\n\t\"show spaces\": \"نمایش فضاها\",\n\t\"soft tab\": \"فاصله با دکمه 'tab'\",\n\t\"sort by name\": \"مرتب سازی براساس نام\",\n\t\"success\": \"موفق\",\n\t\"tab size\": \"اندازه فاصله با دکمه tab\",\n\t\"text wrap\": \"بسته بندی متن\",\n\t\"theme\": \"تم\",\n\t\"unable to delete file\": \"نمیتوانم فایل را حذف کنم\",\n\t\"unable to open file\": \"متأسفم ، نمیتوانم فایل را باز کنم\",\n\t\"unable to open folder\": \"متأسفم ، نمیتوانم پوشه را باز کنم\",\n\t\"unable to save file\": \"متأسفم ، نمیتوانم فایل را ذخیره کنم\",\n\t\"unable to rename\": \"متأسفم ، نمیتوانم نام فایل را تغیبر بدم\",\n\t\"unsaved file\": \"این فایل ذخیره نشده ، خروج حتمی؟\",\n\t\"warning\": \"هشدار\",\n\t\"use emmet\": \"استفاده از emmet\",\n\t\"use quick tools\": \"استفاده از ابزار سریع\",\n\t\"yes\": \"بله\",\n\t\"encoding\": \"رمزگذاری متن\",\n\t\"syntax highlighting\": \"برجسته نحو\",\n\t\"read only\": \"فقط خواندنی\",\n\t\"select all\": \"انتخاب همه\",\n\t\"select branch\": \"انتخاب شاخه\",\n\t\"create new branch\": \"شعبه جدید ایجاد کنید\",\n\t\"use branch\": \"از شاخه استفاده کنید\",\n\t\"new branch\": \"شعبه جدید\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"اتصالات کلیدی\",\n\t\"edit\": \"ویرایش\",\n\t\"reset\": \"تنظیم مجدد\",\n\t\"color\": \"رنگ\",\n\t\"select word\": \"کلمه را انتخاب کنید\",\n\t\"quick tools\": \"ابزار سریع\",\n\t\"select\": \"سره\",\n\t\"editor font\": \"Editor font\",\n\t\"new project\": \"پروژه جدید\",\n\t\"format\": \"قالب\",\n\t\"project name\": \"نام پروژه\",\n\t\"unsupported device\": \"دستگاه شما از موضوع پشتیبانی نمی کند.\",\n\t\"vibrate on tap\": \"Vibrate on tap\",\n\t\"copy command is not supported by ftp.\": \"Copy command is not supported by FTP.\",\n\t\"support title\": \"Support Acode\",\n\t\"fullscreen\": \"fullscreen\",\n\t\"animation\": \"animation\",\n\t\"backup\": \"backup\",\n\t\"restore\": \"restore\",\n\t\"backup successful\": \"Backup successful\",\n\t\"invalid backup file\": \"Invalid backup file\",\n\t\"add path\": \"Add path\",\n\t\"live autocompletion\": \"Live autocompletion\",\n\t\"file properties\": \"File properties\",\n\t\"path\": \"Path\",\n\t\"type\": \"Type\",\n\t\"word count\": \"Word count\",\n\t\"line count\": \"Line count\",\n\t\"last modified\": \"Last modified\",\n\t\"size\": \"Size\",\n\t\"share\": \"Share\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"login\",\n\t\"scrollbar size\": \"Scrollbar size\",\n\t\"cursor controller size\": \"Cursor controller size\",\n\t\"none\": \"none\",\n\t\"small\": \"small\",\n\t\"large\": \"large\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"Confirm on exit\",\n\t\"show console\": \"Show console\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"Turn off power saving mode to preview in external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"light\",\n\t\"dark\": \"dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"حامی مالی\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/it-it.json",
    "content": "{\n\t\"lang\": \"Italiano\",\n\t\"about\": \"Chi siamo\",\n\t\"active files\": \"File aperti\",\n\t\"alert\": \"Avviso\",\n\t\"app theme\": \"Tema dell'app\",\n\t\"autocorrect\": \"Vuoi attivate l'autocorrezione?\",\n\t\"autosave\": \"Autosalvataggio\",\n\t\"cancel\": \"indietro\",\n\t\"change language\": \"Cambia la lingua\",\n\t\"choose color\": \"Scegli il colore\",\n\t\"clear\": \"cancella tutto\",\n\t\"close app\": \"Vuoi chiudere l'applicazione?\",\n\t\"commit message\": \"Commetti il messaggio\",\n\t\"console\": \"Console\",\n\t\"conflict error\": \"Errore di conflitto! Per favore aspetta prima di commettere di nuovo.\",\n\t\"copy\": \"copia\",\n\t\"create folder error\": \"Ci spiace, non è stato possibile creare una nuova cartella\",\n\t\"cut\": \"taglia\",\n\t\"delete\": \"Elimina\",\n\t\"dependencies\": \"Dipendenze\",\n\t\"delay\": \"Tempo in millisecondi\",\n\t\"editor settings\": \"Impostazioni dell'editor\",\n\t\"editor theme\": \"Tema dell'editor\",\n\t\"enter file name\": \"Inserisci il nome del file\",\n\t\"enter folder name\": \"Inserisci il nome della cartella\",\n\t\"empty folder message\": \"La cartella è vuota\",\n\t\"enter line number\": \"Inserisci il numero della linea\",\n\t\"error\": \"errore\",\n\t\"failed\": \"fallito\",\n\t\"file already exists\": \"Il file esiste già\",\n\t\"file already exists force\": \"Il file esiste già. Vuoi sovrascriverlo?\",\n\t\"file changed\": \" è stato modificato, vuoi aprirlo?\",\n\t\"file deleted\": \"file eliminato\",\n\t\"file is not supported\": \"questo file non è supportato\",\n\t\"file not supported\": \"Questo tipo di file non è supportato.\",\n\t\"file too large\": \"Il file è troppo grande per caricarlo. La grandezza massima è {size}\",\n\t\"file renamed\": \"il file è stato rinominato\",\n\t\"file saved\": \"il file è stato salvato\",\n\t\"folder added\": \"la cartella è stata aggiunta\",\n\t\"folder already added\": \"la cartella è già stata aggiunta\",\n\t\"font size\": \"Dimensioni dei caratteri\",\n\t\"goto\": \"Vai alla linea\",\n\t\"icons definition\": \"Definizione delle icone\",\n\t\"info\": \"informazioni\",\n\t\"invalid value\": \"Valore invalido\",\n\t\"language changed\": \"la lingua è stata cambiata con successo\",\n\t\"linting\": \"Controlla per degli errore di sintassi\",\n\t\"logout\": \"Disconnetti\",\n\t\"loading\": \"Caricamento\",\n\t\"my profile\": \"Il mio profilp\",\n\t\"new file\": \"Nuovo file\",\n\t\"new folder\": \"Nuova cartella\",\n\t\"no\": \"No\",\n\t\"no editor message\": \"Apri o crea un nuovo file dal menu\",\n\t\"not set\": \"Non settato\",\n\t\"unsaved files close app\": \"Ci sono dei file non salvati. Vuoi chiudere l'applicazione?\",\n\t\"notice\": \"Notizia\",\n\t\"open file\": \"Apri file\",\n\t\"open files and folders\": \"Apri file e cartelle\",\n\t\"open folder\": \"Apri la cartella\",\n\t\"open recent\": \"Apri file recenti\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"sovrascrovi\",\n\t\"paste\": \"incolla\",\n\t\"preview mode\": \"modalità anteprima\",\n\t\"read only file\": \"Impossibile salvare un file solo lettura. Per favore prova salva come\",\n\t\"reload\": \"ricarica\",\n\t\"rename\": \"rinomina\",\n\t\"replace\": \"rimpiazza\",\n\t\"required\": \"questo campo è richiesto\",\n\t\"run your web app\": \"Avvia la tua app sul web\",\n\t\"save\": \"salva\",\n\t\"saving\": \"salvando\",\n\t\"save as\": \"Salva come\",\n\t\"save file to run\": \"Per favore salva questo file prima di eseguirlo sul web\",\n\t\"search\": \"cerca\",\n\t\"see logs and errors\": \"Guarda i log e gli errori\",\n\t\"select folder\": \"Seleziona la cartella\",\n\t\"settings\": \"impostazioni\",\n\t\"settings saved\": \"impostazioni salvate\",\n\t\"show line numbers\": \"Mostra i numeri delle linee\",\n\t\"show hidden files\": \"Mostra i file nascosti\",\n\t\"show spaces\": \"Mostra gli spazi\",\n\t\"soft tab\": \"Linguetta morbida\",\n\t\"sort by name\": \"Ordina per nome\",\n\t\"success\": \"successo\",\n\t\"tab size\": \"Grandezza della linguetta\",\n\t\"text wrap\": \"A capo automatico\",\n\t\"theme\": \"tema\",\n\t\"unable to delete file\": \"non è stato possibile eliminare il file\",\n\t\"unable to open file\": \"Ci spiace. non è stato possibile aprire il file\",\n\t\"unable to open folder\": \"Ci spiace. non è stato possibile aprire la cartella\",\n\t\"unable to save file\": \"Ci spiace. non è stato possibile salvare il file\",\n\t\"unable to rename\": \"Ci spiace. non è stato possibile rinominare il file\",\n\t\"unsaved file\": \"Questo file non è salvato. Vuoi chiudere comunque?\",\n\t\"warning\": \"avviso\",\n\t\"use emmet\": \"Usa emmet\",\n\t\"use quick tools\": \"Usa gli attrezzo veloci\",\n\t\"yes\": \"si\",\n\t\"encoding\": \"Codifica del testo\",\n\t\"syntax highlighting\": \"Evidenziazione della sintassi\",\n\t\"read only\": \"Sola lettura\",\n\t\"select all\": \"seleziona tutto\",\n\t\"select branch\": \"Seleziona ramo\",\n\t\"create new branch\": \"Crea un nuovo ramo\",\n\t\"use branch\": \"Usa il ramo\",\n\t\"new branch\": \"Nuovo ramo\",\n\t\"branch\": \"ramo\",\n\t\"key bindings\": \"associazione dei tasti\",\n\t\"edit\": \"modifica\",\n\t\"reset\": \"resetta\",\n\t\"color\": \"colore\",\n\t\"select word\": \"Seleziona parole\",\n\t\"quick tools\": \"Strumenti veloci\",\n\t\"select\": \"seleziona\",\n\t\"editor font\": \"Font dell'editor\",\n\t\"new project\": \"Nuovo progetto\",\n\t\"format\": \"formato\",\n\t\"project name\": \"Nome del progetto\",\n\t\"unsupported device\": \"Il tuo dispositivo non supporta questo tema.\",\n\t\"vibrate on tap\": \"Vibra al tocco\",\n\t\"copy command is not supported by ftp.\": \"Il comando copia non è supportato da FTP.\",\n\t\"support title\": \"Supporta Acode\",\n\t\"fullscreen\": \"schermo intero\",\n\t\"animation\": \"animazioni\",\n\t\"backup\": \"backup\",\n\t\"restore\": \"ristabilire\",\n\t\"backup successful\": \"Backup eseguito con successo\",\n\t\"invalid backup file\": \"File di backup invalido\",\n\t\"add path\": \"Aggiungi percorso\",\n\t\"live autocompletion\": \"Autocompletazione dal vivo\",\n\t\"file properties\": \"Proprietà del file\",\n\t\"path\": \"Percorso\",\n\t\"type\": \"Tipo\",\n\t\"word count\": \"Numero delle parole\",\n\t\"line count\": \"Numero dele linee\",\n\t\"last modified\": \"Modificato l'ultima volta\",\n\t\"size\": \"Grandezza\",\n\t\"share\": \"Condividi\",\n\t\"show print margin\": \"Mostra margini di stampa\",\n\t\"login\": \"Accedi\",\n\t\"scrollbar size\": \"Grandezza della barra di scorrimento\",\n\t\"cursor controller size\": \"Dimensione del controller del cursore\",\n\t\"none\": \"niente\",\n\t\"small\": \"piccolo\",\n\t\"large\": \"grande\",\n\t\"floating button\": \"Bottone fluttuante\",\n\t\"confirm on exit\": \"Conferma all'uscita\",\n\t\"show console\": \"Mostra la console\",\n\t\"image\": \"Immagine\",\n\t\"insert file\": \"Inserisci un file\",\n\t\"insert color\": \"Inserisci un colore\",\n\t\"powersave mode warning\": \"Disatriva la modalità risparmio energetico per visualizzare l'anteprima in un browser esterno.\",\n\t\"exit\": \"Esci\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Sei sicuro di voler resettare il tema?\",\n\t\"theme type\": \"Tipo di tema\",\n\t\"light\": \"chiaro\",\n\t\"dark\": \"scuro\",\n\t\"file browser\": \"Browser dei file\",\n\t\"operation not permitted\": \"Azione nnon consentita\",\n\t\"no such file or directory\": \"Nessun file o cartella simile trovato\",\n\t\"input/output error\": \"Errore di input/output\",\n\t\"permission denied\": \"Permesso negato\",\n\t\"bad address\": \"Indirizzo invalido\",\n\t\"file exists\": \"Il file esiste già\",\n\t\"not a directory\": \"Non è una cartella\",\n\t\"is a directory\": \"È una cartella\",\n\t\"invalid argument\": \"Argomento invalido\",\n\t\"too many open files in system\": \"Troppi file aperti nel sistema\",\n\t\"too many open files\": \"Troppi file aperti\",\n\t\"text file busy\": \"File di testo occupato\",\n\t\"no space left on device\": \"La memoria del dispositivo è piena\",\n\t\"read-only file system\": \"File di sistema solo lettura\",\n\t\"file name too long\": \"Il nome del file è troppo lungo\",\n\t\"too many users\": \"Troppi utenti\",\n\t\"connection timed out\": \"La connessione è terminata\",\n\t\"connection refused\": \"Connessione rifiutata\",\n\t\"owner died\": \"Il proprietario è morto\",\n\t\"an error occurred\": \"C'è stato un errore\",\n\t\"add ftp\": \"Aggiungi FTP\",\n\t\"add sftp\": \"Aggiungi SFTP\",\n\t\"save file\": \"Salva il file\",\n\t\"save file as\": \"Salva il file come\",\n\t\"files\": \"Files\",\n\t\"help\": \"Aiuto\",\n\t\"file has been deleted\": \"{file} è stato eliminato!\",\n\t\"feature not available\": \"Questa funzione è disponibile solo nella versione premium dell'app.\",\n\t\"deleted file\": \"File eliminato\",\n\t\"line height\": \"Altezza delle linee\",\n\t\"preview info\": \"Se vuoi eseguire il file corrente clicca e tieni premuto il tasto play.\",\n\t\"manage all files\": \"Da il permesso ad Acode di gestire i file nelle impostazioni a Acode editor per modificare facilmente i file sul tuo dispositivo.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Resetta le connessioni\",\n\t\"check file changes\": \"Controlla cambiamenti nei file\",\n\t\"open in browser\": \"Apri nel browser\",\n\t\"desktop mode\": \"Modalità Desktop\",\n\t\"toggle console\": \"Apri/chiudi la consolr\",\n\t\"new line mode\": \"Modalità nuova linea\",\n\t\"add a storage\": \"Aggiungi una memoria\",\n\t\"rate acode\": \"Recensisci Acode\",\n\t\"support\": \"Supporto\",\n\t\"downloading file\": \"Scaricando {file}\",\n\t\"downloading...\": \"Scaricando...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/ja-jp.json",
    "content": "{\n\t\"lang\": \"日本語 (by wappo56 / fj68)\",\n\t\"about\": \"Acode editor\",\n\t\"active files\": \"アクティブファイル\",\n\t\"alert\": \"警告\",\n\t\"app theme\": \"アプリテーマ\",\n\t\"autocorrect\": \"オートコレクトを有効にする\",\n\t\"autosave\": \"オートセーブ\",\n\t\"cancel\": \"キャンセル\",\n\t\"change language\": \"言語の変更\",\n\t\"choose color\": \"色の選択\",\n\t\"clear\": \"クリア\",\n\t\"close app\": \"アプリを終了しますか？\",\n\t\"commit message\": \"コミットメッセージ\",\n\t\"console\": \"コンソール\",\n\t\"conflict error\": \"競合しました！ 別のコミットまでしばらくお待ちください。\",\n\t\"copy\": \"コピー\",\n\t\"create folder error\": \"新規フォルダの作成に失敗しました。\",\n\t\"cut\": \"切り取り\",\n\t\"delete\": \"削除\",\n\t\"dependencies\": \"依存設定\",\n\t\"delay\": \"ミリ秒単位\",\n\t\"editor settings\": \"エディタ設定\",\n\t\"editor theme\": \"エディタテーマ\",\n\t\"enter file name\": \"ファイル名の入力\",\n\t\"enter folder name\": \"フォルダ名の入力\",\n\t\"empty folder message\": \"フォルダ名が空です\",\n\t\"enter line number\": \"行数の入力\",\n\t\"error\": \"エラー\",\n\t\"failed\": \"失敗\",\n\t\"file already exists\": \"ファイルは既に存在します。\",\n\t\"file already exists force\": \"ファイルは既に存在します。上書きしますか？\",\n\t\"file changed\": \" は変更されました。ファイルを再読み込みしますか？\",\n\t\"file deleted\": \"ファイル削除\",\n\t\"file is not supported\": \"未サポートファイル\",\n\t\"file not supported\": \"このファイルタイプはサポートされていません。\",\n\t\"file too large\": \"ファイルが大き過ぎて処理できません。許可される最大ファイルサイズは {size}\",\n\t\"file renamed\": \"ファイル名変更\",\n\t\"file saved\": \"ファイル保存\",\n\t\"folder added\": \"フォルダ追加\",\n\t\"folder already added\": \"フォルダ追加済み\",\n\t\"font size\": \"フォントのサイズ\",\n\t\"goto\": \"行に移動\",\n\t\"icons definition\": \"アイコン定義\",\n\t\"info\": \"情報\",\n\t\"invalid value\": \"無効な値\",\n\t\"language changed\": \"言語設定を正常に変更しました\",\n\t\"linting\": \"構文エラーのチェック\",\n\t\"logout\": \"ログアウト\",\n\t\"loading\": \"読み込み中\",\n\t\"my profile\": \"マイプロフィール\",\n\t\"new file\": \"新規ファイル\",\n\t\"new folder\": \"新規フォルダ\",\n\t\"no\": \"無効\",\n\t\"no editor message\": \"メニューからファイルとフォルダを開くか新規作成してください\",\n\t\"not set\": \"設定なし\",\n\t\"unsaved files close app\": \"未保存のファイルがあります。アプリケーションを閉じますか？\",\n\t\"notice\": \"お知らせ\",\n\t\"open file\": \"ファイルを開く\",\n\t\"open files and folders\": \"ファイルとフォルダを開く\",\n\t\"open folder\": \"フォルダを開く\",\n\t\"open recent\": \"最近のファイルを開く\",\n\t\"ok\": \"OK\",\n\t\"overwrite\": \"上書き\",\n\t\"paste\": \"貼り付け\",\n\t\"preview mode\": \"プレビューモード\",\n\t\"read only file\": \"読み取り専用ファイルは保存できません。名前を付けて保存してください\",\n\t\"reload\": \"再読み込み\",\n\t\"rename\": \"ファイル名の変更\",\n\t\"replace\": \"置換\",\n\t\"required\": \"この項目は必須です\",\n\t\"run your web app\": \"ウェブアプリを実行\",\n\t\"save\": \"保存\",\n\t\"saving\": \"保存中\",\n\t\"save as\": \"名前を付けて保存\",\n\t\"save file to run\": \"このファイルを保存してブラウザで実行してください\",\n\t\"search\": \"検索\",\n\t\"see logs and errors\": \"ログとエラーの確認\",\n\t\"select folder\": \"フォルダを選択\",\n\t\"settings\": \"設定\",\n\t\"settings saved\": \"設定を保存しました\",\n\t\"show line numbers\": \"行数を表示する\",\n\t\"show hidden files\": \"隠しファイルを表示する\",\n\t\"show spaces\": \"スペースを表示する\",\n\t\"soft tab\": \"ソフトタブ\",\n\t\"sort by name\": \"名前順\",\n\t\"success\": \"成功\",\n\t\"tab size\": \"タブのサイズ\",\n\t\"text wrap\": \"テキストの折り返し\",\n\t\"theme\": \"テーマ\",\n\t\"unable to delete file\": \"ファイルを削除できません\",\n\t\"unable to open file\": \"ファイルを開けません\",\n\t\"unable to open folder\": \"フォルダを開けません\",\n\t\"unable to save file\": \"ファイルを保存できません\",\n\t\"unable to rename\": \"名前を変更できません\",\n\t\"unsaved file\": \"保存されていませんが、閉じますか？\",\n\t\"warning\": \"警告\",\n\t\"use emmet\": \"Emmet使用\",\n\t\"use quick tools\": \"クイックツール使用\",\n\t\"yes\": \"有効\",\n\t\"encoding\": \"テキストエンコード\",\n\t\"syntax highlighting\": \"構文ハイライト\",\n\t\"read only\": \"読み取り専用\",\n\t\"select all\": \"すべて選択\",\n\t\"select branch\": \"ブランチの選択\",\n\t\"create new branch\": \"新規ブランチ作成\",\n\t\"use branch\": \"ブランチを使用\",\n\t\"new branch\": \"新規ブランチ\",\n\t\"branch\": \"ブランチ\",\n\t\"key bindings\": \"キーバインド\",\n\t\"edit\": \"編集\",\n\t\"reset\": \"リセット\",\n\t\"color\": \"カラー\",\n\t\"select word\": \"単語選択\",\n\t\"quick tools\": \"クイックツール\",\n\t\"select\": \"選択\",\n\t\"editor font\": \"フォント\",\n\t\"new project\": \"新規プロジェクト\",\n\t\"format\": \"整形\",\n\t\"project name\": \"プロジェクト名\",\n\t\"unsupported device\": \"お使いのデバイスはテーマをサポートしていません。\",\n\t\"vibrate on tap\": \"タップ時に振動させる\",\n\t\"copy command is not supported by ftp.\": \"FTPではCopyコマンドはサポートされていません。\",\n\t\"support title\": \"Acodeを支援\",\n\t\"fullscreen\": \"フルスクリーン\",\n\t\"animation\": \"アニメーション\",\n\t\"backup\": \"バックアップ\",\n\t\"restore\": \"復元\",\n\t\"backup successful\": \"バックアップ成功\",\n\t\"invalid backup file\": \"バックアップファイルが無効です\",\n\t\"add path\": \"パスを追加\",\n\t\"live autocompletion\": \"自動補完を実行\",\n\t\"file properties\": \"ファイルプロパティ\",\n\t\"path\": \"パス\",\n\t\"type\": \"タイプ\",\n\t\"word count\": \"文字数\",\n\t\"line count\": \"行数\",\n\t\"last modified\": \"最終更新\",\n\t\"size\": \"サイズ\",\n\t\"share\": \"共有\",\n\t\"show print margin\": \"印刷時の余白を表示\",\n\t\"login\": \"ログイン\",\n\t\"scrollbar size\": \"スクロールバーのサイズ\",\n\t\"cursor controller size\": \"カーソルコントローラーのサイズ\",\n\t\"none\": \"なし\",\n\t\"small\": \"小\",\n\t\"large\": \"大\",\n\t\"floating button\": \"フローティングボタン\",\n\t\"confirm on exit\": \"アプリケーション終了時に確認\",\n\t\"show console\": \"コンソールを表示\",\n\t\"image\": \"画像\",\n\t\"insert file\": \"ファイルを挿入\",\n\t\"insert color\": \"色を挿入\",\n\t\"powersave mode warning\": \"外部ブラウザでプレビューするには省電力モードをオフにしてください\",\n\t\"exit\": \"終了\",\n\t\"custom\": \"カスタム\",\n\t\"reset warning\": \"テーマをリセットしてよろしいですか？\",\n\t\"theme type\": \"テーマのタイプ\",\n\t\"light\": \"ライト\",\n\t\"dark\": \"ダーク\",\n\t\"file browser\": \"ファイルブラウザ\",\n\t\"operation not permitted\": \"許可されていない操作です\",\n\t\"no such file or directory\": \"ファイル/ディレクトリが見つかりません\",\n\t\"input/output error\": \"入出力エラー\",\n\t\"permission denied\": \"許可がありません\",\n\t\"bad address\": \"不正なアドレスです\",\n\t\"file exists\": \"ファイルが既に存在しています\",\n\t\"not a directory\": \"ディレクトリではありません\",\n\t\"is a directory\": \"ディレクトリです\",\n\t\"invalid argument\": \"不正な引数です\",\n\t\"too many open files in system\": \"システムで開くファイルが多すぎます\",\n\t\"too many open files\": \"開くファイルが多すぎます\",\n\t\"text file busy\": \"ファイルがビジー状態です\",\n\t\"no space left on device\": \"空き容量が不足しています\",\n\t\"read-only file system\": \"読み取り専用です\",\n\t\"file name too long\": \"ファイル名が長すぎます\",\n\t\"too many users\": \"ユーザーが多すぎます\",\n\t\"connection timed out\": \"接続がタイムアウトしました\",\n\t\"connection refused\": \"接続が拒否されました\",\n\t\"owner died\": \"ファイルを所有しているプロセスが死んでいます\",\n\t\"an error occurred\": \"エラーが発生しました\",\n\t\"add ftp\": \"FTPを追加\",\n\t\"add sftp\": \"SFTPを追加\",\n\t\"save file\": \"保存\",\n\t\"save file as\": \"名前を付けて保存\",\n\t\"files\": \"ファイル一覧\",\n\t\"help\": \"ヘルプ\",\n\t\"file has been deleted\": \"{file}は既に削除されています\",\n\t\"feature not available\": \"この機能は有料版でのみ使用できます\",\n\t\"deleted file\": \"ファイルを削除しました\",\n\t\"line height\": \"行の高さ\",\n\t\"preview info\": \"アクティブファイルを実行するには実行アイコンを長押しして下さい\",\n\t\"manage all files\": \"デバイス内のファイルを簡単に編集できるよう、設定でAcode editorに全てのファイルを管理することを許可してください\",\n\t\"close file\": \"ファイルを閉じる\",\n\t\"reset connections\": \"接続をリセット\",\n\t\"check file changes\": \"変更箇所をチェックする\",\n\t\"open in browser\": \"ブラウザで開く\",\n\t\"desktop mode\": \"デスクトップモード\",\n\t\"toggle console\": \"コンソールを表示/非表示\",\n\t\"new line mode\": \"改行モード\",\n\t\"add a storage\": \"ストレージを追加\",\n\t\"rate acode\": \"Acodeを評価する\",\n\t\"support\": \"支援する\",\n\t\"downloading file\": \"{file}をダウンロード中\",\n\t\"downloading...\": \"ダウンロード中...\",\n\t\"folder name\": \"フォルダ名\",\n\t\"keyboard mode\": \"キーボードモード\",\n\t\"normal\": \"通常\",\n\t\"app settings\": \"アプリ設定\",\n\t\"disable in-app-browser caching\": \"アプリ内ブラウザのキャッシュを無効\",\n\t\"copied to clipboard\": \"クリップボードにコピーしました\",\n\t\"remember opened files\": \"開いたファイルを記憶する\",\n\t\"remember opened folders\": \"開いたフォルダを記憶する\",\n\t\"no suggestions\": \"候補を表示しない\",\n\t\"no suggestions aggressive\": \"候補を積極的に提案しない\",\n\t\"install\": \"インストール\",\n\t\"installing\": \"インストール中...\",\n\t\"plugins\": \"プラグイン\",\n\t\"recently used\": \"最近使用\",\n\t\"update\": \"更新\",\n\t\"uninstall\": \"アンインストール\",\n\t\"download acode pro\": \"Acode Proをダウンロード\",\n\t\"loading plugins\": \"プラグインを読み込み中\",\n\t\"faqs\": \"よくある質問\",\n\t\"feedback\": \"フィードバック\",\n\t\"header\": \"ヘッダー\",\n\t\"sidebar\": \"スライドバー\",\n\t\"inapp\": \"アプリ内\",\n\t\"browser\": \"ブラウザ\",\n\t\"diagonal scrolling\": \"斜めスクロール\",\n\t\"reverse scrolling\": \"逆スクロール\",\n\t\"formatter\": \"整形\",\n\t\"format on save\": \"保存時に整形\",\n\t\"remove ads\": \"広告を除去\",\n\t\"fast\": \"高速\",\n\t\"slow\": \"低速\",\n\t\"scroll settings\": \"スクロール設定\",\n\t\"scroll speed\": \"スクロール速度\",\n\t\"loading...\": \"読み込み中...\",\n\t\"no plugins found\": \"プラグインが見つかりません\",\n\t\"name\": \"名前\",\n\t\"username\": \"ユーザー名\",\n\t\"optional\": \"オプション\",\n\t\"hostname\": \"ホスト名\",\n\t\"password\": \"パスワード\",\n\t\"security type\": \"セキュリティタイプ\",\n\t\"connection mode\": \"接続モード\",\n\t\"port\": \"ポート\",\n\t\"key file\": \"キーファイル\",\n\t\"select key file\": \"キーファイルの選択\",\n\t\"passphrase\": \"パスフレーズ\",\n\t\"connecting...\": \"接続中...\",\n\t\"type filename\": \"ファイル名を入力\",\n\t\"unable to load files\": \"ファイルを読み込めません\",\n\t\"preview port\": \"プレビューポート\",\n\t\"find file\": \"ファイルを検索\",\n\t\"system\": \"システム\",\n\t\"please select a formatter\": \"整形方法を選択してください\",\n\t\"case sensitive\": \"大文字と小文字を区別\",\n\t\"regular expression\": \"正規表現\",\n\t\"whole word\": \"単語単位\",\n\t\"edit with\": \"...で編集\",\n\t\"open with\": \"...で開く\",\n\t\"no app found to handle this file\": \"このファイルを扱えるアプリがありません\",\n\t\"restore default settings\": \"デフォルト設定の復元\",\n\t\"server port\": \"サーバーポート\",\n\t\"preview settings\": \"プレビュー設定\",\n\t\"preview settings note\": \"プレビューポートとサーバーポートが異なる場合、アプリはサーバーを起動せず、代わりにブラウザやアプリ内ブラウザで https://<host>:<preview port> を開きます。これは別の場所でサーバーを動かしている場合に役立ちます。\",\n\t\"backup/restore note\": \"設定、カスタムテーマ、キーバインドのみバックアップされます。FTP/SFTPやGitHubプロファイルはバックアップされません。\",\n\t\"host\": \"ホスト\",\n\t\"retry ftp/sftp when fail\": \"FTP/SFTP接続失敗時に再試行\",\n\t\"more\": \"さらに表示\",\n\t\"thank you :)\": \"ありがとうございます :)\",\n\t\"purchase pending\": \"購入保留中\",\n\t\"cancelled\": \"キャンセルしました\",\n\t\"local\": \"ローカル\",\n\t\"remote\": \"リモート\",\n\t\"show console toggler\": \"コンソール切り替えボタンを表示\",\n\t\"binary file\": \"このファイルにはバイナリデータが含まれていますが、開きますか？\",\n\t\"relative line numbers\": \"相対行番号\",\n\t\"elastic tabstops\": \"タブ位置調整 (Elastic tabstops)\",\n\t\"line based rtl switching\": \"行ベースのRTL切り替え\",\n\t\"hard wrap\": \"ハードラップ\",\n\t\"spellcheck\": \"スペルチェック\",\n\t\"wrap method\": \"折り返しの方法\",\n\t\"use textarea for ime\": \"IME用のテキストエリアを使用\",\n\t\"invalid plugin\": \"無効なプラグイン\",\n\t\"type command\": \"コマンドを入力\",\n\t\"plugin\": \"プラグイン\",\n\t\"quicktools trigger mode\": \"クイックツールのトリガーモード\",\n\t\"print margin\": \"印刷の余白\",\n\t\"touch move threshold\": \"タッチ移動のしきい値\",\n\t\"info-retryremotefsafterfail\": \"FTP/SFTP接続失敗時に再試行を行います。\",\n\t\"info-fullscreen\": \"ホーム画面のタイトルバーを非表示にします。\",\n\t\"info-checkfiles\": \"アプリがバックグラウンドのときにファイルの変更をチェックします。\",\n\t\"info-console\": \"JavaScriptのコンソールを選択します。 [LEGACY] はデフォルトのコンソール、 [ERUDA] はサードパーティのコンソールです。\",\n\t\"info-keyboardmode\": \"テキスト入力用のキーボードモードです。 [候補を表示しない] はサジェストとオートコレクトを非表示にします。 [候補を表示しない] が機能しない場合、[候補を積極的に提案しない] に変更してみてください。\",\n\t\"info-rememberfiles\": \"アプリを閉じるときに開いているファイルを記憶します。\",\n\t\"info-rememberfolders\": \"アプリを閉じるときに開いているフォルダを記憶します。\",\n\t\"info-floatingbutton\": \"クイックツールのフローティングボタンの表示/非表示を切り替えます。\",\n\t\"info-openfilelistpos\": \"アクティブなファイルのリストを表示する位置です。\",\n\t\"info-touchmovethreshold\": \"デバイスのタッチ感度が高すぎる場合、この値を増やすと、意図しないタッチ移動を防ぐことができます。\",\n\t\"info-scroll-settings\": \"この設定では、テキストの折り返しを含むスクロールの設定ができます。\",\n\t\"info-animation\": \"アプリの動作が遅いと感じる場合、アニメーションを無効にしてください。\",\n\t\"info-quicktoolstriggermode\": \"クイックツールのボタンが機能しない場合、この値を変更してみてください。\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"所有済み\",\n\t\"api_error\": \"APIサーバーDown。しばらくしてから実行してください。\",\n\t\"installed\": \"インストール済み\",\n\t\"all\": \"すべて\",\n\t\"medium\": \"中\",\n\t\"refund\": \"払い戻し\",\n\t\"product not available\": \"利用不可の製品\",\n\t\"no-product-info\": \"現在、この製品はお住まいの国でご利用できません。後でもう一度お試しください。\",\n\t\"close\": \"閉じる\",\n\t\"explore\": \"エクスプローラー\",\n\t\"key bindings updated\": \"キーバインディングが更新されました\",\n\t\"search in files\": \"ファイル内を検索\",\n\t\"exclude files\": \"ファイルを除外\",\n\t\"include files\": \"ファイルを含める\",\n\t\"search result\": \"{matches} 個の結果が {files} 個のファイルでみつかりました。\",\n\t\"invalid regex\": \"正規表現が無効です: {message}\",\n\t\"bottom\": \"下\",\n\t\"save all\": \"すべて保存\",\n\t\"close all\": \"すべて閉じる\",\n\t\"unsaved files warning\": \"保存されていないファイルがあります。「OK」をクリックして処理を選択するか「キャンセル」をクリックして戻ります。\",\n\t\"save all warning\": \"すべてのファイルを保存して閉じてもよろしいですか？この操作は取り消せません。\",\n\t\"save all changes warning\": \"すべてのファイルを保存してもよろしいですか？\",\n\t\"close all warning\": \"すべてのファイルを閉じてもよろしいですか? 保存されていない変更は失われ、この操作は取り消せません。\",\n\t\"refresh\": \"更新\",\n\t\"shortcut buttons\": \"ショートカットボタン\",\n\t\"no result\": \"結果なし\",\n\t\"searching...\": \"検索中...\",\n\t\"quicktools:ctrl-key\": \"Ctrl/Command キー\",\n\t\"quicktools:tab-key\": \"Tab キー\",\n\t\"quicktools:shift-key\": \"Shift キー\",\n\t\"quicktools:undo\": \"元に戻す\",\n\t\"quicktools:redo\": \"やり直す\",\n\t\"quicktools:search\": \"ファイル内を検索\",\n\t\"quicktools:save\": \"ファイルを保存\",\n\t\"quicktools:esc-key\": \"Esc キー\",\n\t\"quicktools:curlybracket\": \"{ } を挿入\",\n\t\"quicktools:squarebracket\": \"[ ] を挿入\",\n\t\"quicktools:parentheses\": \"( ) を挿入\",\n\t\"quicktools:anglebracket\": \"< > を挿入\",\n\t\"quicktools:left-arrow-key\": \"左矢印キー\",\n\t\"quicktools:right-arrow-key\": \"右矢印キー\",\n\t\"quicktools:up-arrow-key\": \"上矢印キー\",\n\t\"quicktools:down-arrow-key\": \"下矢印キー\",\n\t\"quicktools:moveline-up\": \"行を上に移動\",\n\t\"quicktools:moveline-down\": \"行を下に移動\",\n\t\"quicktools:copyline-up\": \"行を上にコピー\",\n\t\"quicktools:copyline-down\": \"行を下にコピー\",\n\t\"quicktools:semicolon\": \"セミコロンを挿入\",\n\t\"quicktools:quotation\": \"クオーテーションを挿入\",\n\t\"quicktools:and\": \"アンドを挿入\",\n\t\"quicktools:bar\": \"バーを挿入\",\n\t\"quicktools:equal\": \"イコールを挿入\",\n\t\"quicktools:slash\": \"スラッシュを挿入\",\n\t\"quicktools:exclamation\": \"エクスクラメーションを挿入\",\n\t\"quicktools:alt-key\": \"Alt キー\",\n\t\"quicktools:meta-key\": \"Windows/Meta キー\",\n\t\"info-quicktoolssettings\": \"エディターの下にあるクイックツールコンテナ内のショートカットボタンとキーボードキーをカスタマイズして、コーディングエクスペリエンスを向上させましょう。\",\n\t\"info-excludefolders\": \"パターン /node_modules/ を使用して、node_modules フォルダ内のすべてのファイルを無視します。 これによりファイルがリストされるのを防ぎファイル検索に含まれなくなります。\",\n\t\"missed files\": \"検索開始後に {count} 個のファイルをスキャンしましたが検索には含まれません。\",\n\t\"remove\": \"削除\",\n\t\"quicktools:command-palette\": \"コマンドパレット\",\n\t\"default file encoding\": \"デフォルトのファイルエンコーディング\",\n\t\"remove entry\": \"'{name}' を保存されたパスから削除してもよろしいですか？ 削除してもパス自体は削除されないことに注意してください。\",\n\t\"delete entry\": \"'{name}' を削除しますか？ この操作は取り消せません。続行しますか？\",\n\t\"change encoding\": \"'{file}' を '{encoding}' エンコーディングで再度開きますか？ この操作を実行すると、ファイルに対して行われた保存されていない変更がすべて失われます。続行して再度開きますか？\",\n\t\"reopen file\": \"'{file}' を再度開いてよろしいですか？ 保存されていない変更はすべて失われます。\",\n\t\"plugin min version\": \"'{name}' は Acode - {v-code} 以降でのみ使用できます。こちらをクリックして更新してください。\",\n\t\"color preview\": \"カラープレビュー\",\n\t\"confirm\": \"確認\",\n\t\"list files\": \"{name} 内のすべてのファイルを一覧表示しますか？ファイル数が多すぎるとアプリがクラッシュする可能性があります。\",\n\t\"problems\": \"問題\",\n\t\"show side buttons\": \"サイドボタンを表示\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"検証済み発行者\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"スポンサー\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/ko-kr.json",
    "content": "{\n\t\"lang\": \"한국어\",\n\t\"about\": \"정보\",\n\t\"active files\": \"활성파일\",\n\t\"alert\": \"경고\",\n\t\"app theme\": \"앱 테마\",\n\t\"autocorrect\": \"자동 완성 활성화\",\n\t\"autosave\": \"자동 저장\",\n\t\"cancel\": \"취소\",\n\t\"change language\": \"언어 변경\",\n\t\"choose color\": \"생상 선택\",\n\t\"clear\": \"지우기\",\n\t\"close app\": \"앱을 종료 하시겠습니까?\",\n\t\"commit message\": \"커밋 메세지\",\n\t\"console\": \"콘솔\",\n\t\"conflict error\": \"충돌! 다른 커밋을 기다리세요\",\n\t\"copy\": \"복사\",\n\t\"create folder error\": \"새 폴더를 생성 할 수 없습니다.\",\n\t\"cut\": \"잘라내기\",\n\t\"delete\": \"삭제\",\n\t\"dependencies\": \"Dependencies\",\n\t\"delay\": \"밀리 초 시간 단위\",\n\t\"editor settings\": \"편집기 설정\",\n\t\"editor theme\": \"편집기 테마\",\n\t\"enter file name\": \"파일 이름 입력\",\n\t\"enter folder name\": \"폴더 이름 입력\",\n\t\"empty folder message\": \"빈 폴더\",\n\t\"enter line number\": \"라인 번호\",\n\t\"error\": \"오류\",\n\t\"failed\": \"실패\",\n\t\"file already exists\": \"파일이 이미 존재 합니다.\",\n\t\"file already exists force\": \"파일이 이미 존재 합니다. 덮어쓰시겠습니까?\",\n\t\"file changed\": \"파일이 변경되었습니다. 다시 로드하시겠습니까?\",\n\t\"file deleted\": \"파일 삭제\",\n\t\"file is not supported\": \"지원하지 않는 파일\",\n\t\"file not supported\": \"이 파일 유형은 지원하지 않습니다.\",\n\t\"file too large\": \"파일이 너무 크고 처리할 수 없습니다. 최대 파일 크기:{size}\",\n\t\"file renamed\": \"파일 이름 변경\",\n\t\"file saved\": \"파일 저장 완료\",\n\t\"folder added\": \"폴더 추가\",\n\t\"folder already added\": \"이미 추가된 폴더입니다.\",\n\t\"font size\": \"폰트 크기\",\n\t\"goto\": \"행 이동\",\n\t\"icons definition\": \"아이콘 정의\",\n\t\"info\": \"정보\",\n\t\"invalid value\": \"Invalid value\",\n\t\"language changed\": \"성공적으로 언어가 변경되었습니다.\",\n\t\"linting\": \"Check syntax error\",\n\t\"logout\": \"로그아웃\",\n\t\"loading\": \"Loading\",\n\t\"my profile\": \"내 프로필\",\n\t\"new file\": \"새로운 파일\",\n\t\"new folder\": \"새로운 폴더\",\n\t\"no\": \"아니오\",\n\t\"no editor message\": \"메뉴에서 파일또는 폴더를 열거나 새로 작성\",\n\t\"not set\": \"설정 안함\",\n\t\"unsaved files close app\": \"저장안한 파일이 있습니다. 정말로 앱을 종료 하시겠습니까?\",\n\t\"notice\": \"공지\",\n\t\"open file\": \"파일 욜기\",\n\t\"open files and folders\": \"폴더 또는 파일을 선택해 주세요\",\n\t\"open folder\": \"폴더를 선택해 주세요\",\n\t\"open recent\": \"최근 파일\",\n\t\"ok\": \"확인\",\n\t\"overwrite\": \"덮어쓰기\",\n\t\"paste\": \"붙혀넣기\",\n\t\"preview mode\": \"미리보기\",\n\t\"read only file\": \"읽기 전용 파일은 보존되지 않습니다. 저장하세요\",\n\t\"reload\": \"다시 불러오기\",\n\t\"rename\": \"이름 변경\",\n\t\"replace\": \"변경\",\n\t\"required\": \"필수 항목입니다\",\n\t\"run your web app\": \"웹 앱 실행\",\n\t\"save\": \"저장\",\n\t\"saving\": \"저장중\",\n\t\"save as\": \"저장\",\n\t\"save file to run\": \"파일을 저장하고 브라우저를 실행하세요\",\n\t\"search\": \"검색\",\n\t\"see logs and errors\": \"로그와 오류 확인\",\n\t\"select folder\": \"폴더 선택\",\n\t\"settings\": \"설정\",\n\t\"settings saved\": \"설정이 저장되었습니다\",\n\t\"show line numbers\": \"라인 번호 ㅂ기\",\n\t\"show hidden files\": \"숨긴 파일 보기\",\n\t\"show spaces\": \"띄어쓰기 공간 보기\",\n\t\"soft tab\": \"Soft tab\",\n\t\"sort by name\": \"이름 정령\",\n\t\"success\": \"완료\",\n\t\"tab size\": \"Tab 크기\",\n\t\"text wrap\": \"텍스트 반환\",\n\t\"theme\": \"배경\",\n\t\"unable to delete file\": \"파일을 삭제할 수 없습니다\",\n\t\"unable to open file\": \"파일을 열수가 없습니다\",\n\t\"unable to open folder\": \"폴더를 열수가 없습니다\",\n\t\"unable to save file\": \"파일을 저장할 수 없습니다.\",\n\t\"unable to rename\": \"파일 이름변경을 할 수 없습니다\",\n\t\"unsaved file\": \"이 파일은 저장되있지 않습니다 닫으시겠습니까?\",\n\t\"warning\": \"위험\",\n\t\"use emmet\": \"Use emmet\",\n\t\"use quick tools\": \"퀵 도구툴 사용\",\n\t\"yes\": \"예\",\n\t\"encoding\": \"인코딩\",\n\t\"syntax highlighting\": \"구문 강조 표시\",\n\t\"read only\": \"읽기 전용\",\n\t\"select all\": \"모두 선택\",\n\t\"select branch\": \"branch 선택\",\n\t\"create new branch\": \"branch생성\",\n\t\"use branch\": \"branch 사용\",\n\t\"new branch\": \"새로운 branch\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"단축키\",\n\t\"edit\": \"수정\",\n\t\"reset\": \"초기화\",\n\t\"color\": \"색\",\n\t\"select word\": \"단어 선택\",\n\t\"quick tools\": \"퀵툴\",\n\t\"select\": \"선택\",\n\t\"editor font\": \"폰트\",\n\t\"new project\": \"신규 프로젝트\",\n\t\"format\": \"포맷\",\n\t\"project name\": \"프로젝트 이름\",\n\t\"unsupported device\": \"사용자의 디바이스는 지원하지 않습니다\",\n\t\"vibrate on tap\": \"tap 할 때 진동\",\n\t\"copy command is not supported by ftp.\": \"FTP에서 Copy명령은 지원되지 않습니다\",\n\t\"support title\": \"Acode 후원\",\n\t\"fullscreen\": \"풀 스크린\",\n\t\"animation\": \"애니메이션\",\n\t\"backup\": \"백업\",\n\t\"restore\": \"복원\",\n\t\"backup successful\": \"백업 완료\",\n\t\"invalid backup file\": \"백업파일이 없습니다\",\n\t\"add path\": \"경로추가\",\n\t\"live autocompletion\": \"자동 완성실행\",\n\t\"file properties\": \"파일 속성\",\n\t\"path\": \"경로\",\n\t\"type\": \"타입\",\n\t\"word count\": \"문자수\",\n\t\"line count\": \"행 수\",\n\t\"last modified\": \"최종 갱신\",\n\t\"size\": \"크기\",\n\t\"share\": \"공유\",\n\t\"show print margin\": \"여백표시\",\n\t\"login\": \"로그인\",\n\t\"scrollbar size\": \"스크롤바 크기\",\n\t\"cursor controller size\": \"커서 컨트로러 크기\",\n\t\"none\": \"없음\",\n\t\"small\": \"작은\",\n\t\"large\": \"큰\",\n\t\"floating button\": \"유동적인 버튼\",\n\t\"confirm on exit\": \"엡 종료시 확인\",\n\t\"show console\": \"콘솔 보기\",\n\t\"image\": \"사진\",\n\t\"insert file\": \"파일 삽입\",\n\t\"insert color\": \"색 삽입\",\n\t\"powersave mode warning\": \"외부 저장소에서 미리 확인하려면 절전모드를 종료해 주십쇼\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"light\",\n\t\"dark\": \"dark\",\n\t\"file browser\": \"File Browser\",\n\t\"exit\": \"Exit\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"대부분의 다운로드\",\n\t\"newly_added\": \"새로 추가되었습니다\",\n\t\"top_rated\": \"최고 평점\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"스폰서\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/ml-in.json",
    "content": "{\n\t\"lang\": \"മലയാളം\",\n\t\"about\": \"കുറിച്ച്‌\",\n\t\"active files\": \"സജീവ ഫയലുകൾ\",\n\t\"alert\": \"ജാഗത\",\n\t\"app theme\": \"ആപ്പ് പതിപാദം\",\n\t\"autocorrect\": \"ഓട്ടോമാറ്റിക് തിരുത്തൽ പ്രവർത്തനക്ഷമമാക്കുക?\",\n\t\"autosave\": \"ഓട്ടോമാറ്റിക് സൂക്ഷിക്കൽ\",\n\t\"cancel\": \"റദ്ദാക്കുക\",\n\t\"change language\": \"ഭാഷ മാറ്റുക\",\n\t\"choose color\": \"നിറം തിരഞ്ഞെടുക്കുക\",\n\t\"clear\": \"വൃത്തിയാക്കുക\",\n\t\"close app\": \"ആപ്പിൽ നിന്ന് പുറത്ത് കടക്കണോ?\",\n\t\"commit message\": \"സ്ഥിരീകരണ സന്ദേശം\",\n\t\"console\": \"കൺസോൾ\",\n\t\"conflict error\": \"സംഘർഷം! മറ്റൊരു കമ്മിറ്റിന് മുമ്പായി കാത്തിരിക്കുക.\",\n\t\"copy\": \"പകർത്തുക\",\n\t\"create folder error\": \"ക്ഷമിക്കണം, പുതിയ ഫോൾഡർ സൃഷ്ടിക്കാൻ കഴിയില്ല\",\n\t\"cut\": \"കട്ട്\",\n\t\"delete\": \"ഇല്ലാതാക്കുക\",\n\t\"dependencies\": \"ആശ്രിതത്വം\",\n\t\"delay\": \"സമയം മില്ലിസെക്കൻഡിൽ\",\n\t\"editor settings\": \"എഡിറ്റർ ക്രമീകരണങ്ങൾ\",\n\t\"editor theme\": \"എഡിറ്റർ തീം\",\n\t\"enter file name\": \"ഫയലിന്റെ പേര് നൽകുക\",\n\t\"enter folder name\": \"ഫോൾഡറിന്റെ പേര് നൽകുക\",\n\t\"empty folder message\": \"ശൂന്യമായ ഫോൾഡർ\",\n\t\"enter line number\": \"ലൈൻ നമ്പർ നൽകുക\",\n\t\"error\": \"പിശക്\",\n\t\"failed\": \"പരാജയപ്പെട്ടു\",\n\t\"file already exists\": \"ഫയൽ ഇതിനകം നിലവിലുണ്ട്\",\n\t\"file already exists force\": \"ഫയൽ ഇതിനകം നിലവിലുണ്ട്. തിരുത്തിയെഴുതണോ?\",\n\t\"file changed\": \" മാറ്റി, ഫയൽ വീണ്ടും ലോഡുചെയ്യണോ?\",\n\t\"file deleted\": \"ഫയൽ ഇല്ലാതാക്കി\",\n\t\"file is not supported\": \"ഫയൽ പിന്തുണയ്‌ക്കുന്നില്ല\",\n\t\"file not supported\": \"ഈ ഫയൽ തരം പിന്തുണയ്‌ക്കുന്നില്ല.\",\n\t\"file too large\": \"ഫയൽ കൈകാര്യം ചെയ്യാൻ കഴിയാത്തത്ര വലുതാണ്.{size} ആണ് പരമാവധി ഫയൽ വലുപ്പം.\",\n\t\"file renamed\": \"ഫയലിന്റെ പേരുമാറ്റി\",\n\t\"file saved\": \"ഫയൽ സംരക്ഷിച്ചു\",\n\t\"folder added\": \"ഫോൾഡർ ചേർത്തു\",\n\t\"folder already added\": \"ഫോൾഡർ മുന്വേതന്നെ ചേർത്തിരുന്നു\",\n\t\"font size\": \"അക്ഷര വലിപ്പം\",\n\t\"goto\": \"വരിയിലേക്ക് പോകുക\",\n\t\"icons definition\": \"ഐക്കണുകളുടെ നിർവചനം\",\n\t\"info\": \"വിവരം\",\n\t\"invalid value\": \"അസാധുവായ മൂല്യം\",\n\t\"language changed\": \"ഭാഷ വിജയകരമായി മാറ്റി\",\n\t\"linting\": \"വാക്യഘടന പിശക് പരിശോധിക്കുക\",\n\t\"logout\": \"ലോഗൗട്ട്\",\n\t\"loading\": \"ലോഡിംഗ്\",\n\t\"my profile\": \"എന്റെ പ്രൊഫൈൽ\",\n\t\"new file\": \"പുതിയ ഫയൽ\",\n\t\"new folder\": \"പുതിയ ഫോൾഡർ\",\n\t\"no\": \"ഇല്ല\",\n\t\"no editor message\": \"മെനുവിൽ നിന്ന് പുതിയ ഫയലും ഫോൾഡറും തുറക്കുക അല്ലെങ്കിൽ സൃഷ്ടിക്കുക\",\n\t\"not set\": \"സജ്ജമാക്കിയിട്ടില്ല\",\n\t\"unsaved files close app\": \"സംരക്ഷിക്കാത്ത ഫയലുകളുണ്ട്. അപ്ലിക്കേഷൻ അടയ്‌ക്കണോ?\",\n\t\"notice\": \"ശ്രദ്ധിക്കുക\",\n\t\"open file\": \"ഫയൽ തുറക്കുക\",\n\t\"open files and folders\": \"ഫയലുകളും ഫോൾഡറുകളും തുറക്കുക\",\n\t\"open folder\": \"ഫോൾഡർ തുറക്കുക\",\n\t\"open recent\": \"അടുത്തിടെ തുറന്ന ഫയൽ തുറക്കുക\",\n\t\"ok\": \"ശരി\",\n\t\"overwrite\": \"പുനരാലേഖനം ചെയ്യുക\",\n\t\"paste\": \"പേസ്റ്റ്\",\n\t\"preview mode\": \"പ്രിവ്യൂ മോഡ്\",\n\t\"read only file\": \"വായന മാത്രം ഫയൽ സംരക്ഷിക്കാൻ കഴിയില്ല!. ഇതായി സംരക്ഷിക്കുക ഉപയോഗിക്കുക\",\n\t\"reload\": \"വീണ്ടും ലോഡുചെയ്യുക\",\n\t\"rename\": \"പേരുമാറ്റുക\",\n\t\"replace\": \"മാറ്റിസ്ഥാപിക്കുക\",\n\t\"required\": \"ഈ ഫീൽഡ് പൂരിപ്പിക്കേണ്ടതുണ്ട്\",\n\t\"run your web app\": \"നിങ്ങളുടെ വെബ് അപ്ലിക്കേഷൻ പ്രവർത്തിപ്പിക്കുക\",\n\t\"save\": \"സംരക്ഷിക്കുക\",\n\t\"saving\": \"സംരക്ഷിക്കുകയാണ്\",\n\t\"save as\": \"ഇതായി സംരക്ഷിക്കുക\",\n\t\"save file to run\": \"ബ്രൗസറിൽ പ്രവർത്തിക്കാൻ ഈ ഫയൽ സംരക്ഷിക്കുക\",\n\t\"search\": \"തിരയൽ\",\n\t\"see logs and errors\": \"വിവരണപതികയും പിശകുകളും കാണുക\",\n\t\"select folder\": \"ഫോൾഡർ തിരഞ്ഞെടുക്കുക\",\n\t\"settings\": \"ക്രമീകരണങ്ങൾ\",\n\t\"settings saved\": \"ക്രമീകരണങ്ങൾ സംരക്ഷിച്ചു\",\n\t\"show line numbers\": \"ലൈൻ നമ്പറുകൾ കാണിക്കുക\",\n\t\"show hidden files\": \"മറഞ്ഞിരിക്കുന്ന ഫയലുകൾ കാണിക്കുക\",\n\t\"show spaces\": \"ഇടങ്ങൾ കാണിക്കുക\",\n\t\"soft tab\": \"സോഫ്റ്റ് ടാബ്\",\n\t\"sort by name\": \"പേര് പ്രകാരം ഇനം തിരിക്കുക\",\n\t\"success\": \"വിജയം\",\n\t\"tab size\": \"ടാബ് വലുപ്പം\",\n\t\"text wrap\": \"ടെക്സ്റ്റ് റാപ്\",\n\t\"theme\": \"പതിപാദം\",\n\t\"unable to delete file\": \"ഫയൽ ഇല്ലാതാക്കാൻ കഴിയില്ല\",\n\t\"unable to open file\": \"ക്ഷമിക്കണം, ഫയൽ തുറക്കാൻ കഴിഞ്ഞില്ല\",\n\t\"unable to open folder\": \"ക്ഷമിക്കണം, ഫോൾഡർ തുറക്കാൻ കഴിഞ്ഞില്ല\",\n\t\"unable to save file\": \"ക്ഷമിക്കണം, ഫയൽ സംരക്ഷിക്കാൻ കഴിഞ്ഞില്ല\",\n\t\"unable to rename\": \"ക്ഷമിക്കണം, പേരുമാറ്റാൻ കഴിഞ്ഞില്ല\",\n\t\"unsaved file\": \"ഈ ഫയൽ സംരക്ഷിച്ചിട്ടില്ല, എന്തായാലും അടയ്‌ക്കണോ?\",\n\t\"warning\": \"മുന്നറിയിപ്പ്\",\n\t\"use emmet\": \"എമ്മറ്റ് ഉപയോഗിക്കുക\",\n\t\"use quick tools\": \"ദ്രുത ഉപകരണങ്ങൾ ഉപയോഗിക്കുക\",\n\t\"yes\": \"അതെ\",\n\t\"encoding\": \"ടെക്സ്റ്റ് എൻ‌കോഡിംഗ്\",\n\t\"syntax highlighting\": \"സിന്റാക്സ് ഹൈലൈറ്റിംഗ്\",\n\t\"read only\": \"വായിക്കാൻ മാത്രം\",\n\t\"select all\": \"എല്ലാം തിരഞ്ഞെടുക്കുക\",\n\t\"select branch\": \"ശാഖ തിരഞ്ഞെടുക്കുക\",\n\t\"create new branch\": \"പുതിയ ശാഖ സൃഷ്ടിക്കുക\",\n\t\"use branch\": \"ശാഖ ഉപയോഗിക്കുക\",\n\t\"new branch\": \"പുതിയ ശാഖ\",\n\t\"branch\": \"ശാഖ\",\n\t\"key bindings\": \"കീ ബൈൻഡിംഗുകൾ\",\n\t\"edit\": \"തിരുത്തുക\",\n\t\"reset\": \"പുന: സജ്ജമാക്കുക\",\n\t\"color\": \"നിറം\",\n\t\"select word\": \"പദം തിരഞ്ഞെടുക്കുക\",\n\t\"quick tools\": \"ദ്രുത ഉപകരണങ്ങൾ\",\n\t\"select\": \"തിരഞ്ഞെടുക്കുക\",\n\t\"editor font\": \"എഡിറ്റർ ഫോണ്ട്\",\n\t\"new project\": \"പുതിയ പദ്ധതി\",\n\t\"format\": \"രൂപകല്പന\",\n\t\"project name\": \"പദ്ധതിയുടെ പേര്\",\n\t\"unsupported device\": \"നിങ്ങളുടെ ഉപകരണം തീമിനെ പിന്തുണയ്‌ക്കുന്നില്ല.\",\n\t\"vibrate on tap\": \"ടാപ്പിൽ വൈബ്രേറ്റ് ചെയ്യുക\",\n\t\"copy command is not supported by ftp.\": \"കോപ്പി കമാൻഡ് FTP പിന്തുണയ്ക്കുന്നില്ല.\",\n\t\"support title\": \"Support Acode\",\n\t\"fullscreen\": \"പൂർണ്ണ സ്ക്രീൻ\",\n\t\"animation\": \"പൂർണ്ണ സ്ക്രീൻ\",\n\t\"backup\": \"ബാക്കപ്പ്\",\n\t\"restore\": \"പുനഃസ്ഥാപിക്കുക\",\n\t\"backup successful\": \"ബാക്കപ്പ് വിജയിച്ചു\",\n\t\"invalid backup file\": \"അസാധുവായ ബാക്കപ്പ് ഫയൽ\",\n\t\"add path\": \"പാത ചേർക്കുക\",\n\t\"live autocompletion\": \"തത്സമയ യാന്ത്രിക പൂർത്തീകരണം\",\n\t\"file properties\": \"ഫയൽ പ്രോപ്പർട്ടികൾ\",\n\t\"path\": \"പാത\",\n\t\"type\": \"ടൈപ്പ്\",\n\t\"word count\": \"\",\n\t\"line count\": \"വാക്കുകളുടെ എണ്ണം\",\n\t\"last modified\": \"അവസാനം പരിഷ്കരിച്ചത്\",\n\t\"size\": \"വലിപ്പം\",\n\t\"share\": \"ഷെയർ\",\n\t\"show print margin\": \"പ്രിന്റ് മാർജിൻ ഷോ ചെയുക\",\n\t\"login\": \"ലോഗിൻ\",\n\t\"scrollbar size\": \"സ്‌ക്രോൾബാർ സൈസ് \",\n\t\"cursor controller size\": \"കഴ്സർ കൺട്രോളർ സൈസ് \",\n\t\"none\": \"ഒന്നുമില്ല\",\n\t\"small\": \"ചെറുത്\",\n\t\"large\": \"വലുത്\",\n\t\"floating button\": \"ഫ്ലോട്ടിങ് ബട്ടൺ\",\n\t\"confirm on exit\": \"കൺഫേം ഓൺ എക്സിറ് \",\n\t\"show console\": \"കൺസോൾ കാണിക്കുക\",\n\t\"image\": \"ചിത്രം\",\n\t\"insert file\": \"ഫയൽ ഇന്സേര്ട് ചെയുക\",\n\t\"insert color\": \"കളർ ഇന്സേര്ട് ചെയ്ക\",\n\t\"powersave mode warning\": \"external ബ്രൗസറിൽ പ്രിവ്യു ചെയ്യാൻ പവർ സേവിങ് മോഡ് ഓഫ് ആകേണ്ടതാണ് \",\n\t\"exit\": \"പുറത്തേക് പോകുക\",\n\t\"custom\": \"കസ്റ്റമ് \",\n\t\"reset warning\": \"തീം പുനഃസജ്ജമാക്കണമെന്ന് തീർച്ചയാണോ?\",\n\t\"theme type\": \"തീം തരം\",\n\t\"light\": \"ലൈറ്റ്\",\n\t\"dark\": \"ഡാർക്ക്\",\n\t\"file browser\": \"ഫയൽ ബ്രൌസർ\",\n\t\"operation not permitted\": \"ഓപ്പറേഷൻ പെർമിറ് ആയിട്ടില്ല\",\n\t\"no such file or directory\": \"അങ്ങനെ ഒരു ഫയൽ ഓ ഫോൾഡറോ ഇല്ല\",\n\t\"input/output error\": \"ഇന്പുട് ഔട്ട്പുട്ട് എറർ\",\n\t\"permission denied\": \"പെര്മിസ്സഷൻ ഡിനൈദ്‌\",\n\t\"bad address\": \"ബാഡ് അഡ്രസ് \",\n\t\"file exists\": \"ഫയൽ ഉണ്ട്\",\n\t\"not a directory\": \"ഇത് ഒരു ഡയറക്ടറി അല്ല\",\n\t\"is a directory\": \"ഡിറ്റക്ടറി ആണ്\",\n\t\"invalid argument\": \"ഇൻവാലിദ് അർജുമെൻറ്\",\n\t\"too many open files in system\": \"സിസ്റ്റമിൽ നിറയെ ഓപ്പൺഡ് ഫയൽ\",\n\t\"too many open files\": \"നിറയെ ഓപ്പൺഡ് ഫയൽ\",\n\t\"text file busy\": \"ടെക്സ്റ്റ് ഫയൽ ബിസി\",\n\t\"no space left on device\": \"സിസ്റ്റമിൽ സ്പേസ് ഇല്ല\",\n\t\"read-only file system\": \"റീഡ് ഒൺലി ഫയൽ സിസ്റ്റം\",\n\t\"file name too long\": \"ഫയൽ നെയിം വലുതാണ് \",\n\t\"too many users\": \"നിറയെ അധികം യൂസേഴ്സ്\",\n\t\"connection timed out\": \"കണക്ഷൻ കട്ട് ആയി\",\n\t\"connection refused\": \"കണക്ഷൻ പോയി \",\n\t\"owner died\": \"ഓണർ പോയി \",\n\t\"an error occurred\": \"ഒരു എറർ വന്നു \",\n\t\"add ftp\": \"ആഡ് FTP \",\n\t\"add sftp\": \"ആഡ് SFTP \",\n\t\"save file\": \"ഫയൽ സേവ് ചെയുക \",\n\t\"save file as\": \"ഫയൽ സേവ് ആസ് \",\n\t\"files\": \"ഫയൽസ് \",\n\t\"help\": \"ഹെല്പ് \",\n\t\"file has been deleted\": \"{file} ഡിലീറ്റ് ആയി !\",\n\t\"feature not available\": \"ഈ ഫീചർ പൈഡ് ആപ്പിൽ മാത്രമേ ലഫ്യമാകു .\",\n\t\"deleted file\": \"ഡെലീറ്റഡ് ഫയൽ \",\n\t\"line height\": \"ലൈൻ ഹൈറ് \",\n\t\"preview info\": \"ആക്റ്റീവ് ഫയൽ റൺ ചെയ്യാൻ റൺ ബട്ടനിൽ ടാപ് ആൻഡ് ഹോൾഡ് ചെയുക .\",\n\t\"manage all files\": \"നിങ്ങളുടെ ഉപകരണത്തിലെ ഫയലുകൾ എളുപ്പത്തിൽ എഡിറ്റുചെയ്യുന്നതിന് ക്രമീകരണങ്ങളിലെ എല്ലാ ഫയലുകളും നിയന്ത്രിക്കാൻ Acode എഡിറ്ററെ അനുവദിക്കുക.\",\n\t\"close file\": \"ക്ലോസ് ഫയൽ \",\n\t\"reset connections\": \"കണക്ഷൻ റിസറ്റ് ചെയുക \",\n\t\"check file changes\": \"ചെക്ക് ഫയൽ ചേഞ്ച്‌ \",\n\t\"open in browser\": \"ബ്രോസ്വേറിൽ ഓപ്പൺ ചെയുക \",\n\t\"desktop mode\": \"ഡെസ്ക്‌റ്റോപ് മോഡ് \",\n\t\"toggle console\": \"ടോകൾ console\",\n\t\"new line mode\": \"ന്യൂ ലൈൻ മോഡ് \",\n\t\"add a storage\": \"സ്റ്റോറേജ് ആഡ് ചെയുക \",\n\t\"rate acode\": \"റേറ്റ് ആക്കോട് \",\n\t\"support\": \"സപ്പോർട്ട് \",\n\t\"downloading file\": \"ഡൗൺലോഡിങ് {file}\",\n\t\"downloading...\": \"ഡൗൺലോഡിങ്...\",\n\t\"folder name\": \"ഫോൾഡർ നെയിം\",\n\t\"keyboard mode\": \"കീബോർഡ് മോഡ്\",\n\t\"normal\": \"നോർമൽ\",\n\t\"app settings\": \"ആപ്പ് സെറ്റിംഗ്സ്\",\n\t\"disable in-app-browser caching\": \"ഇൻ-ആപ്പ്-ബ്രൗസർ കാഷിംഗ് പ്രവർത്തനരഹിതമാക്കുക\",\n\t\"copied to clipboard\": \"ക്ലിപ്പ് ബോർഡിൽ കോപ്പി ചെയ്തു \",\n\t\"remember opened files\": \"ഓപ്പൺഡ് ഫയൽസ് റിമെംബേർ ചെയുക\",\n\t\"remember opened folders\": \"ഓപ്പൺഡ് ഫോൾഡർ റിമെംബേർ ചെയുക\",\n\t\"no suggestions\": \"നോ സഗ്ഗെസ്ഷൻ \",\n\t\"no suggestions aggressive\": \"നോ സഗ്ഗെസ്ഷൻ ആഗ്ഗ്രെസ്സീവ്\",\n\t\"install\": \"ഇൻസ്റ്റാൾ \",\n\t\"installing\": \"ഇൻസ്റ്റലിങ്...\",\n\t\"plugins\": \"പ്ലഗിൻസ്\",\n\t\"recently used\": \"റീസെന്റലി യൂസ്ഡ്\",\n\t\"update\": \"അപ്ഡേറ്റ് \",\n\t\"uninstall\": \"യൂണിൻസ്റ്റാൾ\",\n\t\"download acode pro\": \"ആക്കോട് പ്രൊ ഡൌൺലോഡ് \",\n\t\"loading plugins\": \"പ്ലജിനുകൾ ലോഡ് ആകുന്നു \",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"ഫീഡ്ബാക്ക്സ്\",\n\t\"header\": \"ഹെയ്ഡർ \",\n\t\"sidebar\": \"സൈഡിബർ \",\n\t\"inapp\": \"ആപ്പിന്റെ അകത്തു\",\n\t\"browser\": \"ബ്രൗസേറിൽ\",\n\t\"diagonal scrolling\": \"ദിയഗ്ണൽ സ്ക്രോളിങ് \",\n\t\"reverse scrolling\": \"റിവേഴ്‌സ് സഫ്രിലിങ് \",\n\t\"formatter\": \"ഫോർമാറ്റർ\",\n\t\"format on save\": \"ഫോർമാറ്റ്‌ ഓൺ സേവ് \",\n\t\"remove ads\": \"പരസ്യം റിമോവ് ചെയുക\",\n\t\"fast\": \"പെട്ടന്ന്\",\n\t\"slow\": \"പതുക്കെ\",\n\t\"scroll settings\": \"സ്ക്രോൽ സെറ്റിംഗ്സ്\",\n\t\"scroll speed\": \"സ്ക്രോൽ സ്പീഡ്\",\n\t\"loading...\": \"ലോഡിങ്...\",\n\t\"no plugins found\": \"ഒരു പ്ലഗിംനും ഇല\",\n\t\"name\": \"പേര്\",\n\t\"username\": \"യൂസർ നെയിം\",\n\t\"optional\": \"ഓപ്ഷണൽ\",\n\t\"hostname\": \"ഹോസ്റ്റ് നെയിം\",\n\t\"password\": \"പാസ്സ്‌വേർഡ്‌\",\n\t\"security type\": \"സെക്യൂരിറ്റി ടൈപ്പ്\",\n\t\"connection mode\": \"കണക്ഷൻ മോഡ്\",\n\t\"port\": \"പോർട്ട്\",\n\t\"key file\": \"കീ ഫയൽ\",\n\t\"select key file\": \"കീ ഫയൽ സെലക്റ്റ് ചെയുക\",\n\t\"passphrase\": \"പാസ്സ്‌ഫെരസ്\",\n\t\"connecting...\": \"കണക്ടിങ്...\",\n\t\"type filename\": \"ഫയൽ നെയിം ടൈപ്പ് ചെയ്ക\",\n\t\"unable to load files\": \"ഫയൽസ് ലോഡ് ചെയ്യാൻ പറ്റുന്നില്ല\",\n\t\"preview port\": \"പോർട്ട് പ്രേവ്യൂ\",\n\t\"find file\": \"ഫയൽ കണ്ടതുക\",\n\t\"system\": \"സിസ്റ്റം\",\n\t\"please select a formatter\": \"ഒരു ഫോർമാറ്റർ സെലക്ട് ചെയുക\",\n\t\"case sensitive\": \"കേസ് സെൻസിറ്റീവ്\",\n\t\"regular expression\": \"റെഗുലർ എക്സ്പ്രഷൻ\",\n\t\"whole word\": \"മുഴുവൻ വേർഡ്\",\n\t\"edit with\": \"എഡിറ്റ്‌ വിത്ത്\",\n\t\"open with\": \"ഓപ്പൺ വിത്ത്\",\n\t\"no app found to handle this file\": \"ഈ ഫയൽ handle ചെയ്യാൻ പറ്റിയ ആപ്പ് ഒന്നും കണ്ടില്ല\",\n\t\"restore default settings\": \"പഴയ സെറ്റിംഗ്സ് റെസ്റ്റോർ ചെയുക\",\n\t\"server port\": \"സെർവർ പോർട്ട്\",\n\t\"preview settings\": \"പ്രേവ്യൂ സെറ്റിംഗ്സ്\",\n\t\"preview settings note\": \"പ്രിവ്യൂ പോർട്ടും സെർവർ പോർട്ടും വ്യത്യസ്തമാണെങ്കിൽ, ആപ്പ് സെർവർ ആരംഭിക്കില്ല, പകരം അത് ബ്രൗസറിലോ ആപ്പ് ബ്രൗസറിലോ https://<host>:<preview port> തുറക്കും. നിങ്ങൾ മറ്റെവിടെയെങ്കിലും ഒരു സെർവർ പ്രവർത്തിപ്പിക്കുമ്പോൾ ഇത് ഉപയോഗപ്രദമാണ്.\",\n\t\"backup/restore note\": \"ഇത് നിങ്ങളുടെ ക്രമീകരണങ്ങൾ, ഇഷ്‌ടാനുസൃത തീം, കീ ബൈൻഡിംഗുകൾ എന്നിവ മാത്രമേ ബാക്കപ്പ് ചെയ്യുകയുള്ളൂ. ഇത് നിങ്ങളുടെ FPT/SFTP ബാക്കപ്പ് ചെയ്യില്ല.\",\n\t\"host\": \"ഹോസ്റ്റ്\",\n\t\"retry ftp/sftp when fail\": \"പരാജയപ്പെടുമ്പോൾ ftp/sftp വീണ്ടും ശ്രമിക്കുക\",\n\t\"more\": \"മോർ\",\n\t\"thank you :)\": \"നന്ദി :)\",\n\t\"purchase pending\": \"Purchase പെന്റിങ്\",\n\t\"cancelled\": \"ക്യാൻസൽ ചെയ്തു\",\n\t\"local\": \"ലോക്കൽ\",\n\t\"remote\": \"റിമോട്ട്\",\n\t\"show console toggler\": \"Console ടോഗ്ഗ്‌ലെർ കാണിക്കുക\",\n\t\"binary file\": \"ഈ ഫിയലിൽ binary ഡാറ്റാ ഇണ്ട്, നിങ്ങൾക്ക് ഇത് ഓപ്പൺ ചെയണോ?\",\n\t\"relative line numbers\": \"റിലേറ്റീവ് ലൈൻ നമ്പേഴ്സ്\",\n\t\"elastic tabstops\": \"ഏലസ്റ്റിക് ടാബ്‌സ്‌റ്റോപ്സ്\",\n\t\"line based rtl switching\": \"ലൈൻ അടിസ്ഥാനമാക്കിയുള്ള RTL സ്വിച്ചിംഗ്\",\n\t\"hard wrap\": \"ഹാർഡ് Wrap\",\n\t\"spellcheck\": \"സ്പെല്ലചെക്ക്\",\n\t\"wrap method\": \"Wrap മെത്തേഡ്\",\n\t\"use textarea for ime\": \"IME-യ്‌ക്ക് ടെക്‌സ്‌റ്റേറിയ ഉപയോഗിക്കുക\",\n\t\"invalid plugin\": \"പ്ലെഗിൻ തെറ്റ് ആണ്\",\n\t\"type command\": \"കമാൻഡ് ടൈപ്പ് ചെയുക\",\n\t\"plugin\": \"പ്ലെഗിൻ\",\n\t\"quicktools trigger mode\": \"ക്വിക്ട്ടൂൾസ് ട്രിഗ്ഗർ മോഡ്\",\n\t\"print margin\": \"പ്രിന്റ് മാർജിൻ\",\n\t\"touch move threshold\": \"ടച്ച് മൂവ് ത്രെഷോൾഡ്\",\n\t\"info-retryremotefsafterfail\": \"പരാജയപ്പെടുമ്പോൾ FTP/SFTP കണക്ഷൻ വീണ്ടും ശ്രമിക്കുക\",\n\t\"info-fullscreen\": \"ഹോം സ്ക്രീനിൽ ടൈറ്റിൽ ബാർ മറയ്ക്കുക.\",\n\t\"info-checkfiles\": \"ആപ്പ് പശ്ചാത്തലത്തിലായിരിക്കുമ്പോൾ ഫയലിലെ മാറ്റങ്ങൾ പരിശോധിക്കുക.\",\n\t\"info-console\": \"JavaScript കൺസോൾ തിരഞ്ഞെടുക്കുക. ലെഗസി ഡിഫോൾട്ട് കൺസോളാണ്, എരുഡ ഒരു മൂന്നാം കക്ഷി കൺസോളാണ്.\",\n\t\"info-keyboardmode\": \"ടെക്സ്റ്റ് ഇൻപുട്ടിനുള്ള കീബോർഡ് മോഡ്, നിർദ്ദേശങ്ങളൊന്നും നിർദ്ദേശങ്ങൾ മറയ്ക്കുകയും സ്വയമേവ ശരിയാക്കുകയും ചെയ്യും. നിർദ്ദേശങ്ങളൊന്നും പ്രവർത്തിക്കുന്നില്ലെങ്കിൽ, നിർദ്ദേശങ്ങളൊന്നും ആക്രമണാത്മകമല്ല എന്നതിലേക്ക് മൂല്യം മാറ്റാൻ ശ്രമിക്കുക.\",\n\t\"info-rememberfiles\": \"ആപ്പ് അടയ്‌ക്കുമ്പോൾ തുറന്ന ഫയലുകൾ ഓർക്കുക.\",\n\t\"info-rememberfolders\": \"ആപ്പ് അടയ്‌ക്കുമ്പോൾ തുറന്ന ഫോൾഡറുകൾ ഓർക്കുക.\",\n\t\"info-floatingbutton\": \"ദ്രുത ഉപകരണങ്ങൾ ഫ്ലോട്ടിംഗ് ബട്ടൺ കാണിക്കുക അല്ലെങ്കിൽ മറയ്ക്കുക.\",\n\t\"info-openfilelistpos\": \"സജീവ ഫയലുകളുടെ ലിസ്റ്റ് എവിടെ കാണിക്കണം.\",\n\t\"info-touchmovethreshold\": \"നിങ്ങളുടെ ഉപകരണ ടച്ച് സെൻസിറ്റിവിറ്റി വളരെ ഉയർന്നതാണെങ്കിൽ, ആകസ്മികമായ ടച്ച് നീക്കം തടയാൻ നിങ്ങൾക്ക് ഈ മൂല്യം വർദ്ധിപ്പിക്കാം.\",\n\t\"info-scroll-settings\": \"ഈ ക്രമീകരണങ്ങളിൽ ടെക്സ്റ്റ് റാപ്പ് ഉൾപ്പെടെയുള്ള സ്ക്രോൾ ക്രമീകരണങ്ങൾ അടങ്ങിയിരിക്കുന്നു.\",\n\t\"info-animation\": \"ആപ്പ് മന്ദഗതിയിലാണെന്ന് തോന്നുന്നുവെങ്കിൽ, ആനിമേഷൻ പ്രവർത്തനരഹിതമാക്കുക.\",\n\t\"info-quicktoolstriggermode\": \"ദ്രുത ടൂളുകളിലെ ബട്ടൺ പ്രവർത്തിക്കുന്നില്ലെങ്കിൽ, ഈ മൂല്യം മാറ്റാൻ ശ്രമിക്കുക.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"ഉടമസ്ഥതയിലുള്ളത്\",\n\t\"api_error\": \"API സെർവർ പ്രവർത്തനരഹിതമാണ്, കുറച്ച് സമയത്തിന് ശേഷം ശ്രമിക്കുക.\",\n\t\"installed\": \"ഇൻസ്റ്റാൾ ചെയ്തു\",\n\t\"all\": \"എല്ലാം\",\n\t\"medium\": \"മീഡിയം\",\n\t\"refund\": \"റീഫണ്ട്\",\n\t\"product not available\": \"പ്രോഡക്റ്റ് ആവില്ലബിൾ അല്ല\",\n\t\"no-product-info\": \"ഈ പ്രോഡക്റ്റ് ഇപ്പൊ നിങ്ങളുടെ നാട്ടിൽ അവൈലബിൾ അല്ല, ദയവായി പിന്നെ ട്രൈ ചെയ്തു നോക്കുക.\",\n\t\"close\": \"ക്ലോസ്\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"കീ ബൈൻഡിംഗുകൾ അപ്ഡേറ്റ് ചെയ്തു\",\n\t\"search in files\": \"ഫയലുകളിൽ തിരയുക\",\n\t\"exclude files\": \"എസ്ക്ലൂടെ ഫയൽസ്\",\n\t\"include files\": \"ഇൻക്ലൂടെ ഫയൽസ്\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"അസാധുവായ റെഗുലർ എക്സ്പ്രഷൻ: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"സേവ് ആൾ\",\n\t\"close all\": \"ക്ലോസ് ആൾ\",\n\t\"unsaved files warning\": \"ചില ഫയലുകൾ സേവ് ചെയ്തിട്ടില്ല. എന്താണ് ചെയ്യേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക 'ശരി' ക്ലിക്ക് ചെയ്യുക അല്ലെങ്കിൽ തിരികെ പോകാൻ 'റദ്ദാക്കുക' അമർത്തുക.\",\n\t\"save all warning\": \"എല്ലാ ഫയലുകളും സംരക്ഷിച്ച് അടയ്ക്കണമെന്ന് തീർച്ചയാണോ? ഈ പ്രവർത്തനം പഴയപടിയാക്കാനാകില്ല.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"എല്ലാ ഫയലുകളും അടയ്ക്കണമെന്ന് തീർച്ചയാണോ? സംരക്ഷിക്കാത്ത മാറ്റങ്ങൾ നിങ്ങൾക്ക് നഷ്‌ടമാകും, ഈ പ്രവർത്തനം പഴയപടിയാക്കാനാകില്ല.\",\n\t\"refresh\": \"പുതുക്കുക\",\n\t\"shortcut buttons\": \"കുറുക്കുവഴി ബട്ടണുകൾ\",\n\t\"no result\": \"ഫലമില്ല\",\n\t\"searching...\": \"തിരയുന്നു...\",\n\t\"quicktools:ctrl-key\": \"കൺട്രോൾ/കമാൻഡ് കീ\",\n\t\"quicktools:tab-key\": \"ടാബ് കീ\",\n\t\"quicktools:shift-key\": \"ഷിഫ്റ്റ് കീ\",\n\t\"quicktools:undo\": \"പഴയപടിയാക്കുക\",\n\t\"quicktools:redo\": \"വീണ്ടും ചെയ്യുക\",\n\t\"quicktools:search\": \"ഫയലിൽ തിരയുക\",\n\t\"quicktools:save\": \"ഫയൽ സംരക്ഷിക്കുക\",\n\t\"quicktools:esc-key\": \"എസ്കേപ്പ് കീ\",\n\t\"quicktools:curlybracket\": \"ചുരുണ്ട ബ്രാക്കറ്റ് ചേർക്കുക\",\n\t\"quicktools:squarebracket\": \"ചതുര ബ്രാക്കറ്റ് ചേർക്കുക\",\n\t\"quicktools:parentheses\": \"പരാൻതീസിസുകൾ ചേർക്കുക\",\n\t\"quicktools:anglebracket\": \"ആംഗിൾ ബ്രാക്കറ്റ് ചേർക്കുക\",\n\t\"quicktools:left-arrow-key\": \"ഇടത് അമ്പടയാള കീ\",\n\t\"quicktools:right-arrow-key\": \"വലത് അമ്പടയാള കീ\",\n\t\"quicktools:up-arrow-key\": \"മുകളിലേക്കുള്ള അമ്പടയാള കീ\",\n\t\"quicktools:down-arrow-key\": \"താഴേക്കുള്ള അമ്പടയാള കീ\",\n\t\"quicktools:moveline-up\": \"ലൈൻ അപ്പ് നീക്കുക\",\n\t\"quicktools:moveline-down\": \"ലൈൻ താഴേക്ക് നീക്കുക\",\n\t\"quicktools:copyline-up\": \"ലൈൻ അപ്പ് പകർത്തുക\",\n\t\"quicktools:copyline-down\": \"ലൈൻ താഴേക്ക് പകർത്തുക\",\n\t\"quicktools:semicolon\": \"അർദ്ധവിരാമം ചേർക്കുക\",\n\t\"quicktools:quotation\": \"ഉദ്ധരണി ചേർക്കുക\",\n\t\"quicktools:and\": \"തിരുകുക, ചിഹ്നം\",\n\t\"quicktools:bar\": \"ബാർ ചിഹ്നം ചേർക്കുക\",\n\t\"quicktools:equal\": \"തുല്യ ചിഹ്നം ചേർക്കുക\",\n\t\"quicktools:slash\": \"സ്ലാഷ് ചിഹ്നം ചേർക്കുക\",\n\t\"quicktools:exclamation\": \"ഇൻസർട് എക്സലമഷൻ\",\n\t\"quicktools:alt-key\": \"Alt കീ\",\n\t\"quicktools:meta-key\": \"Windows/Meta കീ\",\n\t\"info-quicktoolssettings\": \"നിങ്ങളുടെ കോഡിംഗ് അനുഭവം മെച്ചപ്പെടുത്താൻ എഡിറ്ററിന് താഴെയുള്ള Quicktools കണ്ടെയ്‌നറിൽ കുറുക്കുവഴി ബട്ടണുകളും കീബോർഡ് കീകളും ഇഷ്ടാനുസൃതമാക്കുക.\",\n\t\"info-excludefolders\": \"node_modules ഫോൾഡറിൽ നിന്നുള്ള എല്ലാ ഫയലുകളും അവഗണിക്കാൻ **/node_modules/** പാറ്റേൺ ഉപയോഗിക്കുക. ഇത് ഫയലുകളെ ലിസ്റ്റുചെയ്യുന്നതിൽ നിന്ന് ഒഴിവാക്കുകയും ഫയൽ തിരയലിൽ ഉൾപ്പെടുത്തുന്നതിൽ നിന്ന് തടയുകയും ചെയ്യും.\",\n\t\"missed files\": \"തിരയൽ ആരംഭിച്ചതിന് ശേഷം സ്‌കാൻ ചെയ്‌ത {count} ഫയലുകൾ തിരയലിൽ ഉൾപ്പെടുത്തില്ല.\",\n\t\"remove\": \"നീക്കം ചെയ്യുക\",\n\t\"quicktools:command-palette\": \"കമാൻഡ് പല്ലെറ്റ്\",\n\t\"default file encoding\": \"ഡിഫോൾട്ട് ഫയൽ എൻകോഡിംഗ്\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"ഇല്ലാതാക്കൽ സ്ഥിരീകരിക്കുക: '{name}'. ഈ പ്രവർത്തനം പഴയപടിയാക്കാനാകില്ല. തുടരണോ?\",\n\t\"change encoding\": \"'{file}' എന്ന ഫയൽ '{encoding}' എൻകോഡിംഗ് ഉപയോഗിച്ച് വീണ്ടും തുറക്കണോ? ഈ പ്രവർത്തനം ഫയലിൽ വരുത്തിയ സേവ് ചെയ്യാത്ത മാറ്റങ്ങളുടെ നഷ്ടത്തിന് കാരണമാകും. നിങ്ങൾക്ക് വീണ്ടും തുറക്കുന്നത് തുടരണോ?\",\n\t\"reopen file\": \"'{file}' എന്ന ഫയൽ വീണ്ടും തുറക്കണോ? സേവ് ചെയ്യാത്ത എല്ലാ മാറ്റങ്ങളും നഷ്‌ടമാകും.\",\n\t\"plugin min version\": \"Acode - {v-code} മുകളിൽ മാത്രമേ {name} ലഭ്യമാകുകയുള്ളൂ. അപ്ഡേറ്റ് ചെയ്യാൻ ഇവിടെ ക്ലിക്ക് ചെയ്യുക.\",\n\t\"color preview\": \"കളർ പ്രിവ്യൂ\",\n\t\"confirm\": \"സ്ഥിരീകരിക്കുക\",\n\t\"list files\": \"<strong>{name}</strong> ലെ എല്ലാ ഫയലുകളും കാണിക്കണോ? വളരെയധികം ഫയലുകൾ ആപ്പിനെ ക്രാഷ് ചെയ്തേക്കാം.\",\n\t\"problems\": \"പ്രശ്നങ്ങൾ\",\n\t\"show side buttons\": \"സൈഡ് ബട്ടണുകൾ കാണിക്കുക\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"പരിശോധിച്ച പ്രസാധകൻ\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"സ്പോൺസർ\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/mm-unicode.json",
    "content": "{\n\t\"lang\": \"ဗမာစာ (Unicode)\",\n\t\"about\": \"ကျွန်တော်တို့အကြောင်း\",\n\t\"active files\": \"လက်ရှိဖိုင်များ\",\n\t\"alert\": \"သတိပေးချက်\",\n\t\"app theme\": \"App Theme\",\n\t\"autocorrect\": \"အလိုအလျောက်အမှားစစ်မည်လား?\",\n\t\"autosave\": \"အလိုအလျောက်သိမ်းဆည်းမည်။\",\n\t\"cancel\": \"ပယ်ဖျက်မည်\",\n\t\"change language\": \"ဘာသာစကားပြောင်းမည်\",\n\t\"choose color\": \"အရောင်ရွေးပါ\",\n\t\"clear\": \"ရှင်းလင်းပါ\",\n\t\"close app\": \"ယခုဆော့ဝဲကိုပိတ်မှာလား?\",\n\t\"commit message\": \"commit message\",\n\t\"console\": \"console\",\n\t\"conflict error\": \"လွဲနေပါသလာ?နောက် commit ကိုစောင့်ပါ။\",\n\t\"copy\": \"ကူမည်\",\n\t\"create folder error\": \"စိတ်မကောင်းပါဘူး။Folderတည်ဆောက်လို့မရပါဘူး။\",\n\t\"cut\": \"ဖြတ်မည်\",\n\t\"delete\": \"ဖျက်မည်\",\n\t\"dependencies\": \"Dependencies\",\n\t\"delay\": \"Time in milliseconds\",\n\t\"editor settings\": \"Editor settings\",\n\t\"editor theme\": \"Editor theme\",\n\t\"enter file name\": \"File နာမည်ထည့်ပါ\",\n\t\"enter folder name\": \"Folder နာမည်ထည့်ပါ\",\n\t\"empty folder message\": \"Folder မရှိပါ\",\n\t\"enter line number\": \"လိုင်းနံပါတ်ထည့်ပါ\",\n\t\"error\": \"error\",\n\t\"failed\": \"မအောင်မြင်ပါ။ထပ်ကြိုးစားပါ။\",\n\t\"file already exists\": \"File ရှိပြီးသားဖြစ်ပါသည်။\",\n\t\"file already exists force\": \"File ရှိပြီးသားဖြစ်ပါသည်။File ကိုထပ်ရေးမည်လား။\",\n\t\"file changed\": \" ပြောင်းလဲသွားပြီ။ဖိုင်ကိုပြန်ဖတ်ပါ။\",\n\t\"file deleted\": \"file ဖျက်ပြီးပါပြီ\",\n\t\"file is not supported\": \"file ကိုဖွင့်ဖို့ခွင့်မပြပါ\",\n\t\"file not supported\": \"ယခု file ကိုဖွင့်ဖို့ခွင့်မပြုပါ\",\n\t\"file too large\": \"File Size ကြီးလွန်းတယ်။အများဆုံး {size} ပဲထောက်ပံ့တယ်\",\n\t\"file renamed\": \"file နာမည်ပြောင်းပြီးပါပြီ\",\n\t\"file saved\": \"file သိမ်းပြီးပါပြီ။\",\n\t\"folder added\": \"folder ကိုထပ်ပေါင်းထည့်ပြီးပါပြီ\",\n\t\"folder already added\": \"folder ကထည့်ပြီးသားဖြစ်ပါတယ်\",\n\t\"font size\": \"Font အရွယ်အစား\",\n\t\"goto\": \"Go to line\",\n\t\"icons definition\": \"Icons definition\",\n\t\"info\": \"သတင်းအချက်အလက်\",\n\t\"invalid value\": \"မမှန်ကန်သော value ဖြစ်သည်\",\n\t\"language changed\": \"ဘာသာစကားကိုအောင်မြင်စွာပြောင်းလည်းပြီးပါပြီ။\",\n\t\"linting\": \"syntax error စစ်ဆေးမည်\",\n\t\"logout\": \"ထွက်မည်\",\n\t\"loading\": \"ဖတ်နေတုန်း\",\n\t\"my profile\": \"ကျွန်ုပ် Profile \",\n\t\"new file\": \"Fileအသစ်ဆောက်မည်\",\n\t\"new folder\": \"Folder အသစ်ဆောက်မည်\",\n\t\"no\": \"မဟုတ်ဘူး\",\n\t\"no editor message\": \"ရှိပြီးသားဖိုင်ကိုဖွင့်မှာလား?(သို့မဟုတ်) App Menu မှFile (သိုမဟုတ်) Folder အသစ်တည်ဆောက်ပါ\",\n\t\"not set\": \"Not set\",\n\t\"unsaved files close app\": \"ဖိုင်မသိမ်းရသေးဘူး။ထွက်တော့မှာလား?\",\n\t\"notice\": \"အသိပေးစာ\",\n\t\"open file\": \"File ဖွင့်ပါ\",\n\t\"open files and folders\": \"File နဲ့ Folder များကိုဖွင့်ပါ\",\n\t\"open folder\": \"Folder ဖွင့်ပါ\",\n\t\"open recent\": \"မကြာသေးခင်ကဖွင့်ထားများ\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"ပြင်ရေးမည်\",\n\t\"paste\": \"paste\",\n\t\"preview mode\": \"Preview Mode\",\n\t\"read only file\": \"ဖတ်ခွင့်ပဲပြုပါတယ်။သိမ်းလို့မရပါ။Save as နဲ့သိမ်းပါ\",\n\t\"reload\": \"ပြန်ဖတ်မည်\",\n\t\"rename\": \"နာမည်ပြန်ပေးမည်\",\n\t\"replace\": \"အစားထိုးမည်\",\n\t\"required\": \"လိုအပ်ပါတယ်\",\n\t\"run your web app\": \"Web App ကို Run ပါ\",\n\t\"save\": \"သိမ်းပါ\",\n\t\"saving\": \"သိမ်းနေတုန်း\",\n\t\"save as\": \"Save as\",\n\t\"save file to run\": \"Browser မှာrun ဖို့ဖိုင်ကိုသိမ်းပါ\",\n\t\"search\": \"ရှာမည်\",\n\t\"see logs and errors\": \"logs errors တွေကိုမြင်လား?\",\n\t\"select folder\": \"Folder ရွေးပါ\",\n\t\"settings\": \"settings\",\n\t\"settings saved\": \"Settings သိမ်းပြီးပါပြီ\",\n\t\"show line numbers\": \"Line နံပါတ်များပြပါ\",\n\t\"show hidden files\": \"ဝှက်ထားသည့်File များပြပါ\",\n\t\"show spaces\": \"Space တွေပြပါ\",\n\t\"soft tab\": \"Soft tab\",\n\t\"sort by name\": \"နာမည်နဲ့စီပါ\",\n\t\"success\": \"အောင်မြင်ပါတယ်။\",\n\t\"tab size\": \"Tab အရွယ်အစား\",\n\t\"text wrap\": \"Text wrap / Word wrap\",\n\t\"theme\": \"theme\",\n\t\"unable to delete file\": \"ဖိုင်ဖျက်လို့မရပါ\",\n\t\"unable to open file\": \"ဝမ်းနည်းပါတယ်။ဖိုင်ဖွင့်မရပါ။\",\n\t\"unable to open folder\": \"ဝမ်းနည်းပါတယ်။Folderဖွင့်မရပါ။\",\n\t\"unable to save file\": \"ဝမ်းနည်းပါတယ်။ဖိုင်သိမ်းမရပါ။\",\n\t\"unable to rename\": \"နာမည်ပြန်ပြောင်းလို့မရပါ\",\n\t\"unsaved file\": \"ဖိုင်မသိမ်းရသေးဘူး။ဘာဖြစ်ဖြစ်ပိတ်မှာလား?\",\n\t\"warning\": \"သတိ\",\n\t\"use emmet\": \"Use emmet\",\n\t\"use quick tools\": \"မြန်ဆန်စေမည့်ကိရိယာများသုံးမည်\",\n\t\"yes\": \"ဟုတ်ပြီ\",\n\t\"encoding\": \"Text encoding\",\n\t\"syntax highlighting\": \"Syntax အရောင်\",\n\t\"read only\": \"ဖတ်လို့ပဲရပါတယ်\",\n\t\"select all\": \"အကုန်ရွေးပါ\",\n\t\"select branch\": \"Branch ရွေးပါ\",\n\t\"create new branch\": \"Branch အသစ်ဖန်တီးပါ\",\n\t\"use branch\": \"Branch ကိုသုံးပါ\",\n\t\"new branch\": \"Branch အသစ်\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"Key bindings\",\n\t\"edit\": \"ပြင်မည်\",\n\t\"reset\": \"reset\",\n\t\"color\": \"အရောင်\",\n\t\"select word\": \"စကားလုံးရွေးမည်\",\n\t\"quick tools\": \"Quick tools\",\n\t\"select\": \"ရွေးမည်\",\n\t\"editor font\": \"Editor font\",\n\t\"new project\": \"Project အသစ်\",\n\t\"format\": \"format\",\n\t\"project name\": \"Project နာမည်\",\n\t\"unsupported device\": \"ခင်ဗျား Device မှာ ယခု Theme ကိုမထောက်ပံ့ပါ။\",\n\t\"vibrate on tap\": \"ထိထားရင်တုန်ပါ\",\n\t\"copy command is not supported by ftp.\": \"FTPမှာကူယူတာကိုမထောက်ပံ့ပါ။\",\n\t\"support title\": \"Acode ကိုထောက်ပံ့ပါ ❤️\",\n\t\"fullscreen\": \"မျက်နှာပြင်အပြည့်\",\n\t\"animation\": \"အထူးပြုလုပ်ချက်\",\n\t\"backup\": \"အရံသိမ်းမည်\",\n\t\"restore\": \"ပြန်လည်သိုလှောင်မည်\",\n\t\"backup successful\": \"အရံသိမ်းတာအောင်မြင်ပါတယ်\",\n\t\"invalid backup file\": \"အရံသိမ်းသည့် File ကမမှန်ကန်ပါ။\",\n\t\"add path\": \"File ထားတဲ့နေရာထည့်ပါ\",\n\t\"live autocompletion\": \"Live autocompletion\",\n\t\"file properties\": \"File အချက်အလက်များ\",\n\t\"path\": \"လမ်းကြောင်း\",\n\t\"type\": \"အမျိုးအစား\",\n\t\"word count\": \"စကားလုံးအရေအတွက်စုစုပေါင်း\",\n\t\"line count\": \"Line အရေအတွက်စုစုပေါင်း\",\n\t\"last modified\": \"နောက်ဆုံးပြင်ဆင်ခဲ့သည့်အချိန်\",\n\t\"size\": \"Size\",\n\t\"share\": \"မျှဝေမည်\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"login\",\n\t\"scrollbar size\": \"Scrollbar အရွယ်အစား\",\n\t\"cursor controller size\": \"Cursor အရွယ်အစား\",\n\t\"none\": \"none\",\n\t\"small\": \"သေးမည်\",\n\t\"large\": \"ကြီးမည်\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"App မှထွက်လျှင် Confirm Button နှိပ်ရမည်\",\n\t\"show console\": \"console ကိုပြမည်\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"Turn off power saving mode to preview in external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"light\",\n\t\"dark\": \"dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"စပွန်ဆာ\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/mm-zawgyi.json",
    "content": "{\n\t\"lang\": \"ဗမာစာ (Zawgyi)\",\n\t\"about\": \"ကြၽန္ေတာ္တို႔အေၾကာင္း\",\n\t\"active files\": \"လက္ရွိဖိုင္မ်ား\",\n\t\"alert\": \"သတိေပးခ်က္\",\n\t\"app theme\": \"App Theme\",\n\t\"autocorrect\": \"အလိုအေလ်ာက္အမွားစစ္မည္လား?\",\n\t\"autosave\": \"အလိုအေလ်ာက္သိမ္းဆည္းမည္။\",\n\t\"cancel\": \"ပယ္ဖ်က္မည္\",\n\t\"change language\": \"ဘာသာစကားေျပာင္းမည္\",\n\t\"choose color\": \"အေရာင္ေ႐ြးပါ\",\n\t\"clear\": \"ရွင္းလင္းပါ\",\n\t\"close app\": \"ယခုေဆာ့ဝဲကိုပိတ္မွာလား?\",\n\t\"commit message\": \"commit message\",\n\t\"console\": \"console\",\n\t\"conflict error\": \"လြဲေနပါသလာ?ေနာက္ commit ကိုေစာင့္ပါ။\",\n\t\"copy\": \"ကူမည္\",\n\t\"create folder error\": \"စိတ္မေကာင္းပါဘူး။Folderတည္ေဆာက္လို႔မရပါဘူး။\",\n\t\"cut\": \"ျဖတ္မည္\",\n\t\"delete\": \"ဖ်က္မည္\",\n\t\"dependencies\": \"Dependencies\",\n\t\"delay\": \"Time in milliseconds\",\n\t\"editor settings\": \"Editor settings\",\n\t\"editor theme\": \"Editor theme\",\n\t\"enter file name\": \"File နာမည္ထည့္ပါ\",\n\t\"enter folder name\": \"Folder နာမည္ထည့္ပါ\",\n\t\"empty folder message\": \"Folder မရွိပါ\",\n\t\"enter line number\": \"လိုင္းနံပါတ္ထည့္ပါ\",\n\t\"error\": \"error\",\n\t\"failed\": \"မေအာင္ျမင္ပါ။ထပ္ႀကိဳးစားပါ။\",\n\t\"file already exists\": \"File ရွိၿပီးသားျဖစ္ပါသည္။\",\n\t\"file already exists force\": \"File ရွိၿပီးသားျဖစ္ပါသည္။File ကိုထပ္ေရးမည္လား။\",\n\t\"file changed\": \" ေျပာင္းလဲသြားၿပီ။ဖိုင္ကိုျပန္ဖတ္ပါ။\",\n\t\"file deleted\": \"file ဖ်က္ၿပီးပါၿပီ\",\n\t\"file is not supported\": \"file ကိုဖြင့္ဖို႔ခြင့္မျပပါ\",\n\t\"file not supported\": \"ယခု file ကိုဖြင့္ဖို႔ခြင့္မျပဳပါ\",\n\t\"file too large\": \"File Size ႀကီးလြန္းတယ္။အမ်ားဆုံး {size} ပဲေထာက္ပံ့တယ္\",\n\t\"file renamed\": \"file နာမည္ေျပာင္းၿပီးပါၿပီ\",\n\t\"file saved\": \"file သိမ္းၿပီးပါၿပီ။\",\n\t\"folder added\": \"folder ကိုထပ္ေပါင္းထည့္ၿပီးပါၿပီ\",\n\t\"folder already added\": \"folder ကထည့္ၿပီးသားျဖစ္ပါတယ္\",\n\t\"font size\": \"Font အ႐ြယ္အစား\",\n\t\"goto\": \"Go to line\",\n\t\"icons definition\": \"Icons definition\",\n\t\"info\": \"သတင္းအခ်က္အလက္\",\n\t\"invalid value\": \"မမွန္ကန္ေသာ value ျဖစ္သည္\",\n\t\"language changed\": \"ဘာသာစကားကိုေအာင္ျမင္စြာေျပာင္းလည္းၿပီးပါၿပီ။\",\n\t\"linting\": \"syntax error စစ္ေဆးမည္\",\n\t\"logout\": \"ထြက္မည္\",\n\t\"loading\": \"ဖတ္ေနတုန္း\",\n\t\"my profile\": \"ကြၽန္ုပ္ Profile \",\n\t\"new file\": \"Fileအသစ္ေဆာက္မည္\",\n\t\"new folder\": \"Folder အသစ္ေဆာက္မည္\",\n\t\"no\": \"မဟုတ္ဘူး\",\n\t\"no editor message\": \"ရွိၿပီးသားဖိုင္ကိုဖြင့္မွာလား?(သို႔မဟုတ္) App Menu မွFile (သိုမဟုတ္) Folder အသစ္တည္ေဆာက္ပါ\",\n\t\"not set\": \"Not set\",\n\t\"unsaved files close app\": \"ဖိုင္မသိမ္းရေသးဘူး။ထြက္ေတာ့မွာလား?\",\n\t\"notice\": \"အသိေပးစာ\",\n\t\"open file\": \"File ဖြင့္ပါ\",\n\t\"open files and folders\": \"File နဲ႕ Folder မ်ားကိုဖြင့္ပါ\",\n\t\"open folder\": \"Folder ဖြင့္ပါ\",\n\t\"open recent\": \"မၾကာေသးခင္ကဖြင့္ထားမ်ား\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"ျပင္ေရးမည္\",\n\t\"paste\": \"paste\",\n\t\"preview mode\": \"Preview Mode\",\n\t\"read only file\": \"ဖတ္ခြင့္ပဲျပဳပါတယ္။သိမ္းလို႔မရပါ။Save as နဲ႕သိမ္းပါ\",\n\t\"reload\": \"ျပန္ဖတ္မည္\",\n\t\"rename\": \"နာမည္ျပန္ေပးမည္\",\n\t\"replace\": \"အစားထိုးမည္\",\n\t\"required\": \"လိုအပ္ပါတယ္\",\n\t\"run your web app\": \"Web App ကို Run ပါ\",\n\t\"save\": \"သိမ္းပါ\",\n\t\"saving\": \"သိမ္းေနတုန္း\",\n\t\"save as\": \"Save as\",\n\t\"save file to run\": \"Browser မွာrun ဖို႔ဖိုင္ကိုသိမ္းပါ\",\n\t\"search\": \"ရွာမည္\",\n\t\"see logs and errors\": \"logs errors ေတြကိုျမင္လား?\",\n\t\"select folder\": \"Folder ေ႐ြးပါ\",\n\t\"settings\": \"settings\",\n\t\"settings saved\": \"Settings သိမ္းၿပီးပါၿပီ\",\n\t\"show line numbers\": \"Line နံပါတ္မ်ားျပပါ\",\n\t\"show hidden files\": \"ဝွက္ထားသည့္File မ်ားျပပါ\",\n\t\"show spaces\": \"Space ေတြျပပါ\",\n\t\"soft tab\": \"Soft tab\",\n\t\"sort by name\": \"နာမည္နဲ႕စီပါ\",\n\t\"success\": \"ေအာင္ျမင္ပါတယ္။\",\n\t\"tab size\": \"Tab အ႐ြယ္အစား\",\n\t\"text wrap\": \"Text wrap / Word wrap\",\n\t\"theme\": \"theme\",\n\t\"unable to delete file\": \"ဖိုင္ဖ်က္လို႔မရပါ\",\n\t\"unable to open file\": \"ဝမ္းနည္းပါတယ္။ဖိုင္ဖြင့္မရပါ။\",\n\t\"unable to open folder\": \"ဝမ္းနည္းပါတယ္။Folderဖြင့္မရပါ။\",\n\t\"unable to save file\": \"ဝမ္းနည္းပါတယ္။ဖိုင္သိမ္းမရပါ။\",\n\t\"unable to rename\": \"နာမည္ျပန္ေျပာင္းလို႔မရပါ\",\n\t\"unsaved file\": \"ဖိုင္မသိမ္းရေသးဘူး။ဘာျဖစ္ျဖစ္ပိတ္မွာလား?\",\n\t\"warning\": \"သတိ\",\n\t\"use emmet\": \"Use emmet\",\n\t\"use quick tools\": \"ျမန္ဆန္ေစမည့္ကိရိယာမ်ားသုံးမည္\",\n\t\"yes\": \"ဟုတ္ၿပီ\",\n\t\"encoding\": \"Text encoding\",\n\t\"syntax highlighting\": \"Syntax အေရာင္\",\n\t\"read only\": \"ဖတ္လို႔ပဲရပါတယ္\",\n\t\"select all\": \"အကုန္ေ႐ြးပါ\",\n\t\"select branch\": \"Branch ေ႐ြးပါ\",\n\t\"create new branch\": \"Branch အသစ္ဖန္တီးပါ\",\n\t\"use branch\": \"Branch ကိုသုံးပါ\",\n\t\"new branch\": \"Branch အသစ္\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"Key bindings\",\n\t\"edit\": \"ျပင္မည္\",\n\t\"reset\": \"reset\",\n\t\"color\": \"အေရာင္\",\n\t\"select word\": \"စကားလုံးေ႐ြးမည္\",\n\t\"quick tools\": \"Quick tools\",\n\t\"select\": \"ေ႐ြးမည္\",\n\t\"editor font\": \"Editor font\",\n\t\"new project\": \"Project အသစ္\",\n\t\"format\": \"format\",\n\t\"project name\": \"Project နာမည္\",\n\t\"unsupported device\": \"ခင္ဗ်ား Device မွာ ယခု Theme ကိုမေထာက္ပံ့ပါ။\",\n\t\"vibrate on tap\": \"ထိထားရင္တုန္ပါ\",\n\t\"copy command is not supported by ftp.\": \"FTPမွာကူယူတာကိုမေထာက္ပံ့ပါ။\",\n\t\"support title\": \"Acode ကိုေထာက္ပံ့ပါ ❤️\",\n\t\"fullscreen\": \"မ်က္ႏွာျပင္အျပည့္\",\n\t\"animation\": \"အထူးျပဳလုပ္ခ်က္\",\n\t\"backup\": \"အရံသိမ္းမည္\",\n\t\"restore\": \"ျပန္လည္သိုေလွာင္မည္\",\n\t\"backup successful\": \"အရံသိမ္းတာေအာင္ျမင္ပါတယ္\",\n\t\"invalid backup file\": \"အရံသိမ္းသည့္ File ကမမွန္ကန္ပါ။\",\n\t\"add path\": \"File ထားတဲ့ေနရာထည့္ပါ\",\n\t\"live autocompletion\": \"Live autocompletion\",\n\t\"file properties\": \"File အခ်က္အလက္မ်ား\",\n\t\"path\": \"လမ္းေၾကာင္း\",\n\t\"type\": \"အမ်ိဳးအစား\",\n\t\"word count\": \"စကားလုံးအေရအတြက္စုစုေပါင္း\",\n\t\"line count\": \"Line အေရအတြက္စုစုေပါင္း\",\n\t\"last modified\": \"ေနာက္ဆုံးျပင္ဆင္ခဲ့သည့္အခ်ိန္\",\n\t\"size\": \"Size\",\n\t\"share\": \"မွ်ေဝမည္\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"login\",\n\t\"scrollbar size\": \"Scrollbar အ႐ြယ္အစား\",\n\t\"cursor controller size\": \"Cursor အ႐ြယ္အစား\",\n\t\"none\": \"none\",\n\t\"small\": \"ေသးမည္\",\n\t\"large\": \"ႀကီးမည္\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"App မွထြက္လွ်င္ Confirm Button ႏွိပ္ရမည္\",\n\t\"show console\": \"console ကိုျပမည္\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"Turn off power saving mode to preview in external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"light\",\n\t\"dark\": \"dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"စပွန်ဆာ\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/pl-pl.json",
    "content": "{\n\t\"lang\": \"Polski\",\n\t\"about\": \"O aplikacji\",\n\t\"active files\": \"Aktywne pliki\",\n\t\"alert\": \"Alert\",\n\t\"app theme\": \"Motyw aplikacji\",\n\t\"autocorrect\": \"Aktywować autokorektę?\",\n\t\"autosave\": \"Autozapis\",\n\t\"cancel\": \"Anuluj\",\n\t\"change language\": \"Zmień język\",\n\t\"choose color\": \"Wybierz kolor\",\n\t\"clear\": \"wyczyść\",\n\t\"close app\": \"Zamknąć aplikację?\",\n\t\"commit message\": \"Wiadomość zatwierdzenia (commita)\",\n\t\"console\": \"Konsola\",\n\t\"conflict error\": \"Konflikt! Proszę zaczekać przed następnym zatwierdzeniem (commitem).\",\n\t\"copy\": \"Kopiuj\",\n\t\"create folder error\": \"Nie udało się utworzyć folderu\",\n\t\"cut\": \"Wytnij\",\n\t\"delete\": \"Usuń\",\n\t\"dependencies\": \"Zależności\",\n\t\"delay\": \"Czas w milisekundach\",\n\t\"editor settings\": \"Ustawienia edytora\",\n\t\"editor theme\": \"Motyw edytora\",\n\t\"enter file name\": \"Wprowadź nazwę pliku\",\n\t\"enter folder name\": \"Wprowadź nazwę folderu\",\n\t\"empty folder message\": \"Pusty folder\",\n\t\"enter line number\": \"Wprowadź numer wiersza\",\n\t\"error\": \"Błąd\",\n\t\"failed\": \"Niepowodzenie\",\n\t\"file already exists\": \"Plik już istnieje\",\n\t\"file already exists force\": \"Plik istnieje. Nadpisać go?\",\n\t\"file changed\": \" został zmodyfikowany, przeładować plik?\",\n\t\"file deleted\": \"Plik usunięty\",\n\t\"file is not supported\": \"Plik nie jest wspierany\",\n\t\"file not supported\": \"Ten typ pliku nie jest wspierany.\",\n\t\"file too large\": \"Plik jest zbyt duży. Maksymalny dozwolony rozmiar pliku to {size}\",\n\t\"file renamed\": \"zmieniono nazwę pliku\",\n\t\"file saved\": \"zapisano plik\",\n\t\"folder added\": \"dodano folder\",\n\t\"folder already added\": \"folder został już dodany\",\n\t\"font size\": \"Rozmiar czcionki\",\n\t\"goto\": \"Przejdź do wiersza\",\n\t\"icons definition\": \"Definicja ikon\",\n\t\"info\": \"Informacja\",\n\t\"invalid value\": \"Nieprawidłowa wartość\",\n\t\"language changed\": \"język został zmieniony pomyślnie\",\n\t\"linting\": \"Sprawdź błąd składni\",\n\t\"logout\": \"Wyloguj\",\n\t\"loading\": \"Ładowanie\",\n\t\"my profile\": \"Mój profil\",\n\t\"new file\": \"Nowy plik\",\n\t\"new folder\": \"Nowy folder\",\n\t\"no\": \"Nie\",\n\t\"no editor message\": \"Otwórz lub utwórz nowy plik i folder z poziomu menu\",\n\t\"not set\": \"Nie ustawiony\",\n\t\"unsaved files close app\": \"Niektóre pliki nie zostały jeszcze zapisane. Zamknąć aplikację?\",\n\t\"notice\": \"Komunikat\",\n\t\"open file\": \"Otwórz plik\",\n\t\"open files and folders\": \"Otwórz pliki i foldery\",\n\t\"open folder\": \"Otwórz folder\",\n\t\"open recent\": \"Ostatnio otwarte\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"Nadpisz\",\n\t\"paste\": \"Wklej\",\n\t\"preview mode\": \"Tryb podglądu\",\n\t\"read only file\": \"Nie można zapisać pliku tylko do odczytu. Spróbuj zapisać go opcją Zapisz jako\",\n\t\"reload\": \"Przeładuj\",\n\t\"rename\": \"Zmień nazwę\",\n\t\"replace\": \"Zastąp\",\n\t\"required\": \"To pole jest wymagane\",\n\t\"run your web app\": \"Uruchom swoją aplikację webową\",\n\t\"save\": \"Zapisz\",\n\t\"saving\": \"Zapisywanie\",\n\t\"save as\": \"Zapisz jako\",\n\t\"save file to run\": \"Zapisz ten plik, aby uruchomić go w przeglądarce\",\n\t\"search\": \"Wyszukaj\",\n\t\"see logs and errors\": \"Zobacz błędy i logi\",\n\t\"select folder\": \"Wybierz folder\",\n\t\"settings\": \"Ustawienia\",\n\t\"settings saved\": \"Ustawienia zapisane\",\n\t\"show line numbers\": \"Pokaż numery wierszy\",\n\t\"show hidden files\": \"Pokaż ukryte pliki\",\n\t\"show spaces\": \"Pokaż spacje\",\n\t\"soft tab\": \"Miękka tabulacja\",\n\t\"sort by name\": \"Sortuj według nazwy\",\n\t\"success\": \"Sukces\",\n\t\"tab size\": \"Wielkość tabulacji\",\n\t\"text wrap\": \"Zawijanie tekstu\",\n\t\"theme\": \"Motyw\",\n\t\"unable to delete file\": \"nie można usunąć pliku\",\n\t\"unable to open file\": \"Nie udało się otworzyć pliku\",\n\t\"unable to open folder\": \"Nie udało się otworzyć folderu\",\n\t\"unable to save file\": \"Nie udało się zapisać pliku\",\n\t\"unable to rename\": \"Nie udało się zmienić nazwy\",\n\t\"unsaved file\": \"Ten plik nie został jeszcze zapisany, czy chcesz go mimo to zamknąć?\",\n\t\"warning\": \"Ostrzeżenie\",\n\t\"use emmet\": \"Użyj emmet\",\n\t\"use quick tools\": \"Użyj szybkich narzędzi\",\n\t\"yes\": \"Tak\",\n\t\"encoding\": \"Kodowanie tekstu\",\n\t\"syntax highlighting\": \"Podświetlanie składni\",\n\t\"read only\": \"Tylko do odczytu\",\n\t\"select all\": \"Zaznacz wszystko\",\n\t\"select branch\": \"Wybierz gałąź\",\n\t\"create new branch\": \"Utwórz nową gałąź\",\n\t\"use branch\": \"Użyj gałęzi\",\n\t\"new branch\": \"Nowa gałąź\",\n\t\"branch\": \"Gałąź\",\n\t\"key bindings\": \"Skróty klawiszowe\",\n\t\"edit\": \"Edycja\",\n\t\"reset\": \"Reset\",\n\t\"color\": \"Kolor\",\n\t\"select word\": \"Wybierz słowo\",\n\t\"quick tools\": \"Szybkie narzędzia\",\n\t\"select\": \"Wybierz\",\n\t\"editor font\": \"Czcionka edytora\",\n\t\"new project\": \"Nowy projekt\",\n\t\"format\": \"Formatuj\",\n\t\"project name\": \"Nazwa projektu\",\n\t\"unsupported device\": \"Twoje urządzenie nie wspiera tego motywu.\",\n\t\"vibrate on tap\": \"Wibracja przy dotknięciu\",\n\t\"copy command is not supported by ftp.\": \"Komenda copy nie jest wspierana przez ten serwer FTP.\",\n\t\"support title\": \"Wesprzyj Acode\",\n\t\"fullscreen\": \"Pełny ekran\",\n\t\"animation\": \"Animacja\",\n\t\"backup\": \"Kopia zapasowa\",\n\t\"restore\": \"Przywracanie\",\n\t\"backup successful\": \"Kopia zapasowa wykonana pomyślnie\",\n\t\"invalid backup file\": \"Nieprawidłowy plik kopii zapasowej\",\n\t\"add path\": \"Dodaj ścieżkę\",\n\t\"live autocompletion\": \"Autouzupełnianie kodu\",\n\t\"file properties\": \"Właściwości pliku\",\n\t\"path\": \"Ścieżka\",\n\t\"type\": \"Typ\",\n\t\"word count\": \"Ilość słów\",\n\t\"line count\": \"Ilość wierszy\",\n\t\"last modified\": \"Ostatnia modyfikacja\",\n\t\"size\": \"Rozmiar\",\n\t\"share\": \"Udostępnij\",\n\t\"show print margin\": \"Pokaż margines wydruku\",\n\t\"login\": \"Logowanie\",\n\t\"scrollbar size\": \"Rozmiar scrollbaru\",\n\t\"cursor controller size\": \"Rozmiar znacznika kursora\",\n\t\"none\": \"Brak\",\n\t\"small\": \"Mały\",\n\t\"large\": \"Duży\",\n\t\"floating button\": \"Pływający przycisk\",\n\t\"confirm on exit\": \"Potwierdź przy wyjściu\",\n\t\"show console\": \"Pokaż konsolę\",\n\t\"image\": \"Zdjęcie\",\n\t\"insert file\": \"Wprowadź plik\",\n\t\"insert color\": \"Wprowadź kolor\",\n\t\"powersave mode warning\": \"Wyłącz tryb oszczędzania, aby wyświetlić w zewnętrznej przeglądarce.\",\n\t\"exit\": \"Wyjście\",\n\t\"custom\": \"Niestandardowy\",\n\t\"reset warning\": \"Czy na pewno chcesz zresetować ten motyw?\",\n\t\"theme type\": \"Typ motywu\",\n\t\"light\": \"Jasny\",\n\t\"dark\": \"Ciemny\",\n\t\"file browser\": \"Przeglądarka plików\",\n\t\"operation not permitted\": \"Operacja niedozwolona\",\n\t\"no such file or directory\": \"Brak takiego pliku lub folderu\",\n\t\"input/output error\": \"Błąd wejścia/wyjścia\",\n\t\"permission denied\": \"Dostęp odmówiony\",\n\t\"bad address\": \"Zły adres\",\n\t\"file exists\": \"Plik istnieje\",\n\t\"not a directory\": \"Nie jest folderem\",\n\t\"is a directory\": \"Jest folderem\",\n\t\"invalid argument\": \"Nieprawidłowy argument\",\n\t\"too many open files in system\": \"Zbyt dużo otwartych plików w systemie\",\n\t\"too many open files\": \"Zbyt dużo otwartych plików\",\n\t\"text file busy\": \"Plik tekstowy zajęty\",\n\t\"no space left on device\": \"Brak miejsca na urządzeniu\",\n\t\"read-only file system\": \"System plików tylko do odczytu\",\n\t\"file name too long\": \"Zbyt długa nazwa pliku\",\n\t\"too many users\": \"Zbyt dużo użytkowników\",\n\t\"connection timed out\": \"Zbyt długi okres oczekiwania na połączenie\",\n\t\"connection refused\": \"Połączenie odrzucone\",\n\t\"owner died\": \"Właściciel zmarł\",\n\t\"an error occurred\": \"Wystąpił błąd\",\n\t\"add ftp\": \"Dodaj FTP\",\n\t\"add sftp\": \"Dodaj SFTP\",\n\t\"save file\": \"Zapisz plik\",\n\t\"save file as\": \"Zapisz plik jako\",\n\t\"files\": \"Pliki\",\n\t\"help\": \"Pomoc\",\n\t\"file has been deleted\": \"{file} został usunięty!\",\n\t\"feature not available\": \"Ta funkcja jest dostępna jedynie w płatnej wersji aplikacji.\",\n\t\"deleted file\": \"Usunięte pliki\",\n\t\"line height\": \"Wysokość wiersza\",\n\t\"preview info\": \"Jeśli chcesz uruchomić aktualnie wybrany plik, kliknij i przytrzymaj ikonę odtwarzania.\",\n\t\"manage all files\": \"Zezwól Acode zarządzać wszystkimi plikami w ustawieniach, aby z łatwością edytować pliki na twoim urządzeniu.\",\n\t\"close file\": \"Zamknij plik\",\n\t\"reset connections\": \"Zresetuj połączenia\",\n\t\"check file changes\": \"Sprawdzaj zmiany w plikach\",\n\t\"open in browser\": \"Otwórz w przeglądarce\",\n\t\"desktop mode\": \"Tryb desktopowy\",\n\t\"toggle console\": \"Przełącz konsolę\",\n\t\"new line mode\": \"Sekwencja końca wiersza\",\n\t\"add a storage\": \"Dodaj pamięć\",\n\t\"rate acode\": \"Oceń Acode\",\n\t\"support\": \"Wesprzyj\",\n\t\"downloading file\": \"Pobieranie {file}\",\n\t\"downloading...\": \"Pobieranie...\",\n\t\"folder name\": \"Nazwa folderu\",\n\t\"keyboard mode\": \"Tryb klawiatury\",\n\t\"normal\": \"Normalny\",\n\t\"app settings\": \"Ustawienia aplikacji\",\n\t\"disable in-app-browser caching\": \"Wyłącz cache w wbudowanej przeglądarce\",\n\t\"copied to clipboard\": \"Skopiowano do schowka\",\n\t\"remember opened files\": \"Zapamiętaj otwarte pliki\",\n\t\"remember opened folders\": \"Zapamiętaj otwarte foldery\",\n\t\"no suggestions\": \"Bez sugestii\",\n\t\"no suggestions aggressive\": \"Bez sugestii (agresywnie)\",\n\t\"install\": \"Instaluj\",\n\t\"installing\": \"Instalowanie...\",\n\t\"plugins\": \"Wtyczki\",\n\t\"recently used\": \"Ostatnio używane\",\n\t\"update\": \"Zaktualizuj\",\n\t\"uninstall\": \"Odinstaluj\",\n\t\"download acode pro\": \"Pobierz Acode Pro\",\n\t\"loading plugins\": \"Ładowanie wtyczek\",\n\t\"faqs\": \"Najczęściej zadawane pytania\",\n\t\"feedback\": \"Informacja zwrotna\",\n\t\"header\": \"Nagłówek\",\n\t\"sidebar\": \"Pasek boczny\",\n\t\"inapp\": \"W aplikacji\",\n\t\"browser\": \"Przeglądarka\",\n\t\"diagonal scrolling\": \"Przewijanie po przekątnej\",\n\t\"reverse scrolling\": \"Przewijanie wstecz\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Formatuj podczas zapisu\",\n\t\"remove ads\": \"Usuń reklamy\",\n\t\"fast\": \"Szybko\",\n\t\"slow\": \"Wolno\",\n\t\"scroll settings\": \"Ustawienia przewijania\",\n\t\"scroll speed\": \"Szybkość przewijania\",\n\t\"loading...\": \"Ładowanie...\",\n\t\"no plugins found\": \"Nie znaleziono wtyczek\",\n\t\"name\": \"Nazwa\",\n\t\"username\": \"Nazwa użytkownika\",\n\t\"optional\": \"opcjonalnie\",\n\t\"hostname\": \"Nazwa hosta\",\n\t\"password\": \"Hasło\",\n\t\"security type\": \"Typ zabezpieczeń\",\n\t\"connection mode\": \"Typ połączenia\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Plik klucza\",\n\t\"select key file\": \"Wybierz plik klucza\",\n\t\"passphrase\": \"Fraza do hasła\",\n\t\"connecting...\": \"Łączenie...\",\n\t\"type filename\": \"Wpisz nazwę pliku\",\n\t\"unable to load files\": \"Nie można załadować plików\",\n\t\"preview port\": \"Port podglądu\",\n\t\"find file\": \"Wyszukaj plik\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Wybierz formatter\",\n\t\"case sensitive\": \"Uwzględnianie wielkości liter\",\n\t\"regular expression\": \"Wyrażenia regularne\",\n\t\"whole word\": \"Całe słowo\",\n\t\"edit with\": \"Edytuj za pomocą\",\n\t\"open with\": \"Otwórz za pomocą\",\n\t\"no app found to handle this file\": \"Nie znaleziono aplikacji obsługującej ten plik\",\n\t\"restore default settings\": \"Przywróć ustawienia domyślne\",\n\t\"server port\": \"Port serwera\",\n\t\"preview settings\": \"Ustawienia podglądu\",\n\t\"preview settings note\": \"Jeśli port podglądu i port serwera są różne, aplikacja nie uruchomi serwera i zamiast tego otworzy https://<host>:<port podglądu> w przeglądarce lub w przeglądarce w aplikacji. Jest to przydatne, gdy serwer jest uruchomiony w innej lokalizacji\",\n\t\"backup/restore note\": \"Tworzy kopię zapasową tylko ustawień, niestandardowego motywu i przypisanych klawiszy. Nie tworzy kopii zapasowej FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Ponów ftp/sftp w przypadku niepowodzenia\",\n\t\"more\": \"Więcej\",\n\t\"thank you :)\": \"Dziękuję :)\",\n\t\"purchase pending\": \"zakup w trakcie realizacji\",\n\t\"cancelled\": \"anulowany\",\n\t\"local\": \"Lokalne\",\n\t\"remote\": \"Zdalne\",\n\t\"show console toggler\": \"Pokaż przełącznik konsoli\",\n\t\"binary file\": \"Ten plik zawiera dane binarne, czy chcesz go otworzyć?\",\n\t\"relative line numbers\": \"Relatywne numery linii\",\n\t\"elastic tabstops\": \"Elastyczne tabulatory\",\n\t\"line based rtl switching\": \"Przełącznik RTL oparty na linii\",\n\t\"hard wrap\": \"Twarde zawijanie\",\n\t\"spellcheck\": \"Sprawdzanie pisowni\",\n\t\"wrap method\": \"Metoda zawijania\",\n\t\"use textarea for ime\": \"Użyj textarea dla IME\",\n\t\"invalid plugin\": \"Nieprawidłowa wtyczka\",\n\t\"type command\": \"Wpisz polecenie\",\n\t\"plugin\": \"Wtyczka\",\n\t\"quicktools trigger mode\": \"Tryb wyzwalania szybkich narzędzi\",\n\t\"print margin\": \"Margines wydruku\",\n\t\"touch move threshold\": \"Próg reakcji na dotyk\",\n\t\"info-retryremotefsafterfail\": \"Ponawianie połączenia FTP/SFTP w przypadku niepowodzenia\",\n\t\"info-fullscreen\": \"Ukrywanie paska tytułu na ekranie głównym.\",\n\t\"info-checkfiles\": \"Sprawdza zmiany w plikach, gdy aplikacja działa w tle.\",\n\t\"info-console\": \"Wybór konsoli JavaScript. Legacy to domyślna konsola, eruda to konsola innej firmy.\",\n\t\"info-keyboardmode\": \"Tryb klawiatury do wprowadzania tekstu, brak sugestii ukryje sugestie i automatyczną korektę. Jeśli brak sugestii nie działa, spróbuj zmienić wartość na brak sugestii agresywnych.\",\n\t\"info-rememberfiles\": \"Pamiętaj otwarte pliki po zamknięciu aplikacji.\",\n\t\"info-rememberfolders\": \"Pamiętaj otwarte foldery po zamknięciu aplikacji.\",\n\t\"info-floatingbutton\": \"Pokaż lub ukryj pływający przycisk szybkich narzędzi.\",\n\t\"info-openfilelistpos\": \"Gdzie ma być wyświetlana lista aktywnych plików.\",\n\t\"info-touchmovethreshold\": \"Jeśli czułość urządzenia na dotyk jest zbyt wysoka, można zwiększyć tę wartość, aby zapobiec przypadkowemu dotknięciu.\",\n\t\"info-scroll-settings\": \"Ustawienia te zawierają ustawienia przewijania, w tym zawijanie tekstu.\",\n\t\"info-animation\": \"Jeśli aplikacja działa z opóźnieniem, wyłącz animację.\",\n\t\"info-quicktoolstriggermode\": \"Jeśli przycisk w szybkich narzędziach nie działa, spróbuj zmienić tę wartość.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Posiadane\",\n\t\"api_error\": \"Serwer API nie działa, spróbuj za jakiś czas.\",\n\t\"installed\": \"Zainstalowane\",\n\t\"all\": \"Wszystko\",\n\t\"medium\": \"Średni\",\n\t\"refund\": \"Zwrot\",\n\t\"product not available\": \"Produkt niedostępny\",\n\t\"no-product-info\": \"Ten produkt nie jest obecnie dostępny w Twoim kraju, spróbuj ponownie w późniejszym terminie.\",\n\t\"close\": \"Zamknij\",\n\t\"explore\": \"Eksploruj\",\n\t\"key bindings updated\": \"Zaktualizowano powiązania klawiszy\",\n\t\"search in files\": \"Wyszukaj w plikach\",\n\t\"exclude files\": \"Wyklucz pliki\",\n\t\"include files\": \"Uwzględnij pliki\",\n\t\"search result\": \"{matches} wyniki w {files} plikach.\",\n\t\"invalid regex\": \"Nieprawidłowe wyrażenie regularne: {message}.\",\n\t\"bottom\": \"Na dole\",\n\t\"save all\": \"Zapisz wszystko\",\n\t\"close all\": \"Zamknij wszystko\",\n\t\"unsaved files warning\": \"Niektóre pliki nie zostaną zapisane. Kliknij 'ok', aby wybrać, co chcesz zrobić, lub naciśnij 'anuluj', aby wrócić.\",\n\t\"save all warning\": \"Czy na pewno chcesz zapisać wszystkie pliki i zamknąć? Tego działania nie można cofnąć.\",\n\t\"save all changes warning\": \"Czy na pewno chcesz zapisać wszystkie pliki?\",\n\t\"close all warning\": \"Czy na pewno chcesz zamknąć wszystkie pliki? Niezapisane zmiany zostaną utracone, a działania tego nie można cofnąć.\",\n\t\"refresh\": \"Odśwież\",\n\t\"shortcut buttons\": \"Przyciski skrótów\",\n\t\"no result\": \"Brak wyników\",\n\t\"searching...\": \"Wyszukiwanie...\",\n\t\"quicktools:ctrl-key\": \"Klawisz Control/Command\",\n\t\"quicktools:tab-key\": \"Klawisz Tab\",\n\t\"quicktools:shift-key\": \"Klawisz Shift\",\n\t\"quicktools:undo\": \"Cofnij\",\n\t\"quicktools:redo\": \"Ponów\",\n\t\"quicktools:search\": \"Wyszukaj w plikach\",\n\t\"quicktools:save\": \"Zapisz plik\",\n\t\"quicktools:esc-key\": \"Klawisz Escape\",\n\t\"quicktools:curlybracket\": \"Wstaw nawias klamrowy\",\n\t\"quicktools:squarebracket\": \"Wstaw nawias kwadratowy\",\n\t\"quicktools:parentheses\": \"Wstaw nawiasy\",\n\t\"quicktools:anglebracket\": \"Wstaw nawias kątowy\",\n\t\"quicktools:left-arrow-key\": \"Klawisz strzałki w lewo\",\n\t\"quicktools:right-arrow-key\": \"Klawisz strzałki w prawo\",\n\t\"quicktools:up-arrow-key\": \"Klawisz strzałki w górę\",\n\t\"quicktools:down-arrow-key\": \"Klawisz strzałki w dół\",\n\t\"quicktools:moveline-up\": \"Przesuń linię do góry\",\n\t\"quicktools:moveline-down\": \"Przesuń linię w dół\",\n\t\"quicktools:copyline-up\": \"Kopiuj linię do góry\",\n\t\"quicktools:copyline-down\": \"Kopiuj linię w dół\",\n\t\"quicktools:semicolon\": \"Wstaw średnik\",\n\t\"quicktools:quotation\": \"Wstaw cudzysłów\",\n\t\"quicktools:and\": \"Wstaw symbol and\",\n\t\"quicktools:bar\": \"Wstaw symbol bar\",\n\t\"quicktools:equal\": \"Wstaw symbol equal\",\n\t\"quicktools:slash\": \"Wstaw symbol ukośnika\",\n\t\"quicktools:exclamation\": \"Wstaw wykrzyknik\",\n\t\"quicktools:alt-key\": \"Klawisz Alt\",\n\t\"quicktools:meta-key\": \"Klawisz Windows/Meta\",\n\t\"info-quicktoolssettings\": \"Dostosuj przyciski skrótów i klawisze klawiatury w zasobniku Szybkich narzędzi poniżej edytora, aby zwiększyć komfort kodowania.\",\n\t\"info-excludefolders\": \"Użyj wzorca **/node_modules/**, aby zignorować wszystkie pliki z folderu node_modules. Spowoduje to wykluczenie plików z listy, a także uniemożliwi ich uwzględnienie w wyszukiwaniu plików.\",\n\t\"missed files\": \"Po rozpoczęciu wyszukiwania zeskanowano {count} plików, które nie zostaną uwzględnione w wyszukiwaniu.\",\n\t\"remove\": \"Usuń\",\n\t\"quicktools:command-palette\": \"Paleta poleceń\",\n\t\"default file encoding\": \"Domyślne kodowanie plików\",\n\t\"remove entry\": \"Czy na pewno chcesz usunąć '{name}' z zapisanych ścieżek? Należy pamiętać, że usunięcie go nie spowoduje usunięcia samej ścieżki.\",\n\t\"delete entry\": \"Potwierdź usunięcie: '{name}'. Tej akcji nie można cofnąć. Kontynuować?\",\n\t\"change encoding\": \"Czy ponownie otworzyć '{file}' z kodowaniem '{encoding}'? Ta czynność spowoduje utratę wszelkich niezapisanych zmian dokonanych w pliku. Czy chcesz kontynuować ponowne otwieranie?\",\n\t\"reopen file\": \"Czy na pewno chcesz ponownie otworzyć '{file}'? Wszelkie niezapisane zmiany zostaną utracone.\",\n\t\"plugin min version\": \"{name} dostępne tylko w Acode - {v-code} i nowszych wersjach. Kliknij tutaj, aby zaktualizować.\",\n\t\"color preview\": \"Kolor podglądu\",\n\t\"confirm\": \"Potwierdź\",\n\t\"list files\": \"Lista wszystkich plików w <strong>{name}</strong>? Zbyt wiele plików może spowodować awarię aplikacji.\",\n\t\"problems\": \"Problemy\",\n\t\"show side buttons\": \"Pokaż przyciski boczne\",\n\t\"bug_report\": \"Prześlij raport o błędzie\",\n\t\"verified publisher\": \"Zweryfikowany wydawca\",\n\t\"most_downloaded\": \"Najczęściej pobierane\",\n\t\"newly_added\": \"Ostatnio dodane\",\n\t\"top_rated\": \"Najwyżej oceniane\",\n\t\"rename not supported\": \"Zmiana nazwy katalogu w termux nie jest obsługiwana\",\n\t\"compress\": \"Kompresja\",\n\t\"copy uri\": \"Kopiuj Uri\",\n\t\"delete entries\": \"Czy na pewno chcesz usunąć {count} elementów?\",\n\t\"deleting items\": \"Usuwanie {count} elementów...\",\n\t\"import project zip\": \"Importuj projekt (zip)\",\n\t\"changelog\": \"Dziennik zmian\",\n\t\"notifications\": \"Powiadomienia\",\n\t\"no_unread_notifications\": \"Brak nieodczytanych powiadomień\",\n\t\"should_use_current_file_for_preview\": \"Należy użyć bieżącego pliku do podglądu zamiast domyślnego (index.html)\",\n\t\"fade fold widgets\": \"Widżety Fade Fold\",\n\t\"quicktools:home-key\": \"Klawisz Home\",\n\t\"quicktools:end-key\": \"Klawisz End\",\n\t\"quicktools:pageup-key\": \"Klawisz PageUp\",\n\t\"quicktools:pagedown-key\": \"Klawisz PageDown\",\n\t\"quicktools:delete-key\": \"Klawisz Delete\",\n\t\"quicktools:tilde\": \"Wstaw symbol tyldy\",\n\t\"quicktools:backtick\": \"Wstaw backtick\",\n\t\"quicktools:hash\": \"Wstaw symbol hash\",\n\t\"quicktools:dollar\": \"Wstaw symbol dolara\",\n\t\"quicktools:modulo\": \"Wstaw moduł/symbol procentu\",\n\t\"quicktools:caret\": \"Wstaw symbol karetki\",\n\t\"plugin_enabled\": \"Wtyczka włączona\",\n\t\"plugin_disabled\": \"Wtyczka wyłączona\",\n\t\"enable_plugin\": \"Włącz tę wtyczkę\",\n\t\"disable_plugin\": \"Wyłącz tę wtyczkę\",\n\t\"open_source\": \"Otwarte oprogramowanie\",\n\t\"terminal settings\": \"Ustawienia terminala\",\n\t\"font ligatures\": \"Ligatury czcionek\",\n\t\"letter spacing\": \"Odstępy między literami\",\n\t\"terminal:tab stop width\": \"Szerokość tabulatora\",\n\t\"terminal:scrollback\": \"Linie przewijania wstecz\",\n\t\"terminal:cursor blink\": \"Miganie kursora\",\n\t\"terminal:font weight\": \"Grubość czcionki\",\n\t\"terminal:cursor inactive style\": \"Styl nieaktywnego kursora\",\n\t\"terminal:cursor style\": \"Styl kursora\",\n\t\"terminal:font family\": \"Rodzina czcionek\",\n\t\"terminal:convert eol\": \"Konwersja EOL\",\n\t\"terminal:confirm tab close\": \"Potwierdź zamknięcie karty terminala\",\n\t\"terminal:image support\": \"Obsługa obrazów\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"Dostęp do wszystkich plików\",\n\t\"fonts\": \"Czcionki\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"pobrania\",\n\t\"reviews\": \"recenzje\",\n\t\"overview\": \"Zestawienie\",\n\t\"contributors\": \"Wspierający\",\n\t\"quicktools:hyphen\": \"Wstaw symbol myślnika\",\n\t\"check for app updates\": \"Sprawdź dostępność aktualizacji\",\n\t\"prompt update check consent message\": \"Acode może sprawdzać dostępność aktualizacji aplikacji, gdy jesteś online. Włączyć sprawdzanie aktualizacji?\",\n\t\"keywords\": \"Słowa kluczowe\",\n\t\"author\": \"Autor\",\n\t\"filtered by\": \"Filtrowane wg\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/pt-br.json",
    "content": "{\n\t\"lang\": \"Português (Brasil)\",\n\t\"about\": \"Sobre\",\n\t\"active files\": \"Arquivos ativos\",\n\t\"alert\": \"Alerta\",\n\t\"app theme\": \"Tema do app\",\n\t\"autocorrect\": \"Habilitar autocorreção?\",\n\t\"autosave\": \"Salvamento automático\",\n\t\"cancel\": \"Cancelar\",\n\t\"change language\": \"Mudar idioma\",\n\t\"choose color\": \"Escolher cor\",\n\t\"clear\": \"Limpar\",\n\t\"close app\": \"Fechar a aplicação?\",\n\t\"commit message\": \"Mensagem de commit\",\n\t\"console\": \"Console\",\n\t\"conflict error\": \"Conflito! Por favor aguarde antes de commitar.\",\n\t\"copy\": \"Copiar\",\n\t\"create folder error\": \"Desculpe, não foi possível criar a nova pasta\",\n\t\"cut\": \"Cortar\",\n\t\"delete\": \"Deletar\",\n\t\"dependencies\": \"Dependências\",\n\t\"delay\": \"Tempo em milissegundos\",\n\t\"editor settings\": \"Configurações do editor\",\n\t\"editor theme\": \"Tema do editor\",\n\t\"enter file name\": \"Informar nome do arquivo\",\n\t\"enter folder name\": \"Informar nome da pasta\",\n\t\"empty folder message\": \"Pasta vazia\",\n\t\"enter line number\": \"Informar número da linha\",\n\t\"error\": \"Erro\",\n\t\"failed\": \"Falhou\",\n\t\"file already exists\": \"Arquivo já existente\",\n\t\"file already exists force\": \"Arquivo já existente. Sobrescrever?\",\n\t\"file changed\": \" foi alterado, recarregar o arquivo?\",\n\t\"file deleted\": \"Arquivo deletado\",\n\t\"file is not supported\": \"Arquivo não suportado\",\n\t\"file not supported\": \"Este tipo de arquivo não é suportado.\",\n\t\"file too large\": \"O arquivo é muito grande para manipular. O tamanho máximo permitido por arquivo é {size}\",\n\t\"file renamed\": \"Arquivo renomeado\",\n\t\"file saved\": \"Arquivo salvo\",\n\t\"folder added\": \"Pasta adicionada\",\n\t\"folder already added\": \"A pasta já foi adicionada\",\n\t\"font size\": \"Tamanho da fonte\",\n\t\"goto\": \"Ir para a linha\",\n\t\"icons definition\": \"Definição de ícones\",\n\t\"info\": \"Info\",\n\t\"invalid value\": \"Valor inválido\",\n\t\"language changed\": \"O idioma foi alterado com sucesso\",\n\t\"linting\": \"Verificar o erro de sintaxe\",\n\t\"logout\": \"Sair\",\n\t\"loading\": \"Carregando\",\n\t\"my profile\": \"Meu perfil\",\n\t\"new file\": \"Novo arquivo\",\n\t\"new folder\": \"Nova pasta\",\n\t\"no\": \"Não\",\n\t\"no editor message\": \"Abra ou crie um novo arquivo e pasta no menu\",\n\t\"not set\": \"Não configurado\",\n\t\"unsaved files close app\": \"Existem arquivos não salvos. Fechar aplicação?\",\n\t\"notice\": \"Note\",\n\t\"open file\": \"Abrir arquivo\",\n\t\"open files and folders\": \"Abrir arquivos e pastas\",\n\t\"open folder\": \"Abrir pasta\",\n\t\"open recent\": \"Abrir recentes\",\n\t\"ok\": \"Ok\",\n\t\"overwrite\": \"Sobrescrever\",\n\t\"paste\": \"Colar\",\n\t\"preview mode\": \"Modo de pré-vizualização\",\n\t\"read only file\": \"Não é possível salvar o arquivo, somente leitura. Por favor, tente salvar como\",\n\t\"reload\": \"Recarregar\",\n\t\"rename\": \"Renomear\",\n\t\"replace\": \"Substituir\",\n\t\"required\": \"Este campo é obrigatório\",\n\t\"run your web app\": \"Executar seu web app\",\n\t\"save\": \"Salvar\",\n\t\"saving\": \"Salvando\",\n\t\"save as\": \"Salvar como\",\n\t\"save file to run\": \"Favor salvar este arquivo para executar no navegador\",\n\t\"search\": \"Pesquisar\",\n\t\"see logs and errors\": \"Ver logs e erros\",\n\t\"select folder\": \"Selecionar a pasta\",\n\t\"settings\": \"Configurações\",\n\t\"settings saved\": \"Configurações salvas\",\n\t\"show line numbers\": \"Mostrar números de linha\",\n\t\"show hidden files\": \"Mostrar arquivos ocultos\",\n\t\"show spaces\": \"Mostrar espaços\",\n\t\"soft tab\": \"Usar espaços em vez de tabs?\",\n\t\"sort by name\": \"Classificar por nome\",\n\t\"success\": \"Sucesso\",\n\t\"tab size\": \"Tamanho do tab\",\n\t\"text wrap\": \"Quebra de texto\",\n\t\"theme\": \"Tema\",\n\t\"unable to delete file\": \"não foi possível excluir o arquivo\",\n\t\"unable to open file\": \"Desculpe, não foi possível abrir o arquivo\",\n\t\"unable to open folder\": \"Desculpe, não foi possível abrir a pasta\",\n\t\"unable to save file\": \"Desculpe, não foi possível salvar o arquivo\",\n\t\"unable to rename\": \"Desculpe, não foi possível renomear\",\n\t\"unsaved file\": \"Este arquivo não foi salvo, fechar mesmo assim?\",\n\t\"warning\": \"Aviso\",\n\t\"use emmet\": \"usar emmet\",\n\t\"use quick tools\": \"Usar ferramentas rápidas\",\n\t\"yes\": \"Sim\",\n\t\"encoding\": \"Codificação de texto\",\n\t\"syntax highlighting\": \"Realce de sintaxe\",\n\t\"read only\": \"Somente leitura\",\n\t\"select all\": \"Selecionar tudo\",\n\t\"select branch\": \"Selecionar branch\",\n\t\"create new branch\": \"Criar nova branch\",\n\t\"use branch\": \"Usar branch\",\n\t\"new branch\": \"Nova branch\",\n\t\"branch\": \"Branch\",\n\t\"key bindings\": \"Combinações de teclas\",\n\t\"edit\": \"Editar\",\n\t\"reset\": \"Resetar\",\n\t\"color\": \"Cor\",\n\t\"select word\": \"Selecionar palavra\",\n\t\"quick tools\": \"ferramentas rápidas\",\n\t\"select\": \"Selecionar\",\n\t\"editor font\": \"Fonte do editor\",\n\t\"new project\": \"Novo projeto\",\n\t\"format\": \"Formatar\",\n\t\"project name\": \"Nome do Projeto\",\n\t\"unsupported device\": \"Seu dispositivo não oferece suporte ao tema.\",\n\t\"vibrate on tap\": \"Vibrar ao tocar\",\n\t\"copy command is not supported by ftp.\": \"O comando de cópia não é suportado pelo FTP.\",\n\t\"support title\": \"Acode suporte\",\n\t\"fullscreen\": \"Tela cheia\",\n\t\"animation\": \"Animação\",\n\t\"backup\": \"Backup\",\n\t\"restore\": \"Restaurar\",\n\t\"backup successful\": \"Backup bem-sucedido\",\n\t\"invalid backup file\": \"Arquivo de backup inválido\",\n\t\"add path\": \"Adicionar caminho\",\n\t\"live autocompletion\": \"Observar Preenchimento automático\",\n\t\"file properties\": \"Propriedades do arquivo\",\n\t\"path\": \"Caminho\",\n\t\"type\": \"Tipo\",\n\t\"word count\": \"Contagem de palavras\",\n\t\"line count\": \"Contagem de linhas\",\n\t\"last modified\": \"Última modificação\",\n\t\"size\": \"Tamanho\",\n\t\"share\": \"Compartilhar\",\n\t\"show print margin\": \"Mostrar margem de impressão\",\n\t\"login\": \"Conecte-se\",\n\t\"scrollbar size\": \"Tamanho da barra de rolagem\",\n\t\"cursor controller size\": \"Tamanho do controlador do cursor\",\n\t\"none\": \"Nenhum\",\n\t\"small\": \"Pequeno\",\n\t\"large\": \"Grande\",\n\t\"floating button\": \"Botão flutuante\",\n\t\"confirm on exit\": \"Confirmar saída\",\n\t\"show console\": \"Mostrar console\",\n\t\"image\": \"Imagem\",\n\t\"insert file\": \"Inserir arquivo\",\n\t\"insert color\": \"Inserir cor\",\n\t\"powersave mode warning\": \"Desative o modo de economia de energia para visualizar no navegador externo.\",\n\t\"exit\": \"Sair\",\n\t\"custom\": \"Personalizado\",\n\t\"reset warning\": \"Tem certeza de que deseja redefinir o tema?\",\n\t\"theme type\": \"Tipo de tema\",\n\t\"light\": \"Claro\",\n\t\"dark\": \"Escuro\",\n\t\"file browser\": \"Navegador de arquivos\",\n\t\"operation not permitted\": \"Operação não permitida\",\n\t\"no such file or directory\": \"O arquivo ou diretório não existe\",\n\t\"input/output error\": \"Entrada/Saída de erros\",\n\t\"permission denied\": \"Permissão negada\",\n\t\"bad address\": \"Caminho incorreto\",\n\t\"file exists\": \"O arquivo existe\",\n\t\"not a directory\": \"Não é um diretório\",\n\t\"is a directory\": \"É um diretório\",\n\t\"invalid argument\": \"Argumento inválido\",\n\t\"too many open files in system\": \"Muitos arquivos abertos no sistema\",\n\t\"too many open files\": \"Muitos arquivos abertos\",\n\t\"text file busy\": \"Arquivo de texto ocupado\",\n\t\"no space left on device\": \"Não há mais espaço no dispositivo\",\n\t\"read-only file system\": \"Sistema de arquivos somente leitura\",\n\t\"file name too long\": \"Nome de arquivo muito longo\",\n\t\"too many users\": \"Muitos usuários\",\n\t\"connection timed out\": \"A conexão expirou\",\n\t\"connection refused\": \"Conexão recusada\",\n\t\"owner died\": \"Dono morreu\",\n\t\"an error occurred\": \"Um erro ocorreu\",\n\t\"add ftp\": \"Adicionar FTP\",\n\t\"add sftp\": \"Adicionar SFTP\",\n\t\"save file\": \"Salvar Arquivo\",\n\t\"save file as\": \"Salvar arquivo como\",\n\t\"files\": \"Arquivos\",\n\t\"help\": \"Ajuda\",\n\t\"file has been deleted\": \"{file} foi deletado!\",\n\t\"feature not available\": \"Este recurso está disponível apenas na versão paga do aplicativo.\",\n\t\"deleted file\": \"Arquivo deletado\",\n\t\"line height\": \"Altura da linha\",\n\t\"preview info\": \"Se você deseja executar o arquivo ativo, toque e segure no ícone de execução\",\n\t\"manage all files\": \"Permita que o editor Acode gerencie todos os arquivos nas configurações para editar arquivos em seu dispositivo facilmente.\",\n\t\"close file\": \"Fechar arquivo\",\n\t\"reset connections\": \"Redefinir conexões\",\n\t\"check file changes\": \"Verificar alterações do arquivo\",\n\t\"open in browser\": \"Abrir no navegador\",\n\t\"desktop mode\": \"Modo desktop\",\n\t\"toggle console\": \"Alternar console\",\n\t\"new line mode\": \"Modo de nova linha\",\n\t\"add a storage\": \"Adicionar um armazenamento\",\n\t\"rate acode\": \"Avaliar Acode\",\n\t\"support\": \"Suporte\",\n\t\"downloading file\": \"Baixando {file}\",\n\t\"downloading...\": \"Baixando...\",\n\t\"folder name\": \"Nome da pasta\",\n\t\"keyboard mode\": \"Modo de teclado\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"Configurações do aplicativo\",\n\t\"disable in-app-browser caching\": \"Desativar o cache do navegador no aplicativo\",\n\t\"copied to clipboard\": \"Copiado para a área de transferência\",\n\t\"remember opened files\": \"Manter arquivos abertos\",\n\t\"remember opened folders\": \"Manter pastas abertas\",\n\t\"no suggestions\": \"Nenhuma sugestão\",\n\t\"no suggestions aggressive\": \"Sem sugestões agressivas\",\n\t\"install\": \"Instalar\",\n\t\"installing\": \"Instalando...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Usado recentemente\",\n\t\"update\": \"Atualizar\",\n\t\"uninstall\": \"Desinstalar\",\n\t\"download acode pro\": \"Baixar Acode pro\",\n\t\"loading plugins\": \"Carregando plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Cabeçalho\",\n\t\"sidebar\": \"Barra lateral\",\n\t\"inapp\": \"No app\",\n\t\"browser\": \"Navegador\",\n\t\"diagonal scrolling\": \"Rolagem diagonal\",\n\t\"reverse scrolling\": \"Rolagem reversa\",\n\t\"formatter\": \"Formatador\",\n\t\"format on save\": \"Formatar ao salvar\",\n\t\"remove ads\": \"Remover propagandas\",\n\t\"fast\": \"Rápido\",\n\t\"slow\": \"Lento\",\n\t\"scroll settings\": \"Configurações de rolagem\",\n\t\"scroll speed\": \"Velocidade de rolamento\",\n\t\"loading...\": \"Carregando...\",\n\t\"no plugins found\": \"Nenhum plugin encontrado\",\n\t\"name\": \"Nome\",\n\t\"username\": \"Nome de usuário\",\n\t\"optional\": \"Opcional\",\n\t\"hostname\": \"Nome do host\",\n\t\"password\": \"Senha\",\n\t\"security type\": \"Tipo de segurança\",\n\t\"connection mode\": \"Modo de conexão\",\n\t\"port\": \"Porta\",\n\t\"key file\": \"Arquivo chave\",\n\t\"select key file\": \"Selecione o arquivo de chave\",\n\t\"passphrase\": \"Palavra-chave\",\n\t\"connecting...\": \"Conectando...\",\n\t\"type filename\": \"Digite o nome do arquivo\",\n\t\"unable to load files\": \"Não foi possível carregar os arquivos\",\n\t\"preview port\": \"Porta de pré-visualização\",\n\t\"find file\": \"Achar arquivo\",\n\t\"system\": \"Sistema\",\n\t\"please select a formatter\": \"Favor selecionar um formatador\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Expressão regular\",\n\t\"whole word\": \"Palavra inteira\",\n\t\"edit with\": \"Editar com\",\n\t\"open with\": \"Abrir com\",\n\t\"no app found to handle this file\": \"Nenhum aplicativo encontrado para manipular este arquivo\",\n\t\"restore default settings\": \"Restaurar a configuração original\",\n\t\"server port\": \"Porta do servidor\",\n\t\"preview settings\": \"Configurações da pré-vizualização\",\n\t\"preview settings note\": \"Se a porta de pré-visualização e a porta do servidor forem diferentes, o aplicativo não iniciará o servidor e, em vez disso, abrirá https://<host>:<porta de pré-visualização> no navegador ou no navegador do aplicativo. Isso é útil quando você está executando um servidor.\",\n\t\"backup/restore note\": \"Ele fará backup apenas de suas configurações, tema personalizado e atalhos de teclado. Ele não fará backup do seu FTP/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Tentar FTP/SFTP novamente quando falhar\",\n\t\"more\": \"Mais\",\n\t\"thank you :)\": \"Obrigado :)\",\n\t\"purchase pending\": \"compra pendente\",\n\t\"cancelled\": \"cancelado\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remoto\",\n\t\"show console toggler\": \"Mostrar alternador de console\",\n\t\"binary file\": \"Este arquivo contém dados binários, deseja abri-lo?\",\n\t\"relative line numbers\": \"Números de linha relativos\",\n\t\"elastic tabstops\": \"Paradas elásticas\",\n\t\"line based rtl switching\": \"Comutação RTL baseada em linha\",\n\t\"hard wrap\": \"Quebra rígida\",\n\t\"spellcheck\": \"Verificação ortográfica\",\n\t\"wrap method\": \"Método de quebra\",\n\t\"use textarea for ime\": \"Usar textarea para IME\",\n\t\"invalid plugin\": \"Plugin inválido\",\n\t\"type command\": \"Digite o comando\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Modo de disparo de ferramentas rápidas\",\n\t\"print margin\": \"Margem de impressão\",\n\t\"touch move threshold\": \"Limite de movimento de toque\",\n\t\"info-retryremotefsafterfail\": \"Tentar novamente a conexão FTP/SFTP quando falhar.\",\n\t\"info-fullscreen\": \"Ocultar barra de título na tela inicial.\",\n\t\"info-checkfiles\": \"Verificar alterações nos arquivos quando o aplicativo estiver em segundo plano.\",\n\t\"info-console\": \"Escolha o console JavaScript. Legacy é o console padrão, eruda é um console de terceiros.\",\n\t\"info-keyboardmode\": \"Modo de teclado para entrada de texto, sem sugestões ocultará sugestões e corrigirá automaticamente. Se nenhuma sugestão não funcionar, tente alterar o valor para nenhuma sugestão agressiva.\",\n\t\"info-rememberfiles\": \"Lembrar dos arquivos abertos quando o aplicativo for fechado.\",\n\t\"info-rememberfolders\": \"Lembrar de pastas abertas quando o aplicativo for fechado.\",\n\t\"info-floatingbutton\": \"Mostrar ou ocultar o botão flutuante de ferramentas rápidas.\",\n\t\"info-openfilelistpos\": \"Onde mostrar a lista de arquivos ativos.\",\n\t\"info-touchmovethreshold\": \"Se a sensibilidade ao toque do seu dispositivo for muito alta, você pode aumentar esse valor para evitar movimentos acidentais do toque.\",\n\t\"info-scroll-settings\": \"Essas configurações contêm configurações de rolagem, incluindo quebra automática de texto.\",\n\t\"info-animation\": \"Se o aplicativo parecer lento, desative a animação.\",\n\t\"info-quicktoolstriggermode\": \"Se o botão nas ferramentas rápidas não estiver funcionando, tente alterar este valor.\",\n\t\"info-checkForAppUpdates\": \"Verificar atualizações do aplicativo automaticamente.\",\n\t\"info-quickTools\": \"Mostrar ou ocultar as ferramentas rápidas.\",\n\t\"info-showHiddenFiles\": \"Mostrar arquivos e pastas ocultas. (Eles começam com um .)\",\n\t\"info-all_file_access\": \"Permitir o acesso de /sdcard e /storage no terminal.\",\n\t\"info-fontSize\": \"O tamanho da fonte usado para exibir o texto.\",\n\t\"info-fontFamily\": \"A família de fontes usada para renderizar o texto.\",\n\t\"info-theme\": \"O tema de cores do terminal.\",\n\t\"info-cursorStyle\": \"O estilo do cursor quando o terminal está em foco.\",\n\t\"info-cursorInactiveStyle\": \"O estilo do cursor quando o terminal não está em foco.\",\n\t\"info-fontWeight\": \"A espessura da fonte usada para renderizar texto sem negrito.\",\n\t\"info-cursorBlink\": \"Define se o cursor pisca.\",\n\t\"info-scrollback\": \"A quantidade de rolagem exibida no terminal. A rolagem representa a quantidade de linhas que são mantidas quando as linhas são roladas além da área visível inicial.\",\n\t\"info-tabStopWidth\": \"O tamanho das tabs (tabulações) no terminal.\",\n\t\"info-letterSpacing\": \"O espaçamento em pixels inteiros entre os caracteres.\",\n\t\"info-imageSupport\": \"Define se as imagens são suportadas no terminal.\",\n\t\"info-fontLigatures\": \"Define se as ligaduras de fontes estão ativadas no terminal.\",\n\t\"info-confirmTabClose\": \"Solicitar confirmação antes de fechar as abas do terminal.\",\n\t\"info-backup\": \"Cria um backup da instalação do terminal.\",\n\t\"info-restore\": \"Restaura um backup da instalação do terminal.\",\n\t\"info-uninstall\": \"Desinstala a instalação do terminal.\",\n\t\"owned\": \"Meu\",\n\t\"api_error\": \"Servidor API desativado, por favor, tente depois de algum tempo.\",\n\t\"installed\": \"Instalado\",\n\t\"all\": \"Tudo\",\n\t\"medium\": \"Médio\",\n\t\"refund\": \"Reembolso\",\n\t\"product not available\": \"Produto não disponível\",\n\t\"no-product-info\": \"Este produto não está disponível em seu país no momento, tente novamente mais tarde.\",\n\t\"close\": \"Fechar\",\n\t\"explore\": \"Explorar\",\n\t\"key bindings updated\": \"Atalhos de teclas atualizados\",\n\t\"search in files\": \"Pesquisar em arquivos\",\n\t\"exclude files\": \"Excluir arquivos\",\n\t\"include files\": \"Incluir arquivos\",\n\t\"search result\": \"{matches} resultados em {files} arquivos.\",\n\t\"invalid regex\": \"Expressão regular inválida: {message}.\",\n\t\"bottom\": \"Em baixo\",\n\t\"save all\": \"Salvar tudo\",\n\t\"close all\": \"Fechar tudo\",\n\t\"unsaved files warning\": \"Alguns arquivos não são estão salvos. Clique em 'ok' e selecione o que fazer ou pressione 'cancelar' para voltar.\",\n\t\"save all warning\": \"Tem certeza de que deseja salvar todos os arquivos e fechar? Esta ação não pode ser revertida.\",\n\t\"save all changes warning\": \"Tem certeza de que deseja salvar todos os arquivos?\",\n\t\"close all warning\": \"Tem certeza de que deseja fechar todos os arquivos? Você perderá as alterações não salvas e esta ação não pode ser revertida.\",\n\t\"refresh\": \"Atualizar\",\n\t\"shortcut buttons\": \"Botões de atalho\",\n\t\"no result\": \"Sem resultado\",\n\t\"searching...\": \"Procurando...\",\n\t\"quicktools:ctrl-key\": \"Tecla de CTRL/comando\",\n\t\"quicktools:tab-key\": \"Tecla de tab\",\n\t\"quicktools:shift-key\": \"Tacla de shift\",\n\t\"quicktools:undo\": \"Desfazer\",\n\t\"quicktools:redo\": \"Refazer\",\n\t\"quicktools:search\": \"Pesquisar no arquivo\",\n\t\"quicktools:save\": \"Salvar Arquivo\",\n\t\"quicktools:esc-key\": \"Tecla de escape\",\n\t\"quicktools:curlybracket\": \"Inserir chaves\",\n\t\"quicktools:squarebracket\": \"Inserir colchetes\",\n\t\"quicktools:parentheses\": \"Inserir parênteses\",\n\t\"quicktools:anglebracket\": \"Inserir menor que/maior que\",\n\t\"quicktools:left-arrow-key\": \"Tecla de seta para a esquerda\",\n\t\"quicktools:right-arrow-key\": \"Tecla de seta para a direita\",\n\t\"quicktools:up-arrow-key\": \"Tecla de seta para cima\",\n\t\"quicktools:down-arrow-key\": \"Tecla de seta para baixo\",\n\t\"quicktools:moveline-up\": \"Mover linha para cima\",\n\t\"quicktools:moveline-down\": \"Mover linha para baixo\",\n\t\"quicktools:copyline-up\": \"Copiar alinhamento\",\n\t\"quicktools:copyline-down\": \"Copiar linha para baixo\",\n\t\"quicktools:semicolon\": \"Inserir ponto-e-vírgula\",\n\t\"quicktools:quotation\": \"Inserir citação\",\n\t\"quicktools:and\": \"Inserir símbolo de e comercial (&)\",\n\t\"quicktools:bar\": \"Inserir símbolo de barra\",\n\t\"quicktools:equal\": \"Inserir símbolo de igual\",\n\t\"quicktools:slash\": \"Inserir símbolo de barra\",\n\t\"quicktools:exclamation\": \"Inserir exclamação\",\n\t\"quicktools:alt-key\": \"Tecla Alt\",\n\t\"quicktools:meta-key\": \"Tecla Windows/Meta\",\n\t\"info-quicktoolssettings\": \"Personalize os botões de atalho e as teclas do teclado no contêiner ferramentas rápidas abaixo do editor para aprimorar sua experiência de codificação.\",\n\t\"info-excludefolders\": \"Use o padrão **/node_modules/** para ignorar todos os arquivos da pasta node_modules. Isso excluirá os arquivos da lista e também impedirá que sejam incluídos nas pesquisas de arquivos.\",\n\t\"missed files\": \"{count} arquivos verificados após o início da pesquisa e não serão incluídos na pesquisa.\",\n\t\"remove\": \"Remover\",\n\t\"quicktools:command-palette\": \"Paleta de comandos\",\n\t\"default file encoding\": \"Codificação de arquivo padrão\",\n\t\"remove entry\": \"Tem certeza de que deseja remover '{name}' dos caminhos salvos? Observe que removê-lo não excluirá o caminho em si.\",\n\t\"delete entry\": \"Confirme a exclusão: '{name}'. Essa ação não pode ser desfeita. Continuar?\",\n\t\"change encoding\": \"Reabrir '{file}' com codificação '{encoding}'? Esta ação resultará na perda de quaisquer alterações não salvas feitas no arquivo. Deseja prosseguir com a reabertura?\",\n\t\"reopen file\": \"Tem certeza de que deseja reabrir '{file}'? Quaisquer alterações não salvas serão perdidas.\",\n\t\"plugin min version\": \"{name} disponível apenas no Acode - {v-code} e superior. Clique aqui para atualizar.\",\n\t\"color preview\": \"Pré-vizualização de cores\",\n\t\"confirm\": \"Confirmar\",\n\t\"list files\": \"Listar todos os arquivos em <strong>{name}</strong>? Muitos arquivos podem travar o aplicativo.\",\n\t\"problems\": \"Problemas\",\n\t\"show side buttons\": \"Mostrar botões laterais\",\n\t\"bug_report\": \"Enviar Relatório de Erro\",\n\t\"verified publisher\": \"Editor Verificado\",\n\t\"most_downloaded\": \"Mais Baixados\",\n\t\"newly_added\": \"Recém-Adicionados\",\n\t\"top_rated\": \"Mais Bem Avaliados\",\n\t\"rename not supported\": \"Renomear no diretório do Termux não é suportado\",\n\t\"compress\": \"Comprimir\",\n\t\"copy uri\": \"Copiar URI\",\n\t\"delete entries\": \"Tem certeza de que deseja excluir {count} itens?\",\n\t\"deleting items\": \"Excluindo {count} itens...\",\n\t\"import project zip\": \"Importar Projeto (zip)\",\n\t\"changelog\": \"Registro de Alterações\",\n\t\"notifications\": \"Notificações\",\n\t\"no_unread_notifications\": \"Sem notificações não lidas\",\n\t\"should_use_current_file_for_preview\": \"Deve usar o arquivo atual para pré-visualização em vez do padrão (index.html)\",\n\t\"fade fold widgets\": \"Widgets Fade Fold\",\n\t\"quicktools:home-key\": \"Tecla Home\",\n\t\"quicktools:end-key\": \"Tecla End\",\n\t\"quicktools:pageup-key\": \"Tecla PageUp\",\n\t\"quicktools:pagedown-key\": \"Tecla PageDown\",\n\t\"quicktools:delete-key\": \"Tecla Delete\",\n\t\"quicktools:tilde\": \"Inserir símbolo de til (~)\",\n\t\"quicktools:backtick\": \"Inserir crase (`)\",\n\t\"quicktools:hash\": \"Inserir símbolo de cerquilha (#)\",\n\t\"quicktools:dollar\": \"Inserir símbolo de dólar ($)\",\n\t\"quicktools:modulo\": \"Inserir símbolo de módulo/porcentagem (%)\",\n\t\"quicktools:caret\": \"Inserir símbolo de circunflexo (^)\",\n\t\"plugin_enabled\": \"Plugin ativado\",\n\t\"plugin_disabled\": \"Plugin desativado\",\n\t\"enable_plugin\": \"Ativar este Plugin\",\n\t\"disable_plugin\": \"Desativar este Plugin\",\n\t\"open_source\": \"Código Aberto\",\n\t\"terminal settings\": \"Configurações do Terminal\",\n\t\"font ligatures\": \"Ligaduras da Fonte\",\n\t\"letter spacing\": \"Espaçamento entre Letras\",\n\t\"terminal:tab stop width\": \"Largura do Tab Stop\",\n\t\"terminal:scrollback\": \"Linhas de Scrollback\",\n\t\"terminal:cursor blink\": \"Piscar do Cursor\",\n\t\"terminal:font weight\": \"Peso da Fonte\",\n\t\"terminal:cursor inactive style\": \"Estilo do Cursor Inativo\",\n\t\"terminal:cursor style\": \"Estilo do Cursor\",\n\t\"terminal:font family\": \"Família da Fonte\",\n\t\"terminal:convert eol\": \"Converter EOL\",\n\t\"terminal:confirm tab close\": \"Confirme o fechamento da aba do terminal\",\n\t\"terminal:image support\": \"Suportar imagens\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"Acesso total aos arquivos\",\n\t\"fonts\": \"Fontes\",\n\t\"sponsor\": \"Patrocinador\",\n\t\"downloads\": \"Downloads\",\n\t\"reviews\": \"Avaliações\",\n\t\"overview\": \"Visão Geral\",\n\t\"contributors\": \"Contribuidores\",\n\t\"quicktools:hyphen\": \"Inserir símbolo de hífen (-)\",\n\t\"check for app updates\": \"Verifique se há atualizações do aplicativo\",\n\t\"prompt update check consent message\": \"O Acode pode verificar se há novas atualizações quando você estiver online. Deseja ativar a verificação de atualizações?\",\n\t\"keywords\": \"Palavras-chave\",\n\t\"author\": \"Autor\",\n\t\"filtered by\": \"Filtrado por\",\n\t\"clean install state\": \"Estado de instalação limpa\",\n\t\"backup created\": \"Backup criado\",\n\t\"restore completed\": \"Restauração concluída\",\n\t\"restore will include\": \"Isso irá restaurar\",\n\t\"restore warning\": \"Esta ação não pode ser desfeita. Continuar?\",\n\t\"reload to apply\": \"Recarregar para aplicar as alterações?\",\n\t\"reload app\": \"Recarregar aplicativo\",\n\t\"preparing backup\": \"Preparando backup\",\n\t\"collecting settings\": \"Coletando configurações\",\n\t\"collecting key bindings\": \"Coletando atalhos de teclado\",\n\t\"collecting plugins\": \"Coletando informações dos plugins\",\n\t\"creating backup\": \"Criando arquivo de backup\",\n\t\"validating backup\": \"Validando backup\",\n\t\"restoring key bindings\": \"Restaurando atalhos de teclado\",\n\t\"restoring plugins\": \"Restaurando plugins\",\n\t\"restoring settings\": \"Restaurando configurações\",\n\t\"legacy backup warning\": \"Este é um formato de backup antigo. Alguns recursos podem ser limitados.\",\n\t\"checksum mismatch\": \"Checksum não corresponde — o arquivo de backup pode ter sido modificado ou corrompido.\",\n\t\"plugin not found\": \"Plugin não encontrado no registro\",\n\t\"paid plugin skipped\": \"Plugin pago — compra não encontrada\",\n\t\"source not found\": \"O arquivo de origem não existe mais\",\n\t\"restored\": \"Restaurado\",\n\t\"skipped\": \"Ignorado\",\n\t\"backup not valid object\": \"O arquivo de backup não é um objeto válido\",\n\t\"backup no data\": \"O arquivo de backup não contém dados para restaurar\",\n\t\"backup legacy warning\": \"Este é um formato de backup antigo (v1). Alguns recursos podem ser limitados.\",\n\t\"backup missing metadata\": \"Metadados do backup ausentes — algumas informações podem não estar disponíveis\",\n\t\"backup checksum mismatch\": \"Checksum não corresponde — o arquivo de backup pode ter sido modificado ou corrompido. Prossiga com cautela.\",\n\t\"backup checksum verify failed\": \"Não foi possível verificar o checksum\",\n\t\"backup invalid settings\": \"Formato de configurações inválido\",\n\t\"backup invalid keybindings\": \"Formato de atalhos de teclado inválido\",\n\t\"backup invalid plugins\": \"Formato de plugins instalados inválido\",\n\t\"issues found\": \"Problemas encontrados\",\n\t\"error details\": \"Detalhes do erro\",\n\t\"active tools\": \"Ferramentas ativas\",\n\t\"available tools\": \"Ferramentas disponíveis\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/pu-in.json",
    "content": "{\n\t\"lang\": \"Punjabi (ਪੰਜਾਬੀ) by NiceSapien\",\n\t\"about\": \"Acode ਬਾਰੇ\",\n\t\"active files\": \"ਸਰਗਰਮ ਫਾਇਲ\",\n\t\"alert\": \"ਚੇਤਾਵਨੀ\",\n\t\"app theme\": \"ਐਪ ਥੀਮ\",\n\t\"autocorrect\": \"ਕੀ ਸਵੈ-ਸੁਧਾਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?\",\n\t\"autosave\": \"ਆਟੋ ਸੇਵ\",\n\t\"cancel\": \"ਰੱਦ\",\n\t\"change language\": \"ਭਾਸ਼ਾ ਬਦਲੋ\",\n\t\"choose color\": \"ਰੰਗ ਚੁਣੋ\",\n\t\"clear\": \"ਸਾਫ਼\",\n\t\"close app\": \"ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਬੰਦ ਕਰਨਾ ਹੈ?\",\n\t\"commit message\": \"ਸੁਨੇਹਾ ਵਚਨਬੱਧ ਕਰੋ\",\n\t\"console\": \"ਕੰਸੋਲ\",\n\t\"conflict error\": \"ਟਕਰਾਅ! ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਹੋਰ ਵਚਨਬੱਧਤਾ ਤੋਂ ਪਹਿਲਾਂ ਉਡੀਕ ਕਰੋ।\",\n\t\"copy\": \"ਕਾਪੀ\",\n\t\"create folder error\": \"ਮਾਫ਼ ਕਰਨਾ, ਨਵਾਂ ਫੋਲਡਰ ਬਣਾਉਣ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"cut\": \"ਕੱਟੋ\",\n\t\"delete\": \"\",\n\t\"dependencies\": \"ਨਿਰਭਰਤਾਵਾਂ\",\n\t\"delay\": \"ਮਿਲੀਸਕਿੰਟ ਵਿੱਚ ਸਮਾਂ\",\n\t\"editor settings\": \"ਸੰਪਾਦਕ ਸੈਟਿੰਗਾਂ\",\n\t\"editor theme\": \"ਸੰਪਾਦਕ ਥੀਮ\",\n\t\"enter file name\": \"ਫਾਈਲ ਦਾ ਨਾਮ ਦਰਜ ਕਰੋ\",\n\t\"enter folder name\": \"ਫੋਲਡਰ ਦਾ ਨਾਮ ਦਰਜ ਕਰੋ\",\n\t\"empty folder message\": \"ਖਾਲੀ ਫੋਲਡਰ\",\n\t\"enter line number\": \"ਲਾਈਨ ਨੰਬਰ ਦਰਜ ਕਰੋ\",\n\t\"error\": \"ਗਲਤੀ\",\n\t\"failed\": \"ਅਸਫਲ\",\n\t\"file already exists\": \"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ\",\n\t\"file already exists force\": \"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹੈ। ਓਵਰਰਾਈਟ ਕਰਨਾ ਹੈ?\",\n\t\"file changed\": \" ਬਦਲਿਆ ਗਿਆ ਹੈ, ਫਾਈਲ ਰੀਲੋਡ ਕਰੋ?\",\n\t\"file deleted\": \"ਫਾਇਲ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ\",\n\t\"file is not supported\": \"ਫਾਈਲ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ\",\n\t\"file not supported\": \"ਇਹ ਫਾਈਲ ਕਿਸਮ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।\",\n\t\"file too large\": \"ਹੈਂਡਲ ਕਰਨ ਲਈ ਫ਼ਾਈਲ ਬਹੁਤ ਵੱਡੀ ਹੈ। ਅਧਿਕਤਮ ਫਾਈਲ ਦਾ ਆਕਾਰ {size} ਹੈ\",\n\t\"file renamed\": \"ਫਾਈਲ ਦਾ ਨਾਮ ਬਦਲ ਦਿੱਤਾ ਗਿਆ ਹੈ\",\n\t\"file saved\": \"ਫਾਇਲ ਨੂੰ ਸੰਭਾਲਿਆ ਗਿਆ ਹੈ\",\n\t\"folder added\": \"ਫੋਲਡਰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ\",\n\t\"folder already added\": \"folder already added\",\n\t\"font size\": \"ਫੌਂਟ ਦਾ ਆਕਾਰ\",\n\t\"goto\": \"ਲਾਈਨ 'ਤੇ ਜਾਓ\",\n\t\"icons definition\": \"ਆਈਕਾਨ ਦੀ ਪਰਿਭਾਸ਼ਾ\",\n\t\"info\": \"ਜਾਣਕਾਰੀ\",\n\t\"invalid value\": \"ਅਵੈਧ ਮੁੱਲ\",\n\t\"language changed\": \"ਭਾਸ਼ਾ ਨੂੰ ਸਫਲਤਾਪੂਰਵਕ ਬਦਲ ਦਿੱਤਾ ਗਿਆ ਹੈ\",\n\t\"linting\": \"ਸੰਟੈਕਸ ਗਲਤੀ ਦੀ ਜਾਂਚ ਕਰੋ\",\n\t\"logout\": \"ਲਾੱਗ ਆਊਟ\",\n\t\"loading\": \"ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ\",\n\t\"my profile\": \"ਮੇਰੀ ਪ੍ਰੋਫਾਈਲ\",\n\t\"new file\": \"ਨਵੀਂ ਫ਼ਾਈਲ\",\n\t\"new folder\": \"ਨਵਾਂ ਫੋਲਡਰ\",\n\t\"no\": \"ਨੰ\",\n\t\"no editor message\": \"menu ਤੋਂ ਨਵੀਂ ਫਾਈਲ ਅਤੇ ਫੋਲਡਰ ਖੋਲ੍ਹੋ ਜਾਂ ਬਣਾਓ\",\n\t\"not set\": \"ਸੈੱਟ ਨਹੀਂ ਹੈ\",\n\t\"unsaved files close app\": \"ਅਣ-ਰੱਖਿਅਤ ਫਾਈਲਾਂ ਹਨ। ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਬੰਦ ਕਰਨਾ ਹੈ?\",\n\t\"notice\": \"ਨੋਟਿਸ\",\n\t\"open file\": \"ਫਾਇਲ ਖੋਲੋ\",\n\t\"open files and folders\": \"ਫਾਈਲਾਂ ਅਤੇ ਫੋਲਡਰ ਖੋਲ੍ਹੋ\",\n\t\"open folder\": \"ਫੋਲਡਰ ਖੋਲ੍ਹੋ\",\n\t\"open recent\": \"ਹਾਲੀਆ ਖੋਲ੍ਹੋ\",\n\t\"ok\": \"ਠੀਕ ਹੈ\",\n\t\"overwrite\": \"ਓਵਰਰਾਈਟ\",\n\t\"paste\": \"ਚਿਪਕਾਓ\",\n\t\"preview mode\": \"ਪੂਰਵਦਰਸ਼ਨ ਮੋਡ\",\n\t\"read only file\": \"ਸਿਰਫ਼ ਪੜ੍ਹਨ ਵਾਲੀ ਫ਼ਾਈਲ ਨੂੰ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਤੌਰ 'ਤੇ ਸੁਰੱਖਿਅਤ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ\",\n\t\"reload\": \"ਦੁਬਾਰਾ ਲੋਡ ਕਰੋ\",\n\t\"rename\": \"ਨਾਮ ਬਦਲੋ\",\n\t\"replace\": \"ਬਦਲੋ\",\n\t\"required\": \"ਇਸ ਫੀਲਡ ਦੀ ਲੋੜ ਹੈ\",\n\t\"run your web app\": \"ਆਪਣੀ ਵੈੱਬ ਐਪ ਚਲਾਓ\",\n\t\"save\": \"ਸੇਵ\",\n\t\"saving\": \"saving\",\n\t\"save as\": \"ਬਤੌਰ ਮਹਿਫ਼ੂਜ਼ ਕਰੋ\",\n\t\"save file to run\": \"ਕਿਰਪਾ ਕਰਕੇ ਇਸ ਫ਼ਾਈਲ ਨੂੰ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਚਲਾਉਣ ਲਈ ਸੇਵ ਕਰੋ\",\n\t\"search\": \"ਖੋਜ\",\n\t\"see logs and errors\": \"ਲੌਗ ਅਤੇ ਤਰੁੱਟੀਆਂ ਦੇਖੋ\",\n\t\"select folder\": \"ਫੋਲਡਰ ਚੁਣੋ\",\n\t\"settings\": \"ਸੈਟਿੰਗਾਂ\",\n\t\"settings saved\": \"ਸੈਟਿੰਗਾਂ ਸੁਰੱਖਿਅਤ ਕੀਤੀਆਂ ਗਈਆਂ\",\n\t\"show line numbers\": \"ਲਾਈਨ ਨੰਬਰ ਦਿਖਾਓ\",\n\t\"show hidden files\": \"ਲੁਕੀਆਂ ਹੋਈਆਂ ਫਾਈਲਾਂ ਦਿਖਾਓ\",\n\t\"show spaces\": \"ਸਪੇਸ ਦਿਖਾਓ\",\n\t\"soft tab\": \"ਸਾਫਟ ਟੈਬ\",\n\t\"sort by name\": \"ਨਾਮ ਦੁਆਰਾ ਛਾਂਟੋ\",\n\t\"success\": \"ਸਫਲਤਾ\",\n\t\"tab size\": \"ਟੈਬ ਦਾ ਆਕਾਰ\",\n\t\"text wrap\": \"ਟੈਕਸਟ ਰੈਪ\",\n\t\"theme\": \"ਥੀਮ\",\n\t\"unable to delete file\": \"ਫਾਈਲ ਨੂੰ ਮਿਟਾਉਣ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"unable to open file\": \"ਮਾਫ਼ ਕਰਨਾ, ਫ਼ਾਈਲ ਖੋਲ੍ਹਣ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"unable to open folder\": \"ਮਾਫ਼ ਕਰਨਾ, ਫੋਲਡਰ ਖੋਲ੍ਹਣ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"unable to save file\": \"ਮਾਫ਼ ਕਰਨਾ, ਫ਼ਾਈਲ ਨੂੰ ਸੁਰੱਖਿਅਤ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"unable to rename\": \"ਮਾਫ਼ ਕਰਨਾ, ਨਾਮ ਬਦਲਣ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"unsaved file\": \"ਇਹ ਫਾਈਲ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹੈ, ਫਿਰ ਵੀ ਬੰਦ ਕਰਨਾ ਹੈ?\",\n\t\"warning\": \"ਚੇਤਾਵਨੀ\",\n\t\"use emmet\": \"Emmet ਦੀ ਵਰਤੋਂ ਕਰੋ\",\n\t\"use quick tools\": \"ਤੇਜ਼ ਸਾਧਨਾਂ ਦੀ ਵਰਤੋਂ ਕਰੋ\",\n\t\"yes\": \"ਹਾਂ\",\n\t\"encoding\": \"ਟੈਕਸਟ ਇੰਕੋਡਿੰਗ\",\n\t\"syntax highlighting\": \"ਸਿੰਟੈਕਸ ਹਾਈਲਾਈਟਿੰਗ\",\n\t\"read only\": \"ਸਿਰਫ ਪੜ੍ਹਨ ਲਈ\",\n\t\"select all\": \"ਸਾਰਿਆ ਨੂੰ ਚੁਣੋ\",\n\t\"select branch\": \"ਸ਼ਾਖਾ ਚੁਣੋ\",\n\t\"create new branch\": \"ਨਵੀਂ ਸ਼ਾਖਾ ਬਣਾਓ\",\n\t\"use branch\": \"ਸ਼ਾਖਾ ਦੀ ਵਰਤੋਂ ਕਰੋ\",\n\t\"new branch\": \"ਨਵੀਂ ਸ਼ਾਖਾ\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"ਕੁੰਜੀ ਬੰਧਨ\",\n\t\"edit\": \"ਸੰਪਾਦਿਤ ਕਰੋ\",\n\t\"reset\": \"ਰੀਸੈਟ\",\n\t\"color\": \"ਰੰਗ\",\n\t\"select word\": \"ਸ਼ਬਦ ਚੁਣੋ\",\n\t\"quick tools\": \"ਤੇਜ਼ ਟੂਲ\",\n\t\"select\": \"ਚੁਣੋ\",\n\t\"editor font\": \"ਸੰਪਾਦਕ ਫੌਂਟ\",\n\t\"new project\": \"ਨਵਾਂ ਪ੍ਰੋਜੈਕਟ\",\n\t\"format\": \"ਫਾਰਮੈਟ\",\n\t\"project name\": \"ਪ੍ਰੋਜੈਕਟ ਦਾ ਨਾਮ\",\n\t\"unsupported device\": \"ਤੁਹਾਡੀ ਡਿਵਾਈਸ ਥੀਮ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ ਹੈ।\",\n\t\"vibrate on tap\": \"ਟੈਪ 'ਤੇ ਵਾਈਬ੍ਰੇਟ ਕਰੋ\",\n\t\"copy command is not supported by ftp.\": \"ਕਾਪੀ ਕਮਾਂਡ FTP ਦੁਆਰਾ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।\",\n\t\"support title\": \"Acode ਦਾ ਸਮਰਥਨ ਕਰੋ\",\n\t\"fullscreen\": \"ਪੂਰਾ ਸਕਰੀਨ\",\n\t\"animation\": \"ਐਨੀਮੇਸ਼ਨ\",\n\t\"backup\": \"ਬੈਕਅੱਪ\",\n\t\"restore\": \"ਬਹਾਲ\",\n\t\"backup successful\": \"ਬੈਕਅੱਪ ਸਫਲ\",\n\t\"invalid backup file\": \"ਅਵੈਧ ਬੈਕਅੱਪ ਫ਼ਾਈਲ\",\n\t\"add path\": \"ਫੋਲਡਰ ਸ਼ਾਮਲ ਕਰੋ\",\n\t\"live autocompletion\": \"ਲਾਈਵ ਸਵੈ-ਸੰਪੂਰਨਤਾ\",\n\t\"file properties\": \"ਫਾਈਲ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ\",\n\t\"path\": \"ਮਾਰਗ\",\n\t\"type\": \"ਟਾਈਪ\",\n\t\"word count\": \"ਸ਼ਬਦ ਗਿਣਤੀ\",\n\t\"line count\": \"ਲਾਈਨਾਂ ਦੀ ਗਿਣਤੀ\",\n\t\"last modified\": \"ਪਿਛਲੀ ਵਾਰ ਸੋਧਿਆ ਗਿਆ\",\n\t\"size\": \"ਆਕਾਰ\",\n\t\"share\": \"ਸ਼ੇਅਰ\",\n\t\"show print margin\": \"ਪ੍ਰਿੰਟ ਮਾਰਜਿਨ ਦਿਖਾਓ\",\n\t\"login\": \"ਲਾਗਿਨ\",\n\t\"scrollbar size\": \"ਸਕ੍ਰੋਲਬਾਰ ਦਾ ਆਕਾਰ\",\n\t\"cursor controller size\": \"ਕਰਸਰ ਕੰਟਰੋਲਰ ਦਾ ਆਕਾਰ\",\n\t\"none\": \"ਕੋਈ ਨਹੀਂ\",\n\t\"small\": \"ਛੋਟਾ\",\n\t\"large\": \"ਵੱਡਾ\",\n\t\"floating button\": \"ਫਲੋਟਿੰਗ ਬਟਨ\",\n\t\"confirm on exit\": \"ਬੰਦ ਹੋਣ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ\",\n\t\"show console\": \"ਕੰਸੋਲ ਦਿਖਾਓ\",\n\t\"image\": \"ਚਿੱਤਰ\",\n\t\"insert file\": \"ਫਾਈਲ ਸ਼ਾਮਲ ਕਰੋ\",\n\t\"insert color\": \"ਰੰਗ ਪਾਓ\",\n\t\"powersave mode warning\": \"ਬਾਹਰੀ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਪ੍ਰੀਵਿਊ ਕਰਨ ਲਈ ਪਾਵਰ ਸੇਵਿੰਗ ਮੋਡ ਨੂੰ ਬੰਦ ਕਰੋ।\",\n\t\"exit\": \"ਨਿਕਾਸ\",\n\t\"custom\": \"ਪ੍ਰਥਾ\",\n\t\"reset warning\": \"ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ 'ਤੇ ਥੀਮ ਨੂੰ ਰੀਸੈਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?\",\n\t\"theme type\": \"ਥੀਮ ਦੀ ਕਿਸਮ\",\n\t\"light\": \"ਰੋਸ਼ਨੀ\",\n\t\"dark\": \"ਹਨੇਰ\",\n\t\"file browser\": \"ਫਾਈਲ ਬ੍ਰਾਊਜ਼ਰ\",\n\t\"operation not permitted\": \"ਓਪਰੇਸ਼ਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ\",\n\t\"no such file or directory\": \"ਅਜਿਹੀ ਕੋਈ ਫਾਇਲ ਜਾਂ ਨਿਰਦੇਸ਼ਿਕਾ ਨਹੀਂ\",\n\t\"input/output error\": \"ਇਨਪੁਟ/ਆਊਟਪੁੱਟ ਗਲਤੀ\",\n\t\"permission denied\": \"ਆਗਿਆ ਤੋਂ ਇਨਕਾਰ\",\n\t\"bad address\": \"ਮਾੜਾ ਪਤਾ\",\n\t\"file exists\": \"ਫਾਈਲ ਮੌਜੂਦ ਹੈ\",\n\t\"not a directory\": \"ਡਾਇਰੈਕਟਰੀ ਨਹੀਂ\",\n\t\"is a directory\": \"ਇੱਕ ਡਾਇਰੈਕਟਰੀ ਹੈ\",\n\t\"invalid argument\": \"ਅਵੈਧ ਦਲੀਲ\",\n\t\"too many open files in system\": \"ਸਿਸਟਮ ਵਿੱਚ ਬਹੁਤ ਸਾਰੀਆਂ ਖੁੱਲ੍ਹੀਆਂ ਫਾਈਲਾਂ\",\n\t\"too many open files\": \"ਬਹੁਤ ਸਾਰੀਆਂ ਖੁੱਲ੍ਹੀਆਂ ਫ਼ਾਈਲਾਂ\",\n\t\"text file busy\": \"ਟੈਕਸਟ ਫਾਈਲ ਵਿਅਸਤ ਹੈ\",\n\t\"no space left on device\": \"ਡਿਵਾਈਸ 'ਤੇ ਕੋਈ ਥਾਂ ਨਹੀਂ ਬਚੀ ਹੈ\",\n\t\"read-only file system\": \"ਸਿਰਫ਼-ਪੜ੍ਹਨ ਲਈ ਫਾਈਲ ਸਿਸਟਮ\",\n\t\"file name too long\": \"ਫ਼ਾਈਲ ਦਾ ਨਾਮ ਬਹੁਤ ਲੰਮਾ ਹੈ\",\n\t\"too many users\": \"ਬਹੁਤ ਸਾਰੇ ਉਪਭੋਗਤਾ\",\n\t\"connection timed out\": \"ਕਨੈਕਸ਼ਨ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ\",\n\t\"connection refused\": \"ਸੰਬੰਧ ਠੁਕਰਾਉਣਾ\",\n\t\"owner died\": \"ਮਾਲਕ ਦੀ ਮੌਤ ਹੋ ਗਈ\",\n\t\"an error occurred\": \"ਇੱਕ ਗਲਤੀ ਆਈ ਹੈ\",\n\t\"add ftp\": \"FTP ਸ਼ਾਮਲ ਕਰੋ\",\n\t\"add sftp\": \"SFTP ਸ਼ਾਮਲ ਕਰੋ\",\n\t\"save file\": \"ਫਾਈਲ ਸੇਵ ਕਰੋ\",\n\t\"save file as\": \"ਫਾਈਲ ਨੂੰ ਇਸ ਤਰ੍ਹਾਂ ਸੇਵ ਕਰੋ\",\n\t\"files\": \"ਫਾਈਲਾਂ\",\n\t\"help\": \"ਮਦਦ\",\n\t\"file has been deleted\": \"{file} ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ!\",\n\t\"feature not available\": \"ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਐਪ ਦੇ ਭੁਗਤਾਨ ਕੀਤੇ ਸੰਸਕਰਣ ਵਿੱਚ ਹੀ ਉਪਲਬਧ ਹੈ।\",\n\t\"deleted file\": \"ਮਿਟਾਈ ਗਈ ਫਾਈਲ\",\n\t\"line height\": \"ਲਾਈਨ ਦੀ ਉਚਾਈ\",\n\t\"preview info\": \"ਜੇਕਰ ਤੁਸੀਂ ਐਕਟਿਵ ਫਾਈਲ ਨੂੰ ਚਲਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ ਪਲੇ ਆਈਕਨ 'ਤੇ ਟੈਪ ਕਰੋ ਅਤੇ ਹੋਲਡ ਕਰੋ।\",\n\t\"manage all files\": \"Acode ਸੰਪਾਦਕ ਨੂੰ ਤੁਹਾਡੀ ਡਿਵਾਈਸ 'ਤੇ ਆਸਾਨੀ ਨਾਲ ਫਾਈਲਾਂ ਨੂੰ ਸੰਪਾਦਿਤ ਕਰਨ ਲਈ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਸਾਰੀਆਂ ਫਾਈਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦਿਓ\",\n\t\"close file\": \"ਫਾਈਲ ਬੰਦ ਕਰੋ\",\n\t\"reset connections\": \"ਕਨੈਕਸ਼ਨ ਰੀਸੈਟ ਕਰੋ\",\n\t\"check file changes\": \"ਫਾਈਲ ਤਬਦੀਲੀਆਂ ਦੀ ਜਾਂਚ ਕਰੋ\",\n\t\"open in browser\": \"ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ\",\n\t\"desktop mode\": \"ਡੈਸਕਟਾਪ ਮੋਡ\",\n\t\"toggle console\": \"ਕੰਸੋਲ ਟੌਗਲ ਕਰੋ\",\n\t\"new line mode\": \"ਨਵਾਂ ਲਾਈਨ ਮੋਡ\",\n\t\"add a storage\": \"ਸਟੋਰੇਜ ਸ਼ਾਮਲ ਕਰੋ\",\n\t\"rate acode\": \"Acode ਨੂੰ ਰੇਟ ਕਰੋ (NiceSapien ਦੁਆਰਾ ਪੰਜਾਬੀ ਅਨੁਵਾਦ)\",\n\t\"support\": \"ਸਪੋਰਟ\",\n\t\"downloading file\": \"ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ {file}\",\n\t\"downloading...\": \"ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\",\n\t\"folder name\": \"ਫੋਲਡਰ ਦਾ ਨਾਮ\",\n\t\"keyboard mode\": \"ਕੀਬੋਰਡ ਮੋਡ\",\n\t\"normal\": \"ਸਧਾਰਣ\",\n\t\"app settings\": \"ਐਪ ਸੈਟਿੰਗਾਂ\",\n\t\"disable in-app-browser caching\": \"ਇਨ-ਐਪ-ਬ੍ਰਾਊਜ਼ਰ ਕੈਚਿੰਗ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਓ\",\n\t\"copied to clipboard\": \"ਕਲਿੱਪਬੋਰਡ 'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ\",\n\t\"remember opened files\": \"ਖੋਲ੍ਹੀਆਂ ਫਾਈਲਾਂ ਨੂੰ ਯਾਦ ਰੱਖੋ\",\n\t\"remember opened folders\": \"ਖੋਲ੍ਹੇ ਫੋਲਡਰਾਂ ਨੂੰ ਯਾਦ ਰੱਖੋ\",\n\t\"no suggestions\": \"ਕੋਈ ਸੁਝਾਅ ਨਹੀਂ\",\n\t\"no suggestions aggressive\": \"ਕੋਈ ਸੁਝਾਅ ਹਮਲਾਵਰ ਨਹੀਂ ਹਨ\",\n\t\"install\": \"ਇੰਸਟਾਲ ਕਰੋ\",\n\t\"installing\": \"ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\",\n\t\"plugins\": \"ਪਲੱਗਇਨ\",\n\t\"recently used\": \"ਹਾਲ ਹੀ ਵਿੱਚ ਵਰਤਿਆ\",\n\t\"update\": \"ਅੱਪਡੇਟ ਕਰੋ\",\n\t\"uninstall\": \"ਅਣਇੰਸਟੌਲ ਕਰੋ\",\n\t\"download acode pro\": \"Acode ਪ੍ਰੋ ਨੂੰ ਡਾਊਨਲੋਡ ਕਰੋ\",\n\t\"loading plugins\": \"ਪਲੱਗਇਨ ਲੋਡ ਕੀਤੇ ਜਾ ਰਹੇ ਹਨ\",\n\t\"faqs\": \"ਅਕਸਰ ਪੁੱਛੇ ਜਾਂਦੇ ਸਵਾਲ\",\n\t\"feedback\": \"ਸੁਝਾਅ\",\n\t\"header\": \"ਸਿਰਲੇਖ\",\n\t\"sidebar\": \"ਸਾਈਡਬਾਰ\",\n\t\"inapp\": \"ਇਨਐਪ\",\n\t\"browser\": \"ਬ੍ਰਾਊਜ਼ਰ\",\n\t\"diagonal scrolling\": \"ਡਾਇਗਨਲ ਸਕ੍ਰੋਲਿੰਗ\",\n\t\"reverse scrolling\": \"ਰਿਵਰਸ ਸਕ੍ਰੋਲਿੰਗ\",\n\t\"formatter\": \"ਫਾਰਮੈਟਰ\",\n\t\"format on save\": \"ਸੇਵ 'ਤੇ ਫਾਰਮੈਟ\",\n\t\"remove ads\": \"ਵਿਗਿਆਪਨ ਹਟਾਓ\",\n\t\"fast\": \"ਤੇਜ਼\",\n\t\"slow\": \"ਹੌਲੀ\",\n\t\"scroll settings\": \"ਸਕ੍ਰੋਲ ਸੈਟਿੰਗਾਂ\",\n\t\"scroll speed\": \"ਸਕ੍ਰੌਲ ਸਪੀਡ\",\n\t\"loading...\": \"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\",\n\t\"no plugins found\": \"ਕੋਈ ਪਲੱਗਇਨ ਨਹੀਂ ਮਿਲੇ\",\n\t\"name\": \"ਨਾਮ\",\n\t\"username\": \"ਯੂਜ਼ਰਨੇਮ\",\n\t\"optional\": \"ਵਿਕਲਪਿਕ\",\n\t\"hostname\": \"ਹੋਸਟਨਾਮ\",\n\t\"password\": \"ਪਾਸਵਰਡ\",\n\t\"security type\": \"ਸੁਰੱਖਿਆ ਦੀ ਕਿਸਮ\",\n\t\"connection mode\": \"ਕਨੈਕਸ਼ਨ ਮੋਡ\",\n\t\"port\": \"ਪੋਰਟ\",\n\t\"key file\": \"ਕੁੰਜੀ ਫਾਈਲ\",\n\t\"select key file\": \"ਕੁੰਜੀ ਫਾਈਲ ਚੁਣੋ\",\n\t\"passphrase\": \"ਪਾਸਫਰੇਜ\",\n\t\"connecting...\": \"ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...\",\n\t\"type filename\": \"ਫਾਈਲ ਨਾਮ ਟਾਈਪ ਕਰੋ\",\n\t\"unable to load files\": \"ਫਾਈਲਾਂ ਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ\",\n\t\"preview port\": \"ਝਲਕ ਪੋਰਟ\",\n\t\"find file\": \"ਫਾਈਲ ਲੱਭੋ\",\n\t\"system\": \"ਸਿਸਟਮ\",\n\t\"please select a formatter\": \"ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਫਾਰਮੈਟਰ ਚੁਣੋ\",\n\t\"case sensitive\": \"ਕੇਸ ਸੰਸੇਟਿਵ\",\n\t\"regular expression\": \"ਨਿਯਮਤ ਸਮੀਕਰਨ\",\n\t\"whole word\": \"ਪੂਰਾ ਸ਼ਬਦ\",\n\t\"edit with\": \"ਨਾਲ ਸੰਪਾਦਿਤ ਕਰੋ\",\n\t\"open with\": \"ਨਾਲ ਖੋਲ੍ਹੋ\",\n\t\"no app found to handle this file\": \"ਇਸ ਫ਼ਾਈਲ ਨੂੰ ਸੰਭਾਲਣ ਲਈ ਕੋਈ ਐਪ ਨਹੀਂ ਮਿਲੀ\",\n\t\"restore default settings\": \"ਡਿਫੌਲਟ ਸੈਟਿੰਗਾਂ ਨੂੰ ਰੀਸਟੋਰ ਕਰੋ\",\n\t\"server port\": \"ਸਰਵਰ ਪੋਰਟ\",\n\t\"preview settings\": \"ਪੂਰਵਦਰਸ਼ਨ ਸੈਟਿੰਗਾਂ\",\n\t\"preview settings note\": \"ਜੇਕਰ ਪ੍ਰੀਵਿਊ ਪੋਰਟ ਅਤੇ ਸਰਵਰ ਪੋਰਟ ਵੱਖ-ਵੱਖ ਹਨ, ਤਾਂ ਐਪ ਸਰਵਰ ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕਰੇਗਾ ਅਤੇ ਇਸ ਦੀ ਬਜਾਏ ਬ੍ਰਾਊਜ਼ਰ ਜਾਂ ਇਨ-ਐਪ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ https://<host>:<preview port> ਖੋਲ੍ਹੇਗਾ। ਇਹ ਉਦੋਂ ਲਾਭਦਾਇਕ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਕਿਤੇ ਹੋਰ ਸਰਵਰ ਚਲਾ ਰਹੇ ਹੋ।\",\n\t\"backup/restore note\": \"ਇਹ ਸਿਰਫ਼ ਤੁਹਾਡੀਆਂ ਸੈਟਿੰਗਾਂ, ਕਸਟਮ ਥੀਮ ਅਤੇ ਕੁੰਜੀ ਬਾਈਡਿੰਗ ਦਾ ਬੈਕਅੱਪ ਲਵੇਗਾ। ਇਹ ਤੁਹਾਡੇ FTP/SFTP, GitHub ਪ੍ਰੋਫਾਈਲਾਂ ਦਾ ਬੈਕਅੱਪ ਨਹੀਂ ਲਵੇਗਾ।\",\n\t\"host\": \"ਮੇਜ਼ਬਾਨ\",\n\t\"retry ftp/sftp when fail\": \"ਅਸਫਲ ਹੋਣ 'ਤੇ ftp/sftp ਦੀ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ\",\n\t\"more\": \"ਹੋਰ\",\n\t\"thank you :)\": \"ਤੁਹਾਡਾ ਧੰਨਵਾਦ :)\",\n\t\"purchase pending\": \"ਖਰੀਦ ਬਕਾਇਆ\",\n\t\"cancelled\": \"ਰੱਦ ਕਰ ਦਿੱਤਾ\",\n\t\"local\": \"ਸਥਾਨਕ\",\n\t\"remote\": \"ਰਿਮੋਟ\",\n\t\"show console toggler\": \"ਕੰਸੋਲ ਟੌਗਲਰ ਦਿਖਾਓ\",\n\t\"binary file\": \"ਇਸ ਫ਼ਾਈਲ ਵਿੱਚ ਬਾਈਨਰੀ ਡਾਟਾ ਹੈ, ਕੀ ਤੁਸੀਂ ਇਸਨੂੰ ਖੋਲ੍ਹਣਾ ਚਾਹੁੰਦੇ ਹੋ?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"ਸਪਾਂਸਰ\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/ru-ru.json",
    "content": "{\n\t\"lang\": \"Русский\",\n\t\"about\": \"О приложении\",\n\t\"active files\": \"Открытые файлы\",\n\t\"alert\": \"Предупреждение\",\n\t\"app theme\": \"Тема приложения\",\n\t\"autocorrect\": \"Автозамена\",\n\t\"autosave\": \"Автосохранение\",\n\t\"cancel\": \"Отмена\",\n\t\"change language\": \"Изменить язык\",\n\t\"choose color\": \"Выбрать цвет\",\n\t\"clear\": \"Очистить\",\n\t\"close app\": \"Закрыть приложение?\",\n\t\"commit message\": \"Сообщение к коммиту\",\n\t\"console\": \"Консоль\",\n\t\"conflict error\": \"Конфликт! Пожалуйста подождите, прежде чем оставлять коммит\",\n\t\"copy\": \"Копировать\",\n\t\"create folder error\": \"Не удалось создать папку\",\n\t\"cut\": \"Вырезать\",\n\t\"delete\": \"Удалить\",\n\t\"dependencies\": \"Зависимости\",\n\t\"delay\": \"Задержка в миллисекундах\",\n\t\"editor settings\": \"Настройки редактора\",\n\t\"editor theme\": \"Тема\",\n\t\"enter file name\": \"Введите имя файла\",\n\t\"enter folder name\": \"Введите имя папки\",\n\t\"empty folder message\": \"Пустая папка\",\n\t\"enter line number\": \"Введите номер строки\",\n\t\"error\": \"Ошибка\",\n\t\"failed\": \"Провал\",\n\t\"file already exists\": \"Файл уже существует\",\n\t\"file already exists force\": \"Файл уже существует. Перезаписать?\",\n\t\"file changed\": \" был изменён, перезагрузить его?\",\n\t\"file deleted\": \"Файл удалён\",\n\t\"file is not supported\": \"Файл не поддерживается\",\n\t\"file not supported\": \"Данный тип файла не поддерживается.\",\n\t\"file too large\": \"Файл слишком большой. Допустимый размер: {size}\",\n\t\"file renamed\": \"Файл переименован\",\n\t\"file saved\": \"Файл сохранен\",\n\t\"folder added\": \"Папка добавлена\",\n\t\"folder already added\": \"Папка уже добавлена\",\n\t\"font size\": \"Размер шрифта\",\n\t\"goto\": \"Перейти к строке\",\n\t\"icons definition\": \"Определение значков\",\n\t\"info\": \"Информация\",\n\t\"invalid value\": \"Неверное значение\",\n\t\"language changed\": \"Язык успешно изменен\",\n\t\"linting\": \"Проверять синтаксические ошибки\",\n\t\"logout\": \"Выйти\",\n\t\"loading\": \"Загрузка\",\n\t\"my profile\": \"Мой профиль\",\n\t\"new file\": \"Новый файл\",\n\t\"new folder\": \"Новая папка\",\n\t\"no\": \"Нет\",\n\t\"no editor message\": \"Откройте или создайте новый файл\",\n\t\"not set\": \"Не задано\",\n\t\"unsaved files close app\": \"Имеются несохранённые файлы. Всё равно выйти?\",\n\t\"notice\": \"Уведомление\",\n\t\"open file\": \"Открыть файл\",\n\t\"open files and folders\": \"Открытые файлы и папки\",\n\t\"open folder\": \"Открыть папку\",\n\t\"open recent\": \"Открыть недавние\",\n\t\"ok\": \"OК\",\n\t\"overwrite\": \"Перезаписать\",\n\t\"paste\": \"Вставить\",\n\t\"preview mode\": \"Режим предпросмотра\",\n\t\"read only file\": \"Файл открыт в режиме «Только для чтения»\",\n\t\"reload\": \"Перезагрузить\",\n\t\"rename\": \"Переименовать\",\n\t\"replace\": \"Заменить\",\n\t\"required\": \"Это поле обязательно к заполнению\",\n\t\"run your web app\": \"Запустить своё веб-приложение\",\n\t\"save\": \"Сохранить\",\n\t\"saving\": \"Сохранение\",\n\t\"save as\": \"Сохранить как\",\n\t\"save file to run\": \"Сохраните файл для запуска в браузере\",\n\t\"search\": \"Поиск\",\n\t\"see logs and errors\": \"Показать логи и ошибки\",\n\t\"select folder\": \"Выбрать папку\",\n\t\"settings\": \"Настройки\",\n\t\"settings saved\": \"Настройки сохранены\",\n\t\"show line numbers\": \"Показывать нумерацию строк\",\n\t\"show hidden files\": \"Показывать скрытые файлы\",\n\t\"show spaces\": \"Показывать отступы\",\n\t\"soft tab\": \"Использовать пробелы вместо Tab\",\n\t\"sort by name\": \"Сортировать по имени\",\n\t\"success\": \"Успешно\",\n\t\"tab size\": \"Количество пробелов в табе\",\n\t\"text wrap\": \"Перенос строк\",\n\t\"theme\": \"Тема\",\n\t\"unable to delete file\": \"Невозможно удалить файл\",\n\t\"unable to open file\": \"Невозможно открыть файл\",\n\t\"unable to open folder\": \"Невозможно открыть папку\",\n\t\"unable to save file\": \"Невозможно сохранить файл\",\n\t\"unable to rename\": \"Невозможно переименовать\",\n\t\"unsaved file\": \"Файл не сохранён, закрыть?\",\n\t\"warning\": \"Предупреждение\",\n\t\"use emmet\": \"Использовать Emmet\",\n\t\"use quick tools\": \"Использовать быстрые инструменты\",\n\t\"yes\": \"Да\",\n\t\"encoding\": \"Кодировка\",\n\t\"syntax highlighting\": \"Подсветка синтаксиса\",\n\t\"read only\": \"Только для чтения\",\n\t\"select all\": \"Выбрать всё\",\n\t\"select branch\": \"Выберите ветку\",\n\t\"create new branch\": \"Создать новую ветку\",\n\t\"use branch\": \"Использовать ветку\",\n\t\"new branch\": \"Новая ветка\",\n\t\"branch\": \"Ветка\",\n\t\"key bindings\": \"Сочетания клавиш\",\n\t\"edit\": \"Редактировать\",\n\t\"reset\": \"Сброс\",\n\t\"color\": \"Цвет\",\n\t\"select word\": \"Выбрать слово\",\n\t\"quick tools\": \"Быстрые инструменты\",\n\t\"select\": \"Выбрать\",\n\t\"editor font\": \"Шрифт\",\n\t\"new project\": \"Новый проект\",\n\t\"format\": \"Форматировать\",\n\t\"project name\": \"Название проекта\",\n\t\"unsupported device\": \"На этом устройстве тема не поддерживается\",\n\t\"vibrate on tap\": \"Вибрация при нажатии\",\n\t\"copy command is not supported by ftp.\": \"Копирование пока не поддерживается в режиме FTP\",\n\t\"support title\": \"Поддержи Acode\",\n\t\"fullscreen\": \"Полноэкранный режим\",\n\t\"animation\": \"Анимация\",\n\t\"backup\": \"Резервное копирование\",\n\t\"restore\": \"Восстановление\",\n\t\"backup successful\": \"Бэкап создан\",\n\t\"invalid backup file\": \"Некорректный файл бэкапа\",\n\t\"add path\": \"Добавить путь\",\n\t\"live autocompletion\": \"Мгновенное автозавершение\",\n\t\"file properties\": \"Свойство файла\",\n\t\"path\": \"Путь\",\n\t\"type\": \"Тип\",\n\t\"word count\": \"Количество слов\",\n\t\"line count\": \"Количество строк\",\n\t\"last modified\": \"Последнее изменение\",\n\t\"size\": \"Размер\",\n\t\"share\": \"Поделиться\",\n\t\"show print margin\": \"Показать поле печати\",\n\t\"login\": \"Вход\",\n\t\"scrollbar size\": \"Размер полосы прокрутки\",\n\t\"cursor controller size\": \"Размер контроллера курсора\",\n\t\"none\": \"Выкл.\",\n\t\"small\": \"Маленький\",\n\t\"large\": \"Большой\",\n\t\"floating button\": \"Плавающая кнопка\",\n\t\"confirm on exit\": \"Подтверждение перед выходом\",\n\t\"show console\": \"Показать консоль\",\n\t\"image\": \"Изображение\",\n\t\"insert file\": \"Вставить файл\",\n\t\"insert color\": \"Вставить цвет\",\n\t\"powersave mode warning\": \"Отключите режим энергосбережения для предварительного просмотра во внешнем браузере\",\n\t\"exit\": \"Выйти\",\n\t\"custom\": \"Пользовательский\",\n\t\"reset warning\": \"Вы уверены, что хотите сбросить тему?\",\n\t\"theme type\": \"Тип темы\",\n\t\"light\": \"Светлая\",\n\t\"dark\": \"Тёмная\",\n\t\"file browser\": \"Файловый менеджер\",\n\t\"operation not permitted\": \"Операция не разрешена\",\n\t\"no such file or directory\": \"Данный файл или каталог отсутствует\",\n\t\"input/output error\": \"Ошибка ввода-вывода\",\n\t\"permission denied\": \"Доступ запрещён\",\n\t\"bad address\": \"Неверный адрес\",\n\t\"file exists\": \"Файл существует\",\n\t\"not a directory\": \"Это не каталог\",\n\t\"is a directory\": \"Это каталог\",\n\t\"invalid argument\": \"Недопустимый аргумент\",\n\t\"too many open files in system\": \"Слишком много открытых файлов в системе\",\n\t\"too many open files\": \"Слишком много открытых файлов\",\n\t\"text file busy\": \"Текстовый файл занят\",\n\t\"no space left on device\": \"На устройстве не осталось свободного места\",\n\t\"read-only file system\": \"Файловая система, доступная только для чтения\",\n\t\"file name too long\": \"Название файла слишком длинное\",\n\t\"too many users\": \"Слишком много пользователей\",\n\t\"connection timed out\": \"Время соединения истекло\",\n\t\"connection refused\": \"Отказано в соединении\",\n\t\"owner died\": \"Владелец умер\",\n\t\"an error occurred\": \"Произошла ошибка\",\n\t\"add ftp\": \"Добавить FTP\",\n\t\"add sftp\": \"Добавить SFTP\",\n\t\"save file\": \"Сохранить файл\",\n\t\"save file as\": \"Сохранить как\",\n\t\"files\": \"Файлы\",\n\t\"help\": \"Помощь\",\n\t\"file has been deleted\": \"{file} удалён!\",\n\t\"feature not available\": \"Эта функция доступна только в платной версии приложения.\",\n\t\"deleted file\": \"Удалить файл\",\n\t\"line height\": \"Высота строки\",\n\t\"preview info\": \"Если вы хотите запустить активный файл, нажмите и удерживайте значок воспроизведения\",\n\t\"manage all files\": \"Дайте Acode разрешение на доступ ко всем файлам для их редактирования в настройках\",\n\t\"close file\": \"Закрыть файл\",\n\t\"reset connections\": \"Сбросить соединения\",\n\t\"check file changes\": \"Проверять изменения файлов\",\n\t\"open in browser\": \"Открыть в браузере\",\n\t\"desktop mode\": \"Режим ПК\",\n\t\"toggle console\": \"Переключить консоль\",\n\t\"new line mode\": \"Перенос строки\",\n\t\"add a storage\": \"Добавить хранилище\",\n\t\"rate acode\": \"Оценить Acode\",\n\t\"support\": \"Поддержка\",\n\t\"downloading file\": \"Загрузка {file}\",\n\t\"downloading...\": \"Загрузка...\",\n\t\"folder name\": \"Имя папки\",\n\t\"keyboard mode\": \"Режим клавиатуры\",\n\t\"normal\": \"По умолчанию\",\n\t\"app settings\": \"Настройки приложения\",\n\t\"disable in-app-browser caching\": \"Отключить кэширование во встроенном браузере\",\n\t\"copied to clipboard\": \"Скопировано в буфер обмена\",\n\t\"remember opened files\": \"Запоминать открытые файлы\",\n\t\"remember opened folders\": \"Запоминать открытые папки\",\n\t\"no suggestions\": \"Откл. подсказок\",\n\t\"no suggestions aggressive\": \"Агрессивное откл. подсказок\",\n\t\"install\": \"Установить\",\n\t\"installing\": \"Установка...\",\n\t\"plugins\": \"Плагины\",\n\t\"recently used\": \"Недавно использованные\",\n\t\"update\": \"Обновить\",\n\t\"uninstall\": \"Удалить\",\n\t\"download acode pro\": \"Скачать Acode Pro\",\n\t\"loading plugins\": \"Загрузка плагинов\",\n\t\"faqs\": \"ЧаВо\",\n\t\"feedback\": \"Обратная связь\",\n\t\"header\": \"Сверху\",\n\t\"sidebar\": \"Сбоку\",\n\t\"inapp\": \"Встроенный браузер\",\n\t\"browser\": \"Внешний браузер\",\n\t\"diagonal scrolling\": \"Диагональный скроллинг\",\n\t\"reverse scrolling\": \"Обратный скроллинг\",\n\t\"formatter\": \"Форматтер\",\n\t\"format on save\": \"Форматирование при сохранении\",\n\t\"remove ads\": \"Удалить рекламу\",\n\t\"fast\": \"Быстрая\",\n\t\"slow\": \"Медленная\",\n\t\"scroll settings\": \"Настройки скроллинга\",\n\t\"scroll speed\": \"Скорость скроллинга\",\n\t\"loading...\": \"Загрузка...\",\n\t\"no plugins found\": \"Плагины не найдены\",\n\t\"name\": \"Название\",\n\t\"username\": \"Имя пользователя\",\n\t\"optional\": \"необязательно\",\n\t\"hostname\": \"Имя хоста\",\n\t\"password\": \"Пароль\",\n\t\"security type\": \"Тип защиты\",\n\t\"connection mode\": \"Режим подключения\",\n\t\"port\": \"Порт\",\n\t\"key file\": \"Ключ\",\n\t\"select key file\": \"Выбрать файл ключа\",\n\t\"passphrase\": \"Пасс-фраза\",\n\t\"connecting...\": \"Подключение...\",\n\t\"type filename\": \"Введите имя файла\",\n\t\"unable to load files\": \"Не удалось загрузить файлы\",\n\t\"preview port\": \"Порт предпросмотра\",\n\t\"find file\": \"Поиск\",\n\t\"system\": \"Как в системе\",\n\t\"please select a formatter\": \"Выберите форматтер\",\n\t\"case sensitive\": \"Учитывать регистр\",\n\t\"regular expression\": \"Регулярное выражение\",\n\t\"whole word\": \"Целые слова\",\n\t\"edit with\": \"Редактировать в...\",\n\t\"open with\": \"Открыть в...\",\n\t\"no app found to handle this file\": \"Не найдено ни одного приложения для открытия этого типа файлов\",\n\t\"restore default settings\": \"Восстановить настройки по умолчанию\",\n\t\"server port\": \"Порт сервера\",\n\t\"preview settings\": \"Настройки предпросмотра\",\n\t\"preview settings note\": \"Если порт предпросмотра и порт сервера отличаются, приложение не запустит встроенный сервер, а только откроет https://<хост>:<порт> в браузере. Полезно при использовании сервера на другом устройстве.\",\n\t\"backup/restore note\": \"Будут сохранены только настройки, темы и сочетания клавиш. FTP/SFTP-сервера и профили GitHub не резервируются.\",\n\t\"host\": \"Хост\",\n\t\"retry ftp/sftp when fail\": \"Повторять попытку подключения к FTP/SFTP при ошибке\",\n\t\"more\": \"Ещё\",\n\t\"thank you :)\": \"Спасибо :)\",\n\t\"purchase pending\": \"Ожидание оплаты\",\n\t\"cancelled\": \"Отменено\",\n\t\"local\": \"Локальный\",\n\t\"remote\": \"Удалённый\",\n\t\"show console toggler\": \"Кнопка переключения консоли\",\n\t\"binary file\": \"Файл содержит бинарные данные, открыть?\",\n\t\"relative line numbers\": \"Относительный номер строки\",\n\t\"elastic tabstops\": \"Эластичная табуляция (автовыравнивание табов)\",\n\t\"line based rtl switching\": \"Переключение RTL на основе содержимого строки\",\n\t\"hard wrap\": \"Разрыв строки в месте переноса\",\n\t\"spellcheck\": \"Проверка правописания\",\n\t\"wrap method\": \"Способ переноса строк\",\n\t\"use textarea for ime\": \"Использовать область ввода для IME\",\n\t\"invalid plugin\": \"Некорректный файл плагина\",\n\t\"type command\": \"Введите команду\",\n\t\"plugin\": \"Плагин\",\n\t\"quicktools trigger mode\": \"Режим обработки нажатий в быстрых инструментах\",\n\t\"print margin\": \"Размер поля печати\",\n\t\"touch move threshold\": \"Чувствительность сенсора при перемещении\",\n\t\"info-retryremotefsafterfail\": \"Повторять попытку подключения к FTP/SFTP при ошибке\",\n\t\"info-fullscreen\": \"Скрыть заголовок на главном экране\",\n\t\"info-checkfiles\": \"Проверять изменения файлов в фоновом режиме\",\n\t\"info-console\": \"Выбор консоли JavaScript: Legacy — стандартная, Eruda — сторонняя с поддержкой пользовательских скриптов\",\n\t\"info-keyboardmode\": \"Режим клавиатуры для ввода текста: \\\"Откл. подсказок\\\" отключит подсказки и автоисправление. Если этот режим не сработает, попробуйте \\\"Принудительное откл. подсказок\\\"\",\n\t\"info-rememberfiles\": \"Запоминать открытые файлы после выхода из приложения\",\n\t\"info-rememberfolders\": \"Запоминать открытые папки после выхода из приложения\",\n\t\"info-floatingbutton\": \"Показать или скрыть плавающую кнопку быстрых инструментов\",\n\t\"info-openfilelistpos\": \"Где показывать список открытых файлов\",\n\t\"info-touchmovethreshold\": \"Если на Вашем устройстве слишком чувствительный сенсорный экран, увеличьте значение для предотвращения случайных перемещений курсора/полосы прокрутки\",\n\t\"info-scroll-settings\": \"Этот пункт содержит настройки скролла и переноса строк\",\n\t\"info-animation\": \"Отключите анимацию при лагах и зависаниях в приложении\",\n\t\"info-quicktoolstriggermode\": \"Попробуйте сменить значение этой настройки если не работают кнопки быстрых инструментов\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Свои\",\n\t\"api_error\": \"Сервер API недоступен, повторите попытку позже\",\n\t\"installed\": \"Установленные\",\n\t\"all\": \"Все\",\n\t\"medium\": \"Средний\",\n\t\"refund\": \"Возврат\",\n\t\"product not available\": \"Плагин недоступен\",\n\t\"no-product-info\": \"Плагин недоступен в Вашем регионе на данный момент, попробуйте позже\",\n\t\"close\": \"Закрыть\",\n\t\"explore\": \"Поиск плагинов\",\n\t\"key bindings updated\": \"Сочетания клавиш обновлены\",\n\t\"search in files\": \"Найти в файлах\",\n\t\"exclude files\": \"Исключить файлы\",\n\t\"include files\": \"Включить файлы\",\n\t\"search result\": \"{matches} совпадений обнаружено в {files} файлах.\",\n\t\"invalid regex\": \"Некорректное регулярное выражение: {message}.\",\n\t\"bottom\": \"Снизу\",\n\t\"save all\": \"Сохранить всё\",\n\t\"close all\": \"Закрыть всё\",\n\t\"unsaved files warning\": \"Некоторые файлы не сохранены. Нажмите 'OK' чтобы продолжить или 'Отмена' для возвращения.\",\n\t\"save all warning\": \"Вы действительно хотите сохранить и закрыть всё?\",\n\t\"save all changes warning\": \"Вы действительно хотите сохранить все файлы?\",\n\t\"close all warning\": \"Вы действительно хотите закрыть всё? Все ваши несохранённые файлы или изменения не сохранятся\",\n\t\"refresh\": \"Обновить\",\n\t\"shortcut buttons\": \"Иконки быстрого доступа\",\n\t\"no result\": \"Нет результатов\",\n\t\"searching...\": \"Поиск...\",\n\t\"quicktools:ctrl-key\": \"Control/Command клавиша\",\n\t\"quicktools:tab-key\": \"Клав. Tab\",\n\t\"quicktools:shift-key\": \"Клав. Shift\",\n\t\"quicktools:undo\": \"Отмена\",\n\t\"quicktools:redo\": \"Повтор\",\n\t\"quicktools:search\": \"Поиск в файле\",\n\t\"quicktools:save\": \"Сохранить файл\",\n\t\"quicktools:esc-key\": \"Клав. Escape\",\n\t\"quicktools:curlybracket\": \"Вставить фигурную скобку\",\n\t\"quicktools:squarebracket\": \"Вставить квадратную скобку\",\n\t\"quicktools:parentheses\": \"Вставить круглые скобки\",\n\t\"quicktools:anglebracket\": \"Вставить угловую скобку\",\n\t\"quicktools:left-arrow-key\": \"Клав. стрелка влево\",\n\t\"quicktools:right-arrow-key\": \"Клав. стрелка вправо\",\n\t\"quicktools:up-arrow-key\": \"Клав. стрелка вверх\",\n\t\"quicktools:down-arrow-key\": \"Клав. стрелка вниз \",\n\t\"quicktools:moveline-up\": \"Перенос на линию выше\",\n\t\"quicktools:moveline-down\": \"Перенос на линию ниже\",\n\t\"quicktools:copyline-up\": \"Копировать линию выше\",\n\t\"quicktools:copyline-down\": \"Копировать линию ниже\",\n\t\"quicktools:semicolon\": \"Вставить точку с запятой\",\n\t\"quicktools:quotation\": \"Вставить цитату\",\n\t\"quicktools:and\": \"Вставка и символ\",\n\t\"quicktools:bar\": \"Вставка символа полосы\",\n\t\"quicktools:equal\": \"Вставка эквивалентного символа\",\n\t\"quicktools:slash\": \"Вставка слэш-символа\",\n\t\"quicktools:exclamation\": \"Вставить восклицательный знак\",\n\t\"quicktools:alt-key\": \"Клав. Alt\",\n\t\"quicktools:meta-key\": \"Клав. Windows/Meta\",\n\t\"info-quicktoolssettings\": \"Настройка кнопок на панели быстрых инструментов\",\n\t\"info-excludefolders\": \"Используйте шаблон **/node_modules/**, чтобы игнорировать все файлы из папки node_modules. Это исключит файлы из списка, а также предотвратит их включение в поиске файлов.\",\n\t\"missed files\": \"Найдено {count} файлов, они не будут учитываться.\",\n\t\"remove\": \"Удалить\",\n\t\"quicktools:command-palette\": \"Палитра команд\",\n\t\"default file encoding\": \"Кодировка по умолчанию\",\n\t\"remove entry\": \"Вы уверены, что хотите убрать '{name}' из сохранённых путей? Сама папка удалена не будет.\",\n\t\"delete entry\": \"Удалить '{name}'? Действие отменить невозможно.\",\n\t\"change encoding\": \"Переоткрыть '{file}' с кодировкой '{encoding}'? Вы потеряете все несохранённые изменения.\",\n\t\"reopen file\": \"Переоткрыть '{file}'? Вы потеряете все несохранённые изменения.\",\n\t\"plugin min version\": \"{name} доступен только для Acode версии {v-code} и выше. Нажмите для обновления.\",\n\t\"color preview\": \"Предпросмотр цвета\",\n\t\"confirm\": \"Подтверждение\",\n\t\"list files\": \"В папке <strong>{name}</strong> содержится очень много файлов. Это может повлиять на работу приложения.\",\n\t\"problems\": \"Проблемы\",\n\t\"show side buttons\": \"Показать кнопки на стороне\",\n\t\"bug_report\": \"Сообщить о багах\",\n\t\"verified publisher\": \"Проверенный издатель\",\n\t\"most_downloaded\": \"Популярные\",\n\t\"newly_added\": \"Новые\",\n\t\"top_rated\": \"С высокой оценкой\",\n\t\"rename not supported\": \"Переименование в директории Termux не поддерживается\",\n\t\"compress\": \"Сжать\",\n\t\"copy uri\": \"Копировать URI\",\n\t\"delete entries\": \"Вы уверены, что хотите удалить {count} элементов?\",\n\t\"deleting items\": \"Удаление {count} элементов...\",\n\t\"import project zip\": \"Импортировать проект (zip)\",\n\t\"changelog\": \"Список изменений\",\n\t\"notifications\": \"Уведомления\",\n\t\"no_unread_notifications\": \"Нет непрочитанных уведомлений\",\n\t\"should_use_current_file_for_preview\": \"Использовать текущий файл для предпросмотра вместо файла по умолчанию (index.html)\",\n\t\"fade fold widgets\": \"Затухание виджетов свёртывания\",\n\t\"quicktools:home-key\": \"Клав. Home\",\n\t\"quicktools:end-key\": \"Клав. End\",\n\t\"quicktools:pageup-key\": \"Клав. PageUp\",\n\t\"quicktools:pagedown-key\": \"Клав. PageDown\",\n\t\"quicktools:delete-key\": \"Клав. Delete\",\n\t\"quicktools:tilde\": \"Вставить тильду\",\n\t\"quicktools:backtick\": \"Вставить обратный апостроф\",\n\t\"quicktools:hash\": \"Вставить символ #\",\n\t\"quicktools:dollar\": \"Вставить символ доллара\",\n\t\"quicktools:modulo\": \"Вставить символ процента\",\n\t\"quicktools:caret\": \"Вставить символ каретки\",\n\t\"plugin_enabled\": \"Плагин включен\",\n\t\"plugin_disabled\": \"Плагин отключен\",\n\t\"enable_plugin\": \"Включить этот плагин\",\n\t\"disable_plugin\": \"Отключить этот плагин\",\n\t\"open_source\": \"Открытый исходный код\",\n\t\"terminal settings\": \"Настройки терминала\",\n\t\"font ligatures\": \"Шрифтовые лигатуры\",\n\t\"letter spacing\": \"Межбуквенный интервал\",\n\t\"terminal:tab stop width\": \"Ширина табуляции\",\n\t\"terminal:scrollback\": \"Строк прокрутки\",\n\t\"terminal:cursor blink\": \"Мигание курсора\",\n\t\"terminal:font weight\": \"Насыщенность шрифта\",\n\t\"terminal:cursor inactive style\": \"Стиль неактивного курсора\",\n\t\"terminal:cursor style\": \"Стиль курсора\",\n\t\"terminal:font family\": \"Семейство шрифтов\",\n\t\"terminal:convert eol\": \"Преобразовывать EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Терминал\",\n\t\"allFileAccess\": \"Доступ ко всем файлам\",\n\t\"fonts\": \"Шрифты\",\n\t\"sponsor\": \"Спонсор\",\n\t\"downloads\": \"Загрузки\",\n\t\"reviews\": \"Отзывы\",\n\t\"overview\": \"Обзор\",\n\t\"contributors\": \"Авторы\",\n\t\"quicktools:hyphen\": \"Вставить символ дефиса\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/tl-ph.json",
    "content": "{\n\t\"lang\": \"Tagalog\",\n\t\"about\": \"Kaalaman\",\n\t\"active files\": \"Active files\",\n\t\"alert\": \"Alert\",\n\t\"app theme\": \"Tema ng App\",\n\t\"autocorrect\": \"I-enable ang autocorrect\",\n\t\"autosave\": \"Autosave\",\n\t\"cancel\": \"Cancel\",\n\t\"change language\": \"Baguhin ang Wika\",\n\t\"choose color\": \"Pumili ng Kulay\",\n\t\"clear\": \"Burahin\",\n\t\"close app\": \"I-close ang application?\",\n\t\"commit message\": \"Commit message\",\n\t\"console\": \"Console\",\n\t\"conflict error\": \"Conflict! Hintayin bago mag-commit ulit.\",\n\t\"copy\": \"I-copy\",\n\t\"create folder error\": \"Pasensya, hindi makalikha ng bagong folder\",\n\t\"cut\": \"I-cut\",\n\t\"delete\": \"I-delete\",\n\t\"dependencies\": \"Mga dependency\",\n\t\"delay\": \"Oras sa millisecond\",\n\t\"editor settings\": \"Mga setting ng editor\",\n\t\"editor theme\": \"Tema ng Editor\",\n\t\"enter file name\": \"Maglagay ng pangalan ng file\",\n\t\"enter folder name\": \"Maglagay ng pangalan ng folder\",\n\t\"empty folder message\": \"Walang laman na folder\",\n\t\"enter line number\": \"Maglagay ng bilang ng linya\",\n\t\"error\": \"Error\",\n\t\"failed\": \"Nabigo\",\n\t\"file already exists\": \"Mayroon ng file na ganito\",\n\t\"file already exists force\": \"Mayroon ng file na ganito. I-overwrite?\",\n\t\"file changed\": \"Nabago ang file, I-reload?\",\n\t\"file deleted\": \"File deleted\",\n\t\"file is not supported\": \"Hindi suportado ang file na ito.\",\n\t\"file not supported\": \"Hindi suportado ang file type na ito.\",\n\t\"file too large\": \"Masyadong malaki ang file para i-handle. Ang pinakamalaking file size ay {size}\",\n\t\"file renamed\": \"nai-rename ang file\",\n\t\"file saved\": \"nai-save ang file\",\n\t\"folder added\": \"nadagdag ang folder\",\n\t\"folder already added\": \"nadagdag na ang folder\",\n\t\"font size\": \"Font size\",\n\t\"goto\": \"Pumunta sa linya\",\n\t\"icons definition\": \"Icons definition\",\n\t\"info\": \"Impormasyon\",\n\t\"invalid value\": \"Invalid ang value\",\n\t\"language changed\": \"Matagumpay na nabago ang wika\",\n\t\"linting\": \"I-check ang syntax error\",\n\t\"logout\": \"Logout\",\n\t\"loading\": \"Naglo-load\",\n\t\"my profile\": \"Aking profile\",\n\t\"new file\": \"Bagong file\",\n\t\"new folder\": \"Bagong folder\",\n\t\"no\": \"Hindi\",\n\t\"no editor message\": \"Buksan o lumikha ng bagong file at folder mula sa menu\",\n\t\"not set\": \"Hindi itinakda\",\n\t\"unsaved files close app\": \"Mayroon pang mga hindi na-isave na files. I-close ang aplikasyon?\",\n\t\"notice\": \"Paunawa\",\n\t\"open file\": \"Buksan ang file\",\n\t\"open files and folders\": \"Buksan ang mga file at folder\",\n\t\"open folder\": \"Buksan ang folder\",\n\t\"open recent\": \"Buksan ang kamakailan\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"I-overwrite\",\n\t\"paste\": \"I-paste\",\n\t\"preview mode\": \"I-preview mode\",\n\t\"read only file\": \"Hindi maaring mag-save sa read-only na file. Subukang i-save bilang\",\n\t\"reload\": \"I-reload\",\n\t\"rename\": \"I-rename\",\n\t\"replace\": \"I-replace\",\n\t\"required\": \"Kinakailangan ang field na ito\",\n\t\"run your web app\": \"Patakbuhin ang iyong web app\",\n\t\"save\": \"I-save\",\n\t\"saving\": \"Nagse-save\",\n\t\"save as\": \"I-save bilang\",\n\t\"save file to run\": \"Mangyaring I-save ang file na ito para magamit sa browser\",\n\t\"search\": \"Search\",\n\t\"see logs and errors\": \"Tingnan ang mga log at error\",\n\t\"select folder\": \"Pumili ng folder\",\n\t\"settings\": \"Mga setting\",\n\t\"settings saved\": \"Nai-save ang mga setting\",\n\t\"show line numbers\": \"Ipakita ang line numbers\",\n\t\"show hidden files\": \"Ipakita ang hidden files\",\n\t\"show spaces\": \"Ipakita ang space\",\n\t\"soft tab\": \"Soft tab\",\n\t\"sort by name\": \"I-ayos ayon sa pangalan\",\n\t\"success\": \"Tagumpay\",\n\t\"tab size\": \"Tab size\",\n\t\"text wrap\": \"Text wrap / Word wrap\",\n\t\"theme\": \"Tema\",\n\t\"unable to delete file\": \"hindi ma-delete ang file\",\n\t\"unable to open file\": \"Paumanhin, hindi ma-open ang file\",\n\t\"unable to open folder\": \"Paumanhin, hindi ma-open ang folder\",\n\t\"unable to save file\": \"Paumanhin, hindi ma-save ang file\",\n\t\"unable to rename\": \"Paumanhin, hindi maka-rename\",\n\t\"unsaved file\": \"Hindi nai-save ang file, ipagpatuloy pa rin?\",\n\t\"warning\": \"Babala\",\n\t\"use emmet\": \"Gamitin ang emmet\",\n\t\"use quick tools\": \"Gamitin ang quick tools\",\n\t\"yes\": \"Oo\",\n\t\"encoding\": \"Text encoding\",\n\t\"syntax highlighting\": \"Syntax highlighting\",\n\t\"read only\": \"Read only\",\n\t\"select all\": \"I-select lahat\",\n\t\"select branch\": \"I-select ang branch\",\n\t\"create new branch\": \"Lumikha ng bagong branch\",\n\t\"use branch\": \"Gamitin ang branch\",\n\t\"new branch\": \"Bagong branch\",\n\t\"branch\": \"Branch\",\n\t\"key bindings\": \"Key bindings\",\n\t\"edit\": \"I-edit\",\n\t\"reset\": \"I-reset\",\n\t\"color\": \"Color\",\n\t\"select word\": \"Select word\",\n\t\"quick tools\": \"Quick tools\",\n\t\"select\": \"I-select\",\n\t\"editor font\": \"Editor font\",\n\t\"new project\": \"Bagong project\",\n\t\"format\": \"Format\",\n\t\"project name\": \"Pangalan ng project\",\n\t\"unsupported device\": \"Hindi suportado ang tema sa inyong device.\",\n\t\"vibrate on tap\": \"Vibrate on tap\",\n\t\"copy command is not supported by ftp.\": \"Ang copy command ay hindi suportado ng FTP.\",\n\t\"support title\": \"Suportahan ang Acode\",\n\t\"fullscreen\": \"Fullscreen\",\n\t\"animation\": \"Animation\",\n\t\"backup\": \"Backup\",\n\t\"restore\": \"Restore\",\n\t\"backup successful\": \"Ang backup ay matagumpay\",\n\t\"invalid backup file\": \"Invalid ang backup file\",\n\t\"add path\": \"Add path\",\n\t\"live autocompletion\": \"Live autocompletion\",\n\t\"file properties\": \"File properties\",\n\t\"path\": \"Path\",\n\t\"type\": \"Type\",\n\t\"word count\": \"Word count\",\n\t\"line count\": \"Line count\",\n\t\"last modified\": \"Last modified\",\n\t\"size\": \"Size\",\n\t\"share\": \"Share\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"Login\",\n\t\"scrollbar size\": \"Scrollbar size\",\n\t\"cursor controller size\": \"Cursor controller size\",\n\t\"none\": \"None\",\n\t\"small\": \"Small\",\n\t\"large\": \"Large\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"I-confirm kapag mag-exit\",\n\t\"show console\": \"Ipakita ang console\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"I-turn off ang power saving mode upang ma-preview sa external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"Custom\",\n\t\"reset warning\": \"Sigurado ka bang gusto mong i-reset ang tema?\",\n\t\"theme type\": \"Type ng tema\",\n\t\"light\": \"Light\",\n\t\"dark\": \"Dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Hindi pinahihintulutan ang operasyon\",\n\t\"no such file or directory\": \"Walang ganoong file o directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Hindi directory\",\n\t\"is a directory\": \"Ito ay isang directory\",\n\t\"invalid argument\": \"Invalid na argument\",\n\t\"too many open files in system\": \"Masyadong maraming mga file na nakabukas sa system\",\n\t\"too many open files\": \"Masyadong maraming mga file na nakabukas\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"Wala ng natitirang space sa iyong device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"Masyadong mahaba ang file name\",\n\t\"too many users\": \"Masyadong maraming users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"May error na nangyari\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"I-save ang file\",\n\t\"save file as\": \"I-save ang file bilang\",\n\t\"files\": \"Mga file\",\n\t\"help\": \"Tulong\",\n\t\"file has been deleted\": \"{file} ay nabura na!\",\n\t\"feature not available\": \"Ang feature na ito ay available lamang sa paid version ng app.\",\n\t\"deleted file\": \"Deleted na file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"Kung gusto mong i-run ang active file, i-tap at i-hold ang play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"I-close ang file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Buksan sa browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"I-rate ang Acode\",\n\t\"support\": \"Suportahan\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"Mga settings sa app\",\n\t\"disable in-app-browser caching\": \"I-disable ang in-app-browser caching\",\n\t\"copied to clipboard\": \"Kinopya sa clipboard\",\n\t\"remember opened files\": \"Tandaan ang mga nabuksan na file\",\n\t\"remember opened folders\": \"Tandaan ang mga nabuksan na folder\",\n\t\"no suggestions\": \"Walang suggestions\",\n\t\"no suggestions aggressive\": \"Walang suggestions aggressive\",\n\t\"install\": \"I-install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Ginamit kamakailan\",\n\t\"update\": \"I-update\",\n\t\"uninstall\": \"I-uninstall\",\n\t\"download acode pro\": \"I-download ang Acode pro\",\n\t\"loading plugins\": \"Naglo-load ng mga plugin\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Tanggalin ang ads\",\n\t\"fast\": \"Mabilis\",\n\t\"slow\": \"Mabgal\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Naglo-load...\",\n\t\"no plugins found\": \"Walang nakitang mga plugin\",\n\t\"name\": \"Pangalan\",\n\t\"username\": \"Username\",\n\t\"optional\": \"opsyunal\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Hindi ma-load ang mga file\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Mangyaring pumili ng formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"I-edit gamit ang\",\n\t\"open with\": \"Buksan gamit ang\",\n\t\"no app found to handle this file\": \"Walang app ang mahanap upang ma-handle ang file na ito.\",\n\t\"restore default settings\": \"I-restore ang default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"Kung magkaiba ang preview port at server port, hindi magsisimula ang server sa app at sa halip ay magbubukas ito ng https://<host>:<preview port> sa browser o in-app na browser. Ito ay kapaki-pakinabang kapag nagpapatakbo ka ng isang server sa ibang lugar.\",\n\t\"backup/restore note\": \"Iba-backup lang nito ang iyong mga setting, custom na tema at key binding. Hindi nito iba-backup ang iyong FTP/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"I-retry ang ftp/sftp kapag nabigo\",\n\t\"more\": \"Higit pa\",\n\t\"thank you :)\": \"Salamat :)\",\n\t\"purchase pending\": \"Naka-pending ang pagbili\",\n\t\"cancelled\": \"kinansela\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Ipakita ang console toggler\",\n\t\"binary file\": \"Ang file na ito ay naglalaman ng binary data, gusto mo ba itong buksan?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Gamitin ang textarea para sa IME\",\n\t\"invalid plugin\": \"Invalid ang Plugin\",\n\t\"type command\": \"I-type ang command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"I-retry ang FTP/SFTP connection kapag nabigo.\",\n\t\"info-fullscreen\": \"Itago ang title bar sa home screen.\",\n\t\"info-checkfiles\": \"I-check ang mga pagbabago sa file kapag nasa background ang app.\",\n\t\"info-console\": \"Pumili ng JavaScript console. Ang Legacy ay ang default na console, ang eruda ay isang third party na console.\",\n\t\"info-keyboardmode\": \"Keyboard mode para sa pag-input ng text, ang 'no suggestions' ay magtatago sa mga suggestions at autocorrect. Kung hindi gumagana ang 'no suggestions', subukang baguhin ang value sa 'no suggestions aggressive'.\",\n\t\"info-rememberfiles\": \"Tandaan ang mga binuksang file kapag na-close ang app.\",\n\t\"info-rememberfolders\": \"Tandaan ang mga binuksang folder kapag na-close ang app.\",\n\t\"info-floatingbutton\": \"Ipakita o itago ang quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Saan ipapakita ang mga active file list.\",\n\t\"info-touchmovethreshold\": \"Kung masyadong mataas ang touch sensitivity ng iyong device, maaari mong taasan ang value nito para maiwasan ang touch move.\",\n\t\"info-scroll-settings\": \"Ang mga setting na ito ay naglalaman ng mga scoll settings kasama ang text wrap.\",\n\t\"info-animation\": \"Kung ang app ay nakakaranas ng lag, i-disable ang animation.\",\n\t\"info-quicktoolstriggermode\": \"Kung hindi gumagana ang button sa quick tools, subukang baguhin ang value na ito.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"Ang API server ay down, mangyaring subukan pagkatapos ng ilang oras.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"Lahat\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Ang produktong ito ay hindi available\",\n\t\"no-product-info\": \"Ang produktong ito ay hindi available sa iyong bansa sa ngayon, pakisubukang muli sa ibang pagkakataon.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} resulta sa {files} na files.\",\n\t\"invalid regex\": \"Invalid ang regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"I-save lahat\",\n\t\"close all\": \"I-close lahat\",\n\t\"unsaved files warning\": \"Iilan sa mga file ay hindi nai-save. I-click ang 'ok' at piliin kung ano ang gagawin o pindutin ang 'cancel' upang bumalik.\",\n\t\"save all warning\": \"Sigurado ka bang gusto mong i-save ang lahat ng file at i-close? Ang aksyon na ito ay hindi maaaring baligtarin.\",\n\t\"save all changes warning\": \"Sigurado ka bang gusto mong i-save ang lahat ng file?\",\n\t\"close all warning\": \"Sigurado ka bang gusto mong i-close ang lahat ng file? Mawawala sa iyo ang mga hindi nai-save na pagbabago at hindi na maibabalik ang aksyon na ito.\",\n\t\"refresh\": \"I-refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"Walang resulta\",\n\t\"searching...\": \"Naghahanap...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Mag-search sa file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"I-customize ang mga shortcut button at keyboard key sa Quicktools container sa ibaba ng editor upang ma-enhance ang iyong coding experience.\",\n\t\"info-excludefolders\": \"Gamitin ang pattern **/node_modules/** upang baliwalain ang lahat ng mga file galing sa node_modules folder. Ibubukod nito ang mga file mula sa pagkakalista at pipigilan din ang mga ito na maisama sa file searches.\",\n\t\"missed files\": \"{count} na file ang nai-scan pagkatapos magsimula ang search at hindi ito isasali sa search.\",\n\t\"remove\": \"I-remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default na file encoding\",\n\t\"remove entry\": \"Sigurado ka bang gusto mong burahin ang '{name}' sa mga saved path? Pakitandaan na ang pagtanggal nito ay hindi magbubura sa mismong path.\",\n\t\"delete entry\": \"Kumpirmahin ang pagbura: '{name}'. Ang aksyon na ito ay hindi maaaring baligtarin. Magpatuloy?\",\n\t\"change encoding\": \"Muling buksan ang '{file}' gamit ang '{encoding}' encoding? Ang aksyon na ito ay magreresulta sa pagkawala ng anumang hindi nai-save na mga pagbabagong ginawa sa file. Gusto mo bang magpatuloy sa pagbubukas?\",\n\t\"reopen file\": \"Sigurado ka bang gusto mong muling buksan ang '{file}'? Mawawala ang anumang hindi nai-save na pagbabago.\",\n\t\"plugin min version\": \"Available lang ang {name} sa Acode - {v-code} at pataas. I-click dito para i-update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"Ilista ang lahat ng file sa <strong>{name}</strong>? Ang masyadong maraming file ay maaaring magdulot ng pag-crash sa app.\",\n\t\"problems\": \"Mga Problema\",\n\t\"show side buttons\": \"Ipakita ang side button\",\n\t\"bug_report\": \"Mag-submit ng Bug Report\",\n\t\"verified publisher\": \"Verified na publisher\",\n\t\"most_downloaded\": \"Pinaka-download\",\n\t\"newly_added\": \"Bagong idinagdag\",\n\t\"top_rated\": \"Pinakamataas na rating\",\n\t\"rename not supported\": \"Ang pag-rename sa termux dir ay hindi suportado\",\n\t\"compress\": \"I-compress\",\n\t\"copy uri\": \"I-copy ang Uri\",\n\t\"delete entries\": \"Sigurado ka bang gusto mong burahin ang {count} na item?\",\n\t\"deleting items\": \"Binubura ang {count}  na item...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Mga notification\",\n\t\"no_unread_notifications\": \"Walang hindi pa nababasang mga notification\",\n\t\"should_use_current_file_for_preview\": \"Dapat gamitin ang Current File para sa preview sa halip na default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/tr-tr.json",
    "content": "{\n\t\"lang\": \"Türkçe (by ibrahim)\",\n\t\"about\": \"Hakkında\",\n\t\"active files\": \"Aktif Dosyalar\",\n\t\"alert\": \"Uyarı\",\n\t\"app theme\": \"Uygulama Teması\",\n\t\"autocorrect\": \"Otomatik Düzelt\",\n\t\"autosave\": \"Otomatik Kaydet\",\n\t\"cancel\": \"İptal\",\n\t\"change language\": \"Dili Değiştir\",\n\t\"choose color\": \"Renk Seç\",\n\t\"clear\": \"Temizle\",\n\t\"close app\": \"Uygulama kapatılsın mı?\",\n\t\"commit message\": \"Commit mesajı\",\n\t\"console\": \"Konsol\",\n\t\"conflict error\": \"yanlışlık! Lütfen başka bir committen önce bekleyin\",\n\t\"copy\": \"Kopyala\",\n\t\"create folder error\": \"Yeni klasör oluşturulamıyor\",\n\t\"cut\": \"Kes\",\n\t\"delete\": \"Sil\",\n\t\"dependencies\": \"gereklilikler\",\n\t\"delay\": \"Milisaniye cinsinden süre\",\n\t\"editor settings\": \"Editör Ayarları\",\n\t\"editor theme\": \"Editör Teması\",\n\t\"enter file name\": \"Dosya Adı\",\n\t\"enter folder name\": \"Klasör Adı\",\n\t\"empty folder message\": \"Boş Klasör\",\n\t\"enter line number\": \"Satır numarasını girin\",\n\t\"error\": \"Hata\",\n\t\"failed\": \"Başarısız oldu\",\n\t\"file already exists\": \"Dosya zaten var\",\n\t\"file already exists force\": \"Dosya zaten var. Üzerine yazılsın mı?\",\n\t\"file changed\": \" değiştirildi, yeniden yüklensin mi?\",\n\t\"file deleted\": \"Dosya silindi\",\n\t\"file is not supported\": \"Dosya desteklenmiyor\",\n\t\"file not supported\": \"Bu dosya türü desteklenmiyor\",\n\t\"file too large\": \"Dosya çok büyük. İzin verilen maksimum dosya boyutu {size}\",\n\t\"file renamed\": \"Dosya yeniden adlandırıldı\",\n\t\"file saved\": \"Dosya kaydedildi\",\n\t\"folder added\": \"Klasör eklendi\",\n\t\"folder already added\": \"Klasör zaten ekli\",\n\t\"font size\": \"Yazı Boyutu\",\n\t\"goto\": \"Satıra git\",\n\t\"icons definition\": \"simgeler tanımı\",\n\t\"info\": \"Bilgi\",\n\t\"invalid value\": \"Geçersiz değer\",\n\t\"language changed\": \"Dil başarıyla değiştirildi\",\n\t\"linting\": \"Sözdizimi Hatalarını Kontrol Et\",\n\t\"logout\": \"Çıkış yap\",\n\t\"loading\": \"Yükleniyor\",\n\t\"my profile\": \"Profilim\",\n\t\"new file\": \"Yeni Dosya\",\n\t\"new folder\": \"Yeni Klasör\",\n\t\"no\": \"Hayır\",\n\t\"no editor message\": \"Menüden yeni dosya ve klasör aç veya oluştur\",\n\t\"not set\": \"Ayarlanmadı\",\n\t\"unsaved files close app\": \"Kaydedilmemiş dosyalar var. Uygulama kapatılsın mı?\",\n\t\"notice\": \"Dikkat\",\n\t\"open file\": \"Dosya Aç\",\n\t\"open files and folders\": \"Dosya ve Klasör Aç\",\n\t\"open folder\": \"Klasör Aç\",\n\t\"open recent\": \"Önceklerden Aç\",\n\t\"ok\": \"Tamam\",\n\t\"overwrite\": \"Üzerine yaz\",\n\t\"paste\": \"Yapıştır\",\n\t\"preview mode\": \"Ön-izleme Modu\",\n\t\"read only file\": \"Salt okunur dosya kaydedilemiyor. Lütfen farklı kaydetmeyi deneyin\",\n\t\"reload\": \"Tekrar Yükle\",\n\t\"rename\": \"Yeniden Adlandır\",\n\t\"replace\": \"Değiştir\",\n\t\"required\": \"Bu alan gerekli\",\n\t\"run your web app\": \"Web uygulamanızı çalıştırın\",\n\t\"save\": \"Kaydet\",\n\t\"saving\": \"kaydediliyor\",\n\t\"save as\": \"Farklı Kaydet\",\n\t\"save file to run\": \"Lütfen bu dosyayı tarayıcıda çalışacak şekilde kaydedin\",\n\t\"search\": \"Ara\",\n\t\"see logs and errors\": \"Günlük ve hatalara bak\",\n\t\"select folder\": \"Bu Klasörü Seç\",\n\t\"settings\": \"Ayarlar\",\n\t\"settings saved\": \"Ayarlar Kaydedildi\",\n\t\"show line numbers\": \"Satır Numarasını Göster\",\n\t\"show hidden files\": \"Gizli Dosyaları Göster\",\n\t\"show spaces\": \"Boşlukları Göster\",\n\t\"soft tab\": \"sekmeler Karakteri Yerine Boşluk Kullan\",\n\t\"sort by name\": \"İsme Göre Sırala\",\n\t\"success\": \"Başarılı\",\n\t\"tab size\": \"sekmeler Boyutu\",\n\t\"text wrap\": \"Sözcük Kaydırma\",\n\t\"theme\": \"Tema\",\n\t\"unable to delete file\": \"Dosya silinemedi\",\n\t\"unable to open file\": \"Dosya açılamadı\",\n\t\"unable to open folder\": \"Klasör açılamadı\",\n\t\"unable to save file\": \"Dosya kaydedilemedi\",\n\t\"unable to rename\": \"Yeniden adlandırılamadı\",\n\t\"unsaved file\": \"Bu dosya kaydedilmedi, kapatılsın mı?\",\n\t\"warning\": \"Uyarı\",\n\t\"use emmet\": \"Emmet kullan\",\n\t\"use quick tools\": \"Hızlı Araçlar'ı Kullan\",\n\t\"yes\": \"Evet\",\n\t\"encoding\": \"Metin Kodlaması\",\n\t\"syntax highlighting\": \"Sözdizimi Vurgulaması\",\n\t\"read only\": \"Salt Okunur\",\n\t\"select all\": \"Hepsini Seç\",\n\t\"select branch\": \"Branch'ı Seç\",\n\t\"create new branch\": \"Yeni Branch Oluştur\",\n\t\"use branch\": \"Branch'ı Kullan\",\n\t\"new branch\": \"Yeni Branch\",\n\t\"branch\": \"Branch\",\n\t\"key bindings\": \"Klavye Kısayolları\",\n\t\"edit\": \"Düzenle\",\n\t\"reset\": \"Sıfırla\",\n\t\"color\": \"Renk\",\n\t\"select word\": \"Kelime Seç\",\n\t\"quick tools\": \"Hızlı Araçlar\",\n\t\"select\": \"Seç\",\n\t\"editor font\": \"Editörün Yazı Tipi\",\n\t\"new project\": \"Yeni Proje\",\n\t\"format\": \"Biçimlendir\",\n\t\"project name\": \"Proje Adı\",\n\t\"unsupported device\": \"Cihazınız temayı desteklemiyor\",\n\t\"vibrate on tap\": \"Tıklamayla vibratör\",\n\t\"copy command is not supported by ftp.\": \"Kopyalama komutu FTP tarafından desteklenmiyor.\",\n\t\"support title\": \"Acode'ı destekle\",\n\t\"fullscreen\": \"tüm-ekran\",\n\t\"animation\": \"animasyon\",\n\t\"backup\": \"yedekle\",\n\t\"restore\": \"restore\",\n\t\"backup successful\": \"Backup successful\",\n\t\"invalid backup file\": \"Invalid backup file\",\n\t\"add path\": \"Yol ekle\",\n\t\"live autocompletion\": \"canlı Otomatik Düzenleme\",\n\t\"file properties\": \"dosya bilgileri\",\n\t\"path\": \"Yol\",\n\t\"type\": \"tip\",\n\t\"word count\": \"Kelime sayısı\",\n\t\"line count\": \"Satır sayısı\",\n\t\"last modified\": \"En Son değiştirilen\",\n\t\"size\": \"Boyut\",\n\t\"share\": \"paylaş\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"login\",\n\t\"scrollbar size\": \"Scrollbar size\",\n\t\"cursor controller size\": \"Cursor controller size\",\n\t\"none\": \"none\",\n\t\"small\": \"small\",\n\t\"large\": \"large\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"Confirm on exit\",\n\t\"show console\": \"Show console\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"Turn off power saving mode to preview in external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"light\",\n\t\"dark\": \"dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Sponsor\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/uk-ua.json",
    "content": "{\n\t\"lang\": \"Українська\",\n\t\"about\": \"Про програму\",\n\t\"active files\": \"Активні файли\",\n\t\"alert\": \"Сповіщення\",\n\t\"app theme\": \"Тема\",\n\t\"autocorrect\": \"Дозволити автовиправлення?\",\n\t\"autosave\": \"Автозберігання\",\n\t\"cancel\": \"Скасувати\",\n\t\"change language\": \"Змінити мову\",\n\t\"choose color\": \"Обрати колір\",\n\t\"clear\": \"очистити\",\n\t\"close app\": \"Закрити програму?\",\n\t\"commit message\": \"Повідомлення коміту\",\n\t\"console\": \"Консоль\",\n\t\"conflict error\": \"Конфлікт! Зачекайте перед іншим комітом.\",\n\t\"copy\": \"Копіювати\",\n\t\"create folder error\": \"Вибачте, не вдалося створити нову теку\",\n\t\"cut\": \"Вирізати\",\n\t\"delete\": \"Видалити\",\n\t\"dependencies\": \"Залежності\",\n\t\"delay\": \"Час у мілісекундах\",\n\t\"editor settings\": \"Параметри редактора\",\n\t\"editor theme\": \"Тема редактора\",\n\t\"enter file name\": \"Уведіть назву файла\",\n\t\"enter folder name\": \"Уведіть назву теки\",\n\t\"empty folder message\": \"Порожня тека\",\n\t\"enter line number\": \"Уведіть номер рядка\",\n\t\"error\": \"Помилка\",\n\t\"failed\": \"Не вдалося\",\n\t\"file already exists\": \"Файл уже існує\",\n\t\"file already exists force\": \"Файл уже існує. Перезаписати?\",\n\t\"file changed\": \" змінено, перевантажити файл?\",\n\t\"file deleted\": \"Файл видалено\",\n\t\"file is not supported\": \"Файл не підтримується\",\n\t\"file not supported\": \"Цей тип файлу не підтримується.\",\n\t\"file too large\": \"Файл завеликий для обробки. Найбільший дозволений розмір файлу {size}\",\n\t\"file renamed\": \"файл перейменовано\",\n\t\"file saved\": \"файл збережено\",\n\t\"folder added\": \"теку додано\",\n\t\"folder already added\": \"теку вже додано\",\n\t\"font size\": \"Розмір шрифту\",\n\t\"goto\": \"Перейти до рядка\",\n\t\"icons definition\": \"Визначення значків\",\n\t\"info\": \"Інфо\",\n\t\"invalid value\": \"Неправильне значення\",\n\t\"language changed\": \"мову вдало змінено\",\n\t\"linting\": \"Помилка перевірки синтаксису\",\n\t\"logout\": \"Вихід\",\n\t\"loading\": \"Завантаження\",\n\t\"my profile\": \"Мій профіль\",\n\t\"new file\": \"Новий файл\",\n\t\"new folder\": \"Нова тека\",\n\t\"no\": \"Ні\",\n\t\"no editor message\": \"Відкрити або створити новий файл і теку з меню\",\n\t\"not set\": \"Не задано\",\n\t\"unsaved files close app\": \"Є незбережені файли. Закрити програму?\",\n\t\"notice\": \"Примітка\",\n\t\"open file\": \"Відкрити файл\",\n\t\"open files and folders\": \"Відкрити файли і теки\",\n\t\"open folder\": \"Відкрити теку\",\n\t\"open recent\": \"Відкрити останнє\",\n\t\"ok\": \"гаразд\",\n\t\"overwrite\": \"Перезаписати\",\n\t\"paste\": \"Вставити\",\n\t\"preview mode\": \"Режим перегляду\",\n\t\"read only file\": \"Не можливо змінити файл лише для читання. Спробуйте зберегти як\",\n\t\"reload\": \"Перевантажити\",\n\t\"rename\": \"Перейменувати\",\n\t\"replace\": \"Замінити\",\n\t\"required\": \"Це поле обовʼязкове\",\n\t\"run your web app\": \"Запустити Вашу веб-аплікацію\",\n\t\"save\": \"Зберегти\",\n\t\"saving\": \"Зберігання\",\n\t\"save as\": \"Зберегти як\",\n\t\"save file to run\": \"Збережіть цей файл для запуску в оглядачі\",\n\t\"search\": \"Пошук\",\n\t\"see logs and errors\": \"Дивитися журнал і помилки\",\n\t\"select folder\": \"Вибрати теку\",\n\t\"settings\": \"Параметри\",\n\t\"settings saved\": \"Налаштування збережено\",\n\t\"show line numbers\": \"Показувати номери рядків\",\n\t\"show hidden files\": \"Показувати приховані файли\",\n\t\"show spaces\": \"Показувати пробіли\",\n\t\"soft tab\": \"Мʼякі таби\",\n\t\"sort by name\": \"Сортувати за назвою\",\n\t\"success\": \"Вдало\",\n\t\"tab size\": \"Розмір табів\",\n\t\"text wrap\": \"Перенесення тексту\",\n\t\"theme\": \"Тема\",\n\t\"unable to delete file\": \"не можливо видалити файл\",\n\t\"unable to open file\": \"Вибачте, не можливо відкрити файл\",\n\t\"unable to open folder\": \"Вибачте, не можливо відкрити теку\",\n\t\"unable to save file\": \"Вибачте, не можливо зберегти файл\",\n\t\"unable to rename\": \"Вибачте, не можливо перейменувати\",\n\t\"unsaved file\": \"Цей файл не збережено, закрити попри все?\",\n\t\"warning\": \"Увага\",\n\t\"use emmet\": \"Викор. мурашку\",\n\t\"use quick tools\": \"Викор. швидкі засоби\",\n\t\"yes\": \"Так\",\n\t\"encoding\": \"Кодування тексту\",\n\t\"syntax highlighting\": \"Підсвічування синтаксису\",\n\t\"read only\": \"Лише для читання\",\n\t\"select all\": \"Виділити все\",\n\t\"select branch\": \"Вибрати гілку\",\n\t\"create new branch\": \"Створити нову гілку\",\n\t\"use branch\": \"Викор. гілку\",\n\t\"new branch\": \"Нова гілка\",\n\t\"branch\": \"Гілка\",\n\t\"key bindings\": \"Комбінації клавіш\",\n\t\"edit\": \"Змінити\",\n\t\"reset\": \"Скинути\",\n\t\"color\": \"Колір\",\n\t\"select word\": \"Виділити слово\",\n\t\"quick tools\": \"Швидкі засоби\",\n\t\"select\": \"Виділити\",\n\t\"editor font\": \"Шрифт редактора\",\n\t\"new project\": \"Новий проєкт\",\n\t\"format\": \"Формат\",\n\t\"project name\": \"Назва проєкту\",\n\t\"unsupported device\": \"Ваш пристрій не підтримує тему.\",\n\t\"vibrate on tap\": \"Вібрувати під час дотику\",\n\t\"copy command is not supported by ftp.\": \"Команда копіювання не підтримується FTP.\",\n\t\"support title\": \"Підтримати Acode\",\n\t\"fullscreen\": \"На весь екран\",\n\t\"animation\": \"Анімація\",\n\t\"backup\": \"Резервна копія\",\n\t\"restore\": \"Відновити\",\n\t\"backup successful\": \"Вдало зарезервовано\",\n\t\"invalid backup file\": \"Не коректний файл резервної копії\",\n\t\"add path\": \"Додати шлях\",\n\t\"live autocompletion\": \"Живе автодоповнення\",\n\t\"file properties\": \"Властивості файлу\",\n\t\"path\": \"Шлях\",\n\t\"type\": \"Тип\",\n\t\"word count\": \"Кількість слів\",\n\t\"line count\": \"Кількість рядків\",\n\t\"last modified\": \"Востаннє змінено\",\n\t\"size\": \"Розмір\",\n\t\"share\": \"Поділитися\",\n\t\"show print margin\": \"Показувати поля друку\",\n\t\"login\": \"Вхід\",\n\t\"scrollbar size\": \"Розмір смуги прокручування\",\n\t\"cursor controller size\": \"Розмір контролера курсора\",\n\t\"none\": \"Нема\",\n\t\"small\": \"Малий\",\n\t\"large\": \"Великий\",\n\t\"floating button\": \"Плаваюча кнопка\",\n\t\"confirm on exit\": \"Підтверджувати вихід\",\n\t\"show console\": \"Показати консоль\",\n\t\"image\": \"Зображення\",\n\t\"insert file\": \"Вставити файл\",\n\t\"insert color\": \"Вставити колір\",\n\t\"powersave mode warning\": \"Вимкнути режим збереження енергії для перегляду в зовнішньому оглядачі.\",\n\t\"exit\": \"Вихід\",\n\t\"custom\": \"Власна\",\n\t\"reset warning\": \"Скинути тему?\",\n\t\"theme type\": \"Тип теми\",\n\t\"light\": \"Світла\",\n\t\"dark\": \"Темна\",\n\t\"file browser\": \"Огляд файлів\",\n\t\"operation not permitted\": \"Недозволена операція\",\n\t\"no such file or directory\": \"Нема такого файла або каталога\",\n\t\"input/output error\": \"Помилка вводу/виводу\",\n\t\"permission denied\": \"Дозвіл відхилено\",\n\t\"bad address\": \"Погана адреса\",\n\t\"file exists\": \"Файл існує\",\n\t\"not a directory\": \"Не каталог\",\n\t\"is a directory\": \"Є каталогом\",\n\t\"invalid argument\": \"Неправильний арґумент\",\n\t\"too many open files in system\": \"Забагато відкритих файлів у системі\",\n\t\"too many open files\": \"Забагато відкритих файлів\",\n\t\"text file busy\": \"Текстовий файл зайнятий\",\n\t\"no space left on device\": \"Нема відступів ліворуч пристрою\",\n\t\"read-only file system\": \"Файлова система лише для читання\",\n\t\"file name too long\": \"Задовга назва файлу\",\n\t\"too many users\": \"Забагато користувачів\",\n\t\"connection timed out\": \"Час зʼєднання вичерпано\",\n\t\"connection refused\": \"У зʼєднанні відмовлено\",\n\t\"owner died\": \"Власник помер\",\n\t\"an error occurred\": \"Відбулася помилка\",\n\t\"add ftp\": \"Додати FTP\",\n\t\"add sftp\": \"Додати SFTP\",\n\t\"save file\": \"Зберегти файл\",\n\t\"save file as\": \"Зберегти файл як\",\n\t\"files\": \"Файли\",\n\t\"help\": \"Довідка\",\n\t\"file has been deleted\": \"{file} видалено!\",\n\t\"feature not available\": \"Ця функція доступна лише в платній версії програми.\",\n\t\"deleted file\": \"Видалений файл\",\n\t\"line height\": \"Висота рядка\",\n\t\"preview info\": \"Якщо ви хочете запустити активний файл, натисніть й утримуйте піктограму відтворення.\",\n\t\"manage all files\": \"Дозвольте редактору Acode керувати всіма файлами в налаштуваннях, щоб легко редагувати файли на вашому пристрої.\",\n\t\"close file\": \"Закрити файл\",\n\t\"reset connections\": \"Скинути зʼєднання\",\n\t\"check file changes\": \"Перевіряти зміни файлу\",\n\t\"open in browser\": \"Відкрити в оглядачі\",\n\t\"desktop mode\": \"Режим стільниці\",\n\t\"toggle console\": \"Увімкнути консоль\",\n\t\"new line mode\": \"Режим нового рядка\",\n\t\"add a storage\": \"Додати сховище\",\n\t\"rate acode\": \"Оцінити Acode\",\n\t\"support\": \"Підтримка\",\n\t\"downloading file\": \"Завантажується {file}\",\n\t\"downloading...\": \"Завантаження...\",\n\t\"folder name\": \"Назва теки\",\n\t\"keyboard mode\": \"Режим клавіатури\",\n\t\"normal\": \"Нормальний\",\n\t\"app settings\": \"Налаштування програми\",\n\t\"disable in-app-browser caching\": \"Вимкнути кешування в оглядачі програми\",\n\t\"copied to clipboard\": \"Скопійовано до буфера обміну\",\n\t\"remember opened files\": \"Памʼятати відкриті файли\",\n\t\"remember opened folders\": \"Памʼятати відкриті теки\",\n\t\"no suggestions\": \"Без пропозицій\",\n\t\"no suggestions aggressive\": \"Без аґресивних пропозицій\",\n\t\"install\": \"Встановити\",\n\t\"installing\": \"Встановлення...\",\n\t\"plugins\": \"Плагіни\",\n\t\"recently used\": \"Недавно використане\",\n\t\"update\": \"Оновити\",\n\t\"uninstall\": \"Видалити\",\n\t\"download acode pro\": \"Завантажити Acode pro\",\n\t\"loading plugins\": \"Завантаження плагінів\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Зворотний зв'язок\",\n\t\"header\": \"Заголовок\",\n\t\"sidebar\": \"Бічна панель\",\n\t\"inapp\": \"У додатку\",\n\t\"browser\": \"Браузер\",\n\t\"diagonal scrolling\": \"Діагональне прокручування\",\n\t\"reverse scrolling\": \"Зворотне прокручування\",\n\t\"formatter\": \"Форматувальник\",\n\t\"format on save\": \"Форматування при збереженні\",\n\t\"remove ads\": \"Видалити рекламу\",\n\t\"fast\": \"Швидко\",\n\t\"slow\": \"Повільно\",\n\t\"scroll settings\": \"Налаштування прокручування\",\n\t\"scroll speed\": \"Швидкість прокручування\",\n\t\"loading...\": \"Завантаження...\",\n\t\"no plugins found\": \"Плагіни не знайдені\",\n\t\"name\": \"Ім'я\",\n\t\"username\": \"Ім'я користувача\",\n\t\"optional\": \"Опціонально\",\n\t\"hostname\": \"Ім'я хоста\",\n\t\"password\": \"Пароль\",\n\t\"security type\": \"Тип безпеки\",\n\t\"connection mode\": \"Режим підключення\",\n\t\"port\": \"Порт\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Підключення...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Неможливо завантажити файли\",\n\t\"preview port\": \"Порт попереднього перегляду\",\n\t\"find file\": \"Знайти файл\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Чутливість до регістру\",\n\t\"regular expression\": \"Регулярний вираз\",\n\t\"whole word\": \"Ціле слово\",\n\t\"edit with\": \"Редагувати за допомогою\",\n\t\"open with\": \"Відкрити за допомогою\",\n\t\"no app found to handle this file\": \"Не знайдено програми для роботи з цим файлом\",\n\t\"restore default settings\": \"Відновити стандартні налаштування\",\n\t\"server port\": \"Порт сервера\",\n\t\"preview settings\": \"Налаштування попереднього перегляду\",\n\t\"preview settings note\": \"Якщо порт попереднього перегляду та порт сервера відрізняються, програма не запустить сервер, а замість цього відкриє https://<host>:<preview port> у браузері або вбудованому браузері програми. Це корисно, коли ви запускаєте сервер десь інде.\",\n\t\"backup/restore note\": \"Буде створено резервну копію лише ваших налаштувань, індивідуальної теми та комбінацій клавіш. Резервна копія ваших FTP/SFTP не буде створена.\",\n\t\"host\": \"Хост\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"Більше\",\n\t\"thank you :)\": \"Дякую :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"скасовано\",\n\t\"local\": \"Локальний\",\n\t\"remote\": \"Віддалений\",\n\t\"show console toggler\": \"Показати перемикач консолі\",\n\t\"binary file\": \"Цей файл містить бінарні дані, чи хочете ви його відкрити?\",\n\t\"relative line numbers\": \"Відносні номери рядків\",\n\t\"elastic tabstops\": \"Еластичні табулятори\",\n\t\"line based rtl switching\": \"Комутація RTL на основі ліній\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Перевірка орфографії\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Недійсний плагін\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Плагін\",\n\t\"quicktools trigger mode\": \"Режим запуску Quicktools\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Приховати рядок заголовка на головному екрані.\",\n\t\"info-checkfiles\": \"Перевіряти зміни файлів, коли програма працює у фоновому режимі.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Запам'ятовувати відкриті файли після закриття програми.\",\n\t\"info-rememberfolders\": \"Запам'ятовувати відкриті папки після закриття програми.\",\n\t\"info-floatingbutton\": \"Показати або приховати плаваючу кнопку швидких інструментів.\",\n\t\"info-openfilelistpos\": \"Де відображати список активних файлів.\",\n\t\"info-touchmovethreshold\": \"Якщо чутливість сенсорного екрану вашого пристрою занадто висока, ви можете збільшити це значення, щоб запобігти випадковому переміщенню.\",\n\t\"info-scroll-settings\": \"Ці налаштування містять налаштування прокрутки, включаючи обтікання тексту.\",\n\t\"info-animation\": \"Якщо програма працює повільно, вимкніть анімацію.\",\n\t\"info-quicktoolstriggermode\": \"Якщо кнопка в швидких інструментах не працює, спробуйте змінити це значення.\",\n\t\"info-checkForAppUpdates\": \"Автоматично перевіряти наявність оновлень для додатка.\",\n\t\"info-quickTools\": \"Показати або приховати швидкі інструменти.\",\n\t\"info-showHiddenFiles\": \"Показувати приховані файли та папки. (Починати з .)\",\n\t\"info-all_file_access\": \"Увімкнути доступ до /sdcard та /storage у терміналі.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"Колірна тема терміналу.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Відновлює резервну копію інсталяції терміналу.\",\n\t\"info-uninstall\": \"Видаляє інсталяцію терміналу.\",\n\t\"owned\": \"Власний\",\n\t\"api_error\": \"Сервер API не працює, спробуйте пізніше.\",\n\t\"installed\": \"Встановлено\",\n\t\"all\": \"Усі\",\n\t\"medium\": \"Середній\",\n\t\"refund\": \"Повернення коштів\",\n\t\"product not available\": \"Продукт недоступний\",\n\t\"no-product-info\": \"Цей продукт наразі недоступний у вашій країні. Спробуйте пізніше.\",\n\t\"close\": \"Закрити\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Оновлено комбінації клавіш\",\n\t\"search in files\": \"Пошук у файлах\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Зберегти все\",\n\t\"close all\": \"Закрити все\",\n\t\"unsaved files warning\": \"Деякі файли не збережені. Натисніть «ОК», виберіть, що робити, або натисніть «Скасувати», щоб повернутися назад.\",\n\t\"save all warning\": \"Ви впевнені, що хочете зберегти всі файли і закрити? Цю дію неможливо скасувати.\",\n\t\"save all changes warning\": \"Ви впевнені, що хочете зберегти всі файли?\",\n\t\"close all warning\": \"Ви впевнені, що хочете закрити всі файли? Ви втратите незбережені зміни, і цю дію неможливо скасувати.\",\n\t\"refresh\": \"Оновити\",\n\t\"shortcut buttons\": \"Кнопки швидкого доступу\",\n\t\"no result\": \"Немає результатів\",\n\t\"searching...\": \"Пошук...\",\n\t\"quicktools:ctrl-key\": \"Клавіша Control/Command\",\n\t\"quicktools:tab-key\": \"Клавіша Tab\",\n\t\"quicktools:shift-key\": \"Клавіша Shift\",\n\t\"quicktools:undo\": \"Скасувати\",\n\t\"quicktools:redo\": \"Повторити\",\n\t\"quicktools:search\": \"Пошук у файлі\",\n\t\"quicktools:save\": \"Зберегти файл\",\n\t\"quicktools:esc-key\": \"Клавіша Escape\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Клавіша зі стрілкою вліво\",\n\t\"quicktools:right-arrow-key\": \"Клавіша зі стрілкою вправо\",\n\t\"quicktools:up-arrow-key\": \"Клавіша зі стрілкою вгору\",\n\t\"quicktools:down-arrow-key\": \"Клавіша зі стрілкою вниз\",\n\t\"quicktools:moveline-up\": \"Перемістити лінію вгору\",\n\t\"quicktools:moveline-down\": \"Перемістити лінію вниз\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Вставити лапки\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Вставити знак рівності\",\n\t\"quicktools:slash\": \"Вставити символ косої риски\",\n\t\"quicktools:exclamation\": \"Вставити знак оклику\",\n\t\"quicktools:alt-key\": \"Клавіша Alt\",\n\t\"quicktools:meta-key\": \"Клавіша Windows/Meta\",\n\t\"info-quicktoolssettings\": \"Налаштуйте кнопки швидкого доступу та клавіші клавіатури в контейнері Quicktools під редактором, щоб покращити свій досвід кодування.\",\n\t\"info-excludefolders\": \"Використовуйте шаблон **/node_modules/**, щоб проігнорувати всі файли з папки node_modules. Це виключить файли зі списку та запобіжить їх включенню в пошук файлів.\",\n\t\"missed files\": \"Після початку пошуку було проскановано {count} файлів, які не будуть включені в пошук.\",\n\t\"remove\": \"Видалити\",\n\t\"quicktools:command-palette\": \"Палітра команд\",\n\t\"default file encoding\": \"Кодування файлу за замовчуванням\",\n\t\"remove entry\": \"Ви впевнені, що хочете видалити '{name}' зі збережених шляхів? Зверніть увагу, що його видалення не призведе до видалення самого шляху.\",\n\t\"delete entry\": \"Підтвердити видалення: '{name}'. Цю дію неможливо скасувати. Продовжити?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Ви впевнені, що хочете знову відкрити '{file}'? Усі незбережені зміни будуть втрачені.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Попередній перегляд кольору\",\n\t\"confirm\": \"Підтвердити\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Проблеми\",\n\t\"show side buttons\": \"Показати бічні кнопки\",\n\t\"bug_report\": \"Надіслати звіт про помилку\",\n\t\"verified publisher\": \"Перевірений видавець\",\n\t\"most_downloaded\": \"Найбільш завантажувані\",\n\t\"newly_added\": \"Нещодавно додані\",\n\t\"top_rated\": \"Найвищий рейтинг\",\n\t\"rename not supported\": \"Перейменування в каталозі termux не підтримується\",\n\t\"compress\": \"Стиснути\",\n\t\"copy uri\": \"Копіювати Uri\",\n\t\"delete entries\": \"Ви впевнені, що хочете видалити {count} елементів?\",\n\t\"deleting items\": \"Видалення {count} елементів...\",\n\t\"import project zip\": \"Імпортувати проект (zip)\",\n\t\"changelog\": \"Журнал змін\",\n\t\"notifications\": \"Повідомлення\",\n\t\"no_unread_notifications\": \"Немає непрочитаних повідомлень\",\n\t\"should_use_current_file_for_preview\": \"Слід використовувати Поточний файл для попереднього перегляду замість стандартного (index.html)\",\n\t\"fade fold widgets\": \"Віджети згортання з ефектом зникання\",\n\t\"quicktools:home-key\": \"Клавіша Home\",\n\t\"quicktools:end-key\": \"Клавіша End\",\n\t\"quicktools:pageup-key\": \"Клавіша PageUp\",\n\t\"quicktools:pagedown-key\": \"Клавіша PageDown\",\n\t\"quicktools:delete-key\": \"Клавіша Delete\",\n\t\"quicktools:tilde\": \"Вставити символ тильда\",\n\t\"quicktools:backtick\": \"Вставити зворотну лапку\",\n\t\"quicktools:hash\": \"Вставити символ хеш\",\n\t\"quicktools:dollar\": \"Вставити символ долара\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Вставити символ каретки\",\n\t\"plugin_enabled\": \"Плагін увімкнено\",\n\t\"plugin_disabled\": \"Плагін вимкнено\",\n\t\"enable_plugin\": \"Увімкнути цей плагін\",\n\t\"disable_plugin\": \"Вимкнути цей плагін\",\n\t\"open_source\": \"Відкритий код\",\n\t\"terminal settings\": \"Налаштування терміналу\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Мигання курсору\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Стиль курсора\",\n\t\"terminal:font family\": \"Сімейство шрифтів\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Підтримка зображень\",\n\t\"terminal\": \"Термінал\",\n\t\"allFileAccess\": \"Доступ до всіх файлів\",\n\t\"fonts\": \"Шрифти\",\n\t\"sponsor\": \"Спонсор\",\n\t\"downloads\": \"завантаження\",\n\t\"reviews\": \"відгуки\",\n\t\"overview\": \"Огляд\",\n\t\"contributors\": \"Учасники\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Перевірити наявність оновлень для програми\",\n\t\"prompt update check consent message\": \"Acode може перевіряти наявність нових оновлень програми, коли ви перебуваєте в мережі. Увімкнути перевірку оновлень?\",\n\t\"keywords\": \"Ключові слова\",\n\t\"author\": \"Автор\",\n\t\"filtered by\": \"Відфільтровано за\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Резервна копія створена\",\n\t\"restore completed\": \"Відновлення завершено\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"Цю дію неможливо скасувати. Продовжити?\",\n\t\"reload to apply\": \"Перезавантажити, щоб застосувати зміни?\",\n\t\"reload app\": \"Перезавантажити додаток\",\n\t\"preparing backup\": \"Підготовка резервної копії\",\n\t\"collecting settings\": \"Збір налаштувань\",\n\t\"collecting key bindings\": \"Збір комбінацій клавіш\",\n\t\"collecting plugins\": \"Збір інформації про плагіни\",\n\t\"creating backup\": \"Створення файлу резервної копії\",\n\t\"validating backup\": \"Перевірка резервної копії\",\n\t\"restoring key bindings\": \"Відновлення комбінацій клавіш\",\n\t\"restoring plugins\": \"Відновлення плагінів\",\n\t\"restoring settings\": \"Відновлення налаштувань\",\n\t\"legacy backup warning\": \"Це старий формат резервного копіювання. Деякі функції можуть бути обмежені.\",\n\t\"checksum mismatch\": \"Невідповідність контрольної суми — файл резервної копії, можливо, було змінено або пошкоджено.\",\n\t\"plugin not found\": \"Плагін не знайдено в реєстрі\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Відновлено\",\n\t\"skipped\": \"Пропущено\",\n\t\"backup not valid object\": \"Файл резервної копії не є дійсним об'єктом\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"Це старіший формат резервного копіювання (v1). Деякі функції можуть бути обмежені.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Невідповідність контрольної суми — файл резервної копії, можливо, було змінено або пошкоджено. Дійте обережно.\",\n\t\"backup checksum verify failed\": \"Не вдалося перевірити контрольну суму\",\n\t\"backup invalid settings\": \"Недійсний формат налаштувань\",\n\t\"backup invalid keybindings\": \"Неправильний формат keyBindings\",\n\t\"backup invalid plugins\": \"Неправильний формат installedPlugins\",\n\t\"issues found\": \"Знайдені проблеми\",\n\t\"error details\": \"Деталі помилки\",\n\t\"active tools\": \"Активні інструменти\",\n\t\"available tools\": \"Доступні інструменти\",\n\t\"recent\": \"Нещодавні файли\",\n\t\"command palette\": \"Відкрити палітру команд\",\n\t\"change theme\": \"Змінити тему\",\n\t\"documentation\": \"Документація\",\n\t\"open in terminal\": \"Відкрити в терміналі\",\n\t\"developer mode\": \"Режим розробника\",\n\t\"info-developermode\": \"Увімкнути інструменти розробника (Eruda) для налагодження плагінів та перевірки стану програми. Інспектор буде ініціалізовано під час запуску програми.\",\n\t\"developer mode enabled\": \"Режим розробника ввімкнено. Використовуйте палітру команд для перемикання інспектора (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Режим розробника вимкнено\",\n\t\"copy relative path\": \"Копіювати відносний шлях\",\n\t\"shortcut request sent\": \"Запит на створення ярлика відкрито. Натисніть «Додати», щоб завершити.\",\n\t\"add to home screen\": \"Додати на головний екран\",\n\t\"pin shortcuts not supported\": \"Ярлики на головному екрані не підтримуються на цьому пристрої.\",\n\t\"save file before home shortcut\": \"Збережіть файл, перш ніж додавати його на головний екран.\",\n\t\"terminal_required_message_for_lsp\": \"Термінал не встановлено. Будь ласка, спочатку встановіть Термінал, щоб використовувати сервери LSP.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Діагностика\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Команда встановлення\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Сервер не знайдено\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"вимкнено\",\n\t\"lsp-state-enabled\": \"увімкнено\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Встановлено\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Невідомо\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Версія: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"Про Acode\",\n\t\"settings-category-advanced\": \"Розширений\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Запитати перед закриттям програми.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Приховати рядок стану системи під час використання Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Виберіть мову програми та перекладені мітки.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Запускати форматувальник щоразу, коли файл зберігається.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Шрифти, вкладки, підказки та відображення редактора.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Налаштуйте мовні сервери та інтелектуальні функції редактора.\",\n\t\"settings-info-main-plugins\": \"Керуйте встановленими плагінами та доступними для них діями.\",\n\t\"settings-info-main-preview-settings\": \"Режим попереднього перегляду, порти сервера та поведінка браузера.\",\n\t\"settings-info-main-rateapp\": \"Оцініть Acode в Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Тема терміналу, шрифт, курсор та поведінка сеансу.\",\n\t\"settings-info-main-theme\": \"Тема додатка, контрастність та власні кольори.\",\n\t\"settings-info-preview-disable-cache\": \"Завжди перезавантажуйте вміст у вбудованому браузері програми.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Порт, який використовується сервером попереднього перегляду.\",\n\t\"settings-info-preview-server-port\": \"Порт, що використовується внутрішнім сервером додатків.\",\n\t\"settings-info-preview-show-console-toggler\": \"Показувати кнопку консолі в попередньому перегляді.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Призначте форматувальник для кожної мови. Встановіть плагіни форматувальників, щоб розблокувати більше опцій.\",\n\t\"settings-note-lsp-settings\": \"Мовні сервери додають автозаповнення, діагностику, деталі при наведенні курсора тощо. Тут ви можете встановлювати, оновлювати або визначати власні сервери. Керовані інсталятори працюють у середовищі терміналу/proot.\",\n\t\"search result label singular\": \"результат\",\n\t\"search result label plural\": \"результати\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/uz-uz.json",
    "content": "{\n\t\"lang\": \"O'zbekcha (by TILON)\",\n\t\"about\": \"Ilova haqida\",\n\t\"active files\": \"Faol fayllar\",\n\t\"alert\": \"Ogohlantirish\",\n\t\"app theme\": \"Ilova mavzusi\",\n\t\"autocorrect\": \"Avtomatik tuzatish yoqilsinmi?\",\n\t\"autosave\": \"Avtomatik saqlash\",\n\t\"cancel\": \"bekor qilish\",\n\t\"change language\": \"Tilni o'zgartirish\",\n\t\"choose color\": \"Rangni tanlang\",\n\t\"clear\": \"Tozalash\",\n\t\"close app\": \"Dasturdan chiqmoqchimisiz?\",\n\t\"commit message\": \"Xabar berish\",\n\t\"console\": \"Konsol oynasi\",\n\t\"conflict error\": \"Qarama-qarshilik! Iltimos, boshqa majburiyatlar olishdan oldin kuting\",\n\t\"copy\": \"Nusxalash\",\n\t\"create folder error\": \"Kechirasiz,yangi papka yaratib bo'lmadi\",\n\t\"cut\": \"Qirqish\",\n\t\"delete\": \"o'chirish\",\n\t\"dependencies\": \"Bog'lanishlar\",\n\t\"delay\": \"Vaqt millisekundlarda\",\n\t\"editor settings\": \"Tahrirlash sozlamalari\",\n\t\"editor theme\": \"Tahrirlash mavzulari\",\n\t\"enter file name\": \"fayl nomini kiriting\",\n\t\"enter folder name\": \"papka nomini kiriting\",\n\t\"empty folder message\": \"Ushbu papkada hech narsa yo'q\",\n\t\"enter line number\": \"Qator raqamini kiriting\",\n\t\"error\": \"xatolik\",\n\t\"failed\": \"bajarilmadi\",\n\t\"file already exists\": \"Ushbu fayl oldindan mavjud\",\n\t\"file already exists force\": \"Ushbu fayl oldindan mavjud. Baribir yozilsinmi?\",\n\t\"file changed\": \"o'zgartirildi, faylni qayta yuklaysizmi?\",\n\t\"file deleted\": \"fayl o'chirildi\",\n\t\"file is not supported\": \"ushhu fayl qo'llab-quvvatlanmaydi\",\n\t\"file not supported\": \"fayl turi qo'llab-quvvatlanmaydi\",\n\t\"file too large\": \"Fayl xajmi juda katta. Maksimal hajmdagi fayl ruxsat etilgan: {size}\",\n\t\"file renamed\": \"fayl qayta nomlandi\",\n\t\"file saved\": \"fayl saqlandi\",\n\t\"folder added\": \"papka qo'shildi\",\n\t\"folder already added\": \"ushbu papka oldindan qo'shilgan\",\n\t\"font size\": \"Matn o'lchovi\",\n\t\"goto\": \"Qatorga borish\",\n\t\"icons definition\": \"Ikonlarni aniqlash\",\n\t\"info\": \"Haqida\",\n\t\"invalid value\": \"Kiritishda xatolik\",\n\t\"language changed\": \"Til muvoffaqiyatli o'zgartirildi\",\n\t\"linting\": \"Sintaktik xatolar tekshirilsinmi\",\n\t\"logout\": \"Tark etish\",\n\t\"loading\": \"Yuklanmoqda\",\n\t\"my profile\": \"Mening profilim\",\n\t\"new file\": \"Yangi fayl\",\n\t\"new folder\": \"Yangi papka\",\n\t\"no\": \"Yo'q\",\n\t\"no editor message\": \"Menyudan yangi fayl va papkani oching yoki yarating\",\n\t\"not set\": \"O'rnatilmagan\",\n\t\"unsaved files close app\": \"Saqlanmagan fayllar mavjud. Ilova yopilsinmi?\",\n\t\"notice\": \"Etibor bering\",\n\t\"open file\": \"Faylni ochish\",\n\t\"open files and folders\": \"Fayllarni va papkalarni ochish\",\n\t\"open folder\": \"Papkani ochish\",\n\t\"open recent\": \"So'ngi ochilganlar\",\n\t\"ok\": \"Yaxshi\",\n\t\"overwrite\": \"Baribir yozilsin\",\n\t\"paste\": \"Joylash\",\n\t\"preview mode\": \"Kod natijasini qayerda ko'moqchisiz?\",\n\t\"read only file\": \"Faqat o'qish uchun bo'lgan faylni saqlab bo'lmadi,Iltimos to'g'irlab qayta saqlab ko'ring\",\n\t\"reload\": \"qayta yuklash\",\n\t\"rename\": \"qayta nomlash\",\n\t\"replace\": \"almashtrish\",\n\t\"required\": \"Ushbu qator to'ldirilishi shart\",\n\t\"run your web app\": \"Web-ilovangizni ishga tushiring\",\n\t\"save\": \"Saqlash\",\n\t\"saving\": \"Saqlanmoqda\",\n\t\"save as\": \"Boshqa joyga saqlash\",\n\t\"save file to run\": \"Iltimos, brauzerda ishga tushirish uchun ushbu faylni saqlang\",\n\t\"search\": \"qidirish\",\n\t\"see logs and errors\": \"Loglar va xatoliklarni ko'rish\",\n\t\"select folder\": \"Papkani tanlash\",\n\t\"settings\": \"Sozlamalar\",\n\t\"settings saved\": \"Sozlamalar saqlandi\",\n\t\"show line numbers\": \"Qator raqamlari ko'rinsinmi\",\n\t\"show hidden files\": \"Yashirin fayllar ko'rinsinmi\",\n\t\"show spaces\": \"Bo'sh joylar ko'rinsinmi\",\n\t\"soft tab\": \"Qulay yorliq\",\n\t\"sort by name\": \"Ism bo'yicha saralash\",\n\t\"success\": \"Bajarildi\",\n\t\"tab size\": \"Yorliq hajmi\",\n\t\"text wrap\": \"Matnni o'rash\",\n\t\"theme\": \"Mavzu\",\n\t\"unable to delete file\": \"faylni o'chirib bo'lmadi\",\n\t\"unable to open file\": \"Kechirasiz,faylni ochib bo'lmadi\",\n\t\"unable to open folder\": \"Kechirasiz,papkani ochib bo'lmadi\",\n\t\"unable to save file\": \"Kechirasiz,faylni saqlab bo'lmadi\",\n\t\"unable to rename\": \"Kechirasiz,faylni qayta nomlab bo'lmadi\",\n\t\"unsaved file\": \"Ushbu fayl saqlanmadi, baribir yopilsinmi?\",\n\t\"warning\": \"Ogohlantirish\",\n\t\"use emmet\": \"Emmetdan foydalanish\",\n\t\"use quick tools\": \"Qo'shimcha xususiyatlardan foydalanish\",\n\t\"yes\": \"Ha\",\n\t\"encoding\": \"Matnni kodirivkalash\",\n\t\"syntax highlighting\": \"Sintaktikani ajiratib ko'rsatish\",\n\t\"read only\": \"Faqat o'qish\",\n\t\"select all\": \"Barchasini tanlash\",\n\t\"select branch\": \"Branchni tanlash\",\n\t\"create new branch\": \"Yangi branch yaratish.\",\n\t\"use branch\": \"Branchdan foydalanish\",\n\t\"new branch\": \"Yangi branch\",\n\t\"branch\": \"branch\",\n\t\"key bindings\": \"Tezkor kalitlarni o'zgartrish\",\n\t\"edit\": \"Tahrirlash\",\n\t\"reset\": \"qayta o'rnatish\",\n\t\"color\": \"Rang\",\n\t\"select word\": \"So'zni tanlash\",\n\t\"quick tools\": \"Tezkor xususiyatlar\",\n\t\"select\": \"tanlash\",\n\t\"editor font\": \"Shrift tahrirlash\",\n\t\"new project\": \"Yangi proyekt\",\n\t\"format\": \"format\",\n\t\"project name\": \"Proyekt nomi\",\n\t\"unsupported device\": \"Sizning qurilmangiz ushbu mavzuni qo'llab quvvatlamaydi\",\n\t\"vibrate on tap\": \"Vibrate on tap\",\n\t\"copy command is not supported by ftp.\": \"Copy command is not supported by FTP.\",\n\t\"support title\": \"Support Acode\",\n\t\"fullscreen\": \"fullscreen\",\n\t\"animation\": \"animation\",\n\t\"backup\": \"backup\",\n\t\"restore\": \"restore\",\n\t\"backup successful\": \"Backup successful\",\n\t\"invalid backup file\": \"Invalid backup file\",\n\t\"add path\": \"Add path\",\n\t\"live autocompletion\": \"Live autocompletion\",\n\t\"file properties\": \"File properties\",\n\t\"path\": \"Path\",\n\t\"type\": \"Type\",\n\t\"word count\": \"Word count\",\n\t\"line count\": \"Line count\",\n\t\"last modified\": \"Last modified\",\n\t\"size\": \"Size\",\n\t\"share\": \"Share\",\n\t\"show print margin\": \"Show print margin\",\n\t\"login\": \"login\",\n\t\"scrollbar size\": \"Scrollbar size\",\n\t\"cursor controller size\": \"Cursor controller size\",\n\t\"none\": \"none\",\n\t\"small\": \"small\",\n\t\"large\": \"large\",\n\t\"floating button\": \"Floating button\",\n\t\"confirm on exit\": \"Confirm on exit\",\n\t\"show console\": \"Show console\",\n\t\"image\": \"Image\",\n\t\"insert file\": \"Insert file\",\n\t\"insert color\": \"Insert color\",\n\t\"powersave mode warning\": \"Turn off power saving mode to preview in external browser.\",\n\t\"exit\": \"Exit\",\n\t\"custom\": \"custom\",\n\t\"reset warning\": \"Are you sure you want to reset theme?\",\n\t\"theme type\": \"Theme type\",\n\t\"light\": \"light\",\n\t\"dark\": \"dark\",\n\t\"file browser\": \"File Browser\",\n\t\"operation not permitted\": \"Operation not permitted\",\n\t\"no such file or directory\": \"No such file or directory\",\n\t\"input/output error\": \"Input/output error\",\n\t\"permission denied\": \"Permission denied\",\n\t\"bad address\": \"Bad address\",\n\t\"file exists\": \"File exists\",\n\t\"not a directory\": \"Not a directory\",\n\t\"is a directory\": \"Is a directory\",\n\t\"invalid argument\": \"Invalid argument\",\n\t\"too many open files in system\": \"Too many open files in system\",\n\t\"too many open files\": \"Too many open files\",\n\t\"text file busy\": \"Text file busy\",\n\t\"no space left on device\": \"No space left on device\",\n\t\"read-only file system\": \"Read-only file system\",\n\t\"file name too long\": \"File name too long\",\n\t\"too many users\": \"Too many users\",\n\t\"connection timed out\": \"Connection timed out\",\n\t\"connection refused\": \"Connection refused\",\n\t\"owner died\": \"Owner died\",\n\t\"an error occurred\": \"An error occurred\",\n\t\"add ftp\": \"Add FTP\",\n\t\"add sftp\": \"Add SFTP\",\n\t\"save file\": \"Save file\",\n\t\"save file as\": \"Save file as\",\n\t\"files\": \"Files\",\n\t\"help\": \"Help\",\n\t\"file has been deleted\": \"{file} has been deleted!\",\n\t\"feature not available\": \"This feature is only available in paid version of the app.\",\n\t\"deleted file\": \"Deleted file\",\n\t\"line height\": \"Line height\",\n\t\"preview info\": \"If you want run the active file, tap and hold on play icon.\",\n\t\"manage all files\": \"Allow Acode editor to manage all files in settings to edit files on your device easily.\",\n\t\"close file\": \"Close file\",\n\t\"reset connections\": \"Reset connections\",\n\t\"check file changes\": \"Check file changes\",\n\t\"open in browser\": \"Open in browser\",\n\t\"desktop mode\": \"Desktop mode\",\n\t\"toggle console\": \"Toggle console\",\n\t\"new line mode\": \"New line mode\",\n\t\"add a storage\": \"Add a storage\",\n\t\"rate acode\": \"Rate Acode\",\n\t\"support\": \"Support\",\n\t\"downloading file\": \"Downloading {file}\",\n\t\"downloading...\": \"Downloading...\",\n\t\"folder name\": \"Folder name\",\n\t\"keyboard mode\": \"Keyboard mode\",\n\t\"normal\": \"Normal\",\n\t\"app settings\": \"App settings\",\n\t\"disable in-app-browser caching\": \"Disable in-app-browser caching\",\n\t\"copied to clipboard\": \"Copied to clipboard\",\n\t\"remember opened files\": \"Remember opened files\",\n\t\"remember opened folders\": \"Remember opened folders\",\n\t\"no suggestions\": \"No suggestions\",\n\t\"no suggestions aggressive\": \"No suggestions aggressive\",\n\t\"install\": \"Install\",\n\t\"installing\": \"Installing...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Recently used\",\n\t\"update\": \"Update\",\n\t\"uninstall\": \"Uninstall\",\n\t\"download acode pro\": \"Download Acode pro\",\n\t\"loading plugins\": \"Loading plugins\",\n\t\"faqs\": \"FAQs\",\n\t\"feedback\": \"Feedback\",\n\t\"header\": \"Header\",\n\t\"sidebar\": \"Sidebar\",\n\t\"inapp\": \"Inapp\",\n\t\"browser\": \"Browser\",\n\t\"diagonal scrolling\": \"Diagonal scrolling\",\n\t\"reverse scrolling\": \"Reverse Scrolling\",\n\t\"formatter\": \"Formatter\",\n\t\"format on save\": \"Format on save\",\n\t\"remove ads\": \"Remove ads\",\n\t\"fast\": \"Fast\",\n\t\"slow\": \"Slow\",\n\t\"scroll settings\": \"Scroll settings\",\n\t\"scroll speed\": \"Scroll speed\",\n\t\"loading...\": \"Loading...\",\n\t\"no plugins found\": \"No plugins found\",\n\t\"name\": \"Name\",\n\t\"username\": \"Username\",\n\t\"optional\": \"optional\",\n\t\"hostname\": \"Hostname\",\n\t\"password\": \"Password\",\n\t\"security type\": \"Security Type\",\n\t\"connection mode\": \"Connection mode\",\n\t\"port\": \"Port\",\n\t\"key file\": \"Key file\",\n\t\"select key file\": \"Select key file\",\n\t\"passphrase\": \"Passphrase\",\n\t\"connecting...\": \"Connecting...\",\n\t\"type filename\": \"Type filename\",\n\t\"unable to load files\": \"Unable to load files\",\n\t\"preview port\": \"Preview port\",\n\t\"find file\": \"Find file\",\n\t\"system\": \"System\",\n\t\"please select a formatter\": \"Please select a formatter\",\n\t\"case sensitive\": \"Case sensitive\",\n\t\"regular expression\": \"Regular expression\",\n\t\"whole word\": \"Whole word\",\n\t\"edit with\": \"Edit with\",\n\t\"open with\": \"Open with\",\n\t\"no app found to handle this file\": \"No app found to handle this file\",\n\t\"restore default settings\": \"Restore default settings\",\n\t\"server port\": \"Server port\",\n\t\"preview settings\": \"Preview settings\",\n\t\"preview settings note\": \"If preview port and server port are different, app will not start server and it will instead open https://<host>:<preview port> in browser or in-app browser. This is useful when you are running a server somewhere else.\",\n\t\"backup/restore note\": \"It will only backup your settings, custom theme and key bindings. It will not backup your FPT/SFTP.\",\n\t\"host\": \"Host\",\n\t\"retry ftp/sftp when fail\": \"Retry ftp/sftp when fail\",\n\t\"more\": \"More\",\n\t\"thank you :)\": \"Thank you :)\",\n\t\"purchase pending\": \"purchase pending\",\n\t\"cancelled\": \"cancelled\",\n\t\"local\": \"Local\",\n\t\"remote\": \"Remote\",\n\t\"show console toggler\": \"Show console toggler\",\n\t\"binary file\": \"This file contains binary data, do you want to open it?\",\n\t\"relative line numbers\": \"Relative line numbers\",\n\t\"elastic tabstops\": \"Elastic tabstops\",\n\t\"line based rtl switching\": \"Line based RTL switching\",\n\t\"hard wrap\": \"Hard wrap\",\n\t\"spellcheck\": \"Spellcheck\",\n\t\"wrap method\": \"Wrap Method\",\n\t\"use textarea for ime\": \"Use textarea for IME\",\n\t\"invalid plugin\": \"Invalid Plugin\",\n\t\"type command\": \"Type command\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Quicktools trigger mode\",\n\t\"print margin\": \"Print margin\",\n\t\"touch move threshold\": \"Touch move threshold\",\n\t\"info-retryremotefsafterfail\": \"Retry FTP/SFTP connection when fails\",\n\t\"info-fullscreen\": \"Hide title bar in home screen.\",\n\t\"info-checkfiles\": \"Check file changes when app is in background.\",\n\t\"info-console\": \"Choose JavaScript console. Legacy is default console, eruda is a third party console.\",\n\t\"info-keyboardmode\": \"Keyboard mode for text input, no suggestions will hide suggestions and auto correct. If no suggestions does not work, try to change value to no suggestions aggressive.\",\n\t\"info-rememberfiles\": \"Remember opened files when app is closed.\",\n\t\"info-rememberfolders\": \"Remember opened folders when app is closed.\",\n\t\"info-floatingbutton\": \"Show or hide quick tools floating button.\",\n\t\"info-openfilelistpos\": \"Where to show active files list.\",\n\t\"info-touchmovethreshold\": \"If your device touch sensitivity is too high, you can increase this value to prevent accidental touch move.\",\n\t\"info-scroll-settings\": \"This settings contain scroll settings including text wrap.\",\n\t\"info-animation\": \"If the app feels laggy, disable animation.\",\n\t\"info-quicktoolstriggermode\": \"If button in quick tools is not working, try to change this value.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Owned\",\n\t\"api_error\": \"API server down, please try after some time.\",\n\t\"installed\": \"Installed\",\n\t\"all\": \"All\",\n\t\"medium\": \"Medium\",\n\t\"refund\": \"Refund\",\n\t\"product not available\": \"Product not available\",\n\t\"no-product-info\": \"This product is not available in your country at this moment, please try again later.\",\n\t\"close\": \"Close\",\n\t\"explore\": \"Explore\",\n\t\"key bindings updated\": \"Key bindings updated\",\n\t\"search in files\": \"Search in files\",\n\t\"exclude files\": \"Exclude files\",\n\t\"include files\": \"Include files\",\n\t\"search result\": \"{matches} results in {files} files.\",\n\t\"invalid regex\": \"Invalid regular expression: {message}.\",\n\t\"bottom\": \"Bottom\",\n\t\"save all\": \"Save all\",\n\t\"close all\": \"Close all\",\n\t\"unsaved files warning\": \"Some files are not saved. Click 'ok' select what to do or press 'cancel' to go back.\",\n\t\"save all warning\": \"Are you sure you want to save all files and close? This action cannot be reversed.\",\n\t\"save all changes warning\": \"Are you sure you want to save all files?\",\n\t\"close all warning\": \"Are you sure you want to close all files? You will lose the unsaved changes and this action cannot be reversed.\",\n\t\"refresh\": \"Refresh\",\n\t\"shortcut buttons\": \"Shortcut buttons\",\n\t\"no result\": \"No result\",\n\t\"searching...\": \"Searching...\",\n\t\"quicktools:ctrl-key\": \"Control/Command key\",\n\t\"quicktools:tab-key\": \"Tab key\",\n\t\"quicktools:shift-key\": \"Shift key\",\n\t\"quicktools:undo\": \"Undo\",\n\t\"quicktools:redo\": \"Redo\",\n\t\"quicktools:search\": \"Search in file\",\n\t\"quicktools:save\": \"Save file\",\n\t\"quicktools:esc-key\": \"Escape key\",\n\t\"quicktools:curlybracket\": \"Insert curly bracket\",\n\t\"quicktools:squarebracket\": \"Insert square bracket\",\n\t\"quicktools:parentheses\": \"Insert parentheses\",\n\t\"quicktools:anglebracket\": \"Insert angle bracket\",\n\t\"quicktools:left-arrow-key\": \"Left arrow key\",\n\t\"quicktools:right-arrow-key\": \"Right arrow key\",\n\t\"quicktools:up-arrow-key\": \"Up arrow key\",\n\t\"quicktools:down-arrow-key\": \"Down arrow key\",\n\t\"quicktools:moveline-up\": \"Move line up\",\n\t\"quicktools:moveline-down\": \"Move line down\",\n\t\"quicktools:copyline-up\": \"Copy line up\",\n\t\"quicktools:copyline-down\": \"Copy line down\",\n\t\"quicktools:semicolon\": \"Insert semicolon\",\n\t\"quicktools:quotation\": \"Insert quotation\",\n\t\"quicktools:and\": \"Insert and symbol\",\n\t\"quicktools:bar\": \"Insert bar symbol\",\n\t\"quicktools:equal\": \"Insert equal symbol\",\n\t\"quicktools:slash\": \"Insert slash symbol\",\n\t\"quicktools:exclamation\": \"Insert exclamation\",\n\t\"quicktools:alt-key\": \"Alt key\",\n\t\"quicktools:meta-key\": \"Windows/Meta key\",\n\t\"info-quicktoolssettings\": \"Customize shortcut buttons and keyboard keys in the Quicktools container below the editor to enhance your coding experience.\",\n\t\"info-excludefolders\": \"Use the pattern **/node_modules/** to ignore all files from the node_modules folder. This will exclude the files from being listed and will also prevent them from being included in file searches.\",\n\t\"missed files\": \"Scanned {count} files after search started and will not be included in search.\",\n\t\"remove\": \"Remove\",\n\t\"quicktools:command-palette\": \"Command palette\",\n\t\"default file encoding\": \"Default file encoding\",\n\t\"remove entry\": \"Are you sure you want to remove '{name}' from the saved paths? Please note that removing it will not delete the path itself.\",\n\t\"delete entry\": \"Confirm deletion: '{name}'. This action cannot be undone. Proceed?\",\n\t\"change encoding\": \"Reopen '{file}' with '{encoding}' encoding? This action will result in the loss of any unsaved changes made to the file. Do you want to proceed with reopening?\",\n\t\"reopen file\": \"Are you sure you want to reopen '{file}'? Any unsaved changes will be lost.\",\n\t\"plugin min version\": \"{name} only available in Acode - {v-code} and above. Click here to update.\",\n\t\"color preview\": \"Color preview\",\n\t\"confirm\": \"Confirm\",\n\t\"list files\": \"List all files in <strong>{name}</strong>? Too many files may crash the app.\",\n\t\"problems\": \"Problems\",\n\t\"show side buttons\": \"Show side buttons\",\n\t\"bug_report\": \"Submit a Bug Report\",\n\t\"verified publisher\": \"Verified publisher\",\n\t\"most_downloaded\": \"Most Downloaded\",\n\t\"newly_added\": \"Newly Added\",\n\t\"top_rated\": \"Top Rated\",\n\t\"rename not supported\": \"Rename on termux dir isn't supported\",\n\t\"compress\": \"Compress\",\n\t\"copy uri\": \"Copy Uri\",\n\t\"delete entries\": \"Are you sure you want to delete {count} items?\",\n\t\"deleting items\": \"Deleting {count} items...\",\n\t\"import project zip\": \"Import Project(zip)\",\n\t\"changelog\": \"Change Log\",\n\t\"notifications\": \"Notifications\",\n\t\"no_unread_notifications\": \"No unread notifications\",\n\t\"should_use_current_file_for_preview\": \"Should use Current File For preview instead of default (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Homiy\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/vi-vn.json",
    "content": "{\n\t\"lang\": \"Tiếng Việt\",\n\t\"about\": \"Về phần mềm\",\n\t\"active files\": \"Các tệp hoạt động \",\n\t\"alert\": \"Cảnh báo\",\n\t\"app theme\": \"Chủ đề ứng dụng\",\n\t\"autocorrect\": \"Bật tự động sửa lỗi?\",\n\t\"autosave\": \"Tự động lưu\",\n\t\"cancel\": \"Hủy bỏ\",\n\t\"change language\": \"Thay đổi ngôn ngữ\",\n\t\"choose color\": \"Chọn màu\",\n\t\"clear\": \"xoá hết\",\n\t\"close app\": \"Đóng ứng dụng?\",\n\t\"commit message\": \"Tin nhắn commit\",\n\t\"console\": \"Bảng điều khiển\",\n\t\"conflict error\": \"Xung đột! Vui lòng đợi trước khi thực hiện commit kế tiếp.\",\n\t\"copy\": \"Sao chép\",\n\t\"create folder error\": \"Xin lỗi, không thể tạo thư mục mới\",\n\t\"cut\": \"Cắt\",\n\t\"delete\": \"Xóa\",\n\t\"dependencies\": \"Phụ thuộc\",\n\t\"delay\": \"Thời gian tính bằng mili giây\",\n\t\"editor settings\": \"Cài đặt soạn thảo\",\n\t\"editor theme\": \"Chủ đề soạn thảo\",\n\t\"enter file name\": \"Nhập tên tệp\",\n\t\"enter folder name\": \"Nhập tên thư mục\",\n\t\"empty folder message\": \"Thư mục trống\",\n\t\"enter line number\": \"Nhập dòng số\",\n\t\"error\": \"Lỗi\",\n\t\"failed\": \"Thất bại\",\n\t\"file already exists\": \"Tệp đã tồn tại\",\n\t\"file already exists force\": \"Tệp đã tồn tại. Ghi đè?\",\n\t\"file changed\": \" đã bị thay đổi, tải lại tệp?\",\n\t\"file deleted\": \"Tệp đã xóa\",\n\t\"file is not supported\": \"Tệp không hỗ trợ\",\n\t\"file not supported\": \"Loại tệp này không được hỗ trợ.\",\n\t\"file too large\": \"Tệp quá lớn để xử lý. Độ lớn tối đa tệp cho phép là {size}\",\n\t\"file renamed\": \"tệp đã được đổi tên\",\n\t\"file saved\": \"tệp đã được lưu\",\n\t\"folder added\": \"thư mục đã được thêm\",\n\t\"folder already added\": \"thư mục đã thêm trước đó\",\n\t\"font size\": \"Kích thước phông chữ\",\n\t\"goto\": \"Đi đến dòng\",\n\t\"icons definition\": \"Định nghĩa biểu tượng\",\n\t\"info\": \"Thông tin\",\n\t\"invalid value\": \"Giá trị không hợp lệ\",\n\t\"language changed\": \"ngôn ngữ đã được thay đổi thành công\",\n\t\"linting\": \"Kiểm tra lỗi cú pháp\",\n\t\"logout\": \"Đăng xuất\",\n\t\"loading\": \"Đang tải\",\n\t\"my profile\": \"Hồ sơ của tôi\",\n\t\"new file\": \"Tệp mới\",\n\t\"new folder\": \"Thư mục mới\",\n\t\"no\": \"Không\",\n\t\"no editor message\": \"Mở hoặc tạo tệp và thư mục mới từ menu\",\n\t\"not set\": \"Chưa được đặt\",\n\t\"unsaved files close app\": \"Có những tệp chưa lưu. Đóng ứng dụng?\",\n\t\"notice\": \"Chú ý\",\n\t\"open file\": \"Mở tệp\",\n\t\"open files and folders\": \"Mở tệp và thư mục\",\n\t\"open folder\": \"Mở thư mục\",\n\t\"open recent\": \"Mở mục gần đây\",\n\t\"ok\": \"ok\",\n\t\"overwrite\": \"Ghi đè\",\n\t\"paste\": \"Dán\",\n\t\"preview mode\": \"Chế độ xem trước\",\n\t\"read only file\": \"Không thể tệp chỉ đọc. Hãy thử lưu dưới dạng\",\n\t\"reload\": \"Tải lại\",\n\t\"rename\": \"Đổi tên\",\n\t\"replace\": \"Thay thế\",\n\t\"required\": \"Trường này là bắt buộc\",\n\t\"run your web app\": \"Chạy ứng dụng web của bạn\",\n\t\"save\": \"Lưu\",\n\t\"saving\": \"Đang lưu\",\n\t\"save as\": \"Lưu dưới dạng\",\n\t\"save file to run\": \"Hãy lưu tệp này để chạy trong trình duyệt\",\n\t\"search\": \"Tìm\",\n\t\"see logs and errors\": \"Xem nhật ký và lỗi\",\n\t\"select folder\": \"Chọn thư mục\",\n\t\"settings\": \"Cài đặt\",\n\t\"settings saved\": \"Đã lưu cài đặt\",\n\t\"show line numbers\": \"Hiển thị số dòng\",\n\t\"show hidden files\": \"Hiển thị tệp ẩn\",\n\t\"show spaces\": \"Hiển thị khoảng trắng\",\n\t\"soft tab\": \"Tab mềm\",\n\t\"sort by name\": \"Sắp xếp bằng tên\",\n\t\"success\": \"Thành công\",\n\t\"tab size\": \"Kích thước Tab\",\n\t\"text wrap\": \"Ngắt dòng\",\n\t\"theme\": \"Chủ đề\",\n\t\"unable to delete file\": \"không thể xóa tệp\",\n\t\"unable to open file\": \"Xin lỗi, không thể mở tệp\",\n\t\"unable to open folder\": \"Xin lỗi, không thể mở thư mục\",\n\t\"unable to save file\": \"Xin lỗi, không thể lưu tệp\",\n\t\"unable to rename\": \"Xin lỗi, không thể đổi tên\",\n\t\"unsaved file\": \"Tệp này chưa được lữu , vẫn đóng lại?\",\n\t\"warning\": \"Cảnh báo\",\n\t\"use emmet\": \"Sử dụng Emmet\",\n\t\"use quick tools\": \"Sử dụng công cụ nhanh\",\n\t\"yes\": \"Có\",\n\t\"encoding\": \"Mã hóa văn bản\",\n\t\"syntax highlighting\": \"Tô sáng cú pháp\",\n\t\"read only\": \"Chỉ đọc\",\n\t\"select all\": \"Chọn tất cà\",\n\t\"select branch\": \"Chọn nhánh\",\n\t\"create new branch\": \"Tạo nhánh mới\",\n\t\"use branch\": \"Sử dụng nhánh\",\n\t\"new branch\": \"Nhánh mới\",\n\t\"branch\": \"Nhánh\",\n\t\"key bindings\": \"Phím tắt\",\n\t\"edit\": \"Chỉnh sửa\",\n\t\"reset\": \"Đặt lại\",\n\t\"color\": \"Màu sắc\",\n\t\"select word\": \"Chọn từ\",\n\t\"quick tools\": \"Công cụ nhanh\",\n\t\"select\": \"Chọn\",\n\t\"editor font\": \"Phông chữ soạn thảo\",\n\t\"new project\": \"Dự án mới\",\n\t\"format\": \"Định dạng\",\n\t\"project name\": \"Tên dự án\",\n\t\"unsupported device\": \"Thiết bị của bạn không hỗ trợ chủ đề.\",\n\t\"vibrate on tap\": \"Rung khi chạm\",\n\t\"copy command is not supported by ftp.\": \"Lệnh sao chép không được FTP hỗ trợ.\",\n\t\"support title\": \"Hỗ trợ Acode\",\n\t\"fullscreen\": \"Toàn màn hình\",\n\t\"animation\": \"Hoạt ảnh\",\n\t\"backup\": \"Sao lưu\",\n\t\"restore\": \"Khôi phục\",\n\t\"backup successful\": \"Sao lưu thành công\",\n\t\"invalid backup file\": \"Tệp sao lưu không hợp lệ\",\n\t\"add path\": \"Thêm đường dẫn\",\n\t\"live autocompletion\": \"Tự động hoàn thành trực tiếp\",\n\t\"file properties\": \"Thuộc tính tệp\",\n\t\"path\": \"Đường dẫn\",\n\t\"type\": \"Loại\",\n\t\"word count\": \"Số từ\",\n\t\"line count\": \"Số dòng\",\n\t\"last modified\": \"Sửa lần cuối\",\n\t\"size\": \"Kích thước e\",\n\t\"share\": \"Chia sẻ\",\n\t\"show print margin\": \"Hiển thị lề in\",\n\t\"login\": \"Đăng nhập\",\n\t\"scrollbar size\": \"Kích thước thanh cuộn\",\n\t\"cursor controller size\": \"Kích thước điều khiển con trỏ\",\n\t\"none\": \"Không có\",\n\t\"small\": \"Nhỏ\",\n\t\"large\": \"Lớn\",\n\t\"floating button\": \"Nút nổi\",\n\t\"confirm on exit\": \"Xác nhận khi thoát\",\n\t\"show console\": \"Hiển thị bảng điều khiển\",\n\t\"image\": \"Hình ảnh\",\n\t\"insert file\": \"Chèn tệp\",\n\t\"insert color\": \"Chèn màu\",\n\t\"powersave mode warning\": \"Tắt chế độ tiết kiệm điện để xem trước trên trình duyệt bên ngoài.\",\n\t\"exit\": \"Thoát\",\n\t\"custom\": \"Tùy chỉnh\",\n\t\"reset warning\": \"Có chắc muốn đặt lại chủ đề không?\",\n\t\"theme type\": \"Loại chủ đề\",\n\t\"light\": \"Sáng\",\n\t\"dark\": \"Tối\",\n\t\"file browser\": \"Trình duyệt tệp\",\n\t\"operation not permitted\": \"Hoạt động không được phép\",\n\t\"no such file or directory\": \"Không có tệp hoặc thư mục như thế\",\n\t\"input/output error\": \"Lỗi đầu vào/đầu ra\",\n\t\"permission denied\": \"Quyền bị từ chối\",\n\t\"bad address\": \"Địa chỉ không đúng\",\n\t\"file exists\": \"Tệp đã tồn tại\",\n\t\"not a directory\": \"Không phải là thư mục\",\n\t\"is a directory\": \"Là một thư mục\",\n\t\"invalid argument\": \"Tham số không hợp lệ\",\n\t\"too many open files in system\": \"Quá nhiều tệp mở trong hệ thống\",\n\t\"too many open files\": \"Quá nhiều tệp mở\",\n\t\"text file busy\": \"Tệp văn bản đang bận\",\n\t\"no space left on device\": \"Không còn chỗ trống trên thiết bị\",\n\t\"read-only file system\": \"Hệ thống tệp chỉ đọc\",\n\t\"file name too long\": \"Tên tệp quá dài\",\n\t\"too many users\": \"Quá nhiều người dùng\",\n\t\"connection timed out\": \"Kết nối hết thời gian chờ\",\n\t\"connection refused\": \"Kết nối bị từ chối\",\n\t\"owner died\": \"Chủ sở hữu đã nằm\",\n\t\"an error occurred\": \"Đã xảy ra lỗi\",\n\t\"add ftp\": \"Thêm FTP\",\n\t\"add sftp\": \"Thêm SFTP\",\n\t\"save file\": \"Lưu tệp\",\n\t\"save file as\": \"Lưu tệp dưới dạng\",\n\t\"files\": \"Tệp\",\n\t\"help\": \"Trợ giúp\",\n\t\"file has been deleted\": \"{file} đã bị xoá!\",\n\t\"feature not available\": \"Tính năng này chỉ có ở phiên bản trả phí của ứng dụng.\",\n\t\"deleted file\": \"Đã xoá tệp\",\n\t\"line height\": \"Chiều cao dòng\",\n\t\"preview info\": \"Nếu muốn chạy tệp đang hoạt động, hãy chạm giữ vào nút phát.\",\n\t\"manage all files\": \"Cho phép trình soạn thảo Acode quản lý tất cả các tệp trong cài đặt để chỉnh sửa tệp trên thiết bị của bạn một cách dễ dàng.\",\n\t\"close file\": \"Đóng tệp\",\n\t\"reset connections\": \"Đặt lại kết nối\",\n\t\"check file changes\": \"Kiểm tra các thay đổi tệp\",\n\t\"open in browser\": \"Mở trong trình duyệt\",\n\t\"desktop mode\": \"Chế độ máy tính\",\n\t\"toggle console\": \"Chuyển đổi bảng điều khiển\",\n\t\"new line mode\": \"Chế độ dòng mới\",\n\t\"add a storage\": \"Thêm một lưu trữ\",\n\t\"rate acode\": \"Đánh giá Acode\",\n\t\"support\": \"Hỗ trợ\",\n\t\"downloading file\": \"Đang tải {file}\",\n\t\"downloading...\": \"Đang tải...\",\n\t\"folder name\": \"Tên thư mục\",\n\t\"keyboard mode\": \"Chế độ bàn phím\",\n\t\"normal\": \"Bình thường\",\n\t\"app settings\": \"Cài đặt ứng dụng\",\n\t\"disable in-app-browser caching\": \"Tắt bộ nhớ đệm trong trình duyệt ứng dụng\",\n\t\"Should use Current File For preview instead of default (index.html)\": \"Nên sử dụng Tệp hiện tại để xem trước thay vì mặc định (index.html)\",\n\t\"copied to clipboard\": \"Đã sao chép vào bảng nhớ tạm\",\n\t\"remember opened files\": \"Ghi nhớ các tệp đã mở\",\n\t\"remember opened folders\": \"Ghi nhớ các thư mục đã mở\",\n\t\"no suggestions\": \"Không có gợi ý\",\n\t\"no suggestions aggressive\": \"Không có gợi ý một cách tích cực\",\n\t\"install\": \"Cài đặt\",\n\t\"installing\": \"Đăng cài đặt...\",\n\t\"plugins\": \"Plugins\",\n\t\"recently used\": \"Mới sử dụng\",\n\t\"update\": \"Cập nhật\",\n\t\"uninstall\": \"Gỡ cài đặt\",\n\t\"download acode pro\": \"Tải Acode pro\",\n\t\"loading plugins\": \"Đang tải plugins\",\n\t\"faqs\": \"Câu hỏi thường gặp\",\n\t\"feedback\": \"Phản hồi\",\n\t\"header\": \"Tiêu đề\",\n\t\"sidebar\": \"Thanh bên\",\n\t\"inapp\": \"Trong ứng dụng\",\n\t\"browser\": \"Trình duyệt\",\n\t\"diagonal scrolling\": \"Cuộn chéo\",\n\t\"reverse scrolling\": \"Cuộn ngược\",\n\t\"formatter\": \"Trình định dạng\",\n\t\"format on save\": \"Định dạng khi lưu\",\n\t\"remove ads\": \"Xoá quảng cáo\",\n\t\"fast\": \"Nhanh\",\n\t\"slow\": \"Chậm\",\n\t\"scroll settings\": \"Cài đặt cuộn\",\n\t\"scroll speed\": \"Tốc độ cuộn\",\n\t\"loading...\": \"Đang tải...\",\n\t\"no plugins found\": \"Không tìm thấy plugins\",\n\t\"name\": \"Tên\",\n\t\"username\": \"Tên người dùng\",\n\t\"optional\": \"không bắt buộc\",\n\t\"hostname\": \"Tên máy chủ\",\n\t\"password\": \"Mật khẩu\",\n\t\"security type\": \"Loại bảo mật\",\n\t\"connection mode\": \"Loại kết nối\",\n\t\"port\": \"Cổng\",\n\t\"key file\": \"Tệp khoá\",\n\t\"select key file\": \"Chọn tệp khoá\",\n\t\"passphrase\": \"Mật khẩu\",\n\t\"connecting...\": \"Đang kết nối...\",\n\t\"type filename\": \"Nhập tên tệp\",\n\t\"unable to load files\": \"Không thể tải tệp\",\n\t\"preview port\": \"Xem trước cổng\",\n\t\"find file\": \"Tìm tệp\",\n\t\"system\": \"Hệ thống\",\n\t\"please select a formatter\": \"Vui lòng chọn một trình định dạng\",\n\t\"case sensitive\": \"Phân biệt chữ hoa chữ thường\",\n\t\"regular expression\": \"Biểu thức chính quy\",\n\t\"whole word\": \"Toàn bộ từ\",\n\t\"edit with\": \"Sửa với\",\n\t\"open with\": \"Mở với\",\n\t\"no app found to handle this file\": \"Không thấy ứng dụng nào có thể xử lý tệp này\",\n\t\"restore default settings\": \"Khôi phục cài đặt mặc định\",\n\t\"server port\": \"Cổng máy chủ\",\n\t\"preview settings\": \"Cài đặt xem trước\",\n\t\"preview settings note\": \"Nếu cổng xem trước và cổng máy chủ khác nhau, ứng dụng sẽ không khởi động máy chủ mà thay vào đó sẽ mở https://<máy chủ>:<cổng xem trước> trong trình duyệt hoặc trình duyệt trong ứng dụng. Điều này hữu ích khi bạn đang chạy máy chủ ở nơi khác.\",\n\t\"backup/restore note\": \"Nó sẽ chỉ sao lưu cài đặt, chủ đề tùy chỉnh và phím tắt của bạn. Nó sẽ không sao lưu FTP/SFTP của bạn.\",\n\t\"host\": \"Máy chủ\",\n\t\"retry ftp/sftp when fail\": \"Thử lại ftp/sftp khi thất bại\",\n\t\"more\": \"Thêm\",\n\t\"thank you :)\": \"Cảm ơn :)\",\n\t\"purchase pending\": \"đang xử lý giao dịch\",\n\t\"cancelled\": \"đã hủy\",\n\t\"local\": \"Nội bộ\",\n\t\"remote\": \"Từ xa\",\n\t\"show console toggler\": \"Hiển thị nút chuyển đổi bảng điều khiển\",\n\t\"binary file\": \"Tệp này chứa dữ liệu nhị phân, bạn có muốn mở nó không?\",\n\t\"relative line numbers\": \"Số dòng tương đối\",\n\t\"elastic tabstops\": \"Điểm dùng tab đàn hồi\",\n\t\"line based rtl switching\": \"Chuyển mạch dòng từ phải --> trái\",\n\t\"hard wrap\": \"Ngắt dòng cứng\",\n\t\"spellcheck\": \"Kiểm tra chính tả\",\n\t\"wrap method\": \"Cách thức ngắt\",\n\t\"use textarea for ime\": \"Sử dụng textarea cho IME\",\n\t\"invalid plugin\": \"Plugin không hợp lệ\",\n\t\"type command\": \"Nhập lệnh\",\n\t\"plugin\": \"Plugin\",\n\t\"quicktools trigger mode\": \"Chế độ kích hoạt công cụ nhanh\",\n\t\"print margin\": \"In lề\",\n\t\"touch move threshold\": \"Chạm vào ngưỡng di chuyển\",\n\t\"info-retryremotefsafterfail\": \"Thử kết nối FTP/SFTP lại khi không thành công.\",\n\t\"info-fullscreen\": \"Ẩn thanh tiêu đề ở màn hình chính.\",\n\t\"info-checkfiles\": \"Kiểm tra những thay đổi của tệp khi ứng dụng đang chạy nền.\",\n\t\"info-console\": \"Chọn bảng điều khiển JavaScript. Legacy là bảng điều khiển mặc định, eruda là bảng điều khiển của bên thứ ba.\",\n\t\"info-keyboardmode\": \"Chế độ bàn phím để nhập văn bản, không có gợi ý sẽ ẩn gợi ý và tự động sửa. Nếu không có gợi ý không hiệu quả, hãy thử thay đổi giá trị thành không có gợi ý một cách tích cực.\",\n\t\"info-rememberfiles\": \"Ghi nhớ các tệp đã mở khi đóng ứng dụng.\",\n\t\"info-rememberfolders\": \"Ghi nhớ các thư mục đã mở khi đóng ứng dụng.\",\n\t\"info-floatingbutton\": \"Hiển thị hoặc ẩn nút nổi của công cụ nhanh.\",\n\t\"info-openfilelistpos\": \"Nơi hiển thị danh sách các tệp đang hoạt động\",\n\t\"info-touchmovethreshold\": \"Nếu độ nhạy cảm ứng của thiết bị quá cao, bạn có thể tăng giá trị này để tránh việc di chuyển cảm ứng vô tình.\",\n\t\"info-scroll-settings\": \"Các thiết lập này bao gồm các thiết lập cuộn và cả cả ngắt dòng\",\n\t\"info-animation\": \"Hình như hơi dật, tắt hoạt ảnh thử.\",\n\t\"info-quicktoolstriggermode\": \"Nếu nút trong công cụ nhanh không hoạt động, hãy thử thay đổi giá trị này.\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"Đã sở hữu\",\n\t\"api_error\": \"Máy chủ API không hoạt động, Hãy thử lại sau\",\n\t\"installed\": \"Đã cài đặt\",\n\t\"all\": \"Tất cả\",\n\t\"medium\": \"Trung bình\",\n\t\"refund\": \"Hoàn trả\",\n\t\"product not available\": \"Sản phẩm không có sẵn\",\n\t\"no-product-info\": \"Sản phẩm này hiện không có sẵn ở quốc gia của bạn, Hãy thử lại sau\",\n\t\"close\": \"Đóng\",\n\t\"explore\": \"Khám phá\",\n\t\"key bindings updated\": \"Đã cập nhật các phím tắt\",\n\t\"search in files\": \"Tìm kiếm trong các tệp\",\n\t\"exclude files\": \"Loại trừ các tập tin\",\n\t\"include files\": \"Bao gồm các tập tin\",\n\t\"search result\": \"{matches} có trong tệp {files}.\",\n\t\"invalid regex\": \"Biểu thức chính quy không hợp lệ: {message}.\",\n\t\"bottom\": \"Phía dưới\",\n\t\"save all\": \"Lưu tất cả\",\n\t\"close all\": \"Đóng tất cả\",\n\t\"unsaved files warning\": \"Một số tệp không được lưu. Nhấp vào 'ok' để chọn việc cần làm hoặc nhấn 'cancel' để quay lại.\",\n\t\"save all warning\": \"Bạn có chắc muốn lưu tất cả các tệp và đóng không? Việc này không thể hoàn tác.\",\n\t\"save all changes warning\": \"Bạn có chắc chắn muốn lưu tất cả các tệp không?\",\n\t\"close all warning\": \"Bạn có chắc muốn đóng tất cả các tệp không? Bạn sẽ mất những thay đổi chưa lưu và việc này không thể hoàn tác.\",\n\t\"refresh\": \"Làm mới\",\n\t\"shortcut buttons\": \"Các nút tắt\",\n\t\"no result\": \"Không có kết quả\",\n\t\"searching...\": \"Đang tìm...\",\n\t\"quicktools:ctrl-key\": \"Phím Control/Command\",\n\t\"quicktools:tab-key\": \"Phím Tab\",\n\t\"quicktools:shift-key\": \"Phím Shift\",\n\t\"quicktools:undo\": \"Hoàn tác\",\n\t\"quicktools:redo\": \"Làm lại\",\n\t\"quicktools:search\": \"Tìm kiếm trong tệp\",\n\t\"quicktools:save\": \"Lưu tệp\",\n\t\"quicktools:esc-key\": \"Phím Escape\",\n\t\"quicktools:curlybracket\": \"Chèn dấu ngoặc nhọn\",\n\t\"quicktools:squarebracket\": \"Chèn dấu ngoặc vuông\",\n\t\"quicktools:parentheses\": \"Chèn dấu ngoặc đơn\",\n\t\"quicktools:anglebracket\": \"Chèn dấu ngoặc so sánh\",\n\t\"quicktools:left-arrow-key\": \"Phím mũi tên trái\",\n\t\"quicktools:right-arrow-key\": \"Phím mũi tên phải\",\n\t\"quicktools:up-arrow-key\": \"Phím mũi tên lên\",\n\t\"quicktools:down-arrow-key\": \"Phím mũi tên xuống\",\n\t\"quicktools:moveline-up\": \"Chuyển dòng đi lên\",\n\t\"quicktools:moveline-down\": \"Chuyển dòng đi xuống\",\n\t\"quicktools:copyline-up\": \"Chép dòng lên trên\",\n\t\"quicktools:copyline-down\": \"Chép dòng xuống dưới\",\n\t\"quicktools:semicolon\": \"Chèn dấu chấm phẩy\",\n\t\"quicktools:quotation\": \"Chèn dấu nháy kép\",\n\t\"quicktools:and\": \"Chèn dấu AND\",\n\t\"quicktools:bar\": \"Chèn dấu OR\",\n\t\"quicktools:equal\": \"Chèn dấu Bằng\",\n\t\"quicktools:slash\": \"Chèn dấu gạch chéo\",\n\t\"quicktools:exclamation\": \"Chèn dấu chấm than\",\n\t\"quicktools:alt-key\": \"Phím Alt\",\n\t\"quicktools:meta-key\": \"Phím Windows/Meta\",\n\t\"info-quicktoolssettings\": \"Tùy chỉnh các nút tắt và phím bàn phím trong hộp công cụ nhanh bên dưới trình soạn thảo để nâng cao trải nghiệm viết mã.\",\n\t\"info-excludefolders\": \"Sử dụng mẫu **/node_modules/** để bỏ qua tất cả các tệp từ thư mục node_modules. Thao tác này sẽ loại trừ các tệp khỏi danh sách và cũng sẽ ngăn chúng được đưa vào tìm kiếm tệp.\",\n\t\"missed files\": \"Đã quét {count} tệp sau khi bắt đầu tìm kiếm và sẽ không được đưa vào tìm kiếm.\",\n\t\"remove\": \"Loại bỏ\",\n\t\"quicktools:command-palette\": \"Bảng lệnh\",\n\t\"default file encoding\": \"Mã hóa tệp mặc định\",\n\t\"remove entry\": \"Bạn có chắc muốn xóa '{name}' khỏi đường dẫn đã lưu không? Lưu ý rằng việc xóa nó sẽ không xóa chính đường dẫn đó.\",\n\t\"delete entry\": \"Xác nhận xóa: '{name}'. Không thể hoàn tác việc. Tiếp tục?\",\n\t\"change encoding\": \"Mở lại '{file}' với mã hóa '{encoding}'? Hành động này sẽ dẫn đến mất thay đổi nào chưa lưu được thực hiện với tệp . Bạn có muốn tiếp tục mở lại không?\",\n\t\"reopen file\": \"Bạn có chắc muốn mở lại '{file}' không? Thay đổi nào chưa lưu sẽ bị mất.\",\n\t\"plugin min version\": \"{name} chỉ khả dụng trong Acode - {v-code} trở lên. Nhấp vào đây để cập nhật.\",\n\t\"color preview\": \"Xem trước màu\",\n\t\"confirm\": \"Xác nhận\",\n\t\"list files\": \"Liệt kê tất cả các tệp trong <strong>{name}</strong>? Quá nhiều tệp có thể làm sập ứng dụng.\",\n\t\"problems\": \"Vấn đề\",\n\t\"show side buttons\": \"Hiển thị các nút bên\",\n\t\"bug_report\": \"Gửi báo cáo lỗi\",\n\t\"verified publisher\": \"Nhà phát hành đã xác minh\",\n\t\"most_downloaded\": \"Tải xuống nhiều nhất\",\n\t\"newly_added\": \"Mới được thêm vào\",\n\t\"top_rated\": \"Đánh giá cao nhất\",\n\t\"rename not supported\": \"Đổi tên trên thư mục Termux không được hỗ trợ\",\n\t\"compress\": \"Nén lại\",\n\t\"copy uri\": \"Sao chép Uri\",\n\t\"delete entries\": \"Bạn có chắc muốn xoá {count} mục?\",\n\t\"deleting items\": \"Đang xoá {count} mục...\",\n\t\"import project zip\": \"Nhập Dự Án(zip)\",\n\t\"changelog\": \"Nhật Ký Thay Đổi\",\n\t\"notifications\": \"Thông báo\",\n\t\"no_unread_notifications\": \"Thông báo chưa đọc\",\n\t\"should_use_current_file_for_preview\": \"Nên sử dụng Tệp hiện tại để xem trước thay vì mặc định (index.html)\",\n\t\"fade fold widgets\": \"Các tiện ích gập và mờ dần\",\n\t\"quicktools:home-key\": \"Phím Home\",\n\t\"quicktools:end-key\": \"Phím End\",\n\t\"quicktools:pageup-key\": \"Phím PageUp\",\n\t\"quicktools:pagedown-key\": \"Phím PageDown\",\n\t\"quicktools:delete-key\": \"Phím Delete\",\n\t\"quicktools:tilde\": \"Chèn ký tự ngã\",\n\t\"quicktools:backtick\": \"Chèn ký tự nháy ngược\",\n\t\"quicktools:hash\": \"Chèn ký tự thăng\",\n\t\"quicktools:dollar\": \"Chèn ký tự đô la\",\n\t\"quicktools:modulo\": \"Chèn ký tự chia lấy dư/phần trăm\",\n\t\"quicktools:caret\": \"Chèn ký tự gạch dọc\",\n\t\"plugin_enabled\": \"Plugin đã bật\",\n\t\"plugin_disabled\": \"Plugin đã tắt\",\n\t\"enable_plugin\": \"Bật Plugin này\",\n\t\"disable_plugin\": \"Tắt Plugin này\",\n\t\"open_source\": \"Mã Nguồn Mở\",\n\t\"terminal settings\": \"Cài đặt Terminal\",\n\t\"font ligatures\": \"Phông Ghép Chữ\",\n\t\"letter spacing\": \"Khoảng Cách Chữ\",\n\t\"terminal:tab stop width\": \"Độ Rộng Tab\",\n\t\"terminal:scrollback\": \"Số Dòng Cuộn Ngược\",\n\t\"terminal:cursor blink\": \"Con Trỏ Nháy\",\n\t\"terminal:font weight\": \"Độ Đậm Phông\",\n\t\"terminal:cursor inactive style\": \"Kiểu Con Trỏ Bất Hoạt\",\n\t\"terminal:cursor style\": \"Kiểu Con Trỏ\",\n\t\"terminal:font family\": \"Họ phông chữ\",\n\t\"terminal:convert eol\": \"Chuyển Đổi Cuối Dòng\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"Nhà tài trợ\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/zh-cn.json",
    "content": "{\n\t\"lang\": \"简体中文\",\n\t\"about\": \"关于\",\n\t\"active files\": \"打开的文件\",\n\t\"alert\": \"提醒\",\n\t\"app theme\": \"应用主题\",\n\t\"autocorrect\": \"启用自动校正?\",\n\t\"autosave\": \"自动保存\",\n\t\"cancel\": \"取消\",\n\t\"change language\": \"更改语言\",\n\t\"choose color\": \"选择颜色\",\n\t\"clear\": \"清空\",\n\t\"close app\": \"关闭应用程序?\",\n\t\"commit message\": \"提交说明\",\n\t\"console\": \"控制台\",\n\t\"conflict error\": \"冲突! 请等待其他提交完成.\",\n\t\"copy\": \"复制\",\n\t\"create folder error\": \"抱歉, 无法创建新文件夹\",\n\t\"cut\": \"剪切\",\n\t\"delete\": \"删除\",\n\t\"dependencies\": \"依赖项\",\n\t\"delay\": \"以毫秒为单位\",\n\t\"editor settings\": \"编辑器设置\",\n\t\"editor theme\": \"编辑器主题\",\n\t\"enter file name\": \"输入文件名\",\n\t\"enter folder name\": \"输入文件夹名\",\n\t\"empty folder message\": \"空文件夹\",\n\t\"enter line number\": \"输入行号\",\n\t\"error\": \"错误\",\n\t\"failed\": \"失败\",\n\t\"file already exists\": \"文件已存在\",\n\t\"file already exists force\": \"文件已存在. 是否覆盖?\",\n\t\"file changed\": \" 已发生改变, 重新加载文件?\",\n\t\"file deleted\": \"文件已删除\",\n\t\"file is not supported\": \"不支持此文件\",\n\t\"file not supported\": \"不支持此文件类型.\",\n\t\"file too large\": \"文件太大，无法处理。最大支持 {size} 的文件\",\n\t\"file renamed\": \"文件已重命名\",\n\t\"file saved\": \"文件已保存\",\n\t\"folder added\": \"添加文件夹\",\n\t\"folder already added\": \"文件夹已添加\",\n\t\"font size\": \"字体大小\",\n\t\"goto\": \"跳转至行...\",\n\t\"icons definition\": \"图标定义\",\n\t\"info\": \"信息\",\n\t\"invalid value\": \"无效值\",\n\t\"language changed\": \"语言已更改\",\n\t\"linting\": \"检查语法错误\",\n\t\"logout\": \"登出\",\n\t\"loading\": \"加载中\",\n\t\"my profile\": \"我的信息\",\n\t\"new file\": \"创建新文件\",\n\t\"new folder\": \"创建新文件夹\",\n\t\"no\": \"否\",\n\t\"no editor message\": \"从菜单打开或创建新文件和文件夹\",\n\t\"not set\": \"未设置\",\n\t\"unsaved files close app\": \"文件未保存，退出应用?\",\n\t\"notice\": \"注意\",\n\t\"open file\": \"打开文件\",\n\t\"open files and folders\": \"打开文件和文件夹\",\n\t\"open folder\": \"打开文件夹\",\n\t\"open recent\": \"最近打开\",\n\t\"ok\": \"确认\",\n\t\"overwrite\": \"覆盖\",\n\t\"paste\": \"粘贴\",\n\t\"preview mode\": \"预览模式\",\n\t\"read only file\": \"无法保存只读文件. 请尝试另存为\",\n\t\"reload\": \"重新加载\",\n\t\"rename\": \"重命名\",\n\t\"replace\": \"替换\",\n\t\"required\": \"此字段为必填字段\",\n\t\"run your web app\": \"运行你的 web 应用\",\n\t\"save\": \"保存\",\n\t\"saving\": \"保存中\",\n\t\"save as\": \"另存为\",\n\t\"save file to run\": \"请保存此文件以在浏览器中运行\",\n\t\"search\": \"搜索\",\n\t\"see logs and errors\": \"查看日志和错误\",\n\t\"select folder\": \"选择文件夹\",\n\t\"settings\": \"设置\",\n\t\"settings saved\": \"设置已保存\",\n\t\"show line numbers\": \"显示行号\",\n\t\"show hidden files\": \"显示隐藏文件\",\n\t\"show spaces\": \"突出显示空格符\",\n\t\"soft tab\": \"使用空格制表符\",\n\t\"sort by name\": \"按名称排序\",\n\t\"success\": \"成功\",\n\t\"tab size\": \"制表符宽度\",\n\t\"text wrap\": \"行末自动换行\",\n\t\"theme\": \"主题\",\n\t\"unable to delete file\": \"无法删除文件\",\n\t\"unable to open file\": \"无法打开文件\",\n\t\"unable to open folder\": \"无法打开文件夹\",\n\t\"unable to save file\": \"无法保存文件\",\n\t\"unable to rename\": \"无法重命名\",\n\t\"unsaved file\": \"此文件还未保存, 仍然关闭?\",\n\t\"warning\": \"警告\",\n\t\"use emmet\": \"使用 Emmet 语法\",\n\t\"use quick tools\": \"使用快捷工具栏\",\n\t\"yes\": \"是\",\n\t\"encoding\": \"文本编码打开为\",\n\t\"syntax highlighting\": \"语法高亮语言\",\n\t\"read only\": \"只读\",\n\t\"select all\": \"全选\",\n\t\"select branch\": \"选择分支\",\n\t\"create new branch\": \"建立新分支\",\n\t\"use branch\": \"使用分支\",\n\t\"new branch\": \"新分支\",\n\t\"branch\": \"分支\",\n\t\"key bindings\": \"快捷键\",\n\t\"edit\": \"编辑\",\n\t\"reset\": \"重置\",\n\t\"color\": \"颜色\",\n\t\"select word\": \"选择字词\",\n\t\"quick tools\": \"快捷工具栏\",\n\t\"select\": \"选择\",\n\t\"editor font\": \"编辑器字体\",\n\t\"new project\": \"创建新项目\",\n\t\"format\": \"格式化\",\n\t\"project name\": \"项目名\",\n\t\"unsupported device\": \"您的设备不支持应用主题。\",\n\t\"vibrate on tap\": \"点按时震动\",\n\t\"copy command is not supported by ftp.\": \"FTP 不支持复制操作.\",\n\t\"support title\": \"支持 Acode\",\n\t\"fullscreen\": \"全屏\",\n\t\"animation\": \"动画效果\",\n\t\"backup\": \"备份\",\n\t\"restore\": \"恢复\",\n\t\"backup successful\": \"备份成功\",\n\t\"invalid backup file\": \"备份文件无效\",\n\t\"add path\": \"添加路径\",\n\t\"live autocompletion\": \"实时自动补全\",\n\t\"file properties\": \"文件属性\",\n\t\"path\": \"路径\",\n\t\"type\": \"输入\",\n\t\"word count\": \"字数统计\",\n\t\"line count\": \"行数统计\",\n\t\"last modified\": \"最后修改\",\n\t\"size\": \"大小\",\n\t\"share\": \"分享\",\n\t\"show print margin\": \"显示打印页边距\",\n\t\"login\": \"登入\",\n\t\"scrollbar size\": \"滚动条大小\",\n\t\"cursor controller size\": \"文本光标控针大小\",\n\t\"none\": \"无\",\n\t\"small\": \"小\",\n\t\"large\": \"大\",\n\t\"floating button\": \"悬浮按钮\",\n\t\"confirm on exit\": \"退出前确认\",\n\t\"show console\": \"显示控制台\",\n\t\"image\": \"图像\",\n\t\"insert file\": \"插入文件到文件夹\",\n\t\"insert color\": \"插入颜色代码\",\n\t\"powersave mode warning\": \"关闭省电模式以在外部浏览器预览。\",\n\t\"exit\": \"退出\",\n\t\"custom\": \"个性化\",\n\t\"reset warning\": \"确定要重置主题？\",\n\t\"theme type\": \"主题类型\",\n\t\"light\": \"亮色\",\n\t\"dark\": \"深色\",\n\t\"file browser\": \"文件资源浏览器\",\n\t\"operation not permitted\": \"操作未被允许\",\n\t\"no such file or directory\": \"没有此文件或目录\",\n\t\"input/output error\": \"输入/输出错误\",\n\t\"permission denied\": \"没有权限\",\n\t\"bad address\": \"非法地址\",\n\t\"file exists\": \"文件存在\",\n\t\"not a directory\": \"非目录\",\n\t\"is a directory\": \"是目录\",\n\t\"invalid argument\": \"无效参数\",\n\t\"too many open files in system\": \"系统中打开的文件过多\",\n\t\"too many open files\": \"打开的文件过多\",\n\t\"text file busy\": \"文件正在被使用\",\n\t\"no space left on device\": \"设备空间不足\",\n\t\"read-only file system\": \"只读文件系统\",\n\t\"file name too long\": \"文件名过长\",\n\t\"too many users\": \"用户过多\",\n\t\"connection timed out\": \"连接超时\",\n\t\"connection refused\": \"连接被拒\",\n\t\"owner died\": \"管理文件的进程失效\",\n\t\"an error occurred\": \"发生错误\",\n\t\"add ftp\": \"添加 FTP\",\n\t\"add sftp\": \"添加 SFTP\",\n\t\"save file\": \"保存文件\",\n\t\"save file as\": \"另存文件为\",\n\t\"files\": \"打开文件\",\n\t\"help\": \"帮助\",\n\t\"file has been deleted\": \"{file} 已被删除!\",\n\t\"feature not available\": \"此功能仅在付费版中可用。\",\n\t\"deleted file\": \"删除的文件\",\n\t\"line height\": \"行高\",\n\t\"preview info\": \"如果想要运行打开的文件，长按 ▶ 图标\",\n\t\"manage all files\": \"允许在设置中打开 Acode 的管理所有文件权限以方便编辑此设备上的文件。\",\n\t\"close file\": \"关闭文件\",\n\t\"reset connections\": \"重置连接\",\n\t\"check file changes\": \"检查文件更改\",\n\t\"open in browser\": \"在浏览器中打开\",\n\t\"desktop mode\": \"桌面版网站\",\n\t\"toggle console\": \"打开关闭控制台\",\n\t\"new line mode\": \"换行符\",\n\t\"add a storage\": \"添加存储位置\",\n\t\"rate acode\": \"评价 Acode\",\n\t\"support\": \"支持\",\n\t\"downloading file\": \"正在下载 {file}\",\n\t\"downloading...\": \"下载中...\",\n\t\"folder name\": \"文件夹名称\",\n\t\"keyboard mode\": \"键盘模式\",\n\t\"normal\": \"正常\",\n\t\"app settings\": \"应用设置\",\n\t\"disable in-app-browser caching\": \"关闭内置浏览器缓存\",\n\t\"copied to clipboard\": \"复制到剪贴板\",\n\t\"remember opened files\": \"记住打开过的文件\",\n\t\"remember opened folders\": \"记住打开过的文件夹\",\n\t\"no suggestions\": \"不显示建议\",\n\t\"no suggestions aggressive\": \"强制不显示建议\",\n\t\"install\": \"安装\",\n\t\"installing\": \"安装中...\",\n\t\"plugins\": \"插件\",\n\t\"recently used\": \"最近使用\",\n\t\"update\": \"更新\",\n\t\"uninstall\": \"卸载\",\n\t\"download acode pro\": \"下载 Acode pro\",\n\t\"loading plugins\": \"正在加载插件\",\n\t\"faqs\": \"常见问题\",\n\t\"feedback\": \"反馈\",\n\t\"header\": \"水平标签栏\",\n\t\"sidebar\": \"垂直标签栏\",\n\t\"inapp\": \"应用内浏览器\",\n\t\"browser\": \"应用外浏览器\",\n\t\"diagonal scrolling\": \"对角线滚动\",\n\t\"reverse scrolling\": \"反转滚动方向\",\n\t\"formatter\": \"代码格式化工具\",\n\t\"format on save\": \"保存时格式化代码\",\n\t\"remove ads\": \"移除广告\",\n\t\"fast\": \"快速\",\n\t\"slow\": \"慢速\",\n\t\"scroll settings\": \"滚动设置\",\n\t\"scroll speed\": \"滚动速度\",\n\t\"loading...\": \"加载中...\",\n\t\"no plugins found\": \"没有找到插件\",\n\t\"name\": \"姓名\",\n\t\"username\": \"用户名\",\n\t\"optional\": \"可选\",\n\t\"hostname\": \"主机名\",\n\t\"password\": \"密码\",\n\t\"security type\": \"安全类型\",\n\t\"connection mode\": \"连接模式\",\n\t\"port\": \"端口\",\n\t\"key file\": \"密钥文件\",\n\t\"select key file\": \"选择密钥文件\",\n\t\"passphrase\": \"通行证\",\n\t\"connecting...\": \"连接中...\",\n\t\"type filename\": \"输入文件名\",\n\t\"unable to load files\": \"无法加载文件\",\n\t\"preview port\": \"预览端口\",\n\t\"find file\": \"查找文件\",\n\t\"system\": \"系统\",\n\t\"please select a formatter\": \"请选择一个代码格式化工具\",\n\t\"case sensitive\": \"大小写敏感\",\n\t\"regular expression\": \"正则表达式\",\n\t\"whole word\": \"整个字词\",\n\t\"edit with\": \"编辑于\",\n\t\"open with\": \"打开于\",\n\t\"no app found to handle this file\": \"没有找到能处理该文件的应用\",\n\t\"restore default settings\": \"恢复默认设置\",\n\t\"server port\": \"服务器端口\",\n\t\"preview settings\": \"网页预览设置\",\n\t\"preview settings note\": \"如果预览端口和服务器端口不同，应用将不会启动服务器，而会在浏览器或应用内浏览器中打开 https://<host>:<preview port>。这在你运行着其他服务器时会有用。\",\n\t\"backup/restore note\": \"这将只会备份你的设置、个性化主题和快捷键，而不备份你的 FTP/SFTP 和 Github 信息。\",\n\t\"host\": \"主机\",\n\t\"retry ftp/sftp when fail\": \"当 ftp/sftp 连接失败时重试\",\n\t\"more\": \"更多\",\n\t\"thank you :)\": \"感谢你的支持 :)\",\n\t\"purchase pending\": \"待购买\",\n\t\"cancelled\": \"已关闭\",\n\t\"local\": \"本地\",\n\t\"remote\": \"远程\",\n\t\"show console toggler\": \"显示打开/关闭控制台按钮\",\n\t\"binary file\": \"此文件包含二进制数据, 确定要打开它吗?\",\n\t\"relative line numbers\": \"相对行号\",\n\t\"elastic tabstops\": \"自适应的制表缩进风格\",\n\t\"line based rtl switching\": \"基于行的 RTL(从右到左) 转换\",\n\t\"hard wrap\": \"强制换行\",\n\t\"spellcheck\": \"拼写检查\",\n\t\"wrap method\": \"自动换行方法\",\n\t\"use textarea for ime\": \"使用用于输入法的文本输入框\",\n\t\"invalid plugin\": \"无效插件\",\n\t\"type command\": \"输入命令\",\n\t\"plugin\": \"插件\",\n\t\"quicktools trigger mode\": \"快捷工具栏触发模式\",\n\t\"print margin\": \"打印页边距\",\n\t\"touch move threshold\": \"触摸滑动阈值\",\n\t\"info-retryremotefsafterfail\": \"当 FTP/SFTP 连接失败时重试。\",\n\t\"info-fullscreen\": \"隐藏主屏幕的标题栏\",\n\t\"info-checkfiles\": \"当运行于后台时，检查文件变更。\",\n\t\"info-console\": \"选择 JavaScript 控制台，legacy 是默认的控制台，eruda 是一个第三方的控制台。\",\n\t\"info-keyboardmode\": \"输入文本时的键盘工作模式，不显示建议将关闭词汇建议与自动校正。如果不显示建议没有效果，可以尝试强制不显示建议。\",\n\t\"info-rememberfiles\": \"关闭时记住打开的文件。\",\n\t\"info-rememberfolders\": \"关闭时记住打开的文件夹。\",\n\t\"info-floatingbutton\": \"显示或隐藏快捷工具栏的浮动按钮。\",\n\t\"info-openfilelistpos\": \"设置打开的文件标签栏于何处。\",\n\t\"info-touchmovethreshold\": \"如果你的设备触摸灵敏度太高，可以尝试增大此值来防止误触滑动。\",\n\t\"info-scroll-settings\": \"这个设置中包括含有文本换行的滚动设置。\",\n\t\"info-animation\": \"如果感到使用卡顿，可以尝试关闭动画效果。\",\n\t\"info-quicktoolstriggermode\": \"如果快捷工具栏中的按钮不正常工作，可以尝试改变此值。\",\n\t\"info-checkForAppUpdates\": \"自动检查应用更新。\",\n\t\"info-quickTools\": \"显示或隐藏快捷工具栏。\",\n\t\"info-showHiddenFiles\": \"显示隐藏文件和文件夹。（文件名开头为 .）\",\n\t\"info-all_file_access\": \"在终端中启用对 /sdcard 和 /storage 的访问\",\n\t\"info-fontSize\": \"渲染普通文本的字体大小。\",\n\t\"info-fontFamily\": \"渲染普通文本的显示字体。\",\n\t\"info-theme\": \"终端的颜色主题。\",\n\t\"info-cursorStyle\": \"当焦点在终端时的光标显示样式。\",\n\t\"info-cursorInactiveStyle\": \"当焦点不在终端时的光标显示样式。\",\n\t\"info-fontWeight\": \"渲染非粗体文本的字体粗细。\",\n\t\"info-cursorBlink\": \"光标是否闪烁。\",\n\t\"info-scrollback\": \"终端的回滚行数。回滚是指当内容滚动超出初始视口时所保留的文本行数。\",\n\t\"info-tabStopWidth\": \"终端中制表位的宽度。\",\n\t\"info-letterSpacing\": \"字符之间的空隙像素量。\",\n\t\"info-imageSupport\": \"终端里是否支持图像。\",\n\t\"info-fontLigatures\": \"终端里是否启用字体连字(ligatures)。\",\n\t\"info-confirmTabClose\": \"关闭终端标签页前是否需要确认。\",\n\t\"info-backup\": \"创建关于终端安装的备份。\",\n\t\"info-restore\": \"恢复关于终端安装的备份。\",\n\t\"info-uninstall\": \"卸载终端安装。\",\n\t\"owned\": \"已拥有\",\n\t\"api_error\": \"API 服务器未回应，请稍后再试\",\n\t\"installed\": \"已安装\",\n\t\"all\": \"所有\",\n\t\"medium\": \"中等\",\n\t\"refund\": \"退款\",\n\t\"product not available\": \"产品不可用\",\n\t\"no-product-info\": \"该产品目前在您所在的国家不可用，请稍后再试。\",\n\t\"close\": \"关闭\",\n\t\"explore\": \"探索\",\n\t\"key bindings updated\": \"已更新快捷键\",\n\t\"search in files\": \"在所有文件中搜索\",\n\t\"exclude files\": \"排除的文件\",\n\t\"include files\": \"包含的文件\",\n\t\"search result\": \"{matches} 个结果在 {files} 个文件中。\",\n\t\"invalid regex\": \"非法的正则表达式：{message}.\",\n\t\"bottom\": \"底部\",\n\t\"save all\": \"保存所有\",\n\t\"close all\": \"关闭所有\",\n\t\"unsaved files warning\": \"存在未被保存的文件。点击‘确认’选择下一步或者点击‘取消’返回。\",\n\t\"save all warning\": \"确定要保存所有文件并关闭？该操作不可撤销。\",\n\t\"save all changes warning\": \"您确定要保存所有文件吗？\",\n\t\"close all warning\": \"确定要关闭所有文件？该操作将不保存文件更改并且不可撤销。\",\n\t\"refresh\": \"刷新\",\n\t\"shortcut buttons\": \"快捷键按钮\",\n\t\"no result\": \"无结果\",\n\t\"searching...\": \"搜索中...\",\n\t\"quicktools:ctrl-key\": \"Ctrl/Command 键\",\n\t\"quicktools:tab-key\": \"Tab 键\",\n\t\"quicktools:shift-key\": \"Shift 键\",\n\t\"quicktools:undo\": \"撤销\",\n\t\"quicktools:redo\": \"重做\",\n\t\"quicktools:search\": \"在文件中搜索\",\n\t\"quicktools:save\": \"保存文件\",\n\t\"quicktools:esc-key\": \"Escape 键\",\n\t\"quicktools:curlybracket\": \"插入花括号\",\n\t\"quicktools:squarebracket\": \"插入方括号\",\n\t\"quicktools:parentheses\": \"插入括号\",\n\t\"quicktools:anglebracket\": \"插入尖括号\",\n\t\"quicktools:left-arrow-key\": \"向左键\",\n\t\"quicktools:right-arrow-key\": \"向右键\",\n\t\"quicktools:up-arrow-key\": \"向上键\",\n\t\"quicktools:down-arrow-key\": \"向下键\",\n\t\"quicktools:moveline-up\": \"向上移行\",\n\t\"quicktools:moveline-down\": \"向下移行\",\n\t\"quicktools:copyline-up\": \"向上拷行\",\n\t\"quicktools:copyline-down\": \"向下拷行\",\n\t\"quicktools:semicolon\": \"插入分号\",\n\t\"quicktools:quotation\": \"插入引号\",\n\t\"quicktools:and\": \"插入并列符号\",\n\t\"quicktools:bar\": \"插入竖线\",\n\t\"quicktools:equal\": \"插入等于号\",\n\t\"quicktools:slash\": \"插入斜杠\",\n\t\"quicktools:exclamation\": \"插入感叹号\",\n\t\"quicktools:alt-key\": \"Alt 键\",\n\t\"quicktools:meta-key\": \"Windows/Meta 键\",\n\t\"info-quicktoolssettings\": \"个性化编辑器下边的快捷工具栏内的快捷键按钮和键盘按键以增强代码体验。\",\n\t\"info-excludefolders\": \"使用表达式 **/node_modules/** 以忽略所有 node_modules 文件夹下的文件。这将排除文件列表中列出的文件并阻止在这些文件中搜索。\",\n\t\"missed files\": \"自搜索开始后扫描了 {count} 个文件并将不会被包含在搜索中。\",\n\t\"remove\": \"移除\",\n\t\"quicktools:command-palette\": \"命令面板\",\n\t\"default file encoding\": \"默认文本编码\",\n\t\"remove entry\": \"确定要从保存的路径中移除 ‘{name}’ 吗？请注意该移除并不删除路径存在本身。\",\n\t\"delete entry\": \"确认删除：‘{name}’。该操作不可撤销。继续？\",\n\t\"change encoding\": \"确定要重新打开 ‘{file}’ 以使用 ‘{encoding}’ 编码编辑？该操作将丢失该文件所有未保存的修改。继续重新打开？\",\n\t\"reopen file\": \"确定要重新打开 ‘{file}’？所有未保存的修改将会丢失。\",\n\t\"plugin min version\": \"{name} 仅在 Acode - {v-code} 及以上版本有效。点击以更新。\",\n\t\"color preview\": \"颜色预览\",\n\t\"confirm\": \"确定\",\n\t\"list files\": \"列出 <strong>{name}</strong> 下的所有文件吗？过多的文件可能会导致应用崩溃。\",\n\t\"problems\": \"有问题\",\n\t\"show side buttons\": \"显示侧边按钮\",\n\t\"bug_report\": \"提交缺陷报告\",\n\t\"verified publisher\": \"已认证发布者\",\n\t\"most_downloaded\": \"最多下载\",\n\t\"newly_added\": \"最近上架\",\n\t\"top_rated\": \"最多好评\",\n\t\"rename not supported\": \"不支持修改 Termux 内的文件夹名\",\n\t\"compress\": \"压缩\",\n\t\"copy uri\": \"复制 Uri\",\n\t\"delete entries\": \"确定要删除这 {count} 个项目？\",\n\t\"deleting items\": \"正在删除这 {count} 个项目...\",\n\t\"import project zip\": \"导入项目(zip)\",\n\t\"changelog\": \"更新日志\",\n\t\"notifications\": \"消息通知\",\n\t\"no_unread_notifications\": \"没有未读消息\",\n\t\"should_use_current_file_for_preview\": \"应使当前文件用于预览页面而非使用默认文件 (index.html)\",\n\t\"fade fold widgets\": \"淡入淡出代码折叠按钮\",\n\t\"quicktools:home-key\": \"Home 键\",\n\t\"quicktools:end-key\": \"End 键\",\n\t\"quicktools:pageup-key\": \"PageUp 键\",\n\t\"quicktools:pagedown-key\": \"PageDown 键\",\n\t\"quicktools:delete-key\": \"Delete 键\",\n\t\"quicktools:tilde\": \"插入波浪号\",\n\t\"quicktools:backtick\": \"插入反引号\",\n\t\"quicktools:hash\": \"插入井号\",\n\t\"quicktools:dollar\": \"插入美元符号\",\n\t\"quicktools:modulo\": \"插入取模/百分比符号\",\n\t\"quicktools:caret\": \"插入脱字符\",\n\t\"plugin_enabled\": \"插件已启用\",\n\t\"plugin_disabled\": \"插件未启用\",\n\t\"enable_plugin\": \"启用此插件\",\n\t\"disable_plugin\": \"关闭此插件\",\n\t\"open_source\": \"开源\",\n\t\"terminal settings\": \"终端设置\",\n\t\"font ligatures\": \"字体连字\",\n\t\"letter spacing\": \"字母间距\",\n\t\"terminal:tab stop width\": \"Tab 停靠宽度\",\n\t\"terminal:scrollback\": \"终端回溯行数\",\n\t\"terminal:cursor blink\": \"光标闪烁\",\n\t\"terminal:font weight\": \"字体粗细\",\n\t\"terminal:cursor inactive style\": \"光标无活动时样式\",\n\t\"terminal:cursor style\": \"光标样式\",\n\t\"terminal:font family\": \"字体\",\n\t\"terminal:convert eol\": \"转换行尾符\",\n\t\"terminal:confirm tab close\": \"确认关闭终端标签页\",\n\t\"terminal:image support\": \"图像支持\",\n\t\"terminal\": \"终端\",\n\t\"allFileAccess\": \"所有文件读写权限\",\n\t\"fonts\": \"字体\",\n\t\"sponsor\": \"赞助\",\n\t\"downloads\": \"下载量\",\n\t\"reviews\": \"评价\",\n\t\"overview\": \"概览\",\n\t\"contributors\": \"贡献者\",\n\t\"quicktools:hyphen\": \"插入连字符\",\n\t\"check for app updates\": \"检查应用更新\",\n\t\"prompt update check consent message\": \"Acode 能够在有网时检查更新。启用更新检查？\",\n\t\"keywords\": \"关键字\",\n\t\"author\": \"作者\",\n\t\"filtered by\": \"过滤条件\",\n\t\"clean install state\": \"清除安装状态\",\n\t\"backup created\": \"备份已创建\",\n\t\"restore completed\": \"恢复已完成\",\n\t\"restore will include\": \"将会恢复\",\n\t\"restore warning\": \"该操作不可撤销，是否继续？\",\n\t\"reload to apply\": \"重新加载以应用更改？\",\n\t\"reload app\": \"重新加载应用\",\n\t\"preparing backup\": \"正在准备备份\",\n\t\"collecting settings\": \"正在收集设置\",\n\t\"collecting key bindings\": \"正在收集按键绑定\",\n\t\"collecting plugins\": \"正在收集插件信息\",\n\t\"creating backup\": \"正在创建备份文件\",\n\t\"validating backup\": \"正在验证备份\",\n\t\"restoring key bindings\": \"正在恢复按键绑定\",\n\t\"restoring plugins\": \"正在恢复插件\",\n\t\"restoring settings\": \"正在恢复设置\",\n\t\"legacy backup warning\": \"这是旧版的备份格式，可能不支持部分功能或支持有限。\",\n\t\"checksum mismatch\": \"校验码不匹配 - 备份文件可能被修改过或已损坏。\",\n\t\"plugin not found\": \"在注册表中未找到插件\",\n\t\"paid plugin skipped\": \"付费插件 - 未找到付费记录\",\n\t\"source not found\": \"源文件已不存在\",\n\t\"restored\": \"已恢复\",\n\t\"skipped\": \"已跳过\",\n\t\"backup not valid object\": \"备份文件不是一个有效对象\",\n\t\"backup no data\": \"备份文件没有需要恢复的数据\",\n\t\"backup legacy warning\": \"这是旧版备份格式(v1)，可能不支持部分功能或支持有限。\",\n\t\"backup missing metadata\": \"缺失备份元数据 - 部分信息可能不可用\",\n\t\"backup checksum mismatch\": \"校验码不匹配 - 备份文件可能被修改过或已损坏。请谨慎操作。\",\n\t\"backup checksum verify failed\": \"无法验证校验码\",\n\t\"backup invalid settings\": \"无效的设置格式\",\n\t\"backup invalid keybindings\": \"无效的按键绑定格式\",\n\t\"backup invalid plugins\": \"无效的已安装插件格式\",\n\t\"issues found\": \"出现问题\",\n\t\"error details\": \"详细错误信息\",\n\t\"active tools\": \"已启用工具\",\n\t\"available tools\": \"可用的工具\",\n\t\"recent\": \"最近文件\",\n\t\"command palette\": \"打开命令面板\",\n\t\"change theme\": \"更改主题\",\n\t\"documentation\": \"文档\",\n\t\"open in terminal\": \"在终端中打开\",\n\t\"developer mode\": \"开发者模式\",\n\t\"info-developermode\": \"启用开发者工具 (Eruda)，用于调试插件和检查应用状态。检查器将在应用启动时初始化。\",\n\t\"developer mode enabled\": \"开发者模式已启用。使用命令面板切换检查器 (Ctrl+Shift+I)。\",\n\t\"developer mode disabled\": \"开发者模式已禁用\",\n\t\"copy relative path\": \"复制相对路径\",\n\t\"shortcut request sent\": \"快捷方式请求已发送。点击“添加”完成操作。\",\n\t\"add to home screen\": \"添加到主屏幕\",\n\t\"pin shortcuts not supported\": \"此设备不支持主屏幕快捷方式。\",\n\t\"save file before home shortcut\": \"请先保存文件，再添加到主屏幕。\",\n\t\"terminal_required_message_for_lsp\": \"未安装终端。请先安装终端以使用 LSP 服务器。\",\n\t\"shift click selection\": \"Shift + 点击选择\",\n\t\"earn ad-free time\": \"获取免广告时间\",\n\t\"indent guides\": \"缩进指示线\",\n\t\"language servers\": \"语言服务器\",\n\t\"lint gutter\": \"显示 Lint 标记栏\",\n\t\"rainbow brackets\": \"彩虹括号\",\n\t\"lsp-add-custom-server\": \"添加自定义服务器\",\n\t\"lsp-binary-args\": \"可执行参数（JSON 数组）\",\n\t\"lsp-binary-command\": \"可执行命令\",\n\t\"lsp-binary-path-optional\": \"可执行路径（可选）\",\n\t\"lsp-check-command-optional\": \"检查命令（可选覆盖）\",\n\t\"lsp-checking-installation-status\": \"正在检查安装状态…\",\n\t\"lsp-configured\": \"已配置\",\n\t\"lsp-custom-server-added\": \"已添加自定义服务器\",\n\t\"lsp-default\": \"默认\",\n\t\"lsp-details-line\": \"详情：{details}\",\n\t\"lsp-edit-initialization-options\": \"编辑初始化选项\",\n\t\"lsp-empty\": \"空\",\n\t\"lsp-enabled\": \"已启用\",\n\t\"lsp-error-add-server-failed\": \"添加服务器失败\",\n\t\"lsp-error-args-must-be-array\": \"参数必须是 JSON 数组\",\n\t\"lsp-error-binary-command-required\": \"必须填写可执行命令\",\n\t\"lsp-error-language-id-required\": \"至少需要一个语言 ID\",\n\t\"lsp-error-package-required\": \"至少需要一个包\",\n\t\"lsp-error-server-id-required\": \"必须填写服务器 ID\",\n\t\"lsp-feature-completion\": \"代码补全\",\n\t\"lsp-feature-completion-info\": \"启用来自服务器的自动补全建议。\",\n\t\"lsp-feature-diagnostics\": \"诊断\",\n\t\"lsp-feature-diagnostics-info\": \"显示语言服务器返回的错误和警告。\",\n\t\"lsp-feature-formatting\": \"格式化\",\n\t\"lsp-feature-formatting-info\": \"启用语言服务器提供的代码格式化。\",\n\t\"lsp-feature-hover\": \"悬停信息\",\n\t\"lsp-feature-hover-info\": \"悬停时显示类型信息和文档。\",\n\t\"lsp-feature-inlay-hints\": \"内联提示\",\n\t\"lsp-feature-inlay-hints-info\": \"在编辑器中显示类型提示。\",\n\t\"lsp-feature-signature\": \"函数签名提示\",\n\t\"lsp-feature-signature-info\": \"输入时显示函数参数提示。\",\n\t\"lsp-feature-state-toast\": \"{feature} 已{state}\",\n\t\"lsp-initialization-options\": \"初始化选项\",\n\t\"lsp-initialization-options-json\": \"初始化选项（JSON）\",\n\t\"lsp-initialization-options-updated\": \"初始化选项已更新\",\n\t\"lsp-install-command\": \"安装命令\",\n\t\"lsp-install-command-unavailable\": \"安装命令不可用\",\n\t\"lsp-install-info-check-failed\": \"无法验证安装状态。\",\n\t\"lsp-install-info-missing\": \"语言服务器未安装在终端环境中。\",\n\t\"lsp-install-info-ready\": \"语言服务器已安装并可用。\",\n\t\"lsp-install-info-unknown\": \"无法自动检查安装状态。\",\n\t\"lsp-install-info-version-available\": \"可用版本：{version}\",\n\t\"lsp-install-method-apk\": \"APK 包\",\n\t\"lsp-install-method-cargo\": \"Cargo 包\",\n\t\"lsp-install-method-manual\": \"手动二进制\",\n\t\"lsp-install-method-npm\": \"npm 包\",\n\t\"lsp-install-method-pip\": \"pip 包\",\n\t\"lsp-install-method-shell\": \"自定义 Shell\",\n\t\"lsp-install-method-title\": \"安装方式\",\n\t\"lsp-install-repair\": \"安装 / 修复\",\n\t\"lsp-installation-status\": \"安装状态\",\n\t\"lsp-installed\": \"已安装\",\n\t\"lsp-invalid-timeout\": \"无效的超时值\",\n\t\"lsp-language-ids\": \"语言 ID（逗号分隔）\",\n\t\"lsp-packages-prompt\": \"{method} 包（逗号分隔）\",\n\t\"lsp-remove-installed-files\": \"移除 {server} 的已安装文件？\",\n\t\"lsp-server-disabled-toast\": \"服务器已禁用\",\n\t\"lsp-server-enabled-toast\": \"服务器已启用\",\n\t\"lsp-server-id\": \"服务器 ID\",\n\t\"lsp-server-label\": \"服务器标签\",\n\t\"lsp-server-not-found\": \"未找到服务器\",\n\t\"lsp-server-uninstalled\": \"服务器已卸载\",\n\t\"lsp-startup-timeout\": \"启动超时\",\n\t\"lsp-startup-timeout-ms\": \"启动超时（毫秒）\",\n\t\"lsp-startup-timeout-set\": \"启动超时已设置为 {timeout} ms\",\n\t\"lsp-state-disabled\": \"禁用\",\n\t\"lsp-state-enabled\": \"启用\",\n\t\"lsp-status-check-failed\": \"检查失败\",\n\t\"lsp-status-installed\": \"已安装\",\n\t\"lsp-status-installed-version\": \"已安装（{version}）\",\n\t\"lsp-status-line\": \"状态：{status}\",\n\t\"lsp-status-not-installed\": \"未安装\",\n\t\"lsp-status-unknown\": \"未知\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"卸载命令不可用\",\n\t\"lsp-uninstall-server\": \"卸载服务器\",\n\t\"lsp-update-command-optional\": \"更新命令（可选）\",\n\t\"lsp-update-command-unavailable\": \"更新命令不可用\",\n\t\"lsp-update-server\": \"更新服务器\",\n\t\"lsp-version-line\": \"版本：{version}\",\n\t\"lsp-view-initialization-options\": \"查看初始化选项\",\n\t\"settings-category-about-acode\": \"关于 Acode\",\n\t\"settings-category-advanced\": \"高级\",\n\t\"settings-category-assistance\": \"辅助\",\n\t\"settings-category-core\": \"核心设置\",\n\t\"settings-category-cursor\": \"光标\",\n\t\"settings-category-cursor-selection\": \"光标与选区\",\n\t\"settings-category-custom-servers\": \"自定义服务器\",\n\t\"settings-category-customization-tools\": \"自定义与工具\",\n\t\"settings-category-display\": \"显示\",\n\t\"settings-category-editing\": \"编辑\",\n\t\"settings-category-features\": \"功能\",\n\t\"settings-category-files-sessions\": \"文件与会话\",\n\t\"settings-category-fonts\": \"字体\",\n\t\"settings-category-general\": \"通用\",\n\t\"settings-category-guides-indicators\": \"指示线与标记\",\n\t\"settings-category-installation\": \"安装\",\n\t\"settings-category-interface\": \"界面\",\n\t\"settings-category-maintenance\": \"维护\",\n\t\"settings-category-permissions\": \"权限\",\n\t\"settings-category-preview\": \"预览\",\n\t\"settings-category-scrolling\": \"滚动\",\n\t\"settings-category-server\": \"服务器\",\n\t\"settings-category-servers\": \"服务器\",\n\t\"settings-category-session\": \"会话\",\n\t\"settings-category-support-acode\": \"支持 Acode\",\n\t\"settings-category-text-layout\": \"文本与布局\",\n\t\"settings-info-app-animation\": \"控制应用内的过渡动画。\",\n\t\"settings-info-app-check-files\": \"当文件在外部被修改时刷新编辑器。\",\n\t\"settings-info-app-clean-install-state\": \"清除安装流程使用的存储状态。\",\n\t\"settings-info-app-confirm-on-exit\": \"退出应用前进行确认。\",\n\t\"settings-info-app-console\": \"选择 Acode 使用的调试控制台集成方式。\",\n\t\"settings-info-app-default-file-encoding\": \"打开或创建文件时的默认编码。\",\n\t\"settings-info-app-exclude-folders\": \"搜索或扫描时跳过指定文件夹或模式。\",\n\t\"settings-info-app-floating-button\": \"显示悬浮快捷按钮。\",\n\t\"settings-info-app-font-manager\": \"安装、管理或移除应用字体。\",\n\t\"settings-info-app-fullscreen\": \"编辑时隐藏系统状态栏。\",\n\t\"settings-info-app-keybindings\": \"编辑快捷键文件或重置快捷键。\",\n\t\"settings-info-app-keyboard-mode\": \"选择软件键盘的编辑行为。\",\n\t\"settings-info-app-language\": \"选择应用语言和翻译标签。\",\n\t\"settings-info-app-open-file-list-position\": \"选择活动文件列表的位置。\",\n\t\"settings-info-app-quick-tools-settings\": \"自定义和排序快捷工具。\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"选择快捷工具的触发方式。\",\n\t\"settings-info-app-remember-files\": \"重新打开上次编辑的文件。\",\n\t\"settings-info-app-remember-folders\": \"重新打开上次使用的文件夹。\",\n\t\"settings-info-app-retry-remote-fs\": \"远程文件传输失败后自动重试。\",\n\t\"settings-info-app-side-buttons\": \"在编辑器旁显示额外操作按钮。\",\n\t\"settings-info-app-sponsor-sidebar\": \"在侧边栏显示赞助入口。\",\n\t\"settings-info-app-touch-move-threshold\": \"触摸拖动的最小移动距离。\",\n\t\"settings-info-app-vibrate-on-tap\": \"启用触控震动反馈。\",\n\t\"settings-info-editor-autosave\": \"延迟后自动保存更改。\",\n\t\"settings-info-editor-color-preview\": \"在编辑器中预览颜色值。\",\n\t\"settings-info-editor-fade-fold-widgets\": \"折叠标记在非活动时变暗。\",\n\t\"settings-info-editor-font-family\": \"选择编辑器字体。\",\n\t\"settings-info-editor-font-size\": \"设置编辑器字体大小。\",\n\t\"settings-info-editor-format-on-save\": \"保存文件时自动格式化。\",\n\t\"settings-info-editor-hard-wrap\": \"插入真实换行，而不仅是视觉换行。\",\n\t\"settings-info-editor-indent-guides\": \"显示缩进指示线。\",\n\t\"settings-info-editor-line-height\": \"调整行间距。\",\n\t\"settings-info-editor-line-numbers\": \"在边栏显示行号。\",\n\t\"settings-info-editor-lint-gutter\": \"在边栏显示诊断和 Lint 标记。\",\n\t\"settings-info-editor-live-autocomplete\": \"输入时显示自动补全建议。\",\n\t\"settings-info-editor-rainbow-brackets\": \"按嵌套深度为括号着色。\",\n\t\"settings-info-editor-relative-line-numbers\": \"显示相对行号。\",\n\t\"settings-info-editor-rtl-text\": \"按行切换从右到左文本行为。\",\n\t\"settings-info-editor-scroll-settings\": \"调整滚动条大小、速度和手势行为。\",\n\t\"settings-info-editor-shift-click-selection\": \"使用 Shift + 点击扩展选区。\",\n\t\"settings-info-editor-show-spaces\": \"显示空白字符标记。\",\n\t\"settings-info-editor-soft-tab\": \"使用空格代替 Tab 字符。\",\n\t\"settings-info-editor-tab-size\": \"设置 Tab 等效空格数。\",\n\t\"settings-info-editor-teardrop-size\": \"设置触控编辑的光标拖动手柄大小。\",\n\t\"settings-info-editor-text-wrap\": \"在编辑器中自动换行长行。\",\n\t\"settings-info-lsp-add-custom-server\": \"注册自定义语言服务器，包括安装、更新和启动命令。\",\n\t\"settings-info-lsp-edit-init-options\": \"以 JSON 形式编辑初始化选项。\",\n\t\"settings-info-lsp-install-server\": \"安装或修复此语言服务器。\",\n\t\"settings-info-lsp-server-enabled\": \"启用或禁用此语言服务器。\",\n\t\"settings-info-lsp-startup-timeout\": \"设置语言服务器启动等待时间。\",\n\t\"settings-info-lsp-uninstall-server\": \"移除此服务器的已安装包或二进制文件。\",\n\t\"settings-info-lsp-update-server\": \"如果可用，更新此语言服务器。\",\n\t\"settings-info-lsp-view-init-options\": \"以 JSON 查看有效的初始化选项。\",\n\t\"settings-info-main-ad-rewards\": \"观看广告以解锁临时免广告。\",\n\t\"settings-info-main-app-settings\": \"语言、应用行为和快捷工具。\",\n\t\"settings-info-main-backup-restore\": \"导出或恢复设置备份。\",\n\t\"settings-info-main-changelog\": \"查看最近更新和发布说明。\",\n\t\"settings-info-main-edit-settings\": \"直接编辑 settings.json。\",\n\t\"settings-info-main-editor-settings\": \"字体、Tab、建议和编辑器显示。\",\n\t\"settings-info-main-formatter\": \"为每种语言选择格式化器。\",\n\t\"settings-info-main-lsp-settings\": \"配置语言服务器和智能编辑功能。\",\n\t\"settings-info-main-plugins\": \"管理已安装插件及其操作。\",\n\t\"settings-info-main-preview-settings\": \"预览模式、端口和浏览器行为。\",\n\t\"settings-info-main-rateapp\": \"在 Google Play 上评价 Acode。\",\n\t\"settings-info-main-remove-ads\": \"解锁永久免广告。\",\n\t\"settings-info-main-reset\": \"将 Acode 重置为默认配置。\",\n\t\"settings-info-main-sponsors\": \"支持 Acode 的持续开发。\",\n\t\"settings-info-main-terminal-settings\": \"终端主题、字体、光标和会话行为。\",\n\t\"settings-info-main-theme\": \"应用主题、对比度和自定义颜色。\",\n\t\"settings-info-preview-disable-cache\": \"在预览中始终重新加载内容。\",\n\t\"settings-info-preview-host\": \"打开预览 URL 时使用的主机名。\",\n\t\"settings-info-preview-mode\": \"选择预览的打开方式。\",\n\t\"settings-info-preview-preview-port\": \"实时预览服务器使用的端口。\",\n\t\"settings-info-preview-server-port\": \"内部应用服务器使用的端口。\",\n\t\"settings-info-preview-show-console-toggler\": \"在预览中显示控制台按钮。\",\n\t\"settings-info-preview-use-current-file\": \"启动预览时优先使用当前文件。\",\n\t\"settings-info-terminal-convert-eol\": \"粘贴或渲染终端输出时转换行结尾。\",\n\t\"settings-note-formatter-settings\": \"为每种语言分配格式化器。安装格式化插件可解锁更多选项。\",\n\t\"settings-note-lsp-settings\": \"语言服务器提供补全、诊断、悬停等功能。你可以在此安装、更新或定义自定义服务器。托管安装程序在终端/proot 环境中运行。\",\n\t\"search result label singular\": \"条结果\",\n\t\"search result label plural\": \"条结果\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/zh-hant.json",
    "content": "{\n\t\"lang\": \"繁體中文\",\n\t\"about\": \"關於\",\n\t\"active files\": \"打開的文件\",\n\t\"alert\": \"提醒\",\n\t\"app theme\": \"應用主題\",\n\t\"autocorrect\": \"啟用自動校正?\",\n\t\"autosave\": \"自動保存\",\n\t\"cancel\": \"取消\",\n\t\"change language\": \"更改語言\",\n\t\"choose color\": \"選擇顏色\",\n\t\"clear\": \"清空\",\n\t\"close app\": \"關閉應用程序?\",\n\t\"commit message\": \"提交說明\",\n\t\"console\": \"控製臺\",\n\t\"conflict error\": \"沖突! 請等待其他提交完成.\",\n\t\"copy\": \"復製\",\n\t\"create folder error\": \"抱歉, 無法創建新文件夾\",\n\t\"cut\": \"剪切\",\n\t\"delete\": \"刪除\",\n\t\"dependencies\": \"依賴項\",\n\t\"delay\": \"以毫秒為單位\",\n\t\"editor settings\": \"編輯器設置\",\n\t\"editor theme\": \"編輯器主題\",\n\t\"enter file name\": \"輸入文件名\",\n\t\"enter folder name\": \"輸入文件夾名\",\n\t\"empty folder message\": \"空文件夾\",\n\t\"enter line number\": \"輸入行號\",\n\t\"error\": \"錯誤\",\n\t\"failed\": \"失敗\",\n\t\"file already exists\": \"文件已存在\",\n\t\"file already exists force\": \"文件已存在. 是否覆蓋?\",\n\t\"file changed\": \" 已發生改變, 重新加載文件?\",\n\t\"file deleted\": \"文件已刪除\",\n\t\"file is not supported\": \"不支持此文件\",\n\t\"file not supported\": \"不支持此文件類型.\",\n\t\"file too large\": \"文件太大，無法處理。最大支持 {size} 的文件\",\n\t\"file renamed\": \"文件已重命名\",\n\t\"file saved\": \"文件已保存\",\n\t\"folder added\": \"添加文件夾\",\n\t\"folder already added\": \"文件夾已添加\",\n\t\"font size\": \"字體大小\",\n\t\"goto\": \"跳轉至行...\",\n\t\"icons definition\": \"圖標定義\",\n\t\"info\": \"信息\",\n\t\"invalid value\": \"無效值\",\n\t\"language changed\": \"語言已更改\",\n\t\"linting\": \"檢查語法錯誤\",\n\t\"logout\": \"登出\",\n\t\"loading\": \"加載中\",\n\t\"my profile\": \"我的信息\",\n\t\"new file\": \"創建新文件\",\n\t\"new folder\": \"創建新文件夾\",\n\t\"no\": \"否\",\n\t\"no editor message\": \"從菜單打開或創建新文件和文件夾\",\n\t\"not set\": \"未設置\",\n\t\"unsaved files close app\": \"文件未保存，退出應用?\",\n\t\"notice\": \"註意\",\n\t\"open file\": \"打開文件\",\n\t\"open files and folders\": \"打開文件和文件夾\",\n\t\"open folder\": \"打開文件夾\",\n\t\"open recent\": \"最近打開\",\n\t\"ok\": \"確認\",\n\t\"overwrite\": \"覆蓋\",\n\t\"paste\": \"粘貼\",\n\t\"preview mode\": \"預覽模式\",\n\t\"read only file\": \"無法保存只讀文件. 請嘗試另存為\",\n\t\"reload\": \"重新加載\",\n\t\"rename\": \"重命名\",\n\t\"replace\": \"替換\",\n\t\"required\": \"此字段為必填字段\",\n\t\"run your web app\": \"運行你的 web 應用\",\n\t\"save\": \"保存\",\n\t\"saving\": \"保存中\",\n\t\"save as\": \"另存為\",\n\t\"save file to run\": \"請保存此文件以在瀏覽器中運行\",\n\t\"search\": \"搜索\",\n\t\"see logs and errors\": \"查看日誌和錯誤\",\n\t\"select folder\": \"選擇文件夾\",\n\t\"settings\": \"設置\",\n\t\"settings saved\": \"設置已保存\",\n\t\"show line numbers\": \"顯示行號\",\n\t\"show hidden files\": \"顯示隱藏文件\",\n\t\"show spaces\": \"突出顯示空格符\",\n\t\"soft tab\": \"使用空格製表符\",\n\t\"sort by name\": \"按名稱排序\",\n\t\"success\": \"成功\",\n\t\"tab size\": \"製表符寬度\",\n\t\"text wrap\": \"行末自動換行\",\n\t\"theme\": \"主題\",\n\t\"unable to delete file\": \"無法刪除文件\",\n\t\"unable to open file\": \"無法打開文件\",\n\t\"unable to open folder\": \"無法打開文件夾\",\n\t\"unable to save file\": \"無法保存文件\",\n\t\"unable to rename\": \"無法重命名\",\n\t\"unsaved file\": \"此文件還未保存, 仍然關閉?\",\n\t\"warning\": \"警告\",\n\t\"use emmet\": \"使用 Emmet 語法\",\n\t\"use quick tools\": \"使用快捷工具欄\",\n\t\"yes\": \"是\",\n\t\"encoding\": \"文本編碼打開為\",\n\t\"syntax highlighting\": \"語法高亮語言\",\n\t\"read only\": \"只讀\",\n\t\"select all\": \"全選\",\n\t\"select branch\": \"選擇分支\",\n\t\"create new branch\": \"建立新分支\",\n\t\"use branch\": \"使用分支\",\n\t\"new branch\": \"新分支\",\n\t\"branch\": \"分支\",\n\t\"key bindings\": \"快捷鍵\",\n\t\"edit\": \"編輯\",\n\t\"reset\": \"重置\",\n\t\"color\": \"顏色\",\n\t\"select word\": \"選擇字詞\",\n\t\"quick tools\": \"快捷工具欄\",\n\t\"select\": \"選擇\",\n\t\"editor font\": \"編輯器字體\",\n\t\"new project\": \"創建新項目\",\n\t\"format\": \"格式化\",\n\t\"project name\": \"項目名\",\n\t\"unsupported device\": \"您的設備不支持應用主題。\",\n\t\"vibrate on tap\": \"點按時震動\",\n\t\"copy command is not supported by ftp.\": \"FTP 不支持復製操作.\",\n\t\"support title\": \"支持 Acode\",\n\t\"fullscreen\": \"全屏\",\n\t\"animation\": \"動畫效果\",\n\t\"backup\": \"備份\",\n\t\"restore\": \"恢復\",\n\t\"backup successful\": \"備份成功\",\n\t\"invalid backup file\": \"備份文件無效\",\n\t\"add path\": \"添加路徑\",\n\t\"live autocompletion\": \"實時自動補全\",\n\t\"file properties\": \"文件屬性\",\n\t\"path\": \"路徑\",\n\t\"type\": \"輸入\",\n\t\"word count\": \"字數統計\",\n\t\"line count\": \"行數統計\",\n\t\"last modified\": \"最後修改\",\n\t\"size\": \"大小\",\n\t\"share\": \"分享\",\n\t\"show print margin\": \"顯示打印頁邊距\",\n\t\"login\": \"登入\",\n\t\"scrollbar size\": \"滾動條大小\",\n\t\"cursor controller size\": \"文本光標控針大小\",\n\t\"none\": \"無\",\n\t\"small\": \"小\",\n\t\"large\": \"大\",\n\t\"floating button\": \"懸浮按鈕\",\n\t\"confirm on exit\": \"退出前確認\",\n\t\"show console\": \"顯示控製臺\",\n\t\"image\": \"圖像\",\n\t\"insert file\": \"插入文件到文件夾\",\n\t\"insert color\": \"插入顏色代碼\",\n\t\"powersave mode warning\": \"關閉省電模式以在外部瀏覽器預覽。\",\n\t\"exit\": \"退出\",\n\t\"custom\": \"個性化\",\n\t\"reset warning\": \"確定要重置主題？\",\n\t\"theme type\": \"主題類型\",\n\t\"light\": \"亮色\",\n\t\"dark\": \"深色\",\n\t\"file browser\": \"文件資源瀏覽器\",\n\t\"operation not permitted\": \"操作未被允許\",\n\t\"no such file or directory\": \"沒有此文件或目錄\",\n\t\"input/output error\": \"輸入/輸出錯誤\",\n\t\"permission denied\": \"沒有權限\",\n\t\"bad address\": \"非法地址\",\n\t\"file exists\": \"文件存在\",\n\t\"not a directory\": \"非目錄\",\n\t\"is a directory\": \"是目錄\",\n\t\"invalid argument\": \"無效參數\",\n\t\"too many open files in system\": \"系統中打開的文件過多\",\n\t\"too many open files\": \"打開的文件過多\",\n\t\"text file busy\": \"文件正在被使用\",\n\t\"no space left on device\": \"設備空間不足\",\n\t\"read-only file system\": \"只讀文件系統\",\n\t\"file name too long\": \"文件名過長\",\n\t\"too many users\": \"用戶過多\",\n\t\"connection timed out\": \"連接超時\",\n\t\"connection refused\": \"連接被拒\",\n\t\"owner died\": \"管理文件的進程失效\",\n\t\"an error occurred\": \"發生錯誤\",\n\t\"add ftp\": \"添加 FTP\",\n\t\"add sftp\": \"添加 SFTP\",\n\t\"save file\": \"保存文件\",\n\t\"save file as\": \"另存文件為\",\n\t\"files\": \"打開文件\",\n\t\"help\": \"幫助\",\n\t\"file has been deleted\": \"{file} 已被刪除!\",\n\t\"feature not available\": \"此功能僅在付費版中可用。\",\n\t\"deleted file\": \"刪除的文件\",\n\t\"line height\": \"行高\",\n\t\"preview info\": \"如果想要運行打開的文件，長按 ▶ 圖標\",\n\t\"manage all files\": \"允許在設置中打開 Acode 的管理所有文件權限以方便編輯此設備上的文件。\",\n\t\"close file\": \"關閉文件\",\n\t\"reset connections\": \"重置連接\",\n\t\"check file changes\": \"檢查文件更改\",\n\t\"open in browser\": \"在瀏覽器中打開\",\n\t\"desktop mode\": \"桌面版網站\",\n\t\"toggle console\": \"打開關閉控製臺\",\n\t\"new line mode\": \"換行符\",\n\t\"add a storage\": \"添加存儲位置\",\n\t\"rate acode\": \"評價 Acode\",\n\t\"support\": \"支持\",\n\t\"downloading file\": \"正在下載 {file}\",\n\t\"downloading...\": \"下載中...\",\n\t\"folder name\": \"文件夾名稱\",\n\t\"keyboard mode\": \"鍵盤模式\",\n\t\"normal\": \"正常\",\n\t\"app settings\": \"應用設置\",\n\t\"disable in-app-browser caching\": \"關閉內置瀏覽器緩存\",\n\t\"copied to clipboard\": \"復製到剪貼板\",\n\t\"remember opened files\": \"記住打開過的文件\",\n\t\"remember opened folders\": \"記住打開過的文件夾\",\n\t\"no suggestions\": \"不顯示建議\",\n\t\"no suggestions aggressive\": \"強製不顯示建議\",\n\t\"install\": \"安裝\",\n\t\"installing\": \"安裝中...\",\n\t\"plugins\": \"插件\",\n\t\"recently used\": \"最近使用\",\n\t\"update\": \"更新\",\n\t\"uninstall\": \"卸載\",\n\t\"download acode pro\": \"下載 Acode pro\",\n\t\"loading plugins\": \"正在加載插件\",\n\t\"faqs\": \"常見問題\",\n\t\"feedback\": \"反饋\",\n\t\"header\": \"水平標簽欄\",\n\t\"sidebar\": \"垂直標簽欄\",\n\t\"inapp\": \"應用內瀏覽器\",\n\t\"browser\": \"應用外瀏覽器\",\n\t\"diagonal scrolling\": \"對角線滾動\",\n\t\"reverse scrolling\": \"反轉滾動方向\",\n\t\"formatter\": \"代碼格式化工具\",\n\t\"format on save\": \"保存時格式化代碼\",\n\t\"remove ads\": \"移除廣告\",\n\t\"fast\": \"快速\",\n\t\"slow\": \"慢速\",\n\t\"scroll settings\": \"滾動設置\",\n\t\"scroll speed\": \"滾動速度\",\n\t\"loading...\": \"加載中...\",\n\t\"no plugins found\": \"沒有找到插件\",\n\t\"name\": \"姓名\",\n\t\"username\": \"用戶名\",\n\t\"optional\": \"可選\",\n\t\"hostname\": \"主機名\",\n\t\"password\": \"密碼\",\n\t\"security type\": \"安全類型\",\n\t\"connection mode\": \"連接模式\",\n\t\"port\": \"端口\",\n\t\"key file\": \"密鑰文件\",\n\t\"select key file\": \"選擇密鑰文件\",\n\t\"passphrase\": \"通行證\",\n\t\"connecting...\": \"連接中...\",\n\t\"type filename\": \"輸入文件名\",\n\t\"unable to load files\": \"無法加載文件\",\n\t\"preview port\": \"預覽端口\",\n\t\"find file\": \"查找文件\",\n\t\"system\": \"系統\",\n\t\"please select a formatter\": \"請選擇一個代碼格式化工具\",\n\t\"case sensitive\": \"大小寫敏感\",\n\t\"regular expression\": \"正則表達式\",\n\t\"whole word\": \"整個字詞\",\n\t\"edit with\": \"編輯於\",\n\t\"open with\": \"打開於\",\n\t\"no app found to handle this file\": \"沒有找到能處理該文件的應用\",\n\t\"restore default settings\": \"恢復默認設置\",\n\t\"server port\": \"服務器端口\",\n\t\"preview settings\": \"網頁預覽設置\",\n\t\"preview settings note\": \"如果預覽端口和服務器端口不同，應用將不會啟動服務器，而會在瀏覽器或應用內瀏覽器中打開 https://<host>:<preview port>。這在你運行著其他服務器時會有用。\",\n\t\"backup/restore note\": \"這將只會備份你的設置、個性化主題和快捷鍵，而不備份你的 FTP/SFTP 和 Github 信息。\",\n\t\"host\": \"主機\",\n\t\"retry ftp/sftp when fail\": \"當 ftp/sftp 連接失敗時重試\",\n\t\"more\": \"更多\",\n\t\"thank you :)\": \"感謝你的支持 :)\",\n\t\"purchase pending\": \"待購買\",\n\t\"cancelled\": \"已關閉\",\n\t\"local\": \"本地\",\n\t\"remote\": \"遠程\",\n\t\"show console toggler\": \"顯示打開/關閉控製臺按鈕\",\n\t\"binary file\": \"此文件包含二進製數據, 確定要打開它嗎?\",\n\t\"relative line numbers\": \"相對行號\",\n\t\"elastic tabstops\": \"自適應的製表縮進風格\",\n\t\"line based rtl switching\": \"基於行的 RTL(從右到左) 轉換\",\n\t\"hard wrap\": \"強製換行\",\n\t\"spellcheck\": \"拼寫檢查\",\n\t\"wrap method\": \"自動換行方法\",\n\t\"use textarea for ime\": \"使用用於輸入法的文本輸入框\",\n\t\"invalid plugin\": \"無效插件\",\n\t\"type command\": \"輸入命令\",\n\t\"plugin\": \"插件\",\n\t\"quicktools trigger mode\": \"快捷工具欄觸發模式\",\n\t\"print margin\": \"打印頁邊距\",\n\t\"touch move threshold\": \"觸摸滑動閾值\",\n\t\"info-retryremotefsafterfail\": \"當 FTP/SFTP 連接失敗時重試。\",\n\t\"info-fullscreen\": \"隱藏主屏幕的標題欄\",\n\t\"info-checkfiles\": \"當運行於後臺時，檢查文件變更。\",\n\t\"info-console\": \"選擇 JavaScript 控製臺，legacy 是默認的控製臺，eruda 是一個第三方的控製臺。\",\n\t\"info-keyboardmode\": \"輸入文本時的鍵盤工作模式，不顯示建議將關閉詞匯建議與自動校正。如果不顯示建議沒有效果，可以嘗試強製不顯示建議。\",\n\t\"info-rememberfiles\": \"關閉時記住打開的文件。\",\n\t\"info-rememberfolders\": \"關閉時記住打開的文件夾。\",\n\t\"info-floatingbutton\": \"顯示或隱藏快捷工具欄的浮動按鈕。\",\n\t\"info-openfilelistpos\": \"設置打開的文件標簽欄於何處。\",\n\t\"info-touchmovethreshold\": \"如果你的設備觸摸靈敏度太高，可以嘗試增大此值來防止誤觸滑動。\",\n\t\"info-scroll-settings\": \"這個設置中包括含有文本換行的滾動設置。\",\n\t\"info-animation\": \"如果感到使用卡頓，可以嘗試關閉動畫效果。\",\n\t\"info-quicktoolstriggermode\": \"如果快捷工具欄中的按鈕不正常工作，可以嘗試改變此值。\",\n\t\"info-checkForAppUpdates\": \"自動檢查應用更新。\",\n\t\"info-quickTools\": \"顯示或隱藏快捷工具欄。\",\n\t\"info-showHiddenFiles\": \"顯示隱藏文件和文件夾。（文件名開頭為 .）\",\n\t\"info-all_file_access\": \"在終端中啟用對 /sdcard 和 /storage 的訪問\",\n\t\"info-fontSize\": \"渲染普通文本的字體大小。\",\n\t\"info-fontFamily\": \"渲染普通文本的顯示字體。\",\n\t\"info-theme\": \"終端的顏色主題。\",\n\t\"info-cursorStyle\": \"當焦點在終端時的游標顯示樣式。\",\n\t\"info-cursorInactiveStyle\": \"當焦點不在終端時的游標顯示樣式。\",\n\t\"info-fontWeight\": \"渲染非粗體文字的字體粗細。\",\n\t\"info-cursorBlink\": \"游標是否閃爍。\",\n\t\"info-scrollback\": \"終端的回滾行數。回滾是指當內容滾動超出初始視口時所保留的文字行數。\",\n\t\"info-tabStopWidth\": \"終端中製表位的寬度。\",\n\t\"info-letterSpacing\": \"字符之間的空隙像素量。\",\n\t\"info-imageSupport\": \"終端裏是否支持圖像。\",\n\t\"info-fontLigatures\": \"終端裏是否啟用字體連字(ligatures)。\",\n\t\"info-confirmTabClose\": \"關閉終端分頁前是否需要確認。\",\n\t\"info-backup\": \"創建關於終端安裝的備份。\",\n\t\"info-restore\": \"恢復關於終端安裝的備份。\",\n\t\"info-uninstall\": \"卸載終端安裝。\",\n\t\"owned\": \"已擁有\",\n\t\"api_error\": \"API 服務器未回應，請稍後再試\",\n\t\"installed\": \"已安裝\",\n\t\"all\": \"所有\",\n\t\"medium\": \"中等\",\n\t\"refund\": \"退款\",\n\t\"product not available\": \"產品不可用\",\n\t\"no-product-info\": \"該產品目前在您所在的國家不可用，請稍後再試。\",\n\t\"close\": \"關閉\",\n\t\"explore\": \"探索\",\n\t\"key bindings updated\": \"已更新快捷鍵\",\n\t\"search in files\": \"在所有文件中搜索\",\n\t\"exclude files\": \"排除的文件\",\n\t\"include files\": \"包含的文件\",\n\t\"search result\": \"{matches} 個結果在 {files} 個文件中。\",\n\t\"invalid regex\": \"非法的正則表達式：{message}.\",\n\t\"bottom\": \"底部\",\n\t\"save all\": \"保存所有\",\n\t\"close all\": \"關閉所有\",\n\t\"unsaved files warning\": \"存在未被保存的文件。點擊『確認』選擇下一步或者點擊『取消』返回。\",\n\t\"save all warning\": \"確定要保存所有文件並關閉？該操作不可撤銷。\",\n\t\"save all changes warning\": \"確定要保存所有檔案並關閉？該操作不可撤銷。\",\n\t\"close all warning\": \"確定要關閉所有文件？該操作將不保存文件更改並且不可撤銷。\",\n\t\"refresh\": \"刷新\",\n\t\"shortcut buttons\": \"快捷鍵按鈕\",\n\t\"no result\": \"無結果\",\n\t\"searching...\": \"搜索中...\",\n\t\"quicktools:ctrl-key\": \"Control/Command 鍵\",\n\t\"quicktools:tab-key\": \"Tab 鍵\",\n\t\"quicktools:shift-key\": \"Shift 鍵\",\n\t\"quicktools:undo\": \"撤銷\",\n\t\"quicktools:redo\": \"重做\",\n\t\"quicktools:search\": \"在文件中搜索\",\n\t\"quicktools:save\": \"保存文件\",\n\t\"quicktools:esc-key\": \"Escape 鍵\",\n\t\"quicktools:curlybracket\": \"插入花括號\",\n\t\"quicktools:squarebracket\": \"插入方括號\",\n\t\"quicktools:parentheses\": \"插入括號\",\n\t\"quicktools:anglebracket\": \"插入尖括號\",\n\t\"quicktools:left-arrow-key\": \"向左鍵\",\n\t\"quicktools:right-arrow-key\": \"向右鍵\",\n\t\"quicktools:up-arrow-key\": \"向上鍵\",\n\t\"quicktools:down-arrow-key\": \"向下鍵\",\n\t\"quicktools:moveline-up\": \"向上移行\",\n\t\"quicktools:moveline-down\": \"向下移行\",\n\t\"quicktools:copyline-up\": \"向上拷行\",\n\t\"quicktools:copyline-down\": \"向下拷行\",\n\t\"quicktools:semicolon\": \"插入分號\",\n\t\"quicktools:quotation\": \"插入引號\",\n\t\"quicktools:and\": \"插入並列符號\",\n\t\"quicktools:bar\": \"插入豎線\",\n\t\"quicktools:equal\": \"插入等於號\",\n\t\"quicktools:slash\": \"插入斜槓\",\n\t\"quicktools:exclamation\": \"插入感嘆號\",\n\t\"quicktools:alt-key\": \"Alt 鍵\",\n\t\"quicktools:meta-key\": \"Windows/Meta 鍵\",\n\t\"info-quicktoolssettings\": \"個性化編輯器下邊的快捷工具欄內的快捷鍵按鈕和鍵盤按鍵以增強代碼體驗。\",\n\t\"info-excludefolders\": \"使用表達式 **/node_modules/** 以忽略所有 node_modules 文件夾下的文件。這將排除文件列表中列出的文件並阻止在這些文件中搜索。\",\n\t\"missed files\": \"自搜索開始後掃描了 {count} 個文件並將不會被包含在搜索中。\",\n\t\"remove\": \"移除\",\n\t\"quicktools:command-palette\": \"命令面板\",\n\t\"default file encoding\": \"默認文本編碼\",\n\t\"remove entry\": \"確定要從保存的路徑中移除 ‘{name}’ 嗎？請注意該移除並不刪除路徑存在本身。\",\n\t\"delete entry\": \"確認刪除：‘{name}’。該操作不可撤銷。繼續？\",\n\t\"change encoding\": \"確定要重新打開 ‘{file}’ 以使用 ‘{encoding}’ 編碼編輯？該操作將丟失該文件所有未保存的修改。繼續重新打開？\",\n\t\"reopen file\": \"確定要重新打開 ‘{file}’？所有未保存的修改將會丟失。\",\n\t\"plugin min version\": \"{name} 僅在 Acode - {v-code} 及以上版本有效。點擊以更新。\",\n\t\"color preview\": \"顏色預覽\",\n\t\"confirm\": \"確定\",\n\t\"list files\": \"列出 <strong>{name}</strong> 下的所有文件嗎？過多的文件可能會導緻應用崩潰。\",\n\t\"problems\": \"有問題\",\n\t\"show side buttons\": \"顯示側邊按鈕\",\n\t\"bug_report\": \"提交缺陷報告\",\n\t\"verified publisher\": \"已認證發布者\",\n\t\"most_downloaded\": \"最多下載\",\n\t\"newly_added\": \"最近上架\",\n\t\"top_rated\": \"最多好評\",\n\t\"rename not supported\": \"不支持修改 Termux 內的文件夾名\",\n\t\"compress\": \"壓縮\",\n\t\"copy uri\": \"複製 Uri\",\n\t\"delete entries\": \"確定要刪除這 {count} 個項目？\",\n\t\"deleting items\": \"正在刪除這 {count} 個項目...\",\n\t\"import project zip\": \"導入項目(zip)\",\n\t\"changelog\": \"更新日誌\",\n\t\"notifications\": \"消息通知\",\n\t\"no_unread_notifications\": \"沒有未讀消息\",\n\t\"should_use_current_file_for_preview\": \"應使當前文件用於預覽頁面而非使用默認文件 (index.html)\",\n\t\"fade fold widgets\": \"淡入淡出代碼折疊按鈕\",\n\t\"quicktools:home-key\": \"Home 鍵\",\n\t\"quicktools:end-key\": \"End 鍵\",\n\t\"quicktools:pageup-key\": \"PageUp 鍵\",\n\t\"quicktools:pagedown-key\": \"PageDown 鍵\",\n\t\"quicktools:delete-key\": \"Delete 鍵\",\n\t\"quicktools:tilde\": \"插入波浪號\",\n\t\"quicktools:backtick\": \"插入反引號\",\n\t\"quicktools:hash\": \"插入井號\",\n\t\"quicktools:dollar\": \"插入美元符號\",\n\t\"quicktools:modulo\": \"插入取模／百分比符號\",\n\t\"quicktools:caret\": \"插入脫字符\",\n\t\"plugin_enabled\": \"插件已啟用\",\n\t\"plugin_disabled\": \"插件未啟用\",\n\t\"enable_plugin\": \"啟用此插件\",\n\t\"disable_plugin\": \"關閉此插件\",\n\t\"open_source\": \"開源\",\n\t\"terminal settings\": \"終端機設定\",\n\t\"font ligatures\": \"字體連字\",\n\t\"letter spacing\": \"字母間距\",\n\t\"terminal:tab stop width\": \"Tab 停靠寬度\",\n\t\"terminal:scrollback\": \"終端回溯行數\",\n\t\"terminal:cursor blink\": \"游標閃爍\",\n\t\"terminal:font weight\": \"字體粗細\",\n\t\"terminal:cursor inactive style\": \"游標非活動時樣式\",\n\t\"terminal:cursor style\": \"游標樣式\",\n\t\"terminal:font family\": \"字體\",\n\t\"terminal:convert eol\": \"轉換行尾符\",\n\t\"terminal:confirm tab close\": \"確認關閉終端分頁\",\n\t\"terminal:image support\": \"圖像支持\",\n\t\"terminal\": \"終端機\",\n\t\"allFileAccess\": \"所有文件讀寫權限\",\n\t\"fonts\": \"字體\",\n\t\"sponsor\": \"贊助\",\n\t\"downloads\": \"下載量\",\n\t\"reviews\": \"評價\",\n\t\"overview\": \"總覽\",\n\t\"contributors\": \"貢獻者\",\n\t\"quicktools:hyphen\": \"插入連字元\",\n\t\"check for app updates\": \"檢查應用程式更新\",\n\t\"prompt update check consent message\": \"Acode 能夠在有網時檢查更新。啟用更新檢查？\",\n\t\"keywords\": \"關鍵字\",\n\t\"author\": \"作者\",\n\t\"filtered by\": \"篩選條件\",\n\t\"clean install state\": \"清除安裝狀態\",\n\t\"backup created\": \"備份已創建\",\n\t\"restore completed\": \"恢復已完成\",\n\t\"restore will include\": \"將會恢復\",\n\t\"restore warning\": \"該操作不可撤銷，是否繼續？\",\n\t\"reload to apply\": \"重新載入以套用變更？\",\n\t\"reload app\": \"重新載入應用\",\n\t\"preparing backup\": \"正在準備備份\",\n\t\"collecting settings\": \"正在收集設置\",\n\t\"collecting key bindings\": \"正在收集按鍵綁定\",\n\t\"collecting plugins\": \"正在收集插件資訊\",\n\t\"creating backup\": \"正在創建備份文件\",\n\t\"validating backup\": \"正在驗證備份\",\n\t\"restoring key bindings\": \"正在恢復按鍵綁定\",\n\t\"restoring plugins\": \"正在恢復插件\",\n\t\"restoring settings\": \"正在恢復設置\",\n\t\"legacy backup warning\": \"這是舊版的備份格式，可能不支持部分功能或支持有限。\",\n\t\"checksum mismatch\": \"校驗碼不匹配 - 備份文件可能被修改過或已損壞。\",\n\t\"plugin not found\": \"在註冊表中未找到插件\",\n\t\"paid plugin skipped\": \"付費插件 - 未找到付費記錄\",\n\t\"source not found\": \"源文件已不存在\",\n\t\"restored\": \"已恢復\",\n\t\"skipped\": \"已跳過\",\n\t\"backup not valid object\": \"備份文件不是一個有效對象\",\n\t\"backup no data\": \"備份文件沒有需要恢復的數據\",\n\t\"backup legacy warning\": \"這是舊版備份格式(v1)，可能不支持部分功能或支持有限。\",\n\t\"backup missing metadata\": \"缺失備份元資料 - 部分資訊可能無法使用\",\n\t\"backup checksum mismatch\": \"校驗碼不匹配 - 備份文件可能被修改過或已損壞。請謹慎操作。\",\n\t\"backup checksum verify failed\": \"無法驗證校驗碼\",\n\t\"backup invalid settings\": \"無效的設定格式\",\n\t\"backup invalid keybindings\": \"無效的按鍵綁定格式\",\n\t\"backup invalid plugins\": \"無效的已安裝插件格式\",\n\t\"issues found\": \"發現問題\",\n\t\"error details\": \"詳細錯誤資訊\",\n\t\"active tools\": \"已啟用工具\",\n\t\"available tools\": \"可用的工具\",\n\t\"recent\": \"最近檔案\",\n\t\"command palette\": \"打開命令面板\",\n\t\"change theme\": \"更改主題\",\n\t\"documentation\": \"文件\",\n\t\"open in terminal\": \"在終端中打開\",\n\t\"developer mode\": \"開發者模式\",\n\t\"info-developermode\": \"啟用開發者工具 (Eruda)，用於調試插件和檢查應用狀態。檢查器將在應用啟動時初始化。\",\n\t\"developer mode enabled\": \"開發者模式已啟用。使用命令面板切換檢查器 (Ctrl+Shift+I)。\",\n\t\"developer mode disabled\": \"開發者模式已禁用\",\n\t\"copy relative path\": \"複製相對路徑\",\n\t\"shortcut request sent\": \"快捷方式請求已發送。點擊「添加」完成操作。\",\n\t\"add to home screen\": \"添加到主屏幕\",\n\t\"pin shortcuts not supported\": \"此設備不支持主屏幕快捷方式。\",\n\t\"save file before home shortcut\": \"請先保存文件，再添加到主屏幕。\",\n\t\"terminal_required_message_for_lsp\": \"未安裝終端。請先安裝終端以使用 LSP 服務器。\",\n\t\"shift click selection\": \"Shift + 點擊選擇\",\n\t\"earn ad-free time\": \"獲取免廣告時間\",\n\t\"indent guides\": \"縮進指示線\",\n\t\"language servers\": \"語言服務器\",\n\t\"lint gutter\": \"顯示 Lint 標記欄\",\n\t\"rainbow brackets\": \"彩虹括號\",\n\t\"lsp-add-custom-server\": \"添加自定義服務器\",\n\t\"lsp-binary-args\": \"可執行參數（JSON 數組）\",\n\t\"lsp-binary-command\": \"可執行命令\",\n\t\"lsp-binary-path-optional\": \"可執行路徑（可選）\",\n\t\"lsp-check-command-optional\": \"檢查命令（可選覆蓋）\",\n\t\"lsp-checking-installation-status\": \"正在檢查安裝狀態…\",\n\t\"lsp-configured\": \"已配置\",\n\t\"lsp-custom-server-added\": \"已添加自定義服務器\",\n\t\"lsp-default\": \"默認\",\n\t\"lsp-details-line\": \"詳情：{details}\",\n\t\"lsp-edit-initialization-options\": \"編輯初始化選項\",\n\t\"lsp-empty\": \"空\",\n\t\"lsp-enabled\": \"已啟用\",\n\t\"lsp-error-add-server-failed\": \"添加服務器失敗\",\n\t\"lsp-error-args-must-be-array\": \"參數必須是 JSON 數組\",\n\t\"lsp-error-binary-command-required\": \"必須填寫可執行命令\",\n\t\"lsp-error-language-id-required\": \"至少需要一個語言 ID\",\n\t\"lsp-error-package-required\": \"至少需要一個包\",\n\t\"lsp-error-server-id-required\": \"必須填寫服務器 ID\",\n\t\"lsp-feature-completion\": \"代碼補全\",\n\t\"lsp-feature-completion-info\": \"啟用來自服務器的自動補全建議。\",\n\t\"lsp-feature-diagnostics\": \"診斷\",\n\t\"lsp-feature-diagnostics-info\": \"顯示語言服務器返回的錯誤和警告。\",\n\t\"lsp-feature-formatting\": \"格式化\",\n\t\"lsp-feature-formatting-info\": \"啟用語言服務器提供的代碼格式化。\",\n\t\"lsp-feature-hover\": \"懸停信息\",\n\t\"lsp-feature-hover-info\": \"懸停時顯示類型信息和文檔。\",\n\t\"lsp-feature-inlay-hints\": \"內聯提示\",\n\t\"lsp-feature-inlay-hints-info\": \"在編輯器中顯示類型提示。\",\n\t\"lsp-feature-signature\": \"函數簽名提示\",\n\t\"lsp-feature-signature-info\": \"輸入時顯示函數參數提示。\",\n\t\"lsp-feature-state-toast\": \"{feature} 已{state}\",\n\t\"lsp-initialization-options\": \"初始化選項\",\n\t\"lsp-initialization-options-json\": \"初始化選項（JSON）\",\n\t\"lsp-initialization-options-updated\": \"初始化選項已更新\",\n\t\"lsp-install-command\": \"安裝命令\",\n\t\"lsp-install-command-unavailable\": \"安裝命令不可用\",\n\t\"lsp-install-info-check-failed\": \"無法驗證安裝狀態。\",\n\t\"lsp-install-info-missing\": \"語言服務器未安裝在終端環境中。\",\n\t\"lsp-install-info-ready\": \"語言服務器已安裝並可用。\",\n\t\"lsp-install-info-unknown\": \"無法自動檢查安裝狀態。\",\n\t\"lsp-install-info-version-available\": \"可用版本：{version}\",\n\t\"lsp-install-method-apk\": \"APK 包\",\n\t\"lsp-install-method-cargo\": \"Cargo 包\",\n\t\"lsp-install-method-manual\": \"手動二進制\",\n\t\"lsp-install-method-npm\": \"npm 包\",\n\t\"lsp-install-method-pip\": \"pip 包\",\n\t\"lsp-install-method-shell\": \"自定義 Shell\",\n\t\"lsp-install-method-title\": \"安裝方式\",\n\t\"lsp-install-repair\": \"安裝 / 修復\",\n\t\"lsp-installation-status\": \"安裝狀態\",\n\t\"lsp-installed\": \"已安裝\",\n\t\"lsp-invalid-timeout\": \"無效的超時值\",\n\t\"lsp-language-ids\": \"語言 ID（逗號分隔）\",\n\t\"lsp-packages-prompt\": \"{method} 包（逗號分隔）\",\n\t\"lsp-remove-installed-files\": \"移除 {server} 的已安裝文件？\",\n\t\"lsp-server-disabled-toast\": \"服務器已禁用\",\n\t\"lsp-server-enabled-toast\": \"服務器已啟用\",\n\t\"lsp-server-id\": \"服務器 ID\",\n\t\"lsp-server-label\": \"服務器標籤\",\n\t\"lsp-server-not-found\": \"未找到服務器\",\n\t\"lsp-server-uninstalled\": \"服務器已卸載\",\n\t\"lsp-startup-timeout\": \"啟動超時\",\n\t\"lsp-startup-timeout-ms\": \"啟動超時（毫秒）\",\n\t\"lsp-startup-timeout-set\": \"啟動超時已設置為 {timeout} ms\",\n\t\"lsp-state-disabled\": \"禁用\",\n\t\"lsp-state-enabled\": \"啟用\",\n\t\"lsp-status-check-failed\": \"檢查失敗\",\n\t\"lsp-status-installed\": \"已安裝\",\n\t\"lsp-status-installed-version\": \"已安裝（{version}）\",\n\t\"lsp-status-line\": \"狀態：{status}\",\n\t\"lsp-status-not-installed\": \"未安裝\",\n\t\"lsp-status-unknown\": \"未知\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"卸載命令不可用\",\n\t\"lsp-uninstall-server\": \"卸載服務器\",\n\t\"lsp-update-command-optional\": \"更新命令（可選）\",\n\t\"lsp-update-command-unavailable\": \"更新命令不可用\",\n\t\"lsp-update-server\": \"更新服務器\",\n\t\"lsp-version-line\": \"版本：{version}\",\n\t\"lsp-view-initialization-options\": \"查看初始化選項\",\n\t\"settings-category-about-acode\": \"關於 Acode\",\n\t\"settings-category-advanced\": \"高級\",\n\t\"settings-category-assistance\": \"輔助\",\n\t\"settings-category-core\": \"核心設置\",\n\t\"settings-category-cursor\": \"光標\",\n\t\"settings-category-cursor-selection\": \"光標與選區\",\n\t\"settings-category-custom-servers\": \"自定義服務器\",\n\t\"settings-category-customization-tools\": \"自定義與工具\",\n\t\"settings-category-display\": \"顯示\",\n\t\"settings-category-editing\": \"編輯\",\n\t\"settings-category-features\": \"功能\",\n\t\"settings-category-files-sessions\": \"文件與會話\",\n\t\"settings-category-fonts\": \"字體\",\n\t\"settings-category-general\": \"通用\",\n\t\"settings-category-guides-indicators\": \"指示線與標記\",\n\t\"settings-category-installation\": \"安裝\",\n\t\"settings-category-interface\": \"界面\",\n\t\"settings-category-maintenance\": \"維護\",\n\t\"settings-category-permissions\": \"權限\",\n\t\"settings-category-preview\": \"預覽\",\n\t\"settings-category-scrolling\": \"滾動\",\n\t\"settings-category-server\": \"服務器\",\n\t\"settings-category-servers\": \"服務器\",\n\t\"settings-category-session\": \"會話\",\n\t\"settings-category-support-acode\": \"支持 Acode\",\n\t\"settings-category-text-layout\": \"文本與佈局\",\n\t\"settings-info-app-animation\": \"控制應用內的過渡動畫。\",\n\t\"settings-info-app-check-files\": \"當文件在外部被修改時刷新編輯器。\",\n\t\"settings-info-app-clean-install-state\": \"清除安裝流程使用的存儲狀態。\",\n\t\"settings-info-app-confirm-on-exit\": \"退出應用前進行確認。\",\n\t\"settings-info-app-console\": \"選擇 Acode 使用的調試控制台集成方式。\",\n\t\"settings-info-app-default-file-encoding\": \"打開或創建文件時的默認編碼。\",\n\t\"settings-info-app-exclude-folders\": \"搜索或掃描時跳過指定文件夾或模式。\",\n\t\"settings-info-app-floating-button\": \"顯示懸浮快捷按鈕。\",\n\t\"settings-info-app-font-manager\": \"安裝、管理或移除應用字體。\",\n\t\"settings-info-app-fullscreen\": \"編輯時隱藏系統狀態欄。\",\n\t\"settings-info-app-keybindings\": \"編輯快捷鍵文件或重置快捷鍵。\",\n\t\"settings-info-app-keyboard-mode\": \"選擇軟件鍵盤的編輯行為。\",\n\t\"settings-info-app-language\": \"選擇應用語言和翻譯標籤。\",\n\t\"settings-info-app-open-file-list-position\": \"選擇活動文件列表的位置。\",\n\t\"settings-info-app-quick-tools-settings\": \"自定義和排序快捷工具。\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"選擇快捷工具的觸發方式。\",\n\t\"settings-info-app-remember-files\": \"重新打開上次編輯的文件。\",\n\t\"settings-info-app-remember-folders\": \"重新打開上次使用的文件夾。\",\n\t\"settings-info-app-retry-remote-fs\": \"遠程文件傳輸失敗後自動重試。\",\n\t\"settings-info-app-side-buttons\": \"在編輯器旁顯示額外操作按鈕。\",\n\t\"settings-info-app-sponsor-sidebar\": \"在側邊欄顯示贊助入口。\",\n\t\"settings-info-app-touch-move-threshold\": \"觸摸拖動的最小移動距離。\",\n\t\"settings-info-app-vibrate-on-tap\": \"啟用觸控震動反饋。\",\n\t\"settings-info-editor-autosave\": \"延遲後自動保存更改。\",\n\t\"settings-info-editor-color-preview\": \"在編輯器中預覽顏色值。\",\n\t\"settings-info-editor-fade-fold-widgets\": \"折疊標記在非活動時變暗。\",\n\t\"settings-info-editor-font-family\": \"選擇編輯器字體。\",\n\t\"settings-info-editor-font-size\": \"設置編輯器字體大小。\",\n\t\"settings-info-editor-format-on-save\": \"保存文件時自動格式化。\",\n\t\"settings-info-editor-hard-wrap\": \"插入真實換行，而不僅是視覺換行。\",\n\t\"settings-info-editor-indent-guides\": \"顯示縮進指示線。\",\n\t\"settings-info-editor-line-height\": \"調整行間距。\",\n\t\"settings-info-editor-line-numbers\": \"在邊欄顯示行號。\",\n\t\"settings-info-editor-lint-gutter\": \"在邊欄顯示診斷和 Lint 標記。\",\n\t\"settings-info-editor-live-autocomplete\": \"輸入時顯示自動補全建議。\",\n\t\"settings-info-editor-rainbow-brackets\": \"按嵌套深度為括號著色。\",\n\t\"settings-info-editor-relative-line-numbers\": \"顯示相對行號。\",\n\t\"settings-info-editor-rtl-text\": \"按行切換從右到左文本行為。\",\n\t\"settings-info-editor-scroll-settings\": \"調整滾動條大小、速度和手勢行為。\",\n\t\"settings-info-editor-shift-click-selection\": \"使用 Shift + 點擊擴展選區。\",\n\t\"settings-info-editor-show-spaces\": \"顯示空白字符標記。\",\n\t\"settings-info-editor-soft-tab\": \"使用空格代替 Tab 字符。\",\n\t\"settings-info-editor-tab-size\": \"設置 Tab 等效空格數。\",\n\t\"settings-info-editor-teardrop-size\": \"設置觸控編輯的光標拖動手柄大小。\",\n\t\"settings-info-editor-text-wrap\": \"在編輯器中自動換行長行。\",\n\t\"settings-info-lsp-add-custom-server\": \"註冊自定義語言服務器，包括安裝、更新和啟動命令。\",\n\t\"settings-info-lsp-edit-init-options\": \"以 JSON 形式編輯初始化選項。\",\n\t\"settings-info-lsp-install-server\": \"安裝或修復此語言服務器。\",\n\t\"settings-info-lsp-server-enabled\": \"啟用或禁用此語言服務器。\",\n\t\"settings-info-lsp-startup-timeout\": \"設置語言服務器啟動等待時間。\",\n\t\"settings-info-lsp-uninstall-server\": \"移除此服務器的已安裝包或二進制文件。\",\n\t\"settings-info-lsp-update-server\": \"如果可用，更新此語言服務器。\",\n\t\"settings-info-lsp-view-init-options\": \"以 JSON 查看有效的初始化選項。\",\n\t\"settings-info-main-ad-rewards\": \"觀看廣告以解鎖臨時免廣告。\",\n\t\"settings-info-main-app-settings\": \"語言、應用行為和快捷工具。\",\n\t\"settings-info-main-backup-restore\": \"導出或恢復設置備份。\",\n\t\"settings-info-main-changelog\": \"查看最近更新和發布說明。\",\n\t\"settings-info-main-edit-settings\": \"直接編輯 settings.json。\",\n\t\"settings-info-main-editor-settings\": \"字體、Tab、建議和編輯器顯示。\",\n\t\"settings-info-main-formatter\": \"為每種語言選擇格式化器。\",\n\t\"settings-info-main-lsp-settings\": \"配置語言服務器和智能編輯功能。\",\n\t\"settings-info-main-plugins\": \"管理已安裝插件及其操作。\",\n\t\"settings-info-main-preview-settings\": \"預覽模式、端口和瀏覽器行為。\",\n\t\"settings-info-main-rateapp\": \"在 Google Play 上評價 Acode。\",\n\t\"settings-info-main-remove-ads\": \"解鎖永久免廣告。\",\n\t\"settings-info-main-reset\": \"將 Acode 重置為默認配置。\",\n\t\"settings-info-main-sponsors\": \"支持 Acode 的持續開發。\",\n\t\"settings-info-main-terminal-settings\": \"終端主題、字體、光標和會話行為。\",\n\t\"settings-info-main-theme\": \"應用主題、對比度和自定義顏色。\",\n\t\"settings-info-preview-disable-cache\": \"在預覽中始終重新加載內容。\",\n\t\"settings-info-preview-host\": \"打開預覽 URL 時使用的主機名。\",\n\t\"settings-info-preview-mode\": \"選擇預覽的打開方式。\",\n\t\"settings-info-preview-preview-port\": \"實時預覽服務器使用的端口。\",\n\t\"settings-info-preview-server-port\": \"內部應用服務器使用的端口。\",\n\t\"settings-info-preview-show-console-toggler\": \"在預覽中顯示控制台按鈕。\",\n\t\"settings-info-preview-use-current-file\": \"啟動預覽時優先使用當前文件。\",\n\t\"settings-info-terminal-convert-eol\": \"粘貼或渲染終端輸出時轉換行結尾。\",\n\t\"settings-note-formatter-settings\": \"為每種語言分配格式化器。安裝格式化插件可解鎖更多選項。\",\n\t\"settings-note-lsp-settings\": \"語言服務器提供補全、診斷、懸停等功能。你可以在此安裝、更新或定義自定義服務器。託管安裝程序在終端/proot 環境中運行。\",\n\t\"search result label singular\": \"條結果\",\n\t\"search result label plural\": \"條結果\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lang/zh-tw.json",
    "content": "{\n\t\"lang\": \"繁體中文 (台灣)\",\n\t\"about\": \"關於\",\n\t\"active files\": \"開啟的檔案\",\n\t\"alert\": \"提醒\",\n\t\"app theme\": \"應用程式主題\",\n\t\"autocorrect\": \"啟用自動校正？\",\n\t\"autosave\": \"自動儲存\",\n\t\"cancel\": \"取消\",\n\t\"change language\": \"變更語言\",\n\t\"choose color\": \"選擇色彩\",\n\t\"clear\": \"清除\",\n\t\"close app\": \"關閉應用程式？\",\n\t\"commit message\": \"提交訊息\",\n\t\"console\": \"主控台\",\n\t\"conflict error\": \"發生衝突！請等待其他提交完成。\",\n\t\"copy\": \"複製\",\n\t\"create folder error\": \"抱歉，無法建立新資料夾\",\n\t\"cut\": \"剪下\",\n\t\"delete\": \"刪除\",\n\t\"dependencies\": \"相依性\",\n\t\"delay\": \"時間（毫秒）\",\n\t\"editor settings\": \"編輯器設定\",\n\t\"editor theme\": \"編輯器主題\",\n\t\"enter file name\": \"輸入檔案名稱\",\n\t\"enter folder name\": \"輸入資料夾名稱\",\n\t\"empty folder message\": \"空資料夾\",\n\t\"enter line number\": \"輸入行號\",\n\t\"error\": \"錯誤\",\n\t\"failed\": \"失敗\",\n\t\"file already exists\": \"檔案已存在\",\n\t\"file already exists force\": \"檔案已存在。是否覆蓋？\",\n\t\"file changed\": \" 已發生改變，重新載入檔案？\",\n\t\"file deleted\": \"檔案已刪除\",\n\t\"file is not supported\": \"不支援此檔案\",\n\t\"file not supported\": \"不支援此檔案類型。\",\n\t\"file too large\": \"檔案太大，無法處理。最大支援 {size} 的檔案\",\n\t\"file renamed\": \"檔案已重新命名\",\n\t\"file saved\": \"檔案已儲存\",\n\t\"folder added\": \"資料夾已加入\",\n\t\"folder already added\": \"資料夾已加入\",\n\t\"font size\": \"字體大小\",\n\t\"goto\": \"跳轉至行...\",\n\t\"icons definition\": \"圖示定義\",\n\t\"info\": \"資訊\",\n\t\"invalid value\": \"無效值\",\n\t\"language changed\": \"語言已變更\",\n\t\"linting\": \"檢查語法錯誤\",\n\t\"logout\": \"登出\",\n\t\"loading\": \"載入中\",\n\t\"my profile\": \"我的資訊\",\n\t\"new file\": \"建立新檔案\",\n\t\"new folder\": \"建立新資料夾\",\n\t\"no\": \"否\",\n\t\"no editor message\": \"從選單開啟或建立新檔案和資料夾\",\n\t\"not set\": \"沒有設定\",\n\t\"unsaved files close app\": \"有尚未儲存的檔案。確定要關閉應用程式？\",\n\t\"notice\": \"注意\",\n\t\"open file\": \"開啟檔案\",\n\t\"open files and folders\": \"開啟檔案和資料夾\",\n\t\"open folder\": \"開啟資料夾\",\n\t\"open recent\": \"最近開啟\",\n\t\"ok\": \"確認\",\n\t\"overwrite\": \"覆蓋\",\n\t\"paste\": \"貼上\",\n\t\"preview mode\": \"預覽模式\",\n\t\"read only file\": \"無法儲存唯讀檔案。請嘗試另存為\",\n\t\"reload\": \"重新載入\",\n\t\"rename\": \"重新命名\",\n\t\"replace\": \"取代\",\n\t\"required\": \"此欄位為必填欄位\",\n\t\"run your web app\": \"執行你的 web 應用程式\",\n\t\"save\": \"儲存\",\n\t\"saving\": \"儲存中\",\n\t\"save as\": \"另存為\",\n\t\"save file to run\": \"請儲存此檔案以在瀏覽器中執行\",\n\t\"search\": \"搜尋\",\n\t\"see logs and errors\": \"檢視日誌和錯誤\",\n\t\"select folder\": \"選擇資料夾\",\n\t\"settings\": \"設定\",\n\t\"settings saved\": \"設定已儲存\",\n\t\"show line numbers\": \"顯示行號\",\n\t\"show hidden files\": \"顯示隱藏檔案\",\n\t\"show spaces\": \"顯示空白字元\",\n\t\"soft tab\": \"使用 Tab 縮排\",\n\t\"sort by name\": \"依名稱排序\",\n\t\"success\": \"成功\",\n\t\"tab size\": \"Tab 寬度\",\n\t\"text wrap\": \"行末自動換行\",\n\t\"theme\": \"主題\",\n\t\"unable to delete file\": \"無法刪除檔案\",\n\t\"unable to open file\": \"無法開啟檔案\",\n\t\"unable to open folder\": \"無法開啟資料夾\",\n\t\"unable to save file\": \"無法儲存檔案\",\n\t\"unable to rename\": \"無法重新命名\",\n\t\"unsaved file\": \"此檔案還尚未儲存，仍然要關閉？\",\n\t\"warning\": \"警告\",\n\t\"use emmet\": \"使用 Emmet 語法\",\n\t\"use quick tools\": \"使用快捷工具列\",\n\t\"yes\": \"是\",\n\t\"encoding\": \"文字編碼\",\n\t\"syntax highlighting\": \"語法高亮顯示\",\n\t\"read only\": \"唯讀\",\n\t\"select all\": \"全選\",\n\t\"select branch\": \"選擇分支\",\n\t\"create new branch\": \"建立新分支\",\n\t\"use branch\": \"使用分支\",\n\t\"new branch\": \"新分支\",\n\t\"branch\": \"分支\",\n\t\"key bindings\": \"快捷鍵\",\n\t\"edit\": \"編輯\",\n\t\"reset\": \"重設\",\n\t\"color\": \"色彩\",\n\t\"select word\": \"選擇字詞\",\n\t\"quick tools\": \"快捷工具列\",\n\t\"select\": \"選擇\",\n\t\"editor font\": \"編輯器字型\",\n\t\"new project\": \"建立新專案\",\n\t\"format\": \"格式化\",\n\t\"project name\": \"專案名稱\",\n\t\"unsupported device\": \"您的裝置不支援主題。\",\n\t\"vibrate on tap\": \"點選時震動\",\n\t\"copy command is not supported by ftp.\": \"FTP 不支援複製命令。\",\n\t\"support title\": \"支持 Acode\",\n\t\"fullscreen\": \"全螢幕\",\n\t\"animation\": \"動畫效果\",\n\t\"backup\": \"備份\",\n\t\"restore\": \"還原\",\n\t\"backup successful\": \"備份成功\",\n\t\"invalid backup file\": \"備份檔案無效\",\n\t\"add path\": \"加入路徑\",\n\t\"live autocompletion\": \"即時自動補全\",\n\t\"file properties\": \"檔案屬性\",\n\t\"path\": \"路徑\",\n\t\"type\": \"類型\",\n\t\"word count\": \"字數統計\",\n\t\"line count\": \"行數統計\",\n\t\"last modified\": \"最後修改\",\n\t\"size\": \"大小\",\n\t\"share\": \"分享\",\n\t\"show print margin\": \"顯示列印頁邊距\",\n\t\"login\": \"登入\",\n\t\"scrollbar size\": \"捲軸大小\",\n\t\"cursor controller size\": \"游標控制器大小\",\n\t\"none\": \"無\",\n\t\"small\": \"小\",\n\t\"large\": \"大\",\n\t\"floating button\": \"懸浮按鈕\",\n\t\"confirm on exit\": \"退出前確認\",\n\t\"show console\": \"顯示主控台\",\n\t\"image\": \"圖片\",\n\t\"insert file\": \"插入檔案\",\n\t\"insert color\": \"插入顏色\",\n\t\"powersave mode warning\": \"關閉省電模式以在外部瀏覽器預覽。\",\n\t\"exit\": \"退出\",\n\t\"custom\": \"自訂\",\n\t\"reset warning\": \"確定要重設主題？\",\n\t\"theme type\": \"主題類型\",\n\t\"light\": \"亮色\",\n\t\"dark\": \"深色\",\n\t\"file browser\": \"檔案瀏覽器\",\n\t\"operation not permitted\": \"操作不被允許\",\n\t\"no such file or directory\": \"沒有此檔案或目錄\",\n\t\"input/output error\": \"輸入/輸出錯誤\",\n\t\"permission denied\": \"權限被拒\",\n\t\"bad address\": \"錯誤地址\",\n\t\"file exists\": \"檔案已存在\",\n\t\"not a directory\": \"不是目錄\",\n\t\"is a directory\": \"是目錄\",\n\t\"invalid argument\": \"無效參數\",\n\t\"too many open files in system\": \"系統中開啟的檔案過多\",\n\t\"too many open files\": \"開啟的檔案過多\",\n\t\"text file busy\": \"檔案正在使用中\",\n\t\"no space left on device\": \"裝置空間不足\",\n\t\"read-only file system\": \"唯讀檔案系統\",\n\t\"file name too long\": \"檔名過長\",\n\t\"too many users\": \"使用者過多\",\n\t\"connection timed out\": \"連線超時\",\n\t\"connection refused\": \"連線被拒\",\n\t\"owner died\": \"擁有者失效\",\n\t\"an error occurred\": \"發生錯誤\",\n\t\"add ftp\": \"加入 FTP\",\n\t\"add sftp\": \"加入 SFTP\",\n\t\"save file\": \"儲存檔案\",\n\t\"save file as\": \"另存檔案為\",\n\t\"files\": \"檔案\",\n\t\"help\": \"說明\",\n\t\"file has been deleted\": \"{file} 已被刪除！\",\n\t\"feature not available\": \"此功能僅在付費版中可用。\",\n\t\"deleted file\": \"已刪除的檔案\",\n\t\"line height\": \"行高\",\n\t\"preview info\": \"如果想要執行開啟的檔案，長按 ▶ 圖示\",\n\t\"manage all files\": \"允許 Acode 編輯器在設定中管理所有檔案以便於編輯裝置上的檔案。\",\n\t\"close file\": \"關閉檔案\",\n\t\"reset connections\": \"重設連線\",\n\t\"check file changes\": \"檢查檔案變更\",\n\t\"open in browser\": \"在瀏覽器中開啟\",\n\t\"desktop mode\": \"桌面模式\",\n\t\"toggle console\": \"切換主控台\",\n\t\"new line mode\": \"換行符號\",\n\t\"add a storage\": \"加入儲存空間\",\n\t\"rate acode\": \"評價 Acode\",\n\t\"support\": \"支援\",\n\t\"downloading file\": \"正在下載 {file}\",\n\t\"downloading...\": \"下載中...\",\n\t\"folder name\": \"資料夾名稱\",\n\t\"keyboard mode\": \"鍵盤模式\",\n\t\"normal\": \"正常\",\n\t\"app settings\": \"應用程式設定\",\n\t\"disable in-app-browser caching\": \"關閉內建瀏覽器快取\",\n\t\"copied to clipboard\": \"已複製到剪貼簿\",\n\t\"remember opened files\": \"記住開啟過的檔案\",\n\t\"remember opened folders\": \"記住開啟過的資料夾\",\n\t\"no suggestions\": \"不顯示建議\",\n\t\"no suggestions aggressive\": \"強制不顯示建議\",\n\t\"install\": \"安裝\",\n\t\"installing\": \"安裝中...\",\n\t\"plugins\": \"外掛\",\n\t\"recently used\": \"最近使用\",\n\t\"update\": \"更新\",\n\t\"uninstall\": \"移除\",\n\t\"download acode pro\": \"下載 Acode pro\",\n\t\"loading plugins\": \"正在載入外掛\",\n\t\"faqs\": \"常見問題\",\n\t\"feedback\": \"意見回饋\",\n\t\"header\": \"標題列\",\n\t\"sidebar\": \"側邊欄\",\n\t\"inapp\": \"內部瀏覽器\",\n\t\"browser\": \"外部瀏覽器\",\n\t\"diagonal scrolling\": \"對角線捲動\",\n\t\"reverse scrolling\": \"反向捲動\",\n\t\"formatter\": \"格式化工具\",\n\t\"format on save\": \"儲存時格式化\",\n\t\"remove ads\": \"移除廣告\",\n\t\"fast\": \"快速\",\n\t\"slow\": \"慢速\",\n\t\"scroll settings\": \"捲動設定\",\n\t\"scroll speed\": \"捲動速度\",\n\t\"loading...\": \"載入中...\",\n\t\"no plugins found\": \"沒有找到外掛\",\n\t\"name\": \"名稱\",\n\t\"username\": \"使用者名稱\",\n\t\"optional\": \"選填\",\n\t\"hostname\": \"主機名稱\",\n\t\"password\": \"密碼\",\n\t\"security type\": \"安全類型\",\n\t\"connection mode\": \"連線模式\",\n\t\"port\": \"連接埠\",\n\t\"key file\": \"金鑰檔案\",\n\t\"select key file\": \"選擇金鑰檔案\",\n\t\"passphrase\": \"通行密碼\",\n\t\"connecting...\": \"連線中...\",\n\t\"type filename\": \"輸入檔案名稱\",\n\t\"unable to load files\": \"無法載入檔案\",\n\t\"preview port\": \"預覽連接埠\",\n\t\"find file\": \"尋找檔案\",\n\t\"system\": \"系統\",\n\t\"please select a formatter\": \"請選擇一個格式化工具\",\n\t\"case sensitive\": \"區分大小寫\",\n\t\"regular expression\": \"正規表達式\",\n\t\"whole word\": \"整個字詞\",\n\t\"edit with\": \"編輯於\",\n\t\"open with\": \"開啟於\",\n\t\"no app found to handle this file\": \"沒有找到能處理該檔案的應用程式\",\n\t\"restore default settings\": \"還原預設設定\",\n\t\"server port\": \"伺服器連接埠\",\n\t\"preview settings\": \"預覽設定\",\n\t\"preview settings note\": \"如果預覽連接埠和伺服器連接埠不同，應用程式將不會啟動伺服器，而會在瀏覽器或應用程式內瀏覽器中開啟 https://<host>:<preview port>。這在你執行著其他伺服器時會有用。\",\n\t\"backup/restore note\": \"這將只會備份你的設定、自訂主題和快捷鍵，而不備份你的 FTP/SFTP。\",\n\t\"host\": \"主機\",\n\t\"retry ftp/sftp when fail\": \"當 FTP/SFTP 連線失敗時重試\",\n\t\"more\": \"更多\",\n\t\"thank you :)\": \"感謝您的支持 :)\",\n\t\"purchase pending\": \"待購買\",\n\t\"cancelled\": \"已取消\",\n\t\"local\": \"本機\",\n\t\"remote\": \"遠端\",\n\t\"show console toggler\": \"顯示主控台切換按鈕\",\n\t\"binary file\": \"此檔案包含二進位制資料，確定要開啟它嗎？\",\n\t\"relative line numbers\": \"相對行號\",\n\t\"elastic tabstops\": \"彈性的製表縮排風格\",\n\t\"line based rtl switching\": \"基於行的 RTL（從右到左）轉換\",\n\t\"hard wrap\": \"強制換行\",\n\t\"spellcheck\": \"拼寫檢查\",\n\t\"wrap method\": \"換行方法\",\n\t\"use textarea for ime\": \"使用用於輸入法的文字輸入框\",\n\t\"invalid plugin\": \"無效外掛\",\n\t\"type command\": \"輸入命令\",\n\t\"plugin\": \"外掛\",\n\t\"quicktools trigger mode\": \"快捷工具列觸發模式\",\n\t\"print margin\": \"列印邊距\",\n\t\"touch move threshold\": \"觸控移動閾值\",\n\t\"info-retryremotefsafterfail\": \"當 FTP/SFTP 連線失敗時重試。\",\n\t\"info-fullscreen\": \"隱藏主畫面的標題列。\",\n\t\"info-checkfiles\": \"當應用程式在背景執行時，檢查檔案變更。\",\n\t\"info-console\": \"選擇 JavaScript 主控台。Legacy 是預設主控台，Eruda 是第三方主控台。\",\n\t\"info-keyboardmode\": \"輸入文字時的鍵盤模式。不顯示建議將關閉詞彙建議與自動校正。如果不顯示建議無效，請嘗試強制不顯示建議。\",\n\t\"info-rememberfiles\": \"關閉時記住開啟的檔案。\",\n\t\"info-rememberfolders\": \"關閉時記住開啟的資料夾。\",\n\t\"info-floatingbutton\": \"顯示或隱藏快捷工具列的浮動按鈕。\",\n\t\"info-openfilelistpos\": \"設定開啟的檔案列表顯示位置。\",\n\t\"info-touchmovethreshold\": \"如果您的裝置觸控靈敏度過高，可以增加此值以防止誤觸移動。\",\n\t\"info-scroll-settings\": \"這些設定包含捲動設定，包括文字換行。\",\n\t\"info-animation\": \"如果應用程式感覺卡頓，請關閉動畫效果。\",\n\t\"info-quicktoolstriggermode\": \"如果快捷工具列中的按鈕不正常工作，請嘗試更改此值。\",\n\t\"info-checkForAppUpdates\": \"Check for app updates automatically.\",\n\t\"info-quickTools\": \"Show or hide quick tools.\",\n\t\"info-showHiddenFiles\": \"Show hidden files and folders. (Start with .)\",\n\t\"info-all_file_access\": \"Enable access of /sdcard and /storage in terminal.\",\n\t\"info-fontSize\": \"The font size used to render text.\",\n\t\"info-fontFamily\": \"The font family used to render text.\",\n\t\"info-theme\": \"The color theme of the terminal.\",\n\t\"info-cursorStyle\": \"The style of the cursor when the terminal is focused.\",\n\t\"info-cursorInactiveStyle\": \"The style of the cursor when the terminal is not focused.\",\n\t\"info-fontWeight\": \"The font weight used to render non-bold text.\",\n\t\"info-cursorBlink\": \"Whether the cursor blinks.\",\n\t\"info-scrollback\": \"The amount of scrollback in the terminal. Scrollback is the amount of rows that are retained when lines are scrolled beyond the initial viewport.\",\n\t\"info-tabStopWidth\": \"The size of tab stops in the terminal.\",\n\t\"info-letterSpacing\": \"The spacing in whole pixels between characters.\",\n\t\"info-imageSupport\": \"Whether images are supported in the terminal.\",\n\t\"info-fontLigatures\": \"Whether font ligatures are enabled in the terminal.\",\n\t\"info-confirmTabClose\": \"Ask for confirmation before closing terminal tabs.\",\n\t\"info-backup\": \"Creates a backup of the terminal installation.\",\n\t\"info-restore\": \"Restores a backup of the terminal installation.\",\n\t\"info-uninstall\": \"Uninstalls the terminal installation.\",\n\t\"owned\": \"已擁有\",\n\t\"api_error\": \"API 伺服器沒有回應，請稍後再試。\",\n\t\"installed\": \"已安裝\",\n\t\"all\": \"所有\",\n\t\"medium\": \"中等\",\n\t\"refund\": \"退款\",\n\t\"product not available\": \"產品無法使用\",\n\t\"no-product-info\": \"該產品目前在您所在的國家無法使用，請稍後再試。\",\n\t\"close\": \"關閉\",\n\t\"explore\": \"探索\",\n\t\"key bindings updated\": \"按鍵綁定已更新\",\n\t\"search in files\": \"在檔案中搜尋\",\n\t\"exclude files\": \"排除檔案\",\n\t\"include files\": \"包含檔案\",\n\t\"search result\": \"{matches} 個結果在 {files} 個檔案中。\",\n\t\"invalid regex\": \"無效的正規表達式：{message}。\",\n\t\"bottom\": \"底部\",\n\t\"save all\": \"儲存全部\",\n\t\"close all\": \"關閉全部\",\n\t\"unsaved files warning\": \"某些檔案還沒有儲存。點選『確認』選擇要做什麼或『取消』以返回。\",\n\t\"save all warning\": \"您確定要儲存所有檔案並關閉嗎？此操作無法復原。\",\n\t\"save all changes warning\": \"您確定要儲存所有檔案嗎？\",\n\t\"close all warning\": \"您確定要關閉所有檔案嗎？您將失去還沒有儲存的變更，且此操作無法復原。\",\n\t\"refresh\": \"重新整理\",\n\t\"shortcut buttons\": \"快捷鍵按鈕\",\n\t\"no result\": \"沒有結果\",\n\t\"searching...\": \"搜尋中...\",\n\t\"quicktools:ctrl-key\": \"Control/Command 鍵\",\n\t\"quicktools:tab-key\": \"Tab 鍵\",\n\t\"quicktools:shift-key\": \"Shift 鍵\",\n\t\"quicktools:undo\": \"復原\",\n\t\"quicktools:redo\": \"重做\",\n\t\"quicktools:search\": \"在檔案中搜尋\",\n\t\"quicktools:save\": \"儲存檔案\",\n\t\"quicktools:esc-key\": \"Escape 鍵\",\n\t\"quicktools:curlybracket\": \"插入大括號\",\n\t\"quicktools:squarebracket\": \"插入中括號\",\n\t\"quicktools:parentheses\": \"插入括號\",\n\t\"quicktools:anglebracket\": \"插入角括號\",\n\t\"quicktools:left-arrow-key\": \"左方向鍵\",\n\t\"quicktools:right-arrow-key\": \"右方向鍵\",\n\t\"quicktools:up-arrow-key\": \"上方向鍵\",\n\t\"quicktools:down-arrow-key\": \"下方向鍵\",\n\t\"quicktools:moveline-up\": \"向上移行\",\n\t\"quicktools:moveline-down\": \"向下移行\",\n\t\"quicktools:copyline-up\": \"向上複製\",\n\t\"quicktools:copyline-down\": \"向下複製\",\n\t\"quicktools:semicolon\": \"插入分號\",\n\t\"quicktools:quotation\": \"插入引號\",\n\t\"quicktools:and\": \"插入「與」符號\",\n\t\"quicktools:bar\": \"插入豎線符號\",\n\t\"quicktools:equal\": \"插入等號\",\n\t\"quicktools:slash\": \"插入斜線符號\",\n\t\"quicktools:exclamation\": \"插入驚嘆號\",\n\t\"quicktools:alt-key\": \"Alt 鍵\",\n\t\"quicktools:meta-key\": \"Windows/Meta 鍵\",\n\t\"info-quicktoolssettings\": \"自訂在編輯器下方的快捷工具內的快捷按鈕與鍵盤按鍵以增強您的開發體驗。\",\n\t\"info-excludefolders\": \"使用表達式 **/node_modules/** 以忽略所有來自 node_modules 資料夾中的所有檔案。這將排除列出的檔案並且還將避免在這些檔案中搜尋。\",\n\t\"missed files\": \"在搜尋開始後掃描了 {count} 個檔案並且將不會包含在搜尋中。\",\n\t\"remove\": \"移除\",\n\t\"quicktools:command-palette\": \"命令面板\",\n\t\"default file encoding\": \"預設檔案編碼\",\n\t\"remove entry\": \"您確定要從儲存的路徑中移除 '{name}' 嗎？請注意這個刪除不會刪除路徑本身。\",\n\t\"delete entry\": \"確認刪除：'{name}'。此動作無法復原。確定要繼續嗎？\",\n\t\"change encoding\": \"使用 '{encoding}' 重新開啟 '{file}'？此操作將遺失該檔案任何沒有儲存的變更。您是否想要繼續重新開啟？\",\n\t\"reopen file\": \"您確定要重新開啟 '{file}' 嗎？任何沒有儲存的變更都將會遺失。\",\n\t\"plugin min version\": \"{name} 僅可用於 Acode - {v-code} 以上版本。點選這裡以更新。\",\n\t\"color preview\": \"色彩預覽\",\n\t\"confirm\": \"確認\",\n\t\"list files\": \"列出在 <strong>{name}</strong> 中的所有檔案嗎？太多檔案可能會導致應用程式故障。\",\n\t\"problems\": \"問題\",\n\t\"show side buttons\": \"顯示側邊按鈕\",\n\t\"bug_report\": \"提交錯誤回報\",\n\t\"verified publisher\": \"已驗證的發行者\",\n\t\"most_downloaded\": \"最多下載\",\n\t\"newly_added\": \"最近新增\",\n\t\"top_rated\": \"最多好評\",\n\t\"rename not supported\": \"不支援重新命名位於 Termux 目錄中的項目\",\n\t\"compress\": \"壓縮\",\n\t\"copy uri\": \"複製 URI\",\n\t\"delete entries\": \"您是否確定想要刪除 {count} 個項目？\",\n\t\"deleting items\": \"正在刪除 {count} 個項目...\",\n\t\"import project zip\": \"匯入專案(zip)\",\n\t\"changelog\": \"更新日誌\",\n\t\"notifications\": \"通知\",\n\t\"no_unread_notifications\": \"沒有未讀的通知\",\n\t\"should_use_current_file_for_preview\": \"應使用目前的檔案作為預覽頁面，而不是預設檔案 (index.html)\",\n\t\"fade fold widgets\": \"Fade Fold Widgets\",\n\t\"quicktools:home-key\": \"Home Key\",\n\t\"quicktools:end-key\": \"End Key\",\n\t\"quicktools:pageup-key\": \"PageUp Key\",\n\t\"quicktools:pagedown-key\": \"PageDown Key\",\n\t\"quicktools:delete-key\": \"Delete Key\",\n\t\"quicktools:tilde\": \"Insert tilde symbol\",\n\t\"quicktools:backtick\": \"Insert backtick\",\n\t\"quicktools:hash\": \"Insert Hash symbol\",\n\t\"quicktools:dollar\": \"Insert dollar symbol\",\n\t\"quicktools:modulo\": \"Insert modulo/percent symbol\",\n\t\"quicktools:caret\": \"Insert caret symbol\",\n\t\"plugin_enabled\": \"Plugin enabled\",\n\t\"plugin_disabled\": \"Plugin disabled\",\n\t\"enable_plugin\": \"Enable this Plugin\",\n\t\"disable_plugin\": \"Disable this Plugin\",\n\t\"open_source\": \"Open Source\",\n\t\"terminal settings\": \"Terminal Settings\",\n\t\"font ligatures\": \"Font Ligatures\",\n\t\"letter spacing\": \"Letter Spacing\",\n\t\"terminal:tab stop width\": \"Tab Stop Width\",\n\t\"terminal:scrollback\": \"Scrollback Lines\",\n\t\"terminal:cursor blink\": \"Cursor Blink\",\n\t\"terminal:font weight\": \"Font Weight\",\n\t\"terminal:cursor inactive style\": \"Cursor Inactive Style\",\n\t\"terminal:cursor style\": \"Cursor Style\",\n\t\"terminal:font family\": \"Font Family\",\n\t\"terminal:convert eol\": \"Convert EOL\",\n\t\"terminal:confirm tab close\": \"Confirm terminal tab close\",\n\t\"terminal:image support\": \"Image support\",\n\t\"terminal\": \"Terminal\",\n\t\"allFileAccess\": \"All file access\",\n\t\"fonts\": \"Fonts\",\n\t\"sponsor\": \"贊助\",\n\t\"downloads\": \"downloads\",\n\t\"reviews\": \"reviews\",\n\t\"overview\": \"Overview\",\n\t\"contributors\": \"Contributors\",\n\t\"quicktools:hyphen\": \"Insert hyphen symbol\",\n\t\"check for app updates\": \"Check for app updates\",\n\t\"prompt update check consent message\": \"Acode can check for new app updates when you're online. Enable update checks?\",\n\t\"keywords\": \"Keywords\",\n\t\"author\": \"Author\",\n\t\"filtered by\": \"Filtered by\",\n\t\"clean install state\": \"Clean Install State\",\n\t\"backup created\": \"Backup created\",\n\t\"restore completed\": \"Restore completed\",\n\t\"restore will include\": \"This will restore\",\n\t\"restore warning\": \"This action cannot be undone. Continue?\",\n\t\"reload to apply\": \"Reload to apply changes?\",\n\t\"reload app\": \"Reload app\",\n\t\"preparing backup\": \"Preparing backup\",\n\t\"collecting settings\": \"Collecting settings\",\n\t\"collecting key bindings\": \"Collecting key bindings\",\n\t\"collecting plugins\": \"Collecting plugin information\",\n\t\"creating backup\": \"Creating backup file\",\n\t\"validating backup\": \"Validating backup\",\n\t\"restoring key bindings\": \"Restoring key bindings\",\n\t\"restoring plugins\": \"Restoring plugins\",\n\t\"restoring settings\": \"Restoring settings\",\n\t\"legacy backup warning\": \"This is an older backup format. Some features may be limited.\",\n\t\"checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted.\",\n\t\"plugin not found\": \"Plugin not found in registry\",\n\t\"paid plugin skipped\": \"Paid plugin - purchase not found\",\n\t\"source not found\": \"Source file no longer exists\",\n\t\"restored\": \"Restored\",\n\t\"skipped\": \"Skipped\",\n\t\"backup not valid object\": \"Backup file is not a valid object\",\n\t\"backup no data\": \"Backup file contains no data to restore\",\n\t\"backup legacy warning\": \"This is an older backup format (v1). Some features may be limited.\",\n\t\"backup missing metadata\": \"Missing backup metadata - some info may be unavailable\",\n\t\"backup checksum mismatch\": \"Checksum mismatch - backup file may have been modified or corrupted. Proceed with caution.\",\n\t\"backup checksum verify failed\": \"Could not verify checksum\",\n\t\"backup invalid settings\": \"Invalid settings format\",\n\t\"backup invalid keybindings\": \"Invalid keyBindings format\",\n\t\"backup invalid plugins\": \"Invalid installedPlugins format\",\n\t\"issues found\": \"Issues found\",\n\t\"error details\": \"Error details\",\n\t\"active tools\": \"Active tools\",\n\t\"available tools\": \"Available tools\",\n\t\"recent\": \"Recent Files\",\n\t\"command palette\": \"Open Command Palette\",\n\t\"change theme\": \"Change Theme\",\n\t\"documentation\": \"Documentation\",\n\t\"open in terminal\": \"Open in Terminal\",\n\t\"developer mode\": \"Developer Mode\",\n\t\"info-developermode\": \"Enable developer tools (Eruda) for debugging plugins and inspecting app state. Inspector will be initialized on app start.\",\n\t\"developer mode enabled\": \"Developer mode enabled. Use command palette to toggle inspector (Ctrl+Shift+I).\",\n\t\"developer mode disabled\": \"Developer mode disabled\",\n\t\"copy relative path\": \"Copy Relative Path\",\n\t\"shortcut request sent\": \"Shortcut request opened. Tap Add to finish.\",\n\t\"add to home screen\": \"Add to home screen\",\n\t\"pin shortcuts not supported\": \"Home screen shortcuts are not supported on this device.\",\n\t\"save file before home shortcut\": \"Save the file before adding it to the home screen.\",\n\t\"terminal_required_message_for_lsp\": \"Terminal not installed. Please install Terminal first to use LSP servers.\",\n\t\"shift click selection\": \"Shift + tap/click selection\",\n\t\"earn ad-free time\": \"Earn ad-free time\",\n\t\"indent guides\": \"Indent guides\",\n\t\"language servers\": \"Language servers\",\n\t\"lint gutter\": \"Show lint gutter\",\n\t\"rainbow brackets\": \"Rainbow brackets\",\n\t\"lsp-add-custom-server\": \"Add custom server\",\n\t\"lsp-binary-args\": \"Binary args (JSON array)\",\n\t\"lsp-binary-command\": \"Binary command\",\n\t\"lsp-binary-path-optional\": \"Binary path (optional)\",\n\t\"lsp-check-command-optional\": \"Check command (optional override)\",\n\t\"lsp-checking-installation-status\": \"Checking installation status...\",\n\t\"lsp-configured\": \"Configured\",\n\t\"lsp-custom-server-added\": \"Custom server added\",\n\t\"lsp-default\": \"Default\",\n\t\"lsp-details-line\": \"Details: {details}\",\n\t\"lsp-edit-initialization-options\": \"Edit initialization options\",\n\t\"lsp-empty\": \"Empty\",\n\t\"lsp-enabled\": \"Enabled\",\n\t\"lsp-error-add-server-failed\": \"Failed to add server\",\n\t\"lsp-error-args-must-be-array\": \"Arguments must be a JSON array\",\n\t\"lsp-error-binary-command-required\": \"Binary command is required\",\n\t\"lsp-error-language-id-required\": \"At least one language ID is required\",\n\t\"lsp-error-package-required\": \"At least one package is required\",\n\t\"lsp-error-server-id-required\": \"Server ID is required\",\n\t\"lsp-feature-completion\": \"Code completion\",\n\t\"lsp-feature-completion-info\": \"Enable autocomplete suggestions from the server.\",\n\t\"lsp-feature-diagnostics\": \"Diagnostics\",\n\t\"lsp-feature-diagnostics-info\": \"Show errors and warnings from the language server.\",\n\t\"lsp-feature-formatting\": \"Formatting\",\n\t\"lsp-feature-formatting-info\": \"Enable code formatting from the language server.\",\n\t\"lsp-feature-hover\": \"Hover information\",\n\t\"lsp-feature-hover-info\": \"Show type information and documentation on hover.\",\n\t\"lsp-feature-inlay-hints\": \"Inlay hints\",\n\t\"lsp-feature-inlay-hints-info\": \"Show inline type hints in the editor.\",\n\t\"lsp-feature-signature\": \"Signature help\",\n\t\"lsp-feature-signature-info\": \"Show function parameter hints while typing.\",\n\t\"lsp-feature-state-toast\": \"{feature} {state}\",\n\t\"lsp-initialization-options\": \"Initialization options\",\n\t\"lsp-initialization-options-json\": \"Initialization options (JSON)\",\n\t\"lsp-initialization-options-updated\": \"Initialization options updated\",\n\t\"lsp-install-command\": \"Install command\",\n\t\"lsp-install-command-unavailable\": \"Install command not available\",\n\t\"lsp-install-info-check-failed\": \"Acode could not verify the installation status.\",\n\t\"lsp-install-info-missing\": \"Language server is not installed in the terminal environment.\",\n\t\"lsp-install-info-ready\": \"Language server is installed and ready.\",\n\t\"lsp-install-info-unknown\": \"Installation status could not be checked automatically.\",\n\t\"lsp-install-info-version-available\": \"Version {version} is available.\",\n\t\"lsp-install-method-apk\": \"APK package\",\n\t\"lsp-install-method-cargo\": \"Cargo crate\",\n\t\"lsp-install-method-manual\": \"Manual binary\",\n\t\"lsp-install-method-npm\": \"npm package\",\n\t\"lsp-install-method-pip\": \"pip package\",\n\t\"lsp-install-method-shell\": \"Custom shell\",\n\t\"lsp-install-method-title\": \"Install method\",\n\t\"lsp-install-repair\": \"Install / repair\",\n\t\"lsp-installation-status\": \"Installation status\",\n\t\"lsp-installed\": \"Installed\",\n\t\"lsp-invalid-timeout\": \"Invalid timeout value\",\n\t\"lsp-language-ids\": \"Language IDs (comma separated)\",\n\t\"lsp-packages-prompt\": \"{method} packages (comma separated)\",\n\t\"lsp-remove-installed-files\": \"Remove installed files for {server}?\",\n\t\"lsp-server-disabled-toast\": \"Server disabled\",\n\t\"lsp-server-enabled-toast\": \"Server enabled\",\n\t\"lsp-server-id\": \"Server ID\",\n\t\"lsp-server-label\": \"Server label\",\n\t\"lsp-server-not-found\": \"Server not found\",\n\t\"lsp-server-uninstalled\": \"Server uninstalled\",\n\t\"lsp-startup-timeout\": \"Startup timeout\",\n\t\"lsp-startup-timeout-ms\": \"Startup timeout (milliseconds)\",\n\t\"lsp-startup-timeout-set\": \"Startup timeout set to {timeout} ms\",\n\t\"lsp-state-disabled\": \"disabled\",\n\t\"lsp-state-enabled\": \"enabled\",\n\t\"lsp-status-check-failed\": \"Check failed\",\n\t\"lsp-status-installed\": \"Installed\",\n\t\"lsp-status-installed-version\": \"Installed ({version})\",\n\t\"lsp-status-line\": \"Status: {status}\",\n\t\"lsp-status-not-installed\": \"Not installed\",\n\t\"lsp-status-unknown\": \"Unknown\",\n\t\"lsp-timeout-ms\": \"{timeout} ms\",\n\t\"lsp-uninstall-command-unavailable\": \"Uninstall command not available\",\n\t\"lsp-uninstall-server\": \"Uninstall server\",\n\t\"lsp-update-command-optional\": \"Update command (optional)\",\n\t\"lsp-update-command-unavailable\": \"Update command not available\",\n\t\"lsp-update-server\": \"Update server\",\n\t\"lsp-version-line\": \"Version: {version}\",\n\t\"lsp-view-initialization-options\": \"View initialization options\",\n\t\"settings-category-about-acode\": \"About Acode\",\n\t\"settings-category-advanced\": \"Advanced\",\n\t\"settings-category-assistance\": \"Assistance\",\n\t\"settings-category-core\": \"Core settings\",\n\t\"settings-category-cursor\": \"Cursor\",\n\t\"settings-category-cursor-selection\": \"Cursor & selection\",\n\t\"settings-category-custom-servers\": \"Custom servers\",\n\t\"settings-category-customization-tools\": \"Customization & tools\",\n\t\"settings-category-display\": \"Display\",\n\t\"settings-category-editing\": \"Editing\",\n\t\"settings-category-features\": \"Features\",\n\t\"settings-category-files-sessions\": \"Files & sessions\",\n\t\"settings-category-fonts\": \"Fonts\",\n\t\"settings-category-general\": \"General\",\n\t\"settings-category-guides-indicators\": \"Guides & indicators\",\n\t\"settings-category-installation\": \"Installation\",\n\t\"settings-category-interface\": \"Interface\",\n\t\"settings-category-maintenance\": \"Maintenance\",\n\t\"settings-category-permissions\": \"Permissions\",\n\t\"settings-category-preview\": \"Preview\",\n\t\"settings-category-scrolling\": \"Scrolling\",\n\t\"settings-category-server\": \"Server\",\n\t\"settings-category-servers\": \"Servers\",\n\t\"settings-category-session\": \"Session\",\n\t\"settings-category-support-acode\": \"Support Acode\",\n\t\"settings-category-text-layout\": \"Text & layout\",\n\t\"settings-info-app-animation\": \"Control transition animations across the app.\",\n\t\"settings-info-app-check-files\": \"Refresh editors when files change outside Acode.\",\n\t\"settings-info-app-clean-install-state\": \"Clear stored install state used by onboarding and setup flows.\",\n\t\"settings-info-app-confirm-on-exit\": \"Ask before closing the app.\",\n\t\"settings-info-app-console\": \"Choose which debug console integration Acode uses.\",\n\t\"settings-info-app-default-file-encoding\": \"Default encoding when opening or creating files.\",\n\t\"settings-info-app-exclude-folders\": \"Skip folders and patterns while searching or scanning.\",\n\t\"settings-info-app-floating-button\": \"Show the floating quick actions button.\",\n\t\"settings-info-app-font-manager\": \"Install, manage, or remove app fonts.\",\n\t\"settings-info-app-fullscreen\": \"Hide the system status bar while using Acode.\",\n\t\"settings-info-app-keybindings\": \"Edit the key bindings file or reset shortcuts.\",\n\t\"settings-info-app-keyboard-mode\": \"Choose how the software keyboard behaves while editing.\",\n\t\"settings-info-app-language\": \"Choose the app language and translated labels.\",\n\t\"settings-info-app-open-file-list-position\": \"Choose where the active files list appears.\",\n\t\"settings-info-app-quick-tools-settings\": \"Reorder and customize quick tool shortcuts.\",\n\t\"settings-info-app-quick-tools-trigger-mode\": \"Choose how quick tools open on tap or touch.\",\n\t\"settings-info-app-remember-files\": \"Reopen the files that were open last time.\",\n\t\"settings-info-app-remember-folders\": \"Reopen folders from the previous session.\",\n\t\"settings-info-app-retry-remote-fs\": \"Retry remote file operations after a failed transfer.\",\n\t\"settings-info-app-side-buttons\": \"Show extra action buttons beside the editor.\",\n\t\"settings-info-app-sponsor-sidebar\": \"Show the sponsor entry in the sidebar.\",\n\t\"settings-info-app-touch-move-threshold\": \"Minimum movement before a touch drag is detected.\",\n\t\"settings-info-app-vibrate-on-tap\": \"Enable haptic feedback for taps and controls.\",\n\t\"settings-info-editor-autosave\": \"Save changes automatically after a delay.\",\n\t\"settings-info-editor-color-preview\": \"Preview color values inline in the editor.\",\n\t\"settings-info-editor-fade-fold-widgets\": \"Dim fold markers until they are needed.\",\n\t\"settings-info-editor-font-family\": \"Choose the typeface used in the editor.\",\n\t\"settings-info-editor-font-size\": \"Set the editor text size.\",\n\t\"settings-info-editor-format-on-save\": \"Run the formatter whenever a file is saved.\",\n\t\"settings-info-editor-hard-wrap\": \"Insert real line breaks instead of only wrapping visually.\",\n\t\"settings-info-editor-indent-guides\": \"Show indentation guide lines.\",\n\t\"settings-info-editor-line-height\": \"Adjust vertical spacing between lines.\",\n\t\"settings-info-editor-line-numbers\": \"Show line numbers in the gutter.\",\n\t\"settings-info-editor-lint-gutter\": \"Show diagnostics and lint markers in the gutter.\",\n\t\"settings-info-editor-live-autocomplete\": \"Show suggestions while you type.\",\n\t\"settings-info-editor-rainbow-brackets\": \"Color matching brackets by nesting depth.\",\n\t\"settings-info-editor-relative-line-numbers\": \"Show distance from the current line.\",\n\t\"settings-info-editor-rtl-text\": \"Switch right-to-left behavior per line.\",\n\t\"settings-info-editor-scroll-settings\": \"Adjust scrollbar size, speed, and gesture behavior.\",\n\t\"settings-info-editor-shift-click-selection\": \"Extend selection with Shift + tap or click.\",\n\t\"settings-info-editor-show-spaces\": \"Display visible whitespace markers.\",\n\t\"settings-info-editor-soft-tab\": \"Insert spaces instead of tab characters.\",\n\t\"settings-info-editor-tab-size\": \"Set how many spaces each tab step uses.\",\n\t\"settings-info-editor-teardrop-size\": \"Set the cursor handle size for touch editing.\",\n\t\"settings-info-editor-text-wrap\": \"Wrap long lines inside the editor.\",\n\t\"settings-info-lsp-add-custom-server\": \"Register a custom language server with install, update, and launch commands.\",\n\t\"settings-info-lsp-edit-init-options\": \"Edit initialization options as JSON.\",\n\t\"settings-info-lsp-install-server\": \"Install or repair this language server.\",\n\t\"settings-info-lsp-server-enabled\": \"Enable or disable this language server.\",\n\t\"settings-info-lsp-startup-timeout\": \"Set how long Acode waits for the server to start.\",\n\t\"settings-info-lsp-uninstall-server\": \"Remove installed packages or binaries for this server.\",\n\t\"settings-info-lsp-update-server\": \"Update this language server if an update flow is available.\",\n\t\"settings-info-lsp-view-init-options\": \"View the effective initialization options as JSON.\",\n\t\"settings-info-main-ad-rewards\": \"Watch ads to unlock temporary ad-free access.\",\n\t\"settings-info-main-app-settings\": \"Language, app behavior, and quick access tools.\",\n\t\"settings-info-main-backup-restore\": \"Export settings to a backup or restore them later.\",\n\t\"settings-info-main-changelog\": \"See recent updates and release notes.\",\n\t\"settings-info-main-edit-settings\": \"Edit the raw settings.json file directly.\",\n\t\"settings-info-main-editor-settings\": \"Fonts, tabs, suggestions, and editor display.\",\n\t\"settings-info-main-formatter\": \"Choose a formatter for each supported language.\",\n\t\"settings-info-main-lsp-settings\": \"Configure language servers and editor intelligence.\",\n\t\"settings-info-main-plugins\": \"Manage installed plugins and their available actions.\",\n\t\"settings-info-main-preview-settings\": \"Preview mode, server ports, and browser behavior.\",\n\t\"settings-info-main-rateapp\": \"Rate Acode on Google Play.\",\n\t\"settings-info-main-remove-ads\": \"Unlock permanent ad-free access.\",\n\t\"settings-info-main-reset\": \"Reset Acode to its default configuration.\",\n\t\"settings-info-main-sponsors\": \"Support ongoing Acode development.\",\n\t\"settings-info-main-terminal-settings\": \"Terminal theme, font, cursor, and session behavior.\",\n\t\"settings-info-main-theme\": \"App theme, contrast, and custom colors.\",\n\t\"settings-info-preview-disable-cache\": \"Always reload content in the in-app browser.\",\n\t\"settings-info-preview-host\": \"Hostname used when opening the preview URL.\",\n\t\"settings-info-preview-mode\": \"Choose where preview opens when you launch it.\",\n\t\"settings-info-preview-preview-port\": \"Port used by the live preview server.\",\n\t\"settings-info-preview-server-port\": \"Port used by the internal app server.\",\n\t\"settings-info-preview-show-console-toggler\": \"Show the console button in preview.\",\n\t\"settings-info-preview-use-current-file\": \"Prefer the current file when starting preview.\",\n\t\"settings-info-terminal-convert-eol\": \"Convert line endings when pasting or rendering terminal output.\",\n\t\"settings-note-formatter-settings\": \"Assign a formatter to each language. Install formatter plugins to unlock more options.\",\n\t\"settings-note-lsp-settings\": \"Language servers add autocomplete, diagnostics, hover details, and more. You can install, update, or define custom servers here. Managed installers run inside the terminal/proot environment.\",\n\t\"search result label singular\": \"result\",\n\t\"search result label plural\": \"results\",\n\t\"pin tab\": \"Pin tab\",\n\t\"unpin tab\": \"Unpin tab\",\n\t\"pinned tab\": \"Pinned tab\",\n\t\"unpin tab before closing\": \"Unpin the tab before closing it.\",\n\t\"app font\": \"App font\",\n\t\"settings-info-app-font-family\": \"Choose the font used across the app interface.\",\n\t\"lsp-transport-method-stdio\": \"STDIO (launch a binary command)\",\n\t\"lsp-transport-method-websocket\": \"WebSocket (connect to a ws/wss URL)\",\n\t\"lsp-websocket-url\": \"WebSocket URL\",\n\t\"lsp-websocket-server-managed-externally\": \"This server is managed externally over WebSocket.\",\n\t\"lsp-error-websocket-url-invalid\": \"WebSocket URL must start with ws:// or wss://\",\n\t\"lsp-error-websocket-url-required\": \"WebSocket URL is required\",\n\t\"lsp-remove-custom-server\": \"Remove custom server\",\n\t\"lsp-remove-custom-server-confirm\": \"Remove custom language server {server}?\",\n\t\"lsp-custom-server-removed\": \"Custom server removed\",\n\t\"settings-info-lsp-remove-custom-server\": \"Remove this custom language server from Acode.\"\n}\n"
  },
  {
    "path": "src/lib/acode.js",
    "content": "import fsOperation from \"fileSystem\";\nimport sidebarApps from \"sidebarApps\";\nimport * as cmAutocomplete from \"@codemirror/autocomplete\";\nimport * as cmCommands from \"@codemirror/commands\";\nimport * as cmLanguage from \"@codemirror/language\";\nimport * as cmLint from \"@codemirror/lint\";\nimport * as cmSearch from \"@codemirror/search\";\nimport * as cmState from \"@codemirror/state\";\nimport * as cmView from \"@codemirror/view\";\nimport ajax from \"@deadlyjack/ajax\";\nimport * as lezerHighlight from \"@lezer/highlight\";\nimport {\n\tgetRegisteredCommands as listRegisteredCommands,\n\trefreshCommandKeymap,\n\tregisterExternalCommand,\n\tremoveExternalCommand,\n\texecuteCommand as runCommand,\n} from \"cm/commandRegistry\";\nimport { default as lspApi } from \"cm/lsp/api\";\nimport lspClientManager from \"cm/lsp/clientManager\";\nimport { registerLspFormatter } from \"cm/lsp/formatter\";\nimport {\n\taddMode,\n\tgetModeForPath,\n\tgetModes,\n\tgetModesByName,\n\tremoveMode,\n} from \"cm/modelist\";\nimport cmThemeRegistry from \"cm/themes\";\nimport Contextmenu from \"components/contextmenu\";\nimport inputhints from \"components/inputhints\";\nimport Page from \"components/page\";\nimport palette from \"components/palette\";\nimport settingsPage from \"components/settingsPage\";\nimport SideButton from \"components/sideButton\";\nimport { TerminalManager, TerminalThemeManager } from \"components/terminal\";\nimport toast from \"components/toast\";\nimport tutorial from \"components/tutorial\";\nimport alert from \"dialogs/alert\";\nimport box from \"dialogs/box\";\nimport colorPicker from \"dialogs/color\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport multiPrompt from \"dialogs/multiPrompt\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport { addIntentHandler, removeIntentHandler } from \"handlers/intent\";\nimport keyboardHandler from \"handlers/keyboard\";\nimport purchaseListener from \"handlers/purchase\";\nimport windowResize from \"handlers/windowResize\";\nimport actionStack from \"lib/actionStack\";\nimport commands from \"lib/commands\";\nimport EditorFile from \"lib/editorFile\";\nimport files from \"lib/fileList\";\nimport fileTypeHandler from \"lib/fileTypeHandler\";\nimport fonts from \"lib/fonts\";\nimport {\n\tBROKEN_PLUGINS,\n\tLOADED_PLUGINS,\n\tonPluginLoadCallback,\n\tonPluginsLoadCompleteCallback,\n} from \"lib/loadPlugins\";\nimport NotificationManager from \"lib/notificationManager\";\nimport openFolder, { addedFolder } from \"lib/openFolder\";\nimport projects from \"lib/projects\";\nimport selectionMenu from \"lib/selectionMenu\";\nimport appSettings from \"lib/settings\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport formatterSettings from \"settings/formatterSettings\";\nimport ThemeBuilder from \"theme/builder\";\nimport themes from \"theme/list\";\nimport Color from \"utils/color\";\nimport encodings, { decode, encode } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport KeyboardEvent from \"utils/keyboardEvent\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\n\nexport default class Acode {\n\t#modules = {};\n\t#pluginsInit = {};\n\t#pluginUnmount = {};\n\t// Registered formatter implementations (populated by plugins)\n\t#formatter = [];\n\t#pluginWatchers = {};\n\n\t/**\n\t * Clear a plugin's broken mark (so it can be retried)\n\t * @param {string} pluginId\n\t */\n\tclearBrokenPluginMark(pluginId) {\n\t\ttry {\n\t\t\tif (BROKEN_PLUGINS.has(pluginId)) {\n\t\t\t\tBROKEN_PLUGINS.delete(pluginId);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"Failed to clear broken plugin mark:\", e);\n\t\t}\n\t}\n\n\tconstructor() {\n\t\tconst encodingsModule = {\n\t\t\tget encodings() {\n\t\t\t\treturn encodings;\n\t\t\t},\n\t\t\tencode,\n\t\t\tdecode,\n\t\t};\n\n\t\tconst themesModule = {\n\t\t\tadd: themes.add,\n\t\t\tget: themes.get,\n\t\t\tlist: themes.list,\n\t\t\tupdate: themes.update,\n\t\t\t// Deprecated, not supported anymore\n\t\t\tapply: () => {},\n\t\t};\n\n\t\t// CodeMirror editor theme API for plugins\n\t\tconst normalizeThemeSpec = (spec) => {\n\t\t\tif (!spec || typeof spec !== \"object\" || Array.isArray(spec)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[editorThemes] register(spec) expects an object: { id, caption?, dark?, getExtension|extensions|extension|theme, config? }\",\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst id = spec.id || spec.name;\n\t\t\tif (!id) {\n\t\t\t\tconsole.warn(\"[editorThemes] register(spec) requires a valid `id`.\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst extensionSource =\n\t\t\t\tspec.getExtension || spec.extensions || spec.extension || spec.theme;\n\t\t\tif (extensionSource === undefined || extensionSource === null) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[editorThemes] register('${id}') requires extensions via getExtension/extensions/extension/theme.`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tcaption: spec.caption || spec.label || id,\n\t\t\t\tisDark: spec.isDark ?? spec.dark ?? false,\n\t\t\t\tgetExtension:\n\t\t\t\t\ttypeof extensionSource === \"function\"\n\t\t\t\t\t\t? extensionSource\n\t\t\t\t\t\t: () => extensionSource,\n\t\t\t\tconfig: spec.config ?? null,\n\t\t\t};\n\t\t};\n\n\t\tconst createHighlightStyle = (spec) => {\n\t\t\tif (!spec) return null;\n\t\t\tif (Array.isArray(spec)) return cmLanguage.HighlightStyle.define(spec);\n\t\t\treturn spec;\n\t\t};\n\n\t\tconst createTheme = ({\n\t\t\tstyles,\n\t\t\tdark = false,\n\t\t\thighlightStyle,\n\t\t\textensions = [],\n\t\t} = {}) => {\n\t\t\tconst ext = [];\n\n\t\t\tif (styles && typeof styles === \"object\") {\n\t\t\t\text.push(cmView.EditorView.theme(styles, { dark: !!dark }));\n\t\t\t}\n\n\t\t\tconst resolvedHighlight = createHighlightStyle(highlightStyle);\n\t\t\tif (resolvedHighlight) {\n\t\t\t\text.push(cmLanguage.syntaxHighlighting(resolvedHighlight));\n\t\t\t}\n\n\t\t\tif (Array.isArray(extensions)) {\n\t\t\t\text.push(...extensions);\n\t\t\t} else if (extensions) {\n\t\t\t\text.push(extensions);\n\t\t\t}\n\n\t\t\treturn ext;\n\t\t};\n\n\t\tconst editorThemesModule = {\n\t\t\t/**\n\t\t\t * Register a CodeMirror theme from plugin code.\n\t\t\t * @param {{\n\t\t\t *   id: string,\n\t\t\t *   caption?: string,\n\t\t\t *   dark?: boolean,\n\t\t\t *   getExtension?: Function,\n\t\t\t *   extensions?: unknown,\n\t\t\t *   config?: object\n\t\t\t * }} spec\n\t\t\t * `isDark`, `extension`, and `theme` are accepted aliases for compatibility.\n\t\t\t * @returns {boolean}\n\t\t\t */\n\t\t\tregister: (spec) => {\n\t\t\t\tconst resolved = normalizeThemeSpec(spec);\n\t\t\t\tif (!resolved) return false;\n\t\t\t\treturn cmThemeRegistry.addTheme(\n\t\t\t\t\tresolved.id,\n\t\t\t\t\tresolved.caption,\n\t\t\t\t\tresolved.isDark,\n\t\t\t\t\tresolved.getExtension,\n\t\t\t\t\tresolved.config,\n\t\t\t\t);\n\t\t\t},\n\t\t\tunregister: (id) => cmThemeRegistry.removeTheme(id),\n\t\t\tlist: () => cmThemeRegistry.getThemes(),\n\t\t\tapply: (id) => editorManager?.editor?.setTheme?.(id),\n\t\t\tget: (id) => cmThemeRegistry.getThemeById(id),\n\t\t\tgetConfig: (id) => cmThemeRegistry.getThemeConfig(id),\n\t\t\tcreateTheme,\n\t\t\tcreateHighlightStyle,\n\t\t\tcm: {\n\t\t\t\tEditorView: cmView.EditorView,\n\t\t\t\tHighlightStyle: cmLanguage.HighlightStyle,\n\t\t\t\tsyntaxHighlighting: cmLanguage.syntaxHighlighting,\n\t\t\t\ttags: lezerHighlight.tags,\n\t\t\t},\n\t\t};\n\n\t\tconst sidebarAppsModule = {\n\t\t\tadd: sidebarApps.add,\n\t\t\tget: sidebarApps.get,\n\t\t\tremove: sidebarApps.remove,\n\t\t};\n\n\t\tconst lspModule = {\n\t\t\t...lspApi,\n\t\t\tclientManager: {\n\t\t\t\tsetOptions: (options) => lspClientManager.setOptions(options),\n\t\t\t\tgetActiveClients: () => lspClientManager.getActiveClients(),\n\t\t\t},\n\t\t};\n\n\t\tconst getModeByName = (name) => {\n\t\t\tconst normalized = String(name || \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\t\tif (!normalized) return null;\n\t\t\treturn getModesByName()[normalized] || null;\n\t\t};\n\n\t\tconst listModes = () => [...getModes()];\n\t\tconst listModesByName = () => ({ ...getModesByName() });\n\n\t\tconst aceModes = {\n\t\t\taddMode,\n\t\t\tremoveMode,\n\t\t\tgetModeForPath: (path) => getModeForPath(String(path || \"\")),\n\t\t\tgetModes: () => listModes(),\n\t\t\tgetModesByName: () => listModesByName(),\n\t\t\tgetMode: (name) => getModeByName(name),\n\t\t};\n\n\t\t// Preferred CodeMirror language registration API for plugins\n\t\tconst editorLanguages = {\n\t\t\t// name: string, extensions: string|Array<string>, caption?: string,\n\t\t\t// loader?: () => Extension | Promise<Extension>\n\t\t\tregister: (name, extensions, caption, loader) =>\n\t\t\t\taddMode(name, extensions, caption, loader),\n\t\t\tunregister: (name) => removeMode(name),\n\t\t\tadd: (name, extensions, caption, loader) =>\n\t\t\t\taddMode(name, extensions, caption, loader),\n\t\t\tremove: (name) => removeMode(name),\n\t\t\tlist: () => listModes(),\n\t\t\tlistByName: () => listModesByName(),\n\t\t\tget: (name) => getModeByName(name),\n\t\t\tgetForPath: (path) => getModeForPath(String(path || \"\")),\n\t\t};\n\n\t\tconst intent = {\n\t\t\taddHandler: addIntentHandler,\n\t\t\tremoveHandler: removeIntentHandler,\n\t\t};\n\n\t\tconst terminalTouchSelectionMoreOptions = {\n\t\t\tadd: (option) => TerminalManager.addTouchSelectionMoreOption(option),\n\t\t\tremove: (id) => TerminalManager.removeTouchSelectionMoreOption(id),\n\t\t\tlist: () => TerminalManager.getTouchSelectionMoreOptions(),\n\t\t};\n\n\t\tconst terminalModule = {\n\t\t\tcreate: (options) => TerminalManager.createTerminal(options),\n\t\t\tcreateLocal: (options) => TerminalManager.createLocalTerminal(options),\n\t\t\tcreateServer: (options) => TerminalManager.createServerTerminal(options),\n\t\t\tget: (id) => TerminalManager.getTerminal(id),\n\t\t\tgetAll: () => TerminalManager.getAllTerminals(),\n\t\t\twrite: (id, data) => this.#secureTerminalWrite(id, data),\n\t\t\tclear: (id) => TerminalManager.clearTerminal(id),\n\t\t\tclose: (id) => TerminalManager.closeTerminal(id),\n\t\t\tmoreOptions: terminalTouchSelectionMoreOptions,\n\t\t\ttouchSelection: {\n\t\t\t\tmoreOptions: terminalTouchSelectionMoreOptions,\n\t\t\t},\n\t\t\tthemes: {\n\t\t\t\tregister: (name, theme, pluginId) =>\n\t\t\t\t\tTerminalThemeManager.registerTheme(name, theme, pluginId),\n\t\t\t\tunregister: (name, pluginId) =>\n\t\t\t\t\tTerminalThemeManager.unregisterTheme(name, pluginId),\n\t\t\t\tget: (name) => TerminalThemeManager.getTheme(name),\n\t\t\t\tgetAll: () => TerminalThemeManager.getAllThemes(),\n\t\t\t\tgetNames: () => TerminalThemeManager.getThemeNames(),\n\t\t\t\tcreateVariant: (baseName, overrides) =>\n\t\t\t\t\tTerminalThemeManager.createVariant(baseName, overrides),\n\t\t\t},\n\t\t};\n\n\t\tconst codemirrorModule = Object.freeze({\n\t\t\tautocomplete: cmAutocomplete,\n\t\t\tcommands: cmCommands,\n\t\t\tlanguage: cmLanguage,\n\t\t\tlezer: lezerHighlight,\n\t\t\tlint: cmLint,\n\t\t\tsearch: cmSearch,\n\t\t\tstate: cmState,\n\t\t\tview: cmView,\n\t\t});\n\n\t\tthis.define(\"Url\", Url);\n\t\tthis.define(\"page\", Page);\n\t\tthis.define(\"Color\", Color);\n\t\tthis.define(\"fonts\", fonts);\n\t\tthis.define(\"toast\", toast);\n\t\tthis.define(\"alert\", alert);\n\t\tthis.define(\"select\", select);\n\t\tthis.define(\"loader\", loader);\n\t\tthis.define(\"dialogBox\", box);\n\t\tthis.define(\"prompt\", prompt);\n\t\tthis.define(\"intent\", intent);\n\t\tthis.define(\"fileList\", files);\n\t\tthis.define(\"fs\", fsOperation);\n\t\tthis.define(\"confirm\", confirm);\n\t\tthis.define(\"helpers\", helpers);\n\t\tthis.define(\"palette\", palette);\n\t\tthis.define(\"projects\", projects);\n\t\tthis.define(\"tutorial\", tutorial);\n\t\tthis.define(\"aceModes\", aceModes);\n\t\tthis.define(\"themes\", themesModule);\n\t\tthis.define(\"editorLanguages\", editorLanguages);\n\t\tthis.define(\"editorThemes\", editorThemesModule);\n\t\tthis.define(\"lsp\", lspModule);\n\t\tthis.define(\"settings\", appSettings);\n\t\tthis.define(\"sideButton\", SideButton);\n\t\tthis.define(\"EditorFile\", EditorFile);\n\t\tthis.define(\"inputhints\", inputhints);\n\t\tthis.define(\"openfolder\", openFolder);\n\t\tthis.define(\"colorPicker\", colorPicker);\n\t\tthis.define(\"actionStack\", actionStack);\n\t\tthis.define(\"multiPrompt\", multiPrompt);\n\t\tthis.define(\"addedfolder\", addedFolder);\n\t\tthis.define(\"contextMenu\", Contextmenu);\n\t\tthis.define(\"fileBrowser\", FileBrowser);\n\t\tthis.define(\"fsOperation\", fsOperation);\n\t\tthis.define(\"keyboard\", keyboardHandler);\n\t\tthis.define(\"windowResize\", windowResize);\n\t\tthis.define(\"encodings\", encodingsModule);\n\t\tthis.define(\"themeBuilder\", ThemeBuilder);\n\t\tthis.define(\"selectionMenu\", selectionMenu);\n\t\tthis.define(\"sidebarApps\", sidebarAppsModule);\n\t\tthis.define(\"terminal\", terminalModule);\n\t\tthis.define(\"codemirror\", codemirrorModule);\n\t\tthis.define(\"@codemirror/autocomplete\", cmAutocomplete);\n\t\tthis.define(\"@codemirror/commands\", cmCommands);\n\t\tthis.define(\"@codemirror/language\", cmLanguage);\n\t\tthis.define(\"@codemirror/lint\", cmLint);\n\t\tthis.define(\"@codemirror/search\", cmSearch);\n\t\tthis.define(\"@codemirror/state\", cmState);\n\t\tthis.define(\"@codemirror/view\", cmView);\n\t\tthis.define(\"@lezer/highlight\", lezerHighlight);\n\t\tthis.define(\"createKeyboardEvent\", KeyboardEvent);\n\t\tthis.define(\"toInternalUrl\", helpers.toInternalUri);\n\t\tthis.define(\"commands\", this.#createCommandApi());\n\n\t\tregisterLspFormatter(this);\n\t}\n\n\t/**\n\t * Secure terminal write with command validation\n\t * Prevents execution of malicious or dangerous commands through plugin API\n\t * @param {string} id - Terminal ID\n\t * @param {string} data - Data to write\n\t */\n\t#secureTerminalWrite(id, data) {\n\t\tif (typeof data !== \"string\") {\n\t\t\tconsole.warn(\"Terminal write data must be a string\");\n\t\t\treturn;\n\t\t}\n\n\t\t// List of potentially dangerous commands/patterns to block\n\t\tconst dangerousPatterns = [\n\t\t\t// System commands that can cause damage\n\t\t\t/^\\s*rm\\s+-rf?\\s+\\/[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*rm\\s+-rf?\\s+\\*[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*rm\\s+-rf?\\s+~[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*mkfs\\.[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*dd\\s+if=\\/[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*:(){ :|:& };:[^\\r\\n]*[\\r\\n]?$/m, // Fork bomb\n\t\t\t/^\\s*sudo\\s+dd\\s+if=\\/[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*sudo\\s+rm\\s+-rf?\\s+\\/[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*curl\\s+[^\\r\\n]*\\|\\s*sh[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*wget\\s+[^\\r\\n]*\\|\\s*sh[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*bash\\s+<\\s*\\([^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*sh\\s+<\\s*\\([^\\r\\n]*[\\r\\n]?$/m,\n\n\t\t\t// Network-based attacks\n\t\t\t/^\\s*nc\\s+-l\\s+-p\\s+\\d+[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*ncat\\s+-l\\s+-p\\s+\\d+[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*python\\s+.*SimpleHTTPServer[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*python\\s+.*http\\.server[^\\r\\n]*[\\r\\n]?$/m,\n\n\t\t\t// Process manipulation\n\t\t\t/^\\s*kill\\s+-9\\s+1\\s*[\\r\\n]?$/m,\n\t\t\t/^\\s*killall\\s+-9\\s+\\*[^\\r\\n]*[\\r\\n]?$/m,\n\n\t\t\t// File system manipulation\n\t\t\t/^\\s*chmod\\s+777\\s+\\/[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*chown\\s+[^\\s]+\\s+\\/[^\\r\\n]*[\\r\\n]?$/m,\n\n\t\t\t// Sensitive file access attempts\n\t\t\t/^\\s*cat\\s+\\/etc\\/passwd[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*cat\\s+\\/etc\\/shadow[^\\r\\n]*[\\r\\n]?$/m,\n\t\t\t/^\\s*cat\\s+\\/root\\/[^\\r\\n]*[\\r\\n]?$/m,\n\n\t\t\t// Only block null bytes\n\t\t\t/\\x00/g,\n\t\t];\n\n\t\t// Check for dangerous patterns\n\t\tfor (const pattern of dangerousPatterns) {\n\t\t\tif (pattern.test(data)) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Blocked potentially dangerous terminal command: ${data.substring(0, 50)}...`,\n\t\t\t\t);\n\t\t\t\ttoast(\"Potentially dangerous command blocked for security\", 3000);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Additional checks for suspicious character sequences\n\t\tif (data.includes(\"$(\") && data.includes(\")\")) {\n\t\t\tconst commandSubstitution = /\\$\\([^)]*\\)/g;\n\t\t\tconst matches = data.match(commandSubstitution);\n\t\t\tif (matches) {\n\t\t\t\tfor (const match of matches) {\n\t\t\t\t\t// Check if command substitution contains dangerous commands\n\t\t\t\t\tfor (const pattern of dangerousPatterns) {\n\t\t\t\t\t\tif (pattern.test(match)) {\n\t\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t\t`Blocked command substitution with dangerous content: ${match}`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\ttoast(\"Command substitution blocked for security\", 3000);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Sanitize data length to prevent memory exhaustion\n\t\tconst maxLength = 64 * 1024; // 64KB max per write\n\t\tif (data.length > maxLength) {\n\t\t\tconsole.warn(\n\t\t\t\t`Terminal write data truncated - exceeded ${maxLength} characters`,\n\t\t\t);\n\t\t\tdata = data.substring(0, maxLength) + \"\\n[Data truncated for security]\\n\";\n\t\t}\n\n\t\t// If all security checks pass, proceed with writing\n\t\treturn TerminalManager.writeToTerminal(id, data);\n\t}\n\n\t/**\n\t * Define a module\n\t * @param {string} name\n\t * @param {Object|function} module\n\t */\n\tdefine(name, module) {\n\t\tthis.#modules[name.toLowerCase()] = module;\n\t}\n\n\trequire(module) {\n\t\treturn this.#modules[module.toLowerCase()];\n\t}\n\n\texec(key, val) {\n\t\tif (key in commands) {\n\t\t\treturn commands[key](val);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Installs an Acode plugin from registry\n\t * @param {string} pluginId id of the plugin to install\n\t * @param {string} installerPluginName Name of plugin attempting to install\n\t * @returns {Promise<void>}\n\t */\n\tinstallPlugin(pluginId, installerPluginName) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tfsOperation(Url.join(PLUGIN_DIR, pluginId))\n\t\t\t\t.exists()\n\t\t\t\t.then((isPluginExists) => {\n\t\t\t\t\tif (isPluginExists) {\n\t\t\t\t\t\treject(new Error(\"Plugin already installed\"));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconfirm(\n\t\t\t\t\t\tstrings.install,\n\t\t\t\t\t\t`Do you want to install plugin '${pluginId}'${installerPluginName ? ` requested by ${installerPluginName}` : \"\"}?`,\n\t\t\t\t\t).then((confirmation) => {\n\t\t\t\t\t\tif (!confirmation) {\n\t\t\t\t\t\t\treject(new Error(\"User cancelled installation\"));\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet purchaseToken;\n\t\t\t\t\t\tlet product;\n\t\t\t\t\t\tconst pluginUrl = Url.join(\n\t\t\t\t\t\t\tconstants.API_BASE,\n\t\t\t\t\t\t\t`plugin/${pluginId}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tfsOperation(pluginUrl)\n\t\t\t\t\t\t\t.readFile(\"json\")\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\treject(new Error(\"Failed to fetch plugin details\"));\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.then((remotePlugin) => {\n\t\t\t\t\t\t\t\tif (remotePlugin) {\n\t\t\t\t\t\t\t\t\tconst isPaid = remotePlugin.price > 0;\n\t\t\t\t\t\t\t\t\thelpers\n\t\t\t\t\t\t\t\t\t\t.promisify(iap.getProducts, [remotePlugin.sku])\n\t\t\t\t\t\t\t\t\t\t.then((products) => {\n\t\t\t\t\t\t\t\t\t\t\t[product] = products;\n\t\t\t\t\t\t\t\t\t\t\tif (product) {\n\t\t\t\t\t\t\t\t\t\t\t\treturn getPurchase(product.productId);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t.then((purchase) => {\n\t\t\t\t\t\t\t\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\n\t\t\t\t\t\t\t\t\t\t\tif (isPaid && !purchaseToken) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (!product) throw new Error(\"Product not found\");\n\t\t\t\t\t\t\t\t\t\t\t\treturn helpers.checkAPIStatus().then((apiStatus) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (!apiStatus) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talert(strings.error, strings.api_error);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tiap.setPurchaseUpdatedListener(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t...purchaseListener(onpurchase, onerror),\n\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn helpers.promisify(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tiap.purchase,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tproduct.productId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\t\t\t\t\timport(\"lib/installPlugin\").then(\n\t\t\t\t\t\t\t\t\t\t\t\t({ default: installPlugin }) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tinstallPlugin(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpluginId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tremotePlugin.name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpurchaseToken,\n\t\t\t\t\t\t\t\t\t\t\t\t\t).then(() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\t\tasync function onpurchase(e) {\n\t\t\t\t\t\t\t\t\t\tconst purchase = await getPurchase(product.productId);\n\t\t\t\t\t\t\t\t\t\tawait ajax.post(\n\t\t\t\t\t\t\t\t\t\t\tUrl.join(constants.API_BASE, \"plugin/order\"),\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tid: remotePlugin.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttoken: purchase?.purchaseToken,\n\t\t\t\t\t\t\t\t\t\t\t\t\tpackage: BuildInfo.packageName,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tasync function onerror(error) {\n\t\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\tasync function getPurchase(sku) {\n\t\t\t\t\t\t\tconst purchases = await helpers.promisify(iap.getPurchases);\n\t\t\t\t\t\t\tconst purchase = purchases.find((p) =>\n\t\t\t\t\t\t\t\tp.productIds.includes(sku),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn purchase;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t});\n\t}\n\n\t[onPluginLoadCallback](pluginId) {\n\t\tif (this.#pluginWatchers[pluginId]) {\n\t\t\tthis.#pluginWatchers[pluginId].resolve();\n\t\t\tdelete this.#pluginWatchers[pluginId];\n\t\t}\n\t}\n\n\t[onPluginsLoadCompleteCallback]() {\n\t\tfor (const pluginId in this.#pluginWatchers) {\n\t\t\tthis.#pluginWatchers[pluginId].reject(\n\t\t\t\tnew Error(`Plugin '${pluginId}' failed to load.`),\n\t\t\t);\n\t\t}\n\t\tthis.#pluginWatchers = {};\n\t}\n\n\twaitForPlugin(pluginId) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tif (LOADED_PLUGINS.has(pluginId)) {\n\t\t\t\treturn resolve(true);\n\t\t\t}\n\n\t\t\tthis.#pluginWatchers[pluginId] = {\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t};\n\t\t});\n\t}\n\n\tget exitAppMessage() {\n\t\tconst numFiles = editorManager.hasUnsavedFiles();\n\t\tif (numFiles) {\n\t\t\treturn strings[\"unsaved files close app\"];\n\t\t}\n\t\treturn null;\n\t}\n\n\tsetLoadingMessage(message) {\n\t\tdocument.body.setAttribute(\"data-small-msg\", message);\n\t}\n\n\t/**\n\t * Sets plugin init function\n\t * @param {string} id\n\t * @param {() => void} initFunction\n\t * @param {{list: import('components/settingsPage').ListItem[], cb: (key: string, value: string)=>void}} settings\n\t */\n\tsetPluginInit(id, initFunction, settings) {\n\t\tthis.#pluginsInit[id] = initFunction;\n\n\t\tif (!settings) return;\n\t\tappSettings.uiSettings[`plugin-${id}`] = settingsPage(\n\t\t\tid,\n\t\t\tsettings.list,\n\t\t\tsettings.cb,\n\t\t\tundefined,\n\t\t\t{\n\t\t\t\tpreserveOrder: true,\n\t\t\t\tpageClassName: \"detail-settings-page\",\n\t\t\t\tlistClassName: \"detail-settings-list\",\n\t\t\t\tvalueInTail: true,\n\t\t\t\tgroupByDefault: true,\n\t\t\t},\n\t\t);\n\t}\n\n\tsetPluginUnmount(id, unmountFunction) {\n\t\tthis.#pluginUnmount[id] = unmountFunction;\n\t}\n\n\t/**\n\t *\n\t * @param {string} id plugin id\n\t * @param {string} baseUrl local plugin url\n\t * @param {HTMLElement} $page\n\t */\n\tasync initPlugin(id, baseUrl, $page, options) {\n\t\tif (id in this.#pluginsInit) {\n\t\t\tawait this.#pluginsInit[id](baseUrl, $page, options);\n\t\t}\n\t}\n\n\tunmountPlugin(id) {\n\t\tif (id in this.#pluginUnmount) {\n\t\t\tthis.#pluginUnmount[id]();\n\t\t\tfsOperation(Url.join(CACHE_STORAGE, id)).delete();\n\t\t}\n\n\t\tdelete appSettings.uiSettings[`plugin-${id}`];\n\t}\n\n\tregisterFormatter(id, extensions, format, displayName) {\n\t\tlet exts;\n\t\tif (Array.isArray(extensions)) {\n\t\t\texts = extensions.filter(Boolean);\n\t\t\tif (!exts.length) exts = [\"*\"];\n\t\t} else if (typeof extensions === \"string\" && extensions) {\n\t\t\texts = [extensions];\n\t\t} else {\n\t\t\texts = [\"*\"];\n\t\t}\n\t\tthis.#formatter.unshift({\n\t\t\tid,\n\t\t\tname: displayName,\n\t\t\texts: exts,\n\t\t\tformat,\n\t\t});\n\t}\n\n\tunregisterFormatter(id) {\n\t\tthis.#formatter = this.#formatter.filter(\n\t\t\t(formatter) => formatter.id !== id,\n\t\t);\n\t\tconst { formatter } = appSettings.value;\n\t\tfor (const mode of Object.keys(formatter)) {\n\t\t\tif (formatter[mode] === id) {\n\t\t\t\tdelete formatter[mode];\n\t\t\t}\n\t\t}\n\t\tappSettings.update(false);\n\t}\n\n\tasync format(selectIfNull = true) {\n\t\tconst file = editorManager.activeFile;\n\t\tif (!file || file.type !== \"editor\") return false;\n\n\t\tlet resolvedMode = file.currentMode;\n\t\tif (!resolvedMode) {\n\t\t\ttry {\n\t\t\t\tresolvedMode = getModeForPath(file.filename)?.name;\n\t\t\t} catch (_) {\n\t\t\t\tresolvedMode = null;\n\t\t\t}\n\t\t}\n\t\tconst modeName = resolvedMode || \"text\";\n\t\tconst formatterMap = appSettings.value.formatter || {};\n\t\tconst formatterId = formatterMap[modeName];\n\t\tconst formatter = this.#formatter.find(({ id }) => id === formatterId);\n\n\t\tif (!formatter) {\n\t\t\tif (formatterId) {\n\t\t\t\tdelete formatterMap[modeName];\n\t\t\t\tawait appSettings.update(false);\n\t\t\t}\n\n\t\t\tif (selectIfNull) {\n\t\t\t\tformatterSettings(modeName);\n\t\t\t\tthis.#afterSelectFormatter(modeName);\n\t\t\t} else {\n\t\t\t\ttoast(strings[\"please select a formatter\"]);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tawait formatter.format();\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t#afterSelectFormatter(name) {\n\t\tappSettings.on(\"update:formatter\", format);\n\n\t\tfunction format() {\n\t\t\tappSettings.off(\"update:formatter\", format);\n\t\t\tconst id = appSettings.value.formatter[name];\n\t\t\tconst formatter = this.#formatter.find(({ id: _id }) => _id === id);\n\t\t\tformatter?.format();\n\t\t}\n\t}\n\n\tfsOperation(file) {\n\t\treturn fsOperation(file);\n\t}\n\n\tnewEditorFile(filename, options) {\n\t\tnew EditorFile(filename, options);\n\t}\n\n\tget formatters() {\n\t\treturn this.#formatter.map(({ id, name, exts }) => ({\n\t\t\tid,\n\t\t\tname: name || id,\n\t\t\texts,\n\t\t}));\n\t}\n\n\t/**\n\t *\n\t * @param {string[]} extensions\n\t * @returns {Array<[id: String, name: String]>} options\n\t */\n\tgetFormatterFor(extensions) {\n\t\tconst options = [[null, strings.none]];\n\t\tfor (const { id, name, exts } of this.formatters) {\n\t\t\tconst supports = exts.some((ext) => extensions.includes(ext));\n\t\t\tif (supports || exts.includes(\"*\")) {\n\t\t\t\toptions.push([id, name]);\n\t\t\t}\n\t\t}\n\t\treturn options;\n\t}\n\n\talert(title, message, onhide) {\n\t\talert(title, message, onhide);\n\t}\n\n\tloader(title, message, cancel) {\n\t\treturn loader.create(title, message, cancel);\n\t}\n\n\tjoinUrl(...args) {\n\t\treturn Url.join(...args);\n\t}\n\n\t/**\n\t * Adds a custom icon class that can be used with the .icon element\n\t * @param {string} className - The class name for the icon (used as .icon.className)\n\t * @param {string} src - URL or data URI of the icon image\n\t * @param {object} [options] - Optional settings\n\t * @param {boolean} [options.monochrome=false] - If true, icon will use currentColor and adapt to theme\n\t */\n\taddIcon(className, src, options = {}) {\n\t\tlet style = document.head.get(`style[icon=\"${className}\"]`);\n\t\tif (!style) {\n\t\t\tlet css;\n\t\t\tif (options.monochrome) {\n\t\t\t\t// Monochrome icons: use mask-image (on ::before) for currentColor/theme support\n\t\t\t\t// Using ::before ensures we don't mask the ::after active indicator or the background\n\t\t\t\tcss = `.icon.${className}::before {\n\t\t\t\t\tcontent: '';\n\t\t\t\t\tdisplay: inline-block;\n\t\t\t\t\twidth: 24px;\n\t\t\t\t\theight: 24px;\n\t\t\t\t\tvertical-align: middle;\n\t\t\t\t\t-webkit-mask: url(${src}) no-repeat center / contain;\n\t\t\t\t\tmask: url(${src}) no-repeat center / contain;\n\t\t\t\t\tbackground-color: currentColor;\n\t\t\t\t}`;\n\t\t\t} else {\n\t\t\t\t// Default: preserve original icon colors\n\t\t\t\tcss = `.icon.${className}{\n\t\t\t\t\tbackground: url(${src}) no-repeat center / 24px;\n\t\t\t\t}`;\n\t\t\t}\n\t\t\tstyle = <style icon={className}>{css}</style>;\n\t\t\tdocument.head.appendChild(style);\n\t\t}\n\t}\n\n\tasync prompt(message, defaultValue, type, options) {\n\t\tconst response = await prompt(message, defaultValue, type, options);\n\t\treturn response;\n\t}\n\n\tasync confirm(title, message) {\n\t\tconst confirmation = await confirm(title, message);\n\t\treturn confirmation;\n\t}\n\n\tasync select(title, options, config) {\n\t\tconst response = await select(title, options, config);\n\t\treturn response;\n\t}\n\n\tasync multiPrompt(title, inputs, help) {\n\t\tconst values = await multiPrompt(title, inputs, help);\n\t\treturn values;\n\t}\n\n\tasync fileBrowser(mode, info, openLast) {\n\t\tconst res = await FileBrowser(mode, info, openLast);\n\t\treturn res;\n\t}\n\n\tasync toInternalUrl(url) {\n\t\tconst internalUrl = await helpers.toInternalUri(url);\n\t\treturn internalUrl;\n\t}\n\t/**\n\t * Push a notification\n\t * @param {string} title Title of the notification\n\t * @param {string} message Message body of the notification\n\t * @param {Object} options Notification options\n\t * @param {string} [options.icon] Icon for the notification, can be a URL or a base64 encoded image or icon class or svg string\n\t * @param {boolean} [options.autoClose=true] Whether notification should auto close\n\t * @param {Function} [options.action=null] Action callback when notification is clicked\n\t * @param {('info'|'warning'|'error'|'success')} [options.type='info'] Type of notification\n\t */\n\tpushNotification(\n\t\ttitle,\n\t\tmessage,\n\t\t{ icon, autoClose = true, action = null, type = \"info\" } = {},\n\t) {\n\t\tconst nm = new NotificationManager();\n\t\tnm.pushNotification({\n\t\t\ttitle,\n\t\t\tmessage,\n\t\t\ticon,\n\t\t\tautoClose,\n\t\t\taction,\n\t\t\ttype,\n\t\t});\n\t}\n\n\t/**\n\t * Register a custom file type handler\n\t * @param {string} id Unique identifier for the handler\n\t * @param {Object} options Handler configuration\n\t * @param {string[]} options.extensions File extensions to handle (without dots)\n\t * @param {function} options.handleFile Function that handles the file opening\n\t */\n\tregisterFileHandler(id, options) {\n\t\tfileTypeHandler.registerFileHandler(id, options);\n\t}\n\n\t/**\n\t * Unregister a file type handler\n\t * @param {string} id The handler id to remove\n\t */\n\tunregisterFileHandler(id) {\n\t\tfileTypeHandler.unregisterFileHandler(id);\n\t}\n\n\taddCommand(descriptor) {\n\t\tconst command = registerExternalCommand(descriptor);\n\t\tthis.#refreshCommandBindings();\n\t\treturn command;\n\t}\n\n\tremoveCommand(name) {\n\t\tif (!name) return;\n\t\tremoveExternalCommand(name);\n\t\tthis.#refreshCommandBindings();\n\t}\n\n\texecCommand(name, view, args) {\n\t\tif (!name) return false;\n\t\tconst targetView = view || window.editorManager?.editor;\n\t\treturn runCommand(name, targetView, args);\n\t}\n\n\tlistCommands() {\n\t\treturn listRegisteredCommands();\n\t}\n\n\t#refreshCommandBindings() {\n\t\tconst view = window.editorManager?.editor;\n\t\tif (view) refreshCommandKeymap(view);\n\t}\n\n\t#createCommandApi() {\n\t\tconst commandRegistry = {\n\t\t\tadd: this.addCommand,\n\t\t\texecute: this.execCommand,\n\t\t\tremove: this.removeCommand,\n\t\t\tlist: this.listCommands,\n\t\t};\n\n\t\tconst addCommand = (descriptor) => {\n\t\t\ttry {\n\t\t\t\treturn this.addCommand(descriptor);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to add command\", descriptor?.name);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t};\n\n\t\tconst removeCommand = (name) => {\n\t\t\tif (!name) return;\n\t\t\tthis.removeCommand(name);\n\t\t};\n\n\t\treturn {\n\t\t\taddCommand,\n\t\t\tremoveCommand,\n\t\t\tget registry() {\n\t\t\t\treturn commandRegistry;\n\t\t\t},\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "src/lib/actionStack.js",
    "content": "import confirm from \"dialogs/confirm\";\nimport appSettings from \"lib/settings\";\nimport helpers from \"utils/helpers\";\n\nconst stack = [];\nlet mark = null;\nlet onCloseAppCallback;\nlet freeze = false;\n\nexport default {\n\t/**\n\t * Length of stack\n\t * @returns {number}\n\t */\n\tget length() {\n\t\treturn stack.length;\n\t},\n\t/**\n\t * Function to be called when app is closed\n\t * @returns {Function}\n\t */\n\tget onCloseApp() {\n\t\treturn onCloseAppCallback;\n\t},\n\t/**\n\t * Function to be called when app is closed\n\t * @param {Function} cb\n\t */\n\tset onCloseApp(cb) {\n\t\tonCloseAppCallback = cb;\n\t},\n\t/**\n\t * Copy of actionStack for window\n\t * @deprecated\n\t */\n\twindowCopy() {\n\t\tconst copyStack = { ...this };\n\t\tdelete copyStack.windowCopy;\n\t\tcopyStack.pop = (repeat) => {\n\t\t\twindow.log(\n\t\t\t\t\"error\",\n\t\t\t\t\"Deprecated: `window.actionStack` is deprecated, import `actionStack` instead\",\n\t\t\t);\n\t\t\tthis.pop(repeat);\n\t\t};\n\t\treturn copyStack;\n\t},\n\t/**\n\t * Push action to stack\n\t * @param {object} fun\n\t * @param {string} fun.id\n\t * @param {Function} fun.action\n\t */\n\tpush(fun) {\n\t\tstack.push(fun);\n\t},\n\t/**\n\t * Pop action from stack\n\t * @param {number} repeat pop action multiple times\n\t * @returns\n\t */\n\tasync pop(repeat) {\n\t\tif (freeze) return;\n\t\tlet confirmation = true;\n\n\t\tif (typeof repeat === \"number\" && repeat > 1) {\n\t\t\tfor (let i = 0; i < repeat; ++i) {\n\t\t\t\tthis.pop();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst fun = stack.pop();\n\n\t\tif (fun) {\n\t\t\tfun.action();\n\t\t\treturn;\n\t\t}\n\n\t\tif (appSettings.value.confirmOnExit) {\n\t\t\tlet closeMessage =\n\t\t\t\tacode.exitAppMessage || strings[\"close app\"].capitalize(0);\n\t\t\tconfirmation = await confirm(strings.warning.toUpperCase(), closeMessage);\n\t\t}\n\n\t\tif (confirmation) {\n\t\t\tconst { exitApp } = navigator.app;\n\n\t\t\tif (typeof onCloseAppCallback === \"function\") {\n\t\t\t\tconst res = onCloseAppCallback();\n\t\t\t\tif (res instanceof Promise) {\n\t\t\t\t\tres.finally(exitApp);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\thelpers.showInterstitialIfReady();\n\n\t\t\texitApp();\n\t\t}\n\t},\n\tget(id) {\n\t\treturn stack.find((act) => act.id === id);\n\t},\n\t/**\n\t * Remove action with given id from stack\n\t * @param {String} id\n\t * @returns {Boolean}\n\t */\n\tremove(id) {\n\t\tfor (let i = 0; i < stack.length; ++i) {\n\t\t\tlet action = stack[i];\n\t\t\tif (action.id === id) {\n\t\t\t\tstack.splice(i, 1);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\t/**\n\t * Check if action with given id exists in stack\n\t * @param {String} id\n\t * @returns {Boolean}\n\t */\n\thas(id) {\n\t\tfor (let act of stack) if (act.id === id) return true;\n\t\treturn false;\n\t},\n\t/**\n\t * Sets a mark to recently pushed action\n\t */\n\tsetMark() {\n\t\tmark = stack.length;\n\t},\n\t/**\n\t * Remove all actions that are pushed after marked positions (using `setMark()`)\n\t */\n\tclearFromMark() {\n\t\tif (mark === null) return;\n\t\tstack.splice(mark);\n\t\tmark = null;\n\t},\n\tfreeze() {\n\t\tfreeze = true;\n\t},\n\tunfreeze() {\n\t\tfreeze = false;\n\t},\n};\n"
  },
  {
    "path": "src/lib/adRewards.js",
    "content": "import toast from \"components/toast\";\nimport auth from \"./auth\";\nimport secureAdRewardState from \"./secureAdRewardState\";\n\nconst ONE_HOUR = 60 * 60 * 1000;\nconst MAX_TIMEOUT = 2_147_483_647;\nconst REWARDED_RESULT_TIMEOUT_MS = 90 * 1000;\n\nconst OFFERS = [\n\t{\n\t\tid: \"quick\",\n\t\ttitle: \"Quick pass\",\n\t\tdescription: \"Watch 1 rewarded ad and pause ads for 1 hour.\",\n\t\tadsRequired: 1,\n\t\tdurationMs: ONE_HOUR,\n\t\taccentClass: \"is-quick\",\n\t},\n\t{\n\t\tid: \"focus\",\n\t\ttitle: \"Focus block\",\n\t\tdescription:\n\t\t\t\"Watch 2 rewarded ads and pause ads for a random 4, 5, or 6 hours.\",\n\t\tadsRequired: 2,\n\t\tminDurationMs: 4 * ONE_HOUR,\n\t\tmaxDurationMs: 6 * ONE_HOUR,\n\t\taccentClass: \"is-focus\",\n\t},\n];\n\nlet state = getDefaultState();\nlet expiryTimer = null;\nlet activeWatchPromise = null;\nconst listeners = new Set();\n\nfunction getDefaultState() {\n\treturn {\n\t\tadFreeUntil: 0,\n\t\tlastExpiredRewardUntil: 0,\n\t\tisActive: false,\n\t\tremainingMs: 0,\n\t\tredemptionsToday: 0,\n\t\tremainingRedemptions: 3,\n\t\tmaxRedemptionsPerDay: 3,\n\t\tmaxActivePassMs: 10 * ONE_HOUR,\n\t\thasPendingExpiryNotice: false,\n\t\texpiryNoticePendingUntil: 0,\n\t\tcanRedeem: true,\n\t\tredeemDisabledReason: \"\",\n\t};\n}\n\nfunction formatDuration(durationMs) {\n\tconst totalHours = Math.round(durationMs / ONE_HOUR);\n\tif (totalHours < 1) return \"less than 1 hour\";\n\tif (totalHours === 1) return \"1 hour\";\n\treturn `${totalHours} hours`;\n}\n\nfunction formatDurationRange(minDurationMs, maxDurationMs) {\n\tif (!minDurationMs || !maxDurationMs || minDurationMs === maxDurationMs) {\n\t\treturn formatDuration(minDurationMs || maxDurationMs || 0);\n\t}\n\n\tconst minHours = Math.round(minDurationMs / ONE_HOUR);\n\tconst maxHours = Math.round(maxDurationMs / ONE_HOUR);\n\treturn `${minHours}-${maxHours} hours`;\n}\n\nfunction getRewardedUnitId() {\n\treturn window.adRewardedUnitId || \"\";\n}\n\nfunction getExpiryDate() {\n\treturn state.adFreeUntil ? new Date(state.adFreeUntil) : null;\n}\n\nfunction emitChange() {\n\tconst snapshot = {\n\t\t...state,\n\t\texpiryDate: getExpiryDate(),\n\t};\n\tlisteners.forEach((listener) => {\n\t\ttry {\n\t\t\tlistener(snapshot);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Reward state listener failed.\", error);\n\t\t}\n\t});\n}\n\nfunction hideActiveBanner() {\n\tif (window.ad?.active) {\n\t\twindow.ad.active = false;\n\t\twindow.ad.hide?.();\n\t}\n}\n\nfunction notify(title, message, type = \"info\") {\n\ttoast(message, 4000);\n\twindow.acode?.pushNotification?.(title, message, {\n\t\ticon: type === \"success\" ? \"verified\" : \"notifications\",\n\t\ttype,\n\t});\n}\n\nfunction normalizeStatus(status) {\n\tconst fallback = getDefaultState();\n\tif (!status || typeof status !== \"object\") return fallback;\n\n\tconst adFreeUntil = Number(status.adFreeUntil) || 0;\n\tconst remainingMs = Math.max(0, Number(status.remainingMs) || 0);\n\n\treturn {\n\t\t...fallback,\n\t\t...status,\n\t\tadFreeUntil,\n\t\tlastExpiredRewardUntil: Number(status.lastExpiredRewardUntil) || 0,\n\t\tremainingMs,\n\t\tredemptionsToday: Number(status.redemptionsToday) || 0,\n\t\tremainingRedemptions: Number(status.remainingRedemptions) || 0,\n\t\tmaxRedemptionsPerDay:\n\t\t\tNumber(status.maxRedemptionsPerDay) || fallback.maxRedemptionsPerDay,\n\t\tmaxActivePassMs: Number(status.maxActivePassMs) || fallback.maxActivePassMs,\n\t\texpiryNoticePendingUntil: Number(status.expiryNoticePendingUntil) || 0,\n\t\tisActive: Boolean(status.isActive && adFreeUntil > Date.now()),\n\t\thasPendingExpiryNotice: Boolean(status.hasPendingExpiryNotice),\n\t\tcanRedeem: Boolean(status.canRedeem),\n\t\tredeemDisabledReason: String(status.redeemDisabledReason || \"\"),\n\t};\n}\n\nfunction clearExpiryTimer() {\n\tif (expiryTimer) {\n\t\tclearTimeout(expiryTimer);\n\t\texpiryTimer = null;\n\t}\n}\n\nasync function refreshState({ notifyExpiry = false } = {}) {\n\ttry {\n\t\tconst nextState = normalizeStatus(await secureAdRewardState.getStatus());\n\t\tstate = nextState;\n\t\temitChange();\n\t\tscheduleExpiryCheck();\n\n\t\tif (notifyExpiry && nextState.hasPendingExpiryNotice) {\n\t\t\tnotify(\n\t\t\t\t\"Ad-free pass ended\",\n\t\t\t\t\"Your rewarded ad-free time has expired. You can watch another rewarded ad anytime.\",\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t}\n\n\t\treturn nextState;\n\t} catch (error) {\n\t\tconsole.warn(\"Failed to refresh rewarded ad state.\", error);\n\t\treturn state;\n\t}\n}\n\nfunction scheduleExpiryCheck() {\n\tclearExpiryTimer();\n\tif (!state.adFreeUntil) return;\n\n\tconst remainingMs = state.adFreeUntil - Date.now();\n\tif (remainingMs <= 0) {\n\t\tvoid refreshState({ notifyExpiry: true });\n\t\treturn;\n\t}\n\n\texpiryTimer = setTimeout(\n\t\t() => {\n\t\t\tvoid refreshState({ notifyExpiry: true });\n\t\t},\n\t\tMath.min(remainingMs, MAX_TIMEOUT),\n\t);\n}\n\nasync function getRewardIdentity() {\n\ttry {\n\t\tconst user = await auth.getUserInfo();\n\t\tconst userId =\n\t\t\tuser?.id ||\n\t\t\tuser?._id ||\n\t\t\tuser?.github ||\n\t\t\tuser?.username ||\n\t\t\tdevice?.uuid ||\n\t\t\t\"guest\";\n\t\treturn String(userId);\n\t} catch (error) {\n\t\tconsole.warn(\"Failed to resolve rewarded ad user identity.\", error);\n\t\treturn String(device?.uuid || \"guest\");\n\t}\n}\n\nasync function createRewardedAd(offer, step, sessionId) {\n\tconst rewardedUnitId = getRewardedUnitId();\n\tif (!rewardedUnitId || !admob?.RewardedAd) {\n\t\tthrow new Error(\"Rewarded ads are not available in this build.\");\n\t}\n\n\tconst userId = await getRewardIdentity();\n\tconst customData = [\n\t\t`session=${sessionId}`,\n\t\t`offer=${offer.id}`,\n\t\t`step=${step}`,\n\t\t`ads=${offer.adsRequired}`,\n\t].join(\"&\");\n\n\treturn new admob.RewardedAd({\n\t\tadUnitId: rewardedUnitId,\n\t\tserverSideVerification: {\n\t\t\tuserId,\n\t\t\tcustomData,\n\t\t},\n\t});\n}\n\nfunction waitForRewardedResult(ad) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet earned = false;\n\t\tlet settled = false;\n\t\tconst timeoutId = setTimeout(() => {\n\t\t\tfail(\n\t\t\t\tnew Error(\"Rewarded ad timed out before completion. Please try again.\"),\n\t\t\t);\n\t\t}, REWARDED_RESULT_TIMEOUT_MS);\n\n\t\tconst finish = (result) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeoutId);\n\t\t\tresolve(result);\n\t\t};\n\n\t\tconst fail = (error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeoutId);\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? error\n\t\t\t\t\t: new Error(error?.message || \"Rewarded ad failed.\"),\n\t\t\t);\n\t\t};\n\n\t\tad.on(\"reward\", () => {\n\t\t\tearned = true;\n\t\t});\n\n\t\tad.on(\"dismiss\", () => {\n\t\t\tfinish({ earned });\n\t\t});\n\n\t\tad.on(\"showfail\", fail);\n\t\tad.on(\"loadfail\", fail);\n\t});\n}\n\nasync function showRewardedStep(offer, step, sessionId) {\n\tconst rewardedAd = await createRewardedAd(offer, step, sessionId);\n\tconst resultPromise = waitForRewardedResult(rewardedAd);\n\tawait rewardedAd.load();\n\tawait rewardedAd.show();\n\tconst result = await resultPromise;\n\tif (!result.earned) {\n\t\tthrow new Error(\"Reward not earned. The ad was closed before completion.\");\n\t}\n}\n\nexport default {\n\tasync init() {\n\t\tawait refreshState({ notifyExpiry: false });\n\t},\n\tonChange(listener) {\n\t\tlisteners.add(listener);\n\t\treturn () => listeners.delete(listener);\n\t},\n\tasync handleResume() {\n\t\tawait refreshState({ notifyExpiry: true });\n\t},\n\tgetState() {\n\t\treturn {\n\t\t\t...state,\n\t\t\texpiryDate: getExpiryDate(),\n\t\t};\n\t},\n\tgetOffers() {\n\t\treturn OFFERS.map((offer) => ({\n\t\t\t...offer,\n\t\t\tdurationLabel: formatDurationRange(\n\t\t\t\toffer.minDurationMs || offer.durationMs,\n\t\t\t\toffer.maxDurationMs || offer.durationMs,\n\t\t\t),\n\t\t}));\n\t},\n\tgetRemainingMs() {\n\t\treturn Math.max(0, state.remainingMs || state.adFreeUntil - Date.now());\n\t},\n\tgetRemainingLabel() {\n\t\tconst remainingMs = this.getRemainingMs();\n\t\tif (!remainingMs) return \"No active ad-free pass\";\n\n\t\tconst minutes = Math.ceil(remainingMs / (60 * 1000));\n\t\tif (minutes < 60) {\n\t\t\treturn `${minutes} minute${minutes === 1 ? \"\" : \"s\"} remaining`;\n\t\t}\n\n\t\tconst hours = Math.floor(minutes / 60);\n\t\tconst remMinutes = minutes % 60;\n\t\tif (!remMinutes) {\n\t\t\treturn `${hours} hour${hours === 1 ? \"\" : \"s\"} remaining`;\n\t\t}\n\n\t\treturn `${hours}h ${remMinutes}m remaining`;\n\t},\n\tgetExpiryLabel() {\n\t\tconst expiryDate = getExpiryDate();\n\t\tif (!expiryDate) return \"No active pass\";\n\t\treturn expiryDate.toLocaleString();\n\t},\n\tisAdFreeActive() {\n\t\treturn Boolean(state.isActive && state.adFreeUntil > Date.now());\n\t},\n\tcanShowAds() {\n\t\treturn Boolean(window.IS_FREE_VERSION && !this.isAdFreeActive());\n\t},\n\tisRewardedSupported() {\n\t\treturn Boolean(\n\t\t\twindow.IS_FREE_VERSION && admob?.RewardedAd && getRewardedUnitId(),\n\t\t);\n\t},\n\tgetRewardedUnavailableReason() {\n\t\tif (!window.IS_FREE_VERSION)\n\t\t\treturn \"Ads are already disabled on this build.\";\n\t\tif (!admob?.RewardedAd)\n\t\t\treturn \"Rewarded ads are unavailable on this device.\";\n\t\tif (!getRewardedUnitId()) {\n\t\t\treturn \"Rewarded ads are not configured for production yet.\";\n\t\t}\n\t\treturn \"\";\n\t},\n\tcanRedeemNow() {\n\t\treturn {\n\t\t\tok: Boolean(state.canRedeem),\n\t\t\treason: state.redeemDisabledReason || \"\",\n\t\t};\n\t},\n\tisWatchingReward() {\n\t\treturn Boolean(activeWatchPromise);\n\t},\n\tasync watchOffer(offerId, { onStep } = {}) {\n\t\tif (activeWatchPromise) {\n\t\t\treturn activeWatchPromise;\n\t\t}\n\n\t\tconst offer = OFFERS.find((item) => item.id === offerId);\n\t\tif (!offer) {\n\t\t\tthrow new Error(\"Reward offer not found.\");\n\t\t}\n\t\tif (!this.isRewardedSupported()) {\n\t\t\tthrow new Error(this.getRewardedUnavailableReason());\n\t\t}\n\n\t\tawait refreshState({ notifyExpiry: false });\n\t\tconst redemptionStatus = this.canRedeemNow();\n\t\tif (!redemptionStatus.ok) {\n\t\t\tthrow new Error(redemptionStatus.reason);\n\t\t}\n\n\t\tconst sessionId =\n\t\t\ttypeof crypto?.randomUUID === \"function\"\n\t\t\t\t? crypto.randomUUID()\n\t\t\t\t: `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n\n\t\tactiveWatchPromise = (async () => {\n\t\t\tfor (let step = 1; step <= offer.adsRequired; step += 1) {\n\t\t\t\tonStep?.({\n\t\t\t\t\tstep,\n\t\t\t\t\ttotalSteps: offer.adsRequired,\n\t\t\t\t\toffer,\n\t\t\t\t});\n\t\t\t\tawait showRewardedStep(offer, step, sessionId);\n\n\t\t\t\tif (step < offer.adsRequired) {\n\t\t\t\t\ttoast(\n\t\t\t\t\t\t`Reward ${step}/${offer.adsRequired} complete. Loading the next ad...`,\n\t\t\t\t\t\t2500,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst redeemedState = normalizeStatus(\n\t\t\t\tawait secureAdRewardState.redeem(offer.id),\n\t\t\t);\n\t\t\tconst grantedDurationMs =\n\t\t\t\tNumber(redeemedState.appliedDurationMs) ||\n\t\t\t\tNumber(redeemedState.grantedDurationMs) ||\n\t\t\t\t0;\n\n\t\t\tstate = redeemedState;\n\t\t\temitChange();\n\t\t\thideActiveBanner();\n\t\t\tscheduleExpiryCheck();\n\n\t\t\tnotify(\n\t\t\t\t\"Ad-free pass started\",\n\t\t\t\t`${formatDuration(grantedDurationMs)} unlocked. Ads will stay hidden until ${new Date(redeemedState.adFreeUntil).toLocaleString()}.`,\n\t\t\t\t\"success\",\n\t\t\t);\n\n\t\t\treturn {\n\t\t\t\toffer,\n\t\t\t\texpiresAt: redeemedState.adFreeUntil,\n\t\t\t\tgrantedDurationMs,\n\t\t\t};\n\t\t})().finally(() => {\n\t\t\tactiveWatchPromise = null;\n\t\t\temitChange();\n\t\t});\n\n\t\temitChange();\n\t\treturn activeWatchPromise;\n\t},\n};\n"
  },
  {
    "path": "src/lib/applySettings.js",
    "content": "import actions from \"../handlers/quickTools\";\nimport appSettings from \"../lib/settings\";\nimport themes from \"../theme/list\";\nimport constants from \"./constants\";\nimport fonts from \"./fonts\";\n\nexport default {\n\tbeforeRender() {\n\t\t//animation\n\t\tappSettings.applyAnimationSetting();\n\n\t\t//full-screen\n\t\tif (appSettings.value.fullscreen) {\n\t\t\tacode.exec(\"enable-fullscreen\");\n\t\t}\n\n\t\t//setup vibration\n\t\tapp.addEventListener(\"click\", function (e) {\n\t\t\tconst $target = e.target;\n\t\t\tif ($target.hasAttribute(\"vibrate\") && appSettings.value.vibrateOnTap) {\n\t\t\t\tnavigator.vibrate(constants.VIBRATION_TIME);\n\t\t\t}\n\t\t});\n\n\t\tsystem.setInputType(appSettings.value.keyboardMode);\n\t\t// Keep native context menu enabled globally; editor manager scopes disabling to CodeMirror focus.\n\t\tsystem.setNativeContextMenuDisabled(false);\n\t},\n\tafterRender() {\n\t\tconst { value: settings } = appSettings;\n\t\tif (!settings.floatingButton) {\n\t\t\troot.classList.add(\"hide-floating-button\");\n\t\t}\n\n\t\tactions(\"set-height\", settings.quickTools);\n\t\tfonts.setAppFont(settings.appFont);\n\t\tfonts.setEditorFont(settings.editorFont);\n\t\tif (!themes.applied) {\n\t\t\tthemes.apply(\"dark\");\n\t\t}\n\t},\n};\n"
  },
  {
    "path": "src/lib/auth.js",
    "content": "import toast from \"components/toast\";\nimport { addIntentHandler } from \"handlers/intent\";\n\nconst loginEvents = {\n\tlisteners: new Set(),\n\temit(data) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(data);\n\t\t}\n\t},\n\ton(callback) {\n\t\tthis.listeners.add(callback);\n\t},\n\toff(callback) {\n\t\tthis.listeners.delete(callback);\n\t},\n};\n\nclass AuthService {\n\tconstructor() {\n\t\taddIntentHandler(this.onIntentReceiver.bind(this));\n\t}\n\n\tasync onIntentReceiver(event) {\n\t\ttry {\n\t\t\tif (event?.module === \"user\" && event?.action === \"login\") {\n\t\t\t\tif (event?.value) {\n\t\t\t\t\tthis._exec(\"saveToken\", [event.value]);\n\t\t\t\t\ttoast(\"Logged in successfully\");\n\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tloginEvents.emit();\n\t\t\t\t\t}, 500);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to parse intent token.\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Helper to wrap cordova.exec in a Promise\n\t */\n\t_exec(action, args = []) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tcordova.exec(resolve, reject, \"Authenticator\", action, args);\n\t\t});\n\t}\n\n\tasync openLoginUrl() {\n\t\tconst url = \"https://acode.app/login?redirect=app\";\n\n\t\ttry {\n\t\t\tawait new Promise((resolve, reject) => {\n\t\t\t\tCustomTabs.open(url, { showTitle: true }, resolve, reject);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"CustomTabs failed, opening system browser.\", error);\n\t\t\tsystem.openInBrowser(url);\n\t\t}\n\t}\n\n\tasync logout() {\n\t\ttry {\n\t\t\tawait this._exec(\"logout\");\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to logout.\", error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync isLoggedIn() {\n\t\ttry {\n\t\t\t// Native checks EncryptedPrefs and validates with API internally\n\t\t\tawait this._exec(\"isLoggedIn\");\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\t// error is typically the status code (0 if no token, 401 if invalid)\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync getUserInfo() {\n\t\ttry {\n\t\t\tconst data = await this._exec(\"getUserInfo\");\n\t\t\treturn typeof data === \"string\" ? JSON.parse(data) : data;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to fetch user data.\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync getAvatar() {\n\t\ttry {\n\t\t\tconst userData = await this.getUserInfo();\n\t\t\tif (!userData) return null;\n\n\t\t\tif (userData.github) {\n\t\t\t\treturn `https://avatars.githubusercontent.com/${userData.github}`;\n\t\t\t}\n\n\t\t\tif (userData.name) {\n\t\t\t\treturn this._generateInitialsAvatar(userData.name);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to get avatar\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t_generateInitialsAvatar(name) {\n\t\tconst nameParts = name.split(\" \");\n\t\tconst initials =\n\t\t\tnameParts.length >= 2\n\t\t\t\t? `${nameParts[0][0]}${nameParts[1][0]}`.toUpperCase()\n\t\t\t\t: nameParts[0][0].toUpperCase();\n\n\t\tconst canvas = document.createElement(\"canvas\");\n\t\tcanvas.width = 100;\n\t\tcanvas.height = 100;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\n\t\tconst colors = [\n\t\t\t\"#2196F3\",\n\t\t\t\"#9C27B0\",\n\t\t\t\"#E91E63\",\n\t\t\t\"#009688\",\n\t\t\t\"#4CAF50\",\n\t\t\t\"#FF9800\",\n\t\t];\n\t\tctx.fillStyle =\n\t\t\tcolors[\n\t\t\t\tname.split(\"\").reduce((acc, char) => acc + char.charCodeAt(0), 0) %\n\t\t\t\t\tcolors.length\n\t\t\t];\n\t\tctx.fillRect(0, 0, 100, 100);\n\n\t\tctx.fillStyle = \"#ffffff\";\n\t\tctx.font = \"bold 40px Arial\";\n\t\tctx.textAlign = \"center\";\n\t\tctx.textBaseline = \"middle\";\n\t\tctx.fillText(initials, 50, 50);\n\n\t\treturn canvas.toDataURL();\n\t}\n}\n\nexport default new AuthService();\nexport { loginEvents };\n"
  },
  {
    "path": "src/lib/checkFiles.js",
    "content": "import fsOperation from \"fileSystem\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\n\nlet checkFileEnabled = true;\n\nObject.defineProperty(checkFiles, \"check\", {\n\tset(value) {\n\t\tcheckFileEnabled = value;\n\t},\n\tget() {\n\t\treturn checkFileEnabled;\n\t},\n});\n\nexport default async function checkFiles() {\n\tif (!editorManager) return;\n\tif (checkFileEnabled === false) {\n\t\tcheckFileEnabled = true;\n\t\treturn;\n\t}\n\tconst files = editorManager.files;\n\t// @ts-check\n\t/** @type {{ editor: import('@codemirror/view').EditorView }} */\n\tconst { editor } = editorManager;\n\n\trecursiveFileCheck([...files]);\n\n\t/**\n\t * Checks if the file has been changed\n\t * @param {EditorFile[]} files List of files to check\n\t */\n\tasync function recursiveFileCheck(files) {\n\t\tconst file = files.pop();\n\t\tawait checkFile(file);\n\t\tif (files.length) {\n\t\t\trecursiveFileCheck(files);\n\t\t}\n\t\treturn;\n\t}\n\n\t/**\n\t * @typedef {import('./editorFile').default} EditorFile\n\t */\n\n\t/**\n\t * Checks a file for changes\n\t * @param {EditorFile} file File to check\n\t * @returns {Promise<void>}\n\t */\n\tasync function checkFile(file) {\n\t\tif (file === undefined || file.isUnsaved || !file.loaded || file.loading)\n\t\t\treturn;\n\n\t\tif (file.uri) {\n\t\t\tconst fs = fsOperation(file.uri);\n\t\t\tconst exists = await fs.exists();\n\n\t\t\tif (!exists && !file.readOnly) {\n\t\t\t\tfile.isUnsaved = true;\n\t\t\t\tfile.uri = null;\n\t\t\t\teditorManager.onupdate(\"file-changed\");\n\t\t\t\teditorManager.emit(\"update\", \"file-changed\");\n\t\t\t\tawait new Promise((resolve) => {\n\t\t\t\t\talert(\n\t\t\t\t\t\tstrings.info,\n\t\t\t\t\t\tstrings[\"file has been deleted\"].replace(\"{file}\", file.filename),\n\t\t\t\t\t\tresolve,\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst text = await fs.readFile(file.encoding);\n\t\t\tconst loadedText = file.session.doc.toString();\n\n\t\t\tif (text !== loadedText) {\n\t\t\t\ttry {\n\t\t\t\t\tconst confirmation = await confirm(\n\t\t\t\t\t\tstrings.warning.toUpperCase(),\n\t\t\t\t\t\tfile.filename + strings[\"file changed\"],\n\t\t\t\t\t);\n\n\t\t\t\t\tif (!confirmation) return;\n\n\t\t\t\t\tconst cursorPos = editor.getCursorPosition();\n\t\t\t\t\teditorManager.getFile(file.id, \"id\")?.makeActive();\n\n\t\t\t\t\tfile.markChanged = false;\n\t\t\t\t\tfile.session.setValue(text);\n\t\t\t\t\teditor.gotoLine(cursorPos.row, cursorPos.column);\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!editorManager.activeFile) {\n\t\tapp.focus();\n\t}\n}\n"
  },
  {
    "path": "src/lib/checkPluginsUpdate.js",
    "content": "import ajax from \"@deadlyjack/ajax\";\nimport fsOperation from \"../fileSystem\";\nimport Url from \"../utils/Url\";\n\nexport default async function checkPluginsUpdate() {\n\tconst plugins = await fsOperation(PLUGIN_DIR).lsDir();\n\tconst promises = [];\n\tconst updates = [];\n\n\tplugins.forEach((pluginDir) => {\n\t\tpromises.push(\n\t\t\t(async () => {\n\t\t\t\tconst plugin = await fsOperation(\n\t\t\t\t\tUrl.join(pluginDir.url, \"plugin.json\"),\n\t\t\t\t).readFile(\"json\");\n\n\t\t\t\tconst res = await ajax({\n\t\t\t\t\turl: `https://acode.app/api/plugin/check-update/${plugin.id}/${plugin.version}`,\n\t\t\t\t});\n\n\t\t\t\tif (res.update) {\n\t\t\t\t\tupdates.push(plugin.id);\n\t\t\t\t}\n\t\t\t})(),\n\t\t);\n\t});\n\n\tawait Promise.allSettled(promises);\n\treturn updates;\n}\n"
  },
  {
    "path": "src/lib/commands.js",
    "content": "import fsOperation from \"fileSystem\";\nimport { selectAll } from \"@codemirror/commands\";\nimport Sidebar from \"components/sidebar\";\nimport { TerminalManager } from \"components/terminal\";\nimport color from \"dialogs/color\";\nimport confirm from \"dialogs/confirm\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport actions from \"handlers/quickTools\";\nimport recents from \"lib/recents\";\nimport About from \"pages/about\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport plugins from \"pages/plugins\";\nimport Problems from \"pages/problems/problems\";\nimport openWelcomeTab from \"pages/welcome/welcome\";\nimport changeEncoding from \"palettes/changeEncoding\";\nimport changeMode from \"palettes/changeMode\";\nimport changeTheme from \"palettes/changeTheme\";\nimport commandPalette from \"palettes/commandPalette\";\nimport findFile from \"palettes/findFile\";\nimport browser from \"plugins/browser\";\nimport help from \"settings/helpSettings\";\nimport mainSettings from \"settings/mainSettings\";\nimport { runAllTests } from \"test/tester\";\nimport { getColorRange } from \"utils/color/regex\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport checkFiles from \"./checkFiles\";\nimport constants from \"./constants\";\nimport EditorFile from \"./editorFile\";\nimport openFile from \"./openFile\";\nimport openFolder from \"./openFolder\";\nimport run from \"./run\";\nimport saveState from \"./saveState\";\nimport appSettings from \"./settings\";\nimport showFileInfo from \"./showFileInfo\";\n\nexport default {\n\tasync \"run-tests\"() {\n\t\tawait runAllTests();\n\t},\n\tasync \"close-all-tabs\"() {\n\t\tconst closableFiles = editorManager.files.filter((file) => !file.pinned);\n\t\tif (!closableFiles.length) return;\n\n\t\tlet save = false;\n\t\tconst unsavedFiles = closableFiles.filter((file) => file.isUnsaved).length;\n\t\tif (unsavedFiles) {\n\t\t\tconst confirmation = await confirm(\n\t\t\t\tstrings[\"warning\"],\n\t\t\t\tstrings[\"unsaved files warning\"],\n\t\t\t);\n\t\t\tif (!confirmation) return;\n\t\t\tconst option = await select(strings[\"select\"], [\n\t\t\t\t[\"save\", strings[\"save all\"]],\n\t\t\t\t[\"close\", strings[\"close all\"]],\n\t\t\t\t[\"cancel\", strings[\"cancel\"]],\n\t\t\t]);\n\t\t\tif (option === \"cancel\") return;\n\n\t\t\tif (option === \"save\") {\n\t\t\t\tconst doSave = await confirm(\n\t\t\t\t\tstrings[\"warning\"],\n\t\t\t\t\tstrings[\"save all warning\"],\n\t\t\t\t);\n\t\t\t\tif (!doSave) return;\n\t\t\t\tsave = true;\n\t\t\t} else {\n\t\t\t\tconst doClose = await confirm(\n\t\t\t\t\tstrings[\"warning\"],\n\t\t\t\t\tstrings[\"close all warning\"],\n\t\t\t\t);\n\t\t\t\tif (!doClose) return;\n\t\t\t}\n\t\t}\n\n\t\tfor (const file of [...closableFiles]) {\n\t\t\tif (save) {\n\t\t\t\tawait file.save();\n\t\t\t\tawait file.remove(true, { silentPinned: true });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tawait file.remove(true, { silentPinned: true });\n\t\t}\n\t},\n\tasync \"save-all-changes\"() {\n\t\tconst doSave = await confirm(\n\t\t\tstrings[\"warning\"],\n\t\t\tstrings[\"save all changes warning\"],\n\t\t);\n\t\tif (!doSave) return;\n\t\teditorManager.files.forEach((file) => {\n\t\t\tfile.save();\n\t\t\tfile.isUnsaved = false;\n\t\t});\n\t},\n\t\"close-current-tab\"() {\n\t\teditorManager.activeFile.remove();\n\t},\n\t\"toggle-pin-tab\"() {\n\t\teditorManager.activeFile?.togglePinned?.();\n\t},\n\tconsole() {\n\t\trun(true, \"inapp\");\n\t},\n\t\"check-files\"() {\n\t\tif (!appSettings.value.checkFiles) return;\n\t\tcheckFiles();\n\t},\n\t\"command-palette\"() {\n\t\tcommandPalette();\n\t},\n\t\"disable-fullscreen\"() {\n\t\tapp.classList.remove(\"fullscreen-mode\");\n\t\tthis[\"resize-editor\"]();\n\t},\n\t\"enable-fullscreen\"() {\n\t\tapp.classList.add(\"fullscreen-mode\");\n\t\tthis[\"resize-editor\"]();\n\t},\n\tencoding() {\n\t\tchangeEncoding();\n\t},\n\texit() {\n\t\tnavigator.app.exitApp();\n\t},\n\t\"edit-with\"() {\n\t\teditorManager.activeFile.editWith();\n\t},\n\t\"find-file\"() {\n\t\tfindFile();\n\t},\n\tfiles() {\n\t\tFileBrowser(\"both\", strings[\"file browser\"])\n\t\t\t.then(FileBrowser.open)\n\t\t\t.catch(FileBrowser.openError);\n\t},\n\tfind() {\n\t\tactions(\"search\");\n\t},\n\t\"file-info\"(url) {\n\t\tshowFileInfo(url);\n\t},\n\tasync goto() {\n\t\tconst lastLine = editorManager.editor?.state?.doc?.lines;\n\t\tconst message = lastLine\n\t\t\t? `${strings[\"enter line number\"]} (1..${lastLine})`\n\t\t\t: strings[\"enter line number\"];\n\t\tconst res = await prompt(message, \"\", \"number\", {\n\t\t\tplaceholder: \"line.column\",\n\t\t});\n\n\t\tif (!res) return;\n\t\tconst [lineStr, colStr] = String(res).split(\".\");\n\t\teditorManager.editor.gotoLine(lineStr, colStr);\n\t},\n\tasync \"new-file\"() {\n\t\tlet filename = await prompt(strings[\"enter file name\"], \"\", \"filename\", {\n\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\trequired: true,\n\t\t});\n\n\t\tfilename = helpers.fixFilename(filename);\n\t\tif (!filename) return;\n\n\t\tnew EditorFile(filename, {\n\t\t\tisUnsaved: false,\n\t\t});\n\t},\n\t\"next-file\"() {\n\t\tconst len = editorManager.files.length;\n\t\tlet fileIndex = editorManager.files.indexOf(editorManager.activeFile);\n\n\t\tif (fileIndex === len - 1) fileIndex = 0;\n\t\telse ++fileIndex;\n\n\t\teditorManager.files[fileIndex].makeActive();\n\t},\n\topen(page) {\n\t\tswitch (page) {\n\t\t\tcase \"settings\":\n\t\t\t\tmainSettings();\n\t\t\t\tbreak;\n\n\t\t\tcase \"help\":\n\t\t\t\thelp();\n\t\t\t\tbreak;\n\n\t\t\tcase \"problems\":\n\t\t\t\tProblems();\n\t\t\t\tbreak;\n\n\t\t\tcase \"plugins\":\n\t\t\t\tplugins();\n\t\t\t\tbreak;\n\n\t\t\tcase \"file_browser\":\n\t\t\t\tFileBrowser();\n\t\t\t\tbreak;\n\n\t\t\tcase \"about\":\n\t\t\t\tAbout();\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\t\teditorManager.editor.contentDOM.blur();\n\t},\n\t\"open-with\"() {\n\t\teditorManager.activeFile.openWith();\n\t},\n\t\"open-file\"() {\n\t\teditorManager.editor.contentDOM.blur();\n\t\tFileBrowser(\"file\")\n\t\t\t.then(FileBrowser.openFile)\n\t\t\t.catch(FileBrowser.openFileError);\n\t},\n\t\"open-folder\"() {\n\t\teditorManager.editor.contentDOM.blur();\n\t\tFileBrowser(\"folder\")\n\t\t\t.then(FileBrowser.openFolder)\n\t\t\t.catch(FileBrowser.openFolderError);\n\t},\n\t\"prev-file\"() {\n\t\tconst len = editorManager.files.length;\n\t\tlet fileIndex = editorManager.files.indexOf(editorManager.activeFile);\n\n\t\tif (fileIndex === 0) fileIndex = len - 1;\n\t\telse --fileIndex;\n\n\t\teditorManager.files[fileIndex].makeActive();\n\t},\n\t\"read-only\"() {\n\t\tconst file = editorManager.activeFile;\n\t\tfile.editable = !file.editable;\n\t},\n\trecent() {\n\t\trecents.select().then((res) => {\n\t\t\tconst { type } = res;\n\t\t\tif (helpers.isFile(type)) {\n\t\t\t\topenFile(res.val, {\n\t\t\t\t\trender: true,\n\t\t\t\t}).catch((err) => {\n\t\t\t\t\thelpers.error(err);\n\t\t\t\t});\n\t\t\t} else if (helpers.isDir(type)) {\n\t\t\t\topenFolder(res.val.url, res.val.opts);\n\t\t\t} else if (res === \"clear\") {\n\t\t\t\trecents.clear();\n\t\t\t}\n\t\t});\n\t},\n\treplace() {\n\t\tthis.find();\n\t},\n\t\"resize-editor\"() {\n\t\t// TODO : Codemirror\n\t\t//editorManager.editor.resize(true);\n\t},\n\t\"open-inapp-browser\"(url) {\n\t\tbrowser.open(url);\n\t},\n\trun() {\n\t\teditorManager.activeFile[\n\t\t\tappSettings.value.useCurrentFileForPreview ? \"runFile\" : \"run\"\n\t\t]?.();\n\t},\n\t\"run-file\"() {\n\t\teditorManager.activeFile.runFile?.();\n\t},\n\tasync save(showToast) {\n\t\ttry {\n\t\t\tawait editorManager.activeFile.save();\n\t\t\tif (showToast) {\n\t\t\t\ttoast(strings[\"file saved\"]);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t}\n\t},\n\tasync \"save-as\"(showToast) {\n\t\ttry {\n\t\t\tawait editorManager.activeFile.saveAs();\n\t\t\tif (showToast) {\n\t\t\t\ttoast(strings[\"file saved\"]);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t}\n\t},\n\t\"save-state\"() {\n\t\tsaveState();\n\t},\n\tshare() {\n\t\teditorManager.activeFile.share();\n\t},\n\tasync \"pin-file-shortcut\"() {\n\t\tconst file = editorManager.activeFile;\n\t\tif (!file?.uri) {\n\t\t\ttoast(strings[\"save file before home shortcut\"]);\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof system?.pinFileShortcut !== \"function\") {\n\t\t\ttoast(strings[\"pin shortcuts not supported\"]);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { uri, filename } = file;\n\t\tconst label = filename;\n\t\tconst description = filename;\n\n\t\tlet id = uri.replace(/[^a-zA-Z0-9]/g, \"\").toLowerCase();\n\t\tif (!id) {\n\t\t\tid = helpers.uuid();\n\t\t}\n\t\tif (id.length > 40) {\n\t\t\tid = id.slice(-40);\n\t\t}\n\t\tid = `file-${id}`;\n\n\t\tconst shortcut = {\n\t\t\tid,\n\t\t\tlabel,\n\t\t\tdescription,\n\t\t\turi,\n\t\t};\n\n\t\tconst requestShortcut = new Promise((resolve, reject) => {\n\t\t\tsystem.pinFileShortcut(\n\t\t\t\tshortcut,\n\t\t\t\t() => resolve(true),\n\t\t\t\t(err) => reject(err),\n\t\t\t);\n\t\t});\n\n\t\ttry {\n\t\t\tawait requestShortcut;\n\t\t\ttoast(strings[\"shortcut request sent\"]);\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\ttypeof error === \"string\" &&\n\t\t\t\terror.toLowerCase().includes(\"not supported\")\n\t\t\t) {\n\t\t\t\ttoast(strings[\"pin shortcuts not supported\"]);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thelpers.error(error);\n\t\t}\n\t},\n\tsyntax() {\n\t\tchangeMode();\n\t},\n\t\"change-app-theme\"() {\n\t\tchangeTheme(\"app\");\n\t},\n\t\"change-editor-theme\"() {\n\t\tchangeTheme(\"editor\");\n\t},\n\t\"toggle-fullscreen\"() {\n\t\tapp.classList.toggle(\"fullscreen-mode\");\n\t\tthis[\"resize-editor\"]();\n\t},\n\t\"toggle-sidebar\"() {\n\t\tSidebar.toggle();\n\t},\n\t\"toggle-menu\"() {\n\t\ttag.get(\"[action=toggle-menu]\")?.click();\n\t},\n\t\"toggle-editmenu\"() {\n\t\ttag.get(\"[action=toggle-edit-menu\")?.click();\n\t},\n\tasync \"insert-color\"() {\n\t\tconst { editor } = editorManager;\n\t\tconst range = getColorRange();\n\t\tlet defaultColor = \"\";\n\n\t\tif (range) {\n\t\t\ttry {\n\t\t\t\tdefaultColor = editor.state.doc.sliceString(range.from, range.to);\n\t\t\t} catch (_) {\n\t\t\t\tdefaultColor = \"\";\n\t\t\t}\n\t\t}\n\n\t\teditor.contentDOM.blur();\n\t\tconst wasFocused = editorManager.activeFile.focused;\n\t\tconst res = await color(defaultColor, () => {\n\t\t\tif (wasFocused) {\n\t\t\t\teditor.focus();\n\t\t\t}\n\t\t});\n\n\t\tif (range) {\n\t\t\teditor.dispatch({\n\t\t\t\tchanges: { from: range.from, to: range.to, insert: res },\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\teditor.insert(res);\n\t},\n\tcopy() {\n\t\teditorManager.editor.execCommand(\"copy\");\n\t},\n\tcut() {\n\t\teditorManager.editor.execCommand(\"cut\");\n\t},\n\tpaste() {\n\t\teditorManager.editor.execCommand(\"paste\");\n\t},\n\t\"select-all\"() {\n\t\tconst { editor } = editorManager;\n\t\tselectAll(editor);\n\t},\n\tasync rename(file) {\n\t\tfile = file || editorManager.activeFile;\n\n\t\tif (file.mode === \"single\") {\n\t\t\talert(strings.info.toUpperCase(), strings[\"unable to rename\"]);\n\t\t\treturn;\n\t\t}\n\n\t\tlet newname = await prompt(strings.rename, file.filename, \"filename\", {\n\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\tcapitalize: false,\n\t\t});\n\n\t\tnewname = helpers.fixFilename(newname);\n\t\tif (!newname || newname === file.filename) return;\n\n\t\tconst { uri } = file;\n\t\tif (uri) {\n\t\t\tconst fs = fsOperation(uri);\n\t\t\ttry {\n\t\t\t\tlet newUri;\n\t\t\t\tif (uri.startsWith(\"content://com.termux.documents/tree/\")) {\n\t\t\t\t\t// Special handling for Termux content files\n\t\t\t\t\tconst newFilePath = Url.join(Url.dirname(uri), newname);\n\t\t\t\t\tconst content = await fs.readFile();\n\t\t\t\t\tawait fsOperation(Url.dirname(uri)).createFile(newname, content);\n\t\t\t\t\tawait fs.delete();\n\t\t\t\t\tnewUri = newFilePath;\n\t\t\t\t} else {\n\t\t\t\t\tnewUri = await fs.renameTo(newname);\n\t\t\t\t}\n\t\t\t\tconst stat = await fsOperation(newUri).stat();\n\n\t\t\t\tnewname = stat.name;\n\t\t\t\tfile.uri = newUri;\n\t\t\t\tfile.filename = newname;\n\n\t\t\t\topenFolder.renameItem(uri, newUri, newname);\n\t\t\t\ttoast(strings[\"file renamed\"]);\n\t\t\t} catch (err) {\n\t\t\t\thelpers.error(err);\n\t\t\t}\n\t\t} else {\n\t\t\tfile.filename = newname;\n\t\t}\n\t},\n\tasync format(selectIfNull) {\n\t\tconst { editor } = editorManager;\n\t\tconst pos = editor.getCursorPosition();\n\n\t\tconst didFormat = await acode.format(selectIfNull);\n\t\tif (didFormat) {\n\t\t\t// Restore cursor position after formatting (pos.row is now 1-based)\n\t\t\teditor.gotoLine(pos.row, pos.column);\n\t\t}\n\t},\n\tasync eol() {\n\t\tconst eol = await select(strings[\"new line mode\"], [\"unix\", \"windows\"], {\n\t\t\tdefault: editorManager.activeFile.eol,\n\t\t});\n\t\teditorManager.activeFile.eol = eol;\n\t},\n\t\"open-log-file\"() {\n\t\topenFile(Url.join(DATA_STORAGE, constants.LOG_FILE_NAME));\n\t},\n\t\"copy-device-info\"() {\n\t\tlet webviewInfo = {};\n\t\tlet appInfo = {};\n\t\tconst getWebviewInfo = new Promise((resolve, reject) => {\n\t\t\tsystem.getWebviewInfo(\n\t\t\t\t(res) => {\n\t\t\t\t\twebviewInfo = res;\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\tconsole.error(\"Error getting WebView info:\", error);\n\t\t\t\t\treject(error);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t\tconst getAppInfo = new Promise((resolve, reject) => {\n\t\t\tsystem.getAppInfo(\n\t\t\t\t(res) => {\n\t\t\t\t\tappInfo = res;\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\t(error) => {\n\t\t\t\t\tconsole.error(\"Error getting app info:\", error);\n\t\t\t\t\treject(error);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\n\t\tPromise.all([getWebviewInfo, getAppInfo])\n\t\t\t.then(() => {\n\t\t\t\tlet info = `Device Information:\nWebView Info:\n\t\tPackage Name: ${webviewInfo?.packageName || \"N/A\"}\n\t\tVersion: ${webviewInfo?.versionName || \"N/A\"}\n\nApp Info:\n\t\tName: ${appInfo?.label || \"N/A\"}\n\t\tPackage Name: ${appInfo?.packageName || \"N/A\"}\n\t\tVersion: ${appInfo?.versionName || \"N/A\"}\n\t\tVersion Code: ${appInfo?.versionCode || \"N/A\"}\n\nDevice Info:\n\t\tAndroid Version: ${device?.version || \"N/A\"}\n\t\tManufacturer: ${device?.manufacturer || \"N/A\"}\n\t\tModel: ${device?.model || \"N/A\"}\n\t\tPlatform: ${device?.platform || \"N/A\"}\n\t\tCordova Version: ${device?.cordova || \"N/A\"}\n\nScreen Info:\n\t\tWidth: ${screen?.width || \"N/A\"}\n\t\tHeight: ${screen?.height || \"N/A\"}\n\t\tColor Depth: ${screen?.colorDepth || \"N/A\"}\n\nAdditional Info:\n\t\tLanguage: ${navigator?.language || \"N/A\"}\n\t\tUser Agent: ${navigator?.userAgent || \"N/A\"}\n`;\n\n\t\t\t\t// Copy the info to clipboard\n\t\t\t\tif (cordova.plugins.clipboard) {\n\t\t\t\t\tcordova.plugins.clipboard.copy(info);\n\t\t\t\t\ttoast(strings[\"copied to clipboard\"]);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tconsole.error(\"Error getting device info:\", error);\n\t\t\t\ttoast(\"Failed to get device info\");\n\t\t\t});\n\t},\n\tasync \"new-terminal\"() {\n\t\ttry {\n\t\t\tawait TerminalManager.createServerTerminal();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to create terminal:\", error);\n\t\t\twindow.toast(\"Failed to create terminal\");\n\t\t}\n\t},\n\twelcome() {\n\t\topenWelcomeTab();\n\t},\n\tasync \"toggle-inspector\"() {\n\t\tconst devTools = (await import(\"lib/devTools\")).default;\n\t\tdevTools.toggle();\n\t},\n\tasync \"open-inspector\"() {\n\t\tconst devTools = (await import(\"lib/devTools\")).default;\n\t\tdevTools.show();\n\t},\n\tasync \"lsp-info\"() {\n\t\tconst { showLspInfoDialog } = await import(\"components/lspInfoDialog\");\n\t\tshowLspInfoDialog();\n\t},\n};\n"
  },
  {
    "path": "src/lib/console.js",
    "content": "import \"core-js/stable\";\nimport \"html-tag-js/dist/polyfill\";\nimport { parse } from \"acorn\";\nimport css from \"styles/console.m.scss\";\nimport loadPolyFill from \"utils/polyfill\";\n\n(function () {\n\tloadPolyFill.apply(window);\n\n\tlet consoleVisible = false;\n\tconst originalConsole = console;\n\tconst $input = tag(\"textarea\", {\n\t\tid: \"__c-input\",\n\t\tonblur() {\n\t\t\tsetTimeout(() => {\n\t\t\t\tisFocused = false;\n\t\t\t}, 0);\n\t\t},\n\t});\n\tconst $inputContainer = tag(\"c-input\", {\n\t\tchild: $input,\n\t});\n\tconst $toggler = tag(\"c-toggler\", {\n\t\tstyle: {\n\t\t\ttransform: `translate(2px, ${innerHeight / 2}px)`,\n\t\t},\n\t\tonclick() {\n\t\t\tconsoleVisible = !consoleVisible;\n\t\t\tif (consoleVisible) {\n\t\t\t\tshowConsole();\n\t\t\t} else {\n\t\t\t\thideConsole();\n\t\t\t}\n\t\t},\n\t\tontouchstart() {\n\t\t\tdocument.addEventListener(\"touchmove\", touchmove, {\n\t\t\t\tpassive: false,\n\t\t\t});\n\n\t\t\tdocument.ontouchend = function (e) {\n\t\t\t\tdocument.removeEventListener(\"touchmove\", touchmove, {\n\t\t\t\t\tpassive: \"false\",\n\t\t\t\t});\n\t\t\t\tdocument.ontouchend = null;\n\t\t\t};\n\t\t},\n\t});\n\tconst $console = tag(\"c-console\", {\n\t\tchild: $inputContainer,\n\t\tonclick(e) {\n\t\t\tconst el = e.target;\n\t\t\tconst action = el.getAttribute(\"action\");\n\n\t\t\tswitch (action) {\n\t\t\t\tcase \"use code\":\n\t\t\t\t\tconst value = el.getAttribute(\"data-code\");\n\n\t\t\t\t\t$input.value = value;\n\t\t\t\t\t$input.focus();\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t},\n\t});\n\tconst counter = {};\n\tconst timers = {};\n\tlet isFocused = false;\n\n\tif (!window.__objs) window.__objs = {};\n\n\tif (!tag.get(\"c-console\")) {\n\t\tconst $style = tag(\"style\");\n\t\t$style.textContent = css;\n\t\tdocument.head.append($style);\n\t\twindow.addEventListener(\"error\", onError);\n\t\tassignCustomConsole();\n\n\t\tif (sessionStorage.getItem(\"__mode\") === \"console\") {\n\t\t\tshowConsole();\n\t\t\treturn;\n\t\t}\n\n\t\ttag.get(\"html\").append($toggler);\n\t\t$console.setAttribute(\"title\", \"Console\");\n\t\tsessionStorage.setItem(\"__console_available\", true);\n\t\tdocument.addEventListener(\"showconsole\", showConsole);\n\t\tdocument.addEventListener(\"hideconsole\", hideConsole);\n\t}\n\n\tfunction touchmove(e) {\n\t\te.preventDefault();\n\t\t$toggler.style.transform = \"translate(\"\n\t\t\t.concat(e.touches[0].clientX - 20, \"px, \")\n\t\t\t.concat(e.touches[0].clientY - 20, \"px)\");\n\t}\n\n\tfunction assignCustomConsole() {\n\t\twindow.console = {\n\t\t\tassert(condition, msg, ...substitution) {\n\t\t\t\toriginalConsole.assert(condition, msg, ...substitution);\n\t\t\t\tif (!condition) {\n\t\t\t\t\tlog(\"error\", getStack(new Error()), msg, ...substitution);\n\t\t\t\t}\n\t\t\t},\n\t\t\tclear() {\n\t\t\t\toriginalConsole.clear();\n\t\t\t\tif (isFocused) $input.focus();\n\t\t\t\t$console.textContent = \"\";\n\t\t\t\t$console.appendChild($inputContainer);\n\t\t\t},\n\t\t\tcount(hash = \"default\") {\n\t\t\t\toriginalConsole.count(hash);\n\t\t\t\tif (!counter[hash]) {\n\t\t\t\t\tcounter[hash] = 1;\n\t\t\t\t} else {\n\t\t\t\t\t++counter[hash];\n\t\t\t\t}\n\t\t\t\tlog(\"log\", getStack(new Error()), `${hash}: ${counter[hash]}`);\n\t\t\t},\n\t\t\tcountReset(hash) {\n\t\t\t\toriginalConsole.countReset(hash);\n\t\t\t\tdelete counter[hash];\n\t\t\t},\n\t\t\tdebug(...args) {\n\t\t\t\toriginalConsole.debug(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tdir(...args) {\n\t\t\t\toriginalConsole.dir(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tdirxml(...args) {\n\t\t\t\toriginalConsole.dirxml(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\terror(...args) {\n\t\t\t\toriginalConsole.error(...args);\n\t\t\t\tlog(\"error\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tgroup(...args) {\n\t\t\t\toriginalConsole.group(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tgroupCollapsed(...args) {\n\t\t\t\toriginalConsole.groupCollapsed(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tgroupEnd(...args) {\n\t\t\t\toriginalConsole.groupEnd(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tinfo(...args) {\n\t\t\t\toriginalConsole.info(...args);\n\t\t\t\tlog(\"info\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\tlog(msg, ...substitution) {\n\t\t\t\toriginalConsole.log(msg, ...substitution);\n\t\t\t\tlog(\"log\", getStack(new Error()), msg, ...substitution);\n\t\t\t},\n\t\t\ttable(...args) {\n\t\t\t\toriginalConsole.table(...args);\n\t\t\t\tlog(\"log\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\ttime(label = \"default\") {\n\t\t\t\toriginalConsole.time(label);\n\t\t\t\tif (typeof label !== \"string\") {\n\t\t\t\t\tthrow new TypeError(\"label must be a string\");\n\t\t\t\t}\n\t\t\t\ttimers[label] = new Date().getTime();\n\t\t\t},\n\t\t\ttimeEnd(label = \"default\") {\n\t\t\t\toriginalConsole.timeEnd(label);\n\t\t\t\tif (typeof label !== \"string\") {\n\t\t\t\t\tthrow new TypeError(\"label must be a string\");\n\t\t\t\t}\n\t\t\t\tif (!timers[label]) {\n\t\t\t\t\tthrow new Error(`No such label: ${label}`);\n\t\t\t\t}\n\t\t\t\tconst time = new Date().getTime() - timers[label];\n\t\t\t\tlog(\"log\", getStack(new Error()), `${label}: ${time}ms`);\n\t\t\t\tdelete timers[label];\n\t\t\t},\n\t\t\ttimeLog(label = \"default\") {\n\t\t\t\toriginalConsole.timeLog(label);\n\t\t\t\tif (typeof label !== \"string\") {\n\t\t\t\t\tthrow new TypeError(\"label must be a string\");\n\t\t\t\t}\n\t\t\t\tif (!timers[label]) {\n\t\t\t\t\tthrow new Error(`No such label: ${label}`);\n\t\t\t\t}\n\t\t\t\tconst time = new Date().getTime() - timers[label];\n\t\t\t\tlog(\"log\", getStack(new Error()), `${label}: ${time}ms`);\n\t\t\t},\n\t\t\ttrace(...args) {\n\t\t\t\toriginalConsole.trace(...args);\n\t\t\t\tlog(\"trace\", getStack(new Error()), ...args);\n\t\t\t},\n\t\t\twarn(msg, ...substitution) {\n\t\t\t\toriginalConsole.warn(msg, ...substitution);\n\t\t\t\tlog(\"warn\", getStack(new Error()), msg, ...substitution);\n\t\t\t},\n\t\t};\n\t}\n\n\tfunction showConsole() {\n\t\ttag.get(\"html\").append($console);\n\t\t$input.addEventListener(\"keydown\", onCodeInput);\n\t}\n\n\tfunction hideConsole() {\n\t\t$console.remove();\n\t\t$input.removeEventListener(\"keydown\", onCodeInput);\n\t}\n\n\tfunction onCodeInput(e) {\n\t\tconst key = e.key;\n\t\tisFocused = true;\n\t\tif (key === \"Enter\") {\n\t\t\tconst regex = /[\\[|{\\(\\)\\}\\]]/g;\n\t\t\tlet code = this.value.trim();\n\t\t\tlet isOdd = (code.length - code.replace(regex, \"\").length) % 2;\n\n\t\t\tif (!code || isOdd) return;\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\te.stopImmediatePropagation();\n\n\t\t\tlog(\"code\", {}, code);\n\t\t\t$input.value = \"\";\n\t\t\tconst res = execute(code);\n\t\t\tif (res.type === \"error\") {\n\t\t\t\tlog(\"error\", getStack(new Error()), res.value);\n\t\t\t} else {\n\t\t\t\tlog(\"log\", getStack(new Error()), res.value);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction getBody(obj, ...keys) {\n\t\tif (obj instanceof Promise && !(\"[[PromiseStatus]]\" in obj))\n\t\t\tobj = getPromiseStatus(obj);\n\n\t\tlet value = objValue(obj, ...keys);\n\t\tconst $group = tag(\"c-group\");\n\t\tconst $toggler = tag(\"c-type\", {\n\t\t\tattr: {\n\t\t\t\ttype: \"body-toggler\",\n\t\t\t},\n\t\t\ttextContent: value ? value.constructor.name : value + \"\",\n\t\t});\n\n\t\tif (value instanceof Object) {\n\t\t\t$toggler.onclick = function () {\n\t\t\t\tif (this.classList.contains(\"__show-data\")) {\n\t\t\t\t\tthis.classList.remove(\"__show-data\");\n\t\t\t\t\t$group.textContent = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.classList.toggle(\"__show-data\");\n\n\t\t\t\tconst possibleKeys = [];\n\n\t\t\t\tfor (let key in value) {\n\t\t\t\t\tpossibleKeys.push(key);\n\t\t\t\t}\n\n\t\t\t\tpossibleKeys.push(\n\t\t\t\t\t...[\n\t\t\t\t\t\t...Object.keys(value),\n\t\t\t\t\t\t...Object.getOwnPropertyNames(value),\n\t\t\t\t\t\t...Object.keys(value[\"__proto__\"] || {}),\n\t\t\t\t\t],\n\t\t\t\t);\n\n\t\t\t\tif (value[\"__proto__\"]) possibleKeys.push(\"__proto__\");\n\t\t\t\tif (value[\"prototype\"]) possibleKeys.push(\"prototype\");\n\n\t\t\t\t[...new Set(possibleKeys)].forEach((key) =>\n\t\t\t\t\t$group.append(appendProperties(obj, ...keys, key)),\n\t\t\t\t);\n\t\t\t};\n\t\t\t$toggler.textContent = value.constructor.name;\n\t\t} else {\n\t\t\tconst $val = getElement(value);\n\t\t\t$val.textContent = (value ?? value + \"\").toString();\n\t\t\t$group.append($val);\n\t\t}\n\n\t\treturn [$toggler, $group];\n\t}\n\n\tfunction appendProperties(obj, ...keys) {\n\t\tconst key = keys.pop();\n\t\tconst value = objValue(obj, ...keys);\n\t\tconst getter = value.__lookupGetter__(key);\n\t\tconst $key = tag(\"c-key\", {\n\t\t\ttextContent: key + \":\",\n\t\t});\n\t\tlet $val;\n\n\t\tif (getter) {\n\t\t\t$val = tag(\"c-span\", {\n\t\t\t\tstyle: {\n\t\t\t\t\ttextDecoration: \"underline\",\n\t\t\t\t\tcolor: \"#39f\",\n\t\t\t\t\tmargin: \"0 10px\",\n\t\t\t\t},\n\t\t\t\ttextContent: `...`,\n\t\t\t\tonclick() {\n\t\t\t\t\tconst $val = getVal(value[key]);\n\t\t\t\t\tthis.parentElement.replaceChild($val, this);\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\t$val = getVal(value[key]);\n\t\t}\n\n\t\treturn tag(\"c-line\", {\n\t\t\tchildren: [$key, $val],\n\t\t});\n\n\t\tfunction getVal(val) {\n\t\t\tconst type = typeof val;\n\t\t\tconst $val = getElement(type);\n\t\t\tif (type === \"object\" && val !== null) {\n\t\t\t\t$val.append(...getBody(obj, ...keys, key));\n\t\t\t} else {\n\t\t\t\tif (type === \"function\") {\n\t\t\t\t\tval = parseFunction(val);\n\t\t\t\t}\n\t\t\t\t$val.textContent = val + \"\";\n\t\t\t}\n\t\t\treturn $val;\n\t\t}\n\t}\n\n\tfunction objValue(obj, ...keys) {\n\t\treturn keys.reduce((acc, key) => acc[key], obj);\n\t}\n\n\tfunction getPromiseStatus(obj) {\n\t\tif (obj.info) return;\n\t\tlet status = \"pending\";\n\t\tlet value;\n\t\tlet result = obj.then(\n\t\t\t(val) => {\n\t\t\t\tstatus = \"resolved\";\n\t\t\t\tvalue = val;\n\t\t\t},\n\t\t\t(val) => {\n\t\t\t\tstatus = \"rejected\";\n\t\t\t\tvalue = val;\n\t\t\t},\n\t\t);\n\n\t\tObject.defineProperties(result, {\n\t\t\t\"[[PromiseStatus]]\": {\n\t\t\t\tget: () => status,\n\t\t\t},\n\t\t\t\"[[PromiseValue]]\": {\n\t\t\t\tget: () => value,\n\t\t\t},\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tfunction getElement(type) {\n\t\treturn tag(\"c-text\", {\n\t\t\tclassName: `__c-${type}`,\n\t\t});\n\t}\n\n\t/** @type {import(\"acorn\").Options} */\n\tconst acornOptions = {\n\t\tecmaVersion: \"latest\",\n\t};\n\n\tfunction parseFunction(data) {\n\t\tlet parsed;\n\t\tlet str;\n\n\t\ttry {\n\t\t\tparsed = parse(data.toString(), acornOptions).body[0];\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tconst fun = (\"(\" + data.toString() + \")\").replace(/\\{.*\\}/, \"{}\");\n\t\t\t\tparsed = parse(fun, acornOptions).body[0];\n\t\t\t} catch (error) {\n\t\t\t\treturn data\n\t\t\t\t\t.toString()\n\t\t\t\t\t.replace(/({).*(})/, \"$1...$2\")\n\t\t\t\t\t.replace(/^function\\s+[\\w_$\\d]+\\s*/, \"\")\n\t\t\t\t\t.replace(/\\s*/g, \"\");\n\t\t\t}\n\t\t}\n\n\t\tif (parsed.type === \"ExpressionStatement\") {\n\t\t\tconst expression = parsed.expression;\n\t\t\tif (expression.type === \"ArrowFunctionExpression\") {\n\t\t\t\tstr = joinParams(expression.params, \"arrow\");\n\t\t\t} else if (expression.type === \"FunctionExpression\") {\n\t\t\t\tstr = joinParams(expression.params);\n\t\t\t}\n\t\t} else {\n\t\t\tlet string = parsed.id.name + joinParams(parsed.params || []);\n\t\t\tstr = string;\n\t\t}\n\n\t\tfunction joinParams(params, type) {\n\t\t\tlet parameter = \"(\";\n\t\t\tparams.map(\n\t\t\t\t(param) =>\n\t\t\t\t\t(parameter +=\n\t\t\t\t\t\tparam.type === \"RestElement\"\n\t\t\t\t\t\t\t? \"...\" + param.argument.name\n\t\t\t\t\t\t\t: param.name + \",\"),\n\t\t\t);\n\t\t\tparameter = parameter.replace(/,$/, \"\");\n\t\t\tparameter += \")\" + (type === \"arrow\" ? \"=>\" : \"\") + \"{...}\";\n\t\t\treturn parameter;\n\t\t}\n\n\t\treturn str;\n\t}\n\n\t/**\n\t * Prints to the console.\n\t * @param {'log'|'error'|'warn'|'code'|'trace'|'table'} mode\n\t * @param {{stack: string, location: string}} options\n\t * @param  {...any} args\n\t */\n\tfunction log(mode, options, ...args) {\n\t\tlet location = options.location || \"console\";\n\t\tconst $messages = tag(\"c-message\", {\n\t\t\tattr: {\n\t\t\t\t\"log-level\": mode,\n\t\t\t},\n\t\t});\n\n\t\targs = format(args);\n\n\t\tif (args.length === 1 && args[0] instanceof Error) {\n\t\t\targs.unshift(args[0].message);\n\t\t}\n\n\t\tfor (let arg of args) {\n\t\t\tconst typeofArg = typeof arg;\n\t\t\targ = arg ?? arg + \"\";\n\n\t\t\tlet $msg;\n\t\t\tif (mode === \"code\") {\n\t\t\t\t$msg = tag(\"c-code\");\n\t\t\t\t$msg.textContent = arg.length > 50 ? arg.substring(0, 50) + \"...\" : arg;\n\t\t\t\t$msg.setAttribute(\"data-code\", arg);\n\t\t\t\t$msg.setAttribute(\"action\", \"use code\");\n\t\t\t} else {\n\t\t\t\t$msg = getElement(typeofArg);\n\n\t\t\t\tswitch (typeofArg) {\n\t\t\t\t\tcase \"object\":\n\t\t\t\t\t\t$msg.append(...getBody(arg));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"function\":\n\t\t\t\t\t\t$msg.innerHTML = parseFunction(arg);\n\t\t\t\t\t\t$msg.append(\n\t\t\t\t\t\t\ttag(\"c-line\", {\n\t\t\t\t\t\t\t\tchildren: getBody(arg),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t$msg.textContent = arg;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$messages.appendChild($msg);\n\t\t}\n\n\t\tif (location) {\n\t\t\tconst $stack = tag(\"c-stack\");\n\t\t\t$stack.innerHTML = `<c-date>${new Date().toLocaleString()}</c-date><c-trace>${location}</c-trace>`;\n\t\t\t$messages.appendChild($stack);\n\t\t}\n\n\t\t$console.insertBefore($messages, $inputContainer);\n\n\t\twhile ($console.childElementCount > 100) {\n\t\t\t$console.firstElementChild.remove();\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {Array<any>} args\n\t * @returns\n\t */\n\tfunction format(args) {\n\t\tif (args.length <= 1) return [args[0]];\n\n\t\tconst originalArgs = [].concat(args);\n\t\tconst styles = [];\n\t\tlet msg = args.splice(0, 1)[0];\n\n\t\tif (typeof msg !== \"string\") return originalArgs;\n\n\t\tlet matched = matchRegex(msg);\n\t\tlet match;\n\t\twhile ((match = matched.next())) {\n\t\t\tif (match.done) break;\n\t\t\tlet value = \"\";\n\t\t\tconst specifier = match.value[0];\n\t\t\tconst pos = match.value.index;\n\n\t\t\tif (!args.length) {\n\t\t\t\tvalue = specifier;\n\t\t\t} else {\n\t\t\t\tvalue = args.splice(0, 1)[0];\n\t\t\t\tif ([undefined, null].includes(value)) {\n\t\t\t\t\tvalue = value + \"\";\n\t\t\t\t}\n\n\t\t\t\tswitch (specifier) {\n\t\t\t\t\tcase \"%c\":\n\t\t\t\t\t\tstyles.push({\n\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\tpos,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tvalue = \"\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"%s\":\n\t\t\t\t\t\tif (typeof value === \"object\") {\n\t\t\t\t\t\t\tvalue = value.constructor.name;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"%o\":\n\t\t\t\t\tcase \"%O\":\n\t\t\t\t\t\tlet id = new Date().getMilliseconds() + \"\";\n\t\t\t\t\t\twindow.__objs[id] = value;\n\t\t\t\t\t\tvalue = `<c-object onclick='console.log(window.__objs[${id}])'>Object</c-object>`;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"%d\":\n\t\t\t\t\tcase \"%i\":\n\t\t\t\t\t\tvalue = Number.parseInt(value);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"%f\":\n\t\t\t\t\t\tvalue = Number.parseFloat(value);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only escape HTML for the %o/%O case where we're injecting actual HTML\n\t\t\tconst escapedValue =\n\t\t\t\tspecifier === \"%o\" || specifier === \"%O\" ? value : escapeHTML(value);\n\t\t\tmsg = msg.substring(0, pos) + escapedValue + msg.substring(pos + 2);\n\t\t\tmatched = matchRegex(msg);\n\t\t}\n\n\t\tif (styles.length) {\n\t\t\tconst toBeStyled = [];\n\t\t\tlet remainingMsg = msg;\n\t\t\tstyles.reverse().forEach((style, i) => {\n\t\t\t\ttoBeStyled.push(remainingMsg.substring(style.pos));\n\t\t\t\tremainingMsg = msg.substring(0, style.pos);\n\t\t\t\tif (i === styles.length - 1)\n\t\t\t\t\ttoBeStyled.push(msg.substring(0, style.pos));\n\t\t\t});\n\t\t\tmsg = toBeStyled\n\t\t\t\t.map((str, i) => {\n\t\t\t\t\tif (i === toBeStyled.length - 1) return str;\n\t\t\t\t\tconst { value } = styles[i];\n\t\t\t\t\treturn `<c-span style=\"${value}\">${str}</c-span>`;\n\t\t\t\t})\n\t\t\t\t.reverse()\n\t\t\t\t.join(\"\");\n\t\t}\n\n\t\tmsg.replace(/%%[oOsdifc]/g, \"%\");\n\n\t\targs.unshift(msg);\n\t\treturn args;\n\n\t\t/**\n\t\t *\n\t\t * @param {string} str\n\t\t * @returns {IterableIterator<RegExpMatchArray>}\n\t\t */\n\t\tfunction matchRegex(str) {\n\t\t\treturn str.matchAll(/(?<!%)%[oOsdifc]/g);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the stack trace of the current call\n\t * @param {Error} error\n\t * @returns\n\t */\n\tfunction getStack(error, skip = false) {\n\t\tif (error === null) {\n\t\t\terror = new Error();\n\t\t}\n\t\tlet stack = error.stack.split(\"\\n\");\n\t\tif (!skip) stack.splice(1, 1);\n\t\tlet regExecRes = /<(.*)>:(\\d+):(\\d+)/.exec(stack[1]) || [];\n\t\tif (!regExecRes.length) {\n\t\t\tconst errorInfo = stack[1]?.split(\"/\").pop();\n\t\t\tregExecRes = /(.+):(\\d+):(\\d+)/.exec(errorInfo) || [];\n\t\t}\n\t\tlet src = \"\";\n\t\tconst location = regExecRes[1];\n\t\tconst lineno = regExecRes[2];\n\t\tconst colno = regExecRes[3];\n\n\t\tif (location && lineno) {\n\t\t\tsrc = escapeHTML(`${location} ${lineno}${colno ? \":\" + colno : \"\"}`);\n\t\t} else {\n\t\t\tconst res = /\\((.*)\\)/.exec(stack[1]);\n\t\t\tsrc = res && res[1] ? res[1] : \"\";\n\t\t}\n\t\tconst index = src.indexOf(\")\");\n\t\tsrc = src\n\t\t\t.split(\"/\")\n\t\t\t.pop()\n\t\t\t.substring(0, index < 0 ? undefined : index);\n\t\tif (src.length > 50) src = \"...\" + src.substring(src.length - 50);\n\n\t\treturn {\n\t\t\tlocation: src,\n\t\t\tstack: stack.join(\"\\n\"),\n\t\t};\n\t}\n\n\tfunction execute(code) {\n\t\tlet res = null;\n\t\ttry {\n\t\t\tconst parsed = parse(code, acornOptions).body;\n\t\t\tres = execParsedCode(parsed);\n\t\t} catch (e) {\n\t\t\tres = execParsedCode([]);\n\t\t}\n\n\t\treturn res;\n\n\t\tfunction execParsedCode(parsed) {\n\t\t\tlet extra = \"\";\n\t\t\tparsed.map((st) => {\n\t\t\t\tif (st.type === \"VariableDeclaration\") {\n\t\t\t\t\tif ([\"const\", \"let\"].indexOf(st.kind) < 0) return;\n\n\t\t\t\t\tconst exCode = code.substring(st.start, st.end) + \";\";\n\t\t\t\t\textra += exCode;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (extra) {\n\t\t\t\tconst script = tag(\"script\");\n\t\t\t\tscript.textContent = extra;\n\t\t\t\tdocument.body.appendChild(script);\n\t\t\t\tdocument.body.removeChild(script);\n\t\t\t\treturn exec(code);\n\t\t\t} else {\n\t\t\t\treturn exec(code);\n\t\t\t}\n\t\t}\n\n\t\tfunction exec(code) {\n\t\t\tlet res = null;\n\t\t\ttry {\n\t\t\t\tres = { type: \"result\", value: window.eval(code) };\n\t\t\t} catch (error) {\n\t\t\t\tres = { type: \"error\", value: error };\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}\n\t}\n\n\tfunction onError(err) {\n\t\tconst error = err.error;\n\t\tlog(\"error\", getStack(error, true), error);\n\t}\n\n\tfunction escapeHTML(str) {\n\t\tif (typeof str !== \"string\") return str;\n\t\treturn tag(\"textarea\", {\n\t\t\ttextContent: str,\n\t\t}).innerHTML;\n\t}\n})();\n"
  },
  {
    "path": "src/lib/constants.js",
    "content": "export default {\n\tFILE_NAME_REGEX: /^((?![:<>\"\\\\\\|\\?\\*]).)*$/,\n\tFONT_SIZE: /^[0-9\\.]{1,3}(px|rem|em|pt|mm|pc|in)$/,\n\tDEFAULT_FILE_SESSION: \"default-session\",\n\tDEFAULT_FILE_NAME: \"untitled.txt\",\n\tCONSOLE_PORT: 8159,\n\tSERVER_PORT: 8158,\n\tPREVIEW_PORT: 8158,\n\tVIBRATION_TIME: 30,\n\tVIBRATION_TIME_LONG: 150,\n\tSCROLL_SPEED_FAST_X2: \"FAST_X2\",\n\tSCROLL_SPEED_NORMAL: \"NORMAL\",\n\tSCROLL_SPEED_FAST: \"FAST\",\n\tSCROLL_SPEED_SLOW: \"SLOW\",\n\tSIDEBAR_SLIDE_START_THRESHOLD_PX: 20,\n\tCUSTOM_THEME: 'body[theme=\"custom\"]',\n\tFEEDBACK_EMAIL: \"acode@foxdebug.com\",\n\tERUDA_CDN: \"https://cdn.jsdelivr.net/npm/eruda\",\n\tget PLAY_STORE_URL() {\n\t\treturn `https://play.google.com/store/apps/details?id=${BuildInfo.packageName}`;\n\t},\n\tAPI_BASE: \"https://acode.app/api\",\n\t// API_BASE: 'https://192.168.0.102:3001/api', // test api\n\tSKU_LIST: [\"crystal\", \"bronze\", \"silver\", \"gold\", \"platinum\", \"titanium\"],\n\tLOG_FILE_NAME: \"Acode.log\",\n\n\t// Social Links\n\tDOCS_URL: \"https://docs.acode.app\",\n\tWEBSITE_URL: \"https://acode.app\",\n\tGITHUB_URL: \"https://github.com/Acode-Foundation/Acode\",\n\tTELEGRAM_URL: \"https://t.me/foxdebug_acode\",\n\tDISCORD_URL: \"https://discord.gg/nDqZsh7Rqz\",\n\tTWITTER_URL: \"https://x.com/foxbiz_io\",\n\tINSTAGRAM_URL: \"https://www.instagram.com/foxbiz.io/\",\n\tFOXBIZ_URL: \"https://foxbiz.io\",\n};\n"
  },
  {
    "path": "src/lib/devTools.js",
    "content": "import fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport loader from \"dialogs/loader\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\n\nlet erudaInstance = null;\nlet isInitialized = false;\n\n/**\n * Developer tools module for debugging Acode\n */\nconst devTools = {\n\t/**\n\t * Check if Eruda is initialized\n\t * @returns {boolean}\n\t */\n\tget isInitialized() {\n\t\treturn isInitialized;\n\t},\n\n\t/**\n\t * Get the Eruda instance\n\t * @returns {object|null}\n\t */\n\tget eruda() {\n\t\treturn erudaInstance;\n\t},\n\n\t/**\n\t * Initialize Eruda for developer mode\n\t * @param {boolean} showLoader - Whether to show a loading dialog\n\t * @returns {Promise<void>}\n\t */\n\tasync init(showLoader = false) {\n\t\tif (isInitialized) return;\n\n\t\ttry {\n\t\t\tconst erudaPath = Url.join(DATA_STORAGE, \"eruda.js\");\n\t\t\tconst fs = fsOperation(erudaPath);\n\n\t\t\tif (!(await fs.exists())) {\n\t\t\t\tif (showLoader) {\n\t\t\t\t\tloader.create(\n\t\t\t\t\t\tstrings[\"downloading file\"]?.replace(\"{file}\", \"eruda.js\") ||\n\t\t\t\t\t\t\t\"Downloading eruda.js...\",\n\t\t\t\t\t\tstrings[\"downloading...\"] || \"Downloading...\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst erudaScript = await ajax({\n\t\t\t\t\t\turl: constants.ERUDA_CDN,\n\t\t\t\t\t\tresponseType: \"text\",\n\t\t\t\t\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\t\t\t\t});\n\t\t\t\t\tawait fsOperation(DATA_STORAGE).createFile(\"eruda.js\", erudaScript);\n\t\t\t\t} finally {\n\t\t\t\t\tif (showLoader) loader.destroy();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst internalUri = await helpers.toInternalUri(erudaPath);\n\n\t\t\tawait new Promise((resolve, reject) => {\n\t\t\t\tconst script = document.createElement(\"script\");\n\t\t\t\tscript.src = internalUri;\n\t\t\t\tscript.id = \"eruda-script\";\n\t\t\t\tscript.onload = resolve;\n\t\t\t\tscript.onerror = reject;\n\t\t\t\tdocument.head.appendChild(script);\n\t\t\t});\n\n\t\t\tif (window.eruda) {\n\t\t\t\twindow.eruda.init({\n\t\t\t\t\tuseShadowDom: true,\n\t\t\t\t\tautoScale: true,\n\t\t\t\t\tdefaults: {\n\t\t\t\t\t\tdisplaySize: 50,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\twindow.eruda._shadowRoot.querySelector(\n\t\t\t\t\t\".eruda-entry-btn\",\n\t\t\t\t).style.display = \"none\";\n\n\t\t\t\terudaInstance = window.eruda;\n\t\t\t\tisInitialized = true;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to initialize developer tools\", error);\n\t\t\tthrow error;\n\t\t}\n\t},\n\n\t/**\n\t * Show the inspector panel\n\t */\n\tshow() {\n\t\tif (!isInitialized) {\n\t\t\twindow.toast?.(\"Developer mode is not enabled\");\n\t\t\treturn;\n\t\t}\n\t\tconst entryBtn =\n\t\t\terudaInstance?._shadowRoot?.querySelector(\".eruda-entry-btn\");\n\t\tif (entryBtn) entryBtn.style.display = \"\";\n\t\terudaInstance?.show();\n\t},\n\n\t/**\n\t * Hide the inspector panel\n\t */\n\thide() {\n\t\tif (!isInitialized) return;\n\t\terudaInstance?.hide();\n\t\tconst entryBtn =\n\t\t\terudaInstance?._shadowRoot?.querySelector(\".eruda-entry-btn\");\n\t\tif (entryBtn) entryBtn.style.display = \"none\";\n\t},\n\n\t/**\n\t * Toggle the inspector panel visibility\n\t */\n\ttoggle() {\n\t\tif (!isInitialized) {\n\t\t\twindow.toast?.(\"Developer mode is not enabled\");\n\t\t\treturn;\n\t\t}\n\t\tif (erudaInstance?._isShow) {\n\t\t\tthis.hide();\n\t\t} else {\n\t\t\tthis.show();\n\t\t}\n\t},\n\n\t/**\n\t * Destroy Eruda instance\n\t */\n\tdestroy() {\n\t\tif (!isInitialized) return;\n\t\terudaInstance?.destroy();\n\t\terudaInstance = null;\n\t\tisInitialized = false;\n\t\tconst script = document.getElementById(\"eruda-script\");\n\t\tif (script) script.remove();\n\t},\n};\n\nexport default devTools;\n"
  },
  {
    "path": "src/lib/editorFile.js",
    "content": "import fsOperation from \"fileSystem\";\n// CodeMirror imports for document state management\nimport { EditorState, Text } from \"@codemirror/state\";\nimport {\n\tclearSelection,\n\trestoreFolds,\n\trestoreSelection,\n\tsetScrollPosition,\n} from \"cm/editorUtils\";\nimport { getMode, getModeForPath } from \"cm/modelist\";\nimport Sidebar from \"components/sidebar\";\nimport tile from \"components/tile\";\nimport toast from \"components/toast\";\nimport confirm from \"dialogs/confirm\";\nimport DOMPurify from \"dompurify\";\nimport startDrag from \"handlers/editorFileTab\";\nimport actions from \"handlers/quickTools\";\nimport tag from \"html-tag-js\";\nimport mimeTypes from \"mime-types\";\nimport helpers from \"utils/helpers\";\nimport Path from \"utils/Path\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\nimport openFolder from \"./openFolder\";\nimport run from \"./run\";\nimport saveFile from \"./saveFile\";\nimport appSettings from \"./settings\";\n\n/**\n * Creates a Proxy around an EditorState that provides Ace-compatible methods.\n * @param {EditorState} state - The raw CodeMirror EditorState\n * @param {EditorFile} file - The parent EditorFile instance\n * @returns {Proxy} Proxied state with Ace-compatible methods\n */\nfunction createSessionProxy(state, file) {\n\tif (!state) return null;\n\n\t/**\n\t * Convert Ace position {row, column} to CodeMirror offset\n\t */\n\tfunction positionToOffset(pos, doc) {\n\t\tif (!pos || !doc) return 0;\n\t\ttry {\n\t\t\tconst lineNum = Math.max(1, Math.min((pos.row ?? 0) + 1, doc.lines));\n\t\t\tconst line = doc.line(lineNum);\n\t\t\tconst col = Math.max(0, Math.min(pos.column ?? 0, line.length));\n\t\t\treturn line.from + col;\n\t\t} catch (_) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Convert CodeMirror offset to Ace position {row, column}\n\t */\n\tfunction offsetToPosition(offset, doc) {\n\t\tif (!doc) return { row: 0, column: 0 };\n\t\ttry {\n\t\t\tconst line = doc.lineAt(offset);\n\t\t\treturn { row: line.number - 1, column: offset - line.from };\n\t\t} catch (_) {\n\t\t\treturn { row: 0, column: 0 };\n\t\t}\n\t}\n\n\treturn new Proxy(state, {\n\t\tget(target, prop) {\n\t\t\t// Ace-compatible method: getValue()\n\t\t\tif (prop === \"getValue\") {\n\t\t\t\treturn () => target.doc.toString();\n\t\t\t}\n\n\t\t\t// Ace-compatible method: setValue(text)\n\t\t\tif (prop === \"setValue\") {\n\t\t\t\treturn (text) => {\n\t\t\t\t\tconst newText = String(text ?? \"\");\n\t\t\t\t\tconst { activeFile, editor } = editorManager;\n\t\t\t\t\tif (activeFile?.id === file.id && editor) {\n\t\t\t\t\t\t// Active file: dispatch to live EditorView\n\t\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\t\tchanges: {\n\t\t\t\t\t\t\t\tfrom: 0,\n\t\t\t\t\t\t\t\tto: editor.state.doc.length,\n\t\t\t\t\t\t\t\tinsert: newText,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Inactive file: update stored state\n\t\t\t\t\t\tfile._setRawSession(\n\t\t\t\t\t\t\ttarget.update({\n\t\t\t\t\t\t\t\tchanges: { from: 0, to: target.doc.length, insert: newText },\n\t\t\t\t\t\t\t}).state,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Ace-compatible method: getLine(row)\n\t\t\tif (prop === \"getLine\") {\n\t\t\t\treturn (row) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn target.doc.line(row + 1).text;\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Ace-compatible method: getLength()\n\t\t\tif (prop === \"getLength\") {\n\t\t\t\treturn () => target.doc.lines;\n\t\t\t}\n\n\t\t\t// Ace-compatible method: getTextRange(range)\n\t\t\tif (prop === \"getTextRange\") {\n\t\t\t\treturn (range) => {\n\t\t\t\t\tif (!range) return \"\";\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst from = positionToOffset(range.start, target.doc);\n\t\t\t\t\t\tconst to = positionToOffset(range.end, target.doc);\n\t\t\t\t\t\treturn target.doc.sliceString(from, to);\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Ace-compatible method: insert(position, text)\n\t\t\tif (prop === \"insert\") {\n\t\t\t\treturn (position, text) => {\n\t\t\t\t\tconst { activeFile, editor } = editorManager;\n\t\t\t\t\tconst offset = positionToOffset(position, target.doc);\n\t\t\t\t\tif (activeFile?.id === file.id && editor) {\n\t\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\t\tchanges: { from: offset, insert: String(text ?? \"\") },\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfile._setRawSession(\n\t\t\t\t\t\t\ttarget.update({\n\t\t\t\t\t\t\t\tchanges: { from: offset, insert: String(text ?? \"\") },\n\t\t\t\t\t\t\t}).state,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Ace-compatible method: remove(range)\n\t\t\tif (prop === \"remove\") {\n\t\t\t\treturn (range) => {\n\t\t\t\t\tif (!range) return \"\";\n\t\t\t\t\tconst from = positionToOffset(range.start, target.doc);\n\t\t\t\t\tconst to = positionToOffset(range.end, target.doc);\n\t\t\t\t\tconst removed = target.doc.sliceString(from, to);\n\t\t\t\t\tconst { activeFile, editor } = editorManager;\n\t\t\t\t\tif (activeFile?.id === file.id && editor) {\n\t\t\t\t\t\teditor.dispatch({ changes: { from, to, insert: \"\" } });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfile._setRawSession(\n\t\t\t\t\t\t\ttarget.update({ changes: { from, to, insert: \"\" } }).state,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn removed;\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Ace-compatible method: replace(range, text)\n\t\t\tif (prop === \"replace\") {\n\t\t\t\treturn (range, text) => {\n\t\t\t\t\tif (!range) return;\n\t\t\t\t\tconst from = positionToOffset(range.start, target.doc);\n\t\t\t\t\tconst to = positionToOffset(range.end, target.doc);\n\t\t\t\t\tconst { activeFile, editor } = editorManager;\n\t\t\t\t\tif (activeFile?.id === file.id && editor) {\n\t\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\t\tchanges: { from, to, insert: String(text ?? \"\") },\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfile._setRawSession(\n\t\t\t\t\t\t\ttarget.update({\n\t\t\t\t\t\t\t\tchanges: { from, to, insert: String(text ?? \"\") },\n\t\t\t\t\t\t\t}).state,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Ace-compatible method: getWordRange(row, column)\n\t\t\tif (prop === \"getWordRange\") {\n\t\t\t\treturn (row, column) => {\n\t\t\t\t\tconst offset = positionToOffset({ row, column }, target.doc);\n\t\t\t\t\tconst word = target.wordAt(offset);\n\t\t\t\t\tif (word) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tstart: offsetToPosition(word.from, target.doc),\n\t\t\t\t\t\t\tend: offsetToPosition(word.to, target.doc),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn { start: { row, column }, end: { row, column } };\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Pass through all other properties to the real EditorState\n\t\t\tconst value = target[prop];\n\t\t\tif (typeof value === \"function\") {\n\t\t\t\treturn value.bind(target);\n\t\t\t}\n\t\t\treturn value;\n\t\t},\n\t});\n}\n\n/**\n * @typedef {'run'|'save'|'change'|'focus'|'blur'|'close'|'rename'|'load'|'loadError'|'loadStart'|'loadEnd'|'changeMode'|'changeEncoding'|'changeReadOnly'} FileEvents\n */\n\n/**\n * @typedef {object}  FileOptions new file options\n * @property {boolean} [isUnsaved] weather file needs to saved\n * @property {render} [render] make file active\n * @property {string} [id] ID for the file\n * @property {string} [uri] uri of the file\n * @property {string} [text] session text\n * @property {boolean} [editable] enable file to edit or not\n * @property {boolean} [deletedFile] file do not exists at source\n * @property {'single' | 'tree'} [SAFMode] storage access framework mode\n * @property {string} [encoding] text encoding\n * @property {object} [cursorPos] cursor position\n * @property {number} [scrollLeft] scroll left\n * @property {number} [scrollTop] scroll top\n * @property {Array<Fold>} [folds] folds\n * @property {boolean} [pinned] pin the tab to prevent accidental closing\n */\n\nexport default class EditorFile {\n\t/**\n\t * Type of content this file represents but use page in case of custom pages etc\n\t */\n\t#type = \"editor\";\n\t#tabIcon = \"file file_type_default\";\n\t/**\n\t * Custom content element\n\t * @type {HTMLElement}\n\t */\n\t#content = null;\n\t/**\n\t * Whether to hide quicktools for this tab\n\t * @type {boolean}\n\t */\n\thideQuickTools = false;\n\n\t/**\n\t * Custom stylesheets for tab\n\t * @type {string|string[]}\n\t */\n\tstylesheets;\n\n\t/**\n\t * Custom title function for special tab types\n\t * @type {function}\n\t */\n\t#customTitleFn = null;\n\n\t/**\n\t * If editor was focused before resize\n\t */\n\tfocusedBefore = false;\n\t/**\n\t * State of the editor for this file.\n\t */\n\tfocused = false;\n\t/**\n\t * Weather the file has completed loading text or not\n\t * @type {boolean}\n\t */\n\tloaded = true;\n\t/**\n\t * Weather file is still loading the text from the source\n\t * @type {boolean}\n\t */\n\tloading = false;\n\t/**\n\t * Weather file is deleted from source.\n\t * @type {boolean}\n\t */\n\tdeletedFile = false;\n\t/**\n\t * Raw CodeMirror EditorState. Use session getter to access with Ace-compatible methods.\n\t * @type {EditorState}\n\t */\n\t#rawSession = null;\n\t/**\n\t * Encoding of the text e.g. 'gbk'\n\t * @type {string}\n\t */\n\tencoding = appSettings.value.defaultFileEncoding;\n\t/**\n\t * Weather file is readonly\n\t * @type {boolean}\n\t */\n\treadOnly = false;\n\t/**\n\t * mark change when session text is changed\n\t * @type {boolean}\n\t */\n\tmarkChanged = true;\n\t/**\n\t * Storage access framework file mode\n\t * @type {'single' | 'tree' | null}\n\t */\n\t#SAFMode = null;\n\t/**\n\t * Name of the file\n\t * @type {string}\n\t */\n\t#name = constants.DEFAULT_FILE_NAME;\n\t/**\n\t * Location of the file\n\t * @type {string}\n\t */\n\t#uri;\n\t/**\n\t * Unique ID of the file, changed when file is renamed or location/uri is changed.\n\t * @type {string}\n\t */\n\t#id = constants.DEFAULT_FILE_SESSION;\n\t/**\n\t * Associated tile for the file, that is append in the open file list,\n\t * when clicked make the file active.\n\t * @type {HTMLElement}\n\t */\n\t#tab;\n\t/**\n\t * Weather file can be edited or not\n\t * @type {boolean}\n\t */\n\t#editable = true;\n\t/**\n\t * Prevents the tab from being closed until it is unpinned.\n\t * @type {boolean}\n\t */\n\t#pinned = false;\n\t/**\n\t * contains information about cursor position, scroll left, scroll top, folds.\n\t */\n\t#loadOptions;\n\t/**\n\t * Weather file is changed and needs to be saved\n\t * @type {boolean}\n\t */\n\t#isUnsaved = false;\n\t/**\n\t * Whether to show run button or not\n\t */\n\t#canRun = Promise.resolve(false);\n\t/**\n\t * @type {function} event handler\n\t */\n\t#onFilePosChange;\n\t#events = {\n\t\tsave: [],\n\t\tchange: [],\n\t\tfocus: [],\n\t\tblur: [],\n\t\tclose: [],\n\t\trename: [],\n\t\tload: [],\n\t\tloaderror: [],\n\t\tloadstart: [],\n\t\tloadend: [],\n\t\tchangemode: [],\n\t\trun: [],\n\t\tcanrun: [],\n\t};\n\n\tonsave;\n\tonchange;\n\tonfocus;\n\tonblur;\n\tonclose;\n\tonrename;\n\tonload;\n\tonloaderror;\n\tonloadstart;\n\tonloadend;\n\tonchangemode;\n\tonrun;\n\toncanrun;\n\tonpinstatechange;\n\n\t/**\n\t *\n\t * @param {string} [filename] name of file.\n\t * @param {FileOptions} [options]  file create options\n\t */\n\tconstructor(filename, options) {\n\t\tconst { addFile, getFile } = editorManager;\n\t\tlet doesExists = null;\n\n\t\tthis.hideQuickTools = options?.hideQuickTools || false;\n\n\t\t// if options are passed\n\t\tif (options) {\n\t\t\t// if options doesn't contains id, and provide a new id\n\t\t\tif (!options.id) {\n\t\t\t\tif (options.uri) this.#id = options.uri.hashCode();\n\t\t\t\telse this.#id = helpers.uuid();\n\t\t\t} else this.#id = options.id;\n\t\t} else if (!options) {\n\t\t\t// if options aren't passed, that means default file is being created\n\t\t\tthis.#id = constants.DEFAULT_FILE_SESSION;\n\t\t}\n\n\t\tif (options?.type) {\n\t\t\tthis.#type = options.type;\n\t\t\tif (this.#type !== \"editor\") {\n\t\t\t\tlet container;\n\t\t\t\tlet shadow;\n\n\t\t\t\tif (this.#type === \"terminal\") {\n\t\t\t\t\tcontainer = tag(\"div\", {\n\t\t\t\t\t\tclassName: \"tab-page-container\",\n\t\t\t\t\t});\n\t\t\t\t\tconst content = tag(\"div\", {\n\t\t\t\t\t\tclassName: \"tab-page-content\",\n\t\t\t\t\t});\n\t\t\t\t\tcontent.appendChild(options?.content);\n\t\t\t\t\tcontainer.appendChild(content);\n\t\t\t\t\tthis.#content = container;\n\t\t\t\t} else {\n\t\t\t\t\tcontainer = <div className=\"tab-page-container\" />;\n\n\t\t\t\t\t// shadow dom\n\t\t\t\t\tshadow = container.attachShadow({ mode: \"open\" });\n\n\t\t\t\t\t// Add base styles to shadow DOM first\n\t\t\t\t\tshadow.appendChild(<link rel=\"stylesheet\" href=\"build/main.css\" />);\n\n\t\t\t\t\t// Handle custom stylesheets if provided\n\t\t\t\t\tif (options.stylesheets) {\n\t\t\t\t\t\tthis.#addCustomStyles(options.stylesheets, shadow);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst content = <div className=\"tab-page-content\" />;\n\n\t\t\t\t\tif (typeof options.content === \"string\") {\n\t\t\t\t\t\tcontent.innerHTML = DOMPurify.sanitize(options.content);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontent.appendChild(options.content);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Append content container to shadow DOM\n\t\t\t\t\tshadow.appendChild(content);\n\n\t\t\t\t\tthis.#content = container;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.#content = options.content;\n\t\t\t}\n\t\t\tif (options.tabIcon) {\n\t\t\t\tthis.#tabIcon = options.tabIcon;\n\t\t\t}\n\t\t}\n\n\t\tthis.#uri = options?.uri;\n\n\t\tif (this.#id) doesExists = getFile(this.#id, \"id\");\n\t\telse if (this.#uri) doesExists = getFile(this.#uri, \"uri\");\n\n\t\tif (doesExists) {\n\t\t\tdoesExists.makeActive();\n\t\t\treturn;\n\t\t}\n\n\t\tif (filename) this.#name = filename;\n\n\t\tthis.#tab = tile({\n\t\t\ttext: this.#name,\n\t\t\t...(this.#type !== \"editor\" && {\n\t\t\t\tlead: (\n\t\t\t\t\t<span className={this.icon} style={{ paddingRight: \"5px\" }}></span>\n\t\t\t\t),\n\t\t\t}),\n\t\t\ttail: tag(\"span\", {\n\t\t\t\tclassName: \"icon cancel\",\n\t\t\t\tdataset: {\n\t\t\t\t\taction: \"close-file\",\n\t\t\t\t},\n\t\t\t}),\n\t\t});\n\n\t\tconst editable = options?.editable ?? true;\n\n\t\tthis.#SAFMode = options?.SAFMode;\n\t\tthis.isUnsaved = options?.isUnsaved ?? false;\n\n\t\tif (options?.encoding) {\n\t\t\tthis.encoding = options.encoding;\n\t\t}\n\n\t\t// if options contains text property then there is no need to load\n\t\t// set loaded true\n\n\t\tif (this.#id !== constants.DEFAULT_FILE_SESSION) {\n\t\t\tthis.loaded = options?.text !== undefined;\n\t\t}\n\n\t\t// if not loaded then create load options\n\t\tif (!this.loaded) {\n\t\t\tthis.#loadOptions = {\n\t\t\t\tcursorPos: options?.cursorPos,\n\t\t\t\tscrollLeft: options?.scrollLeft,\n\t\t\t\tscrollTop: options?.scrollTop,\n\t\t\t\tfolds: options?.folds,\n\t\t\t\teditable,\n\t\t\t};\n\t\t} else {\n\t\t\tthis.editable = editable;\n\t\t}\n\n\t\tthis.#onFilePosChange = () => {\n\t\t\tconst { openFileListPos } = appSettings.value;\n\t\t\tif (\n\t\t\t\topenFileListPos === appSettings.OPEN_FILE_LIST_POS_HEADER ||\n\t\t\t\topenFileListPos === appSettings.OPEN_FILE_LIST_POS_BOTTOM\n\t\t\t) {\n\t\t\t\tthis.#tab.oncontextmenu = startDrag;\n\t\t\t} else {\n\t\t\t\tthis.#tab.oncontextmenu = null;\n\t\t\t}\n\t\t};\n\n\t\tthis.#onFilePosChange();\n\t\tthis.#tab.addEventListener(\"click\", tabOnclick.bind(this));\n\t\tappSettings.on(\"update:openFileListPos\", this.#onFilePosChange);\n\t\tthis.pinned = !!options?.pinned;\n\n\t\taddFile(this);\n\t\teditorManager.emit(\"new-file\", this);\n\n\t\tif (this.#type === \"editor\") {\n\t\t\tthis.#rawSession = EditorState.create({\n\t\t\t\tdoc: options?.text || \"\",\n\t\t\t});\n\t\t\tthis.setMode();\n\t\t\tthis.#setupSession();\n\t\t}\n\n\t\tif (options?.render ?? true) this.render();\n\t}\n\n\tget type() {\n\t\treturn this.#type;\n\t}\n\n\tget tabIcon() {\n\t\treturn this.#tabIcon;\n\t}\n\n\tget content() {\n\t\treturn this.#content;\n\t}\n\n\t/**\n\t * Session with Ace-compatible methods\n\t * Returns a Proxy over the raw EditorState.\n\t * @returns {Proxy<EditorState>}\n\t */\n\tget session() {\n\t\treturn createSessionProxy(this.#rawSession, this);\n\t}\n\n\t/**\n\t * Set the session\n\t * @param {EditorState} value\n\t */\n\tset session(value) {\n\t\tthis.#rawSession = value;\n\t}\n\n\t/**\n\t * Internal method to update the raw session state.\n\t * Used by the Proxy for inactive file updates.\n\t * @param {EditorState} state\n\t */\n\t_setRawSession(state) {\n\t\tthis.#rawSession = state;\n\t}\n\n\t/**\n\t * File unique id.\n\t */\n\tget id() {\n\t\treturn this.#id;\n\t}\n\n\t/**\n\t * File unique id.\n\t * @param {string} value\n\t */\n\tset id(value) {\n\t\tthis.#renameCacheFile(value);\n\t\tthis.#id = value;\n\t}\n\n\t/**\n\t * File name\n\t */\n\tget filename() {\n\t\treturn this.#name;\n\t}\n\n\t/**\n\t * File name\n\t * @param {string} value\n\t */\n\tset filename(value) {\n\t\tif (!value || this.#SAFMode === \"single\") return;\n\t\tif (this.#name === value) return;\n\n\t\tconst event = createFileEvent(this);\n\t\tthis.#emit(\"rename\", event);\n\n\t\tif (event.defaultPrevented) return;\n\n\t\t(async () => {\n\t\t\tif (this.id === constants.DEFAULT_FILE_SESSION) {\n\t\t\t\tthis.id = helpers.uuid();\n\t\t\t}\n\n\t\t\tif (editorManager.activeFile.id === this.id) {\n\t\t\t\teditorManager.header.text = value;\n\t\t\t}\n\n\t\t\t// const oldExt = helpers.extname(this.#name);\n\t\t\tconst oldExt = Url.extname(this.#name);\n\t\t\t// const newExt = helpers.extname(value);\n\t\t\tconst newExt = Url.extname(value);\n\n\t\t\tthis.#tab.text = value;\n\t\t\tthis.#name = value;\n\n\t\t\tif (oldExt !== newExt) this.setMode();\n\n\t\t\teditorManager.onupdate(\"rename-file\");\n\t\t\teditorManager.emit(\"rename-file\", this);\n\t\t})();\n\t}\n\n\t/**\n\t * Location of the file i.e. dirname\n\t */\n\tget location() {\n\t\tif (this.#SAFMode === \"single\") return null;\n\t\tif (this.#uri) {\n\t\t\ttry {\n\t\t\t\treturn Url.dirname(this.#uri);\n\t\t\t} catch (error) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Location of the file i.e. dirname\n\t * @param {string} value\n\t */\n\tset location(value) {\n\t\tif (!value) return;\n\t\tif (this.#SAFMode === \"single\") return;\n\t\tif (this.location === value) return;\n\n\t\tconst event = createFileEvent(this);\n\t\tthis.#emit(\"rename\", event);\n\t\tif (event.defaultPrevented) return;\n\n\t\tthis.uri = Url.join(value, this.filename);\n\t\tthis.readOnly = false;\n\t}\n\n\t/**\n\t * File location on the device\n\t */\n\tget uri() {\n\t\treturn this.#uri;\n\t}\n\n\t/**\n\t *  File location on the device\n\t * @param {string} value\n\t */\n\tset uri(value) {\n\t\tif (this.#uri === value) return;\n\t\tif (!value) {\n\t\t\tthis.deletedFile = true;\n\t\t\tthis.isUnsaved = true;\n\t\t\tthis.#uri = null;\n\t\t\tthis.id = helpers.uuid();\n\t\t} else {\n\t\t\tthis.#uri = value;\n\t\t\tthis.deletedFile = false;\n\t\t\tthis.readOnly = false;\n\t\t\tthis.id = value.hashCode();\n\t\t}\n\n\t\teditorManager.onupdate(\"rename-file\");\n\t\teditorManager.emit(\"rename-file\", this);\n\n\t\t// if this file is active set sub text of header\n\t\tif (editorManager.activeFile.id === this.id) {\n\t\t\teditorManager.header.subText = this.#getTitle();\n\t\t}\n\t}\n\n\t/**\n\t * End of line\n\t */\n\tget eol() {\n\t\treturn /\\r/.test(this.session.doc.toString()) ? \"windows\" : \"unix\";\n\t}\n\n\t/**\n\t * End of line\n\t * @param {'windows'|'unit'} value\n\t */\n\tset eol(value) {\n\t\tif (this.type !== \"editor\") return;\n\t\tif (this.eol === value) return;\n\t\tlet text = this.session.doc.toString();\n\n\t\tif (value === \"windows\") {\n\t\t\ttext = text.replace(/(?<!\\r)\\n/g, \"\\r\\n\");\n\t\t} else {\n\t\t\ttext = text.replace(/\\r/g, \"\");\n\t\t}\n\n\t\t// Update the document in the session\n\t\tthis.session.setValue(text);\n\t}\n\n\t/**\n\t * Weather file can be edit.\n\t */\n\tget editable() {\n\t\treturn this.#editable;\n\t}\n\n\t/**\n\t * Weather file can be edit.\n\t * @param {boolean} value\n\t */\n\tset editable(value) {\n\t\tif (this.#editable === value) return;\n\t\tthis.setReadOnly(!value);\n\t\teditorManager.onupdate(\"read-only\");\n\t\teditorManager.emit(\"update\", \"read-only\");\n\t\tthis.#editable = value;\n\t}\n\n\tget isUnsaved() {\n\t\treturn this.#isUnsaved;\n\t}\n\n\tset isUnsaved(value) {\n\t\tif (this.#isUnsaved === value) return;\n\t\tthis.#isUnsaved = value;\n\n\t\tthis.#updateTab();\n\t}\n\n\tget pinned() {\n\t\treturn this.#pinned;\n\t}\n\n\tset pinned(value) {\n\t\tthis.setPinnedState(value);\n\t}\n\n\tsetPinnedState(value, options = {}) {\n\t\tconst { reorder = false, emit = true } = options;\n\t\tvalue = !!value;\n\t\tif (this.#pinned === value) return value;\n\n\t\tthis.#pinned = value;\n\t\tthis.#updateTab();\n\t\tthis.onpinstatechange?.(value);\n\n\t\tif (editorManager.files.includes(this) && reorder) {\n\t\t\teditorManager.moveFileByPinnedState?.(this);\n\t\t}\n\n\t\tif (emit) {\n\t\t\teditorManager.onupdate(\"pin-tab\");\n\t\t\teditorManager.emit(\"update\", \"pin-tab\", this);\n\t\t}\n\n\t\treturn value;\n\t}\n\n\ttogglePinned() {\n\t\treturn this.setPinnedState(!this.pinned);\n\t}\n\n\t/**\n\t * DON'T remove, plugin need this property to get filename.\n\t */\n\tget name() {\n\t\treturn this.#name;\n\t}\n\n\t/**\n\t * Readonly, cache file url\n\t */\n\tget cacheFile() {\n\t\treturn Url.join(CACHE_STORAGE, this.#id);\n\t}\n\n\t/**\n\t * File icon\n\t */\n\tget icon() {\n\t\tif (this.#type !== \"editor\") {\n\t\t\treturn this.#tabIcon;\n\t\t}\n\t\treturn helpers.getIconForFile(this.filename);\n\t}\n\n\tget tab() {\n\t\treturn this.#tab;\n\t}\n\n\tget SAFMode() {\n\t\treturn this.#SAFMode;\n\t}\n\n\tasync writeToCache() {\n\t\tconst text = this.session.doc.toString();\n\t\tconst fs = fsOperation(this.cacheFile);\n\n\t\ttry {\n\t\t\tif (!(await fs.exists())) {\n\t\t\t\tawait fsOperation(CACHE_STORAGE).createFile(this.id, text);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait fs.writeFile(text);\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", \"Writing to cache failed:\");\n\t\t\twindow.log(\"error\", error);\n\t\t}\n\t}\n\n\tasync isChanged() {\n\t\tif (this.type !== \"editor\") return false;\n\t\t// if file is not loaded or is loading then it is not changed.\n\t\tif (!this.loaded || this.loading) {\n\t\t\treturn false;\n\t\t}\n\t\t// is changed is called when session text is changed\n\t\t// if file has no uri or is readonly that means file is change\n\t\t// and need to saved to a location.\n\t\t// here readonly means file has uri but has no write permission.\n\t\tif (!this.uri || this.readOnly) {\n\t\t\t// if file is default file and text is changed\n\t\t\tif (this.id === constants.DEFAULT_FILE_SESSION) {\n\t\t\t\t// change id when text is changed\n\t\t\t\tthis.id = helpers.uuid();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tconst protocol = Url.getProtocol(this.#uri);\n\t\tconst text = this.session.doc.toString();\n\n\t\t// Helper for JS-based comparison (used as fallback)\n\t\tconst jsCompare = async (fileUri) => {\n\t\t\tconst fs = fsOperation(fileUri);\n\t\t\tconst oldText = await fs.readFile(this.encoding);\n\t\t\treturn await system.compareTexts(oldText, text);\n\t\t};\n\n\t\tif (/s?ftp:/.test(protocol)) {\n\t\t\t// FTP/SFTP files use cached local file\n\t\t\tconst cacheFilename = protocol.slice(0, -1) + this.id;\n\t\t\tconst cacheFileUri = Url.join(CACHE_STORAGE, cacheFilename);\n\n\t\t\ttry {\n\t\t\t\treturn await system.compareFileText(cacheFileUri, this.encoding, text);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"Native compareFileText failed, using JS fallback:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\treturn await jsCompare(cacheFileUri);\n\t\t\t\t} catch (fallbackError) {\n\t\t\t\t\tconsole.error(fallbackError);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (/^(file|content):/.test(protocol)) {\n\t\t\t// file:// and content:// URIs - try native first, fallback to JS\n\t\t\ttry {\n\t\t\t\treturn await system.compareFileText(this.uri, this.encoding, text);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"Native compareFileText failed, using JS fallback:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\treturn await jsCompare(this.uri);\n\t\t\t\t} catch (fallbackError) {\n\t\t\t\t\tconsole.error(fallbackError);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Other protocols - JS reads file, native compares strings\n\t\ttry {\n\t\t\treturn await jsCompare(this.uri);\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync canRun() {\n\t\tif (!this.loaded || this.loading) return false;\n\t\tawait this.readCanRun();\n\t\treturn this.#canRun;\n\t}\n\n\tasync readCanRun() {\n\t\ttry {\n\t\t\tconst event = createFileEvent(this);\n\t\t\tthis.#emit(\"canrun\", event);\n\t\t\tif (event.defaultPrevented) return;\n\n\t\t\tconst folder = openFolder.find(this.uri);\n\t\t\tif (folder) {\n\t\t\t\tconst url = Url.join(folder.url, \"index.html\");\n\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\tif (await fs.exists()) {\n\t\t\t\t\tthis.#canRun = Promise.resolve(true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst runnableFile = /\\.((html?)|(md)|(js)|(svg))$/;\n\t\t\tif (runnableFile.test(this.filename)) {\n\t\t\t\tthis.#canRun = Promise.resolve(true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#canRun = Promise.resolve(false);\n\t\t} catch (error) {\n\t\t\tif (err instanceof Error) throw err;\n\t\t\telse throw new Error(err);\n\t\t}\n\t}\n\n\t/**\n\t * Set weather to show run button or not\n\t * @param {()=>(boolean|Promise<boolean>)} cb callback function that return true if file can run\n\t */\n\tasync writeCanRun(cb) {\n\t\tif (!cb || typeof cb !== \"function\") return;\n\t\tconst res = cb();\n\t\tif (res instanceof Promise) {\n\t\t\tthis.#canRun = res;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#canRun = Promise.resolve(res);\n\t}\n\n\t/**\n\t * Remove and closes the file.\n\t * @param {boolean} force if true, will prompt to save the file\n\t */\n\tasync remove(force = false, options = {}) {\n\t\tconst { ignorePinned = false, silentPinned = false } = options || {};\n\n\t\tif (\n\t\t\tthis.id === constants.DEFAULT_FILE_SESSION &&\n\t\t\t!editorManager.files.length\n\t\t)\n\t\t\treturn false;\n\t\tif (this.pinned && !ignorePinned) {\n\t\t\tif (!silentPinned) {\n\t\t\t\ttoast(\n\t\t\t\t\tstrings[\"unpin tab before closing\"] ||\n\t\t\t\t\t\t\"Unpin the tab before closing it.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (!force && this.isUnsaved) {\n\t\t\tconst confirmation = await confirm(\n\t\t\t\tstrings.warning.toUpperCase(),\n\t\t\t\tstrings[\"unsaved file\"],\n\t\t\t);\n\t\t\tif (!confirmation) return false;\n\t\t}\n\n\t\tthis.#destroy();\n\n\t\teditorManager.files = editorManager.files.filter(\n\t\t\t(file) => file.id !== this.id,\n\t\t);\n\t\tconst { files, activeFile } = editorManager;\n\t\tif (activeFile.id === this.id) {\n\t\t\teditorManager.activeFile = null;\n\t\t}\n\t\tif (!files.length) {\n\t\t\tSidebar.hide();\n\t\t\teditorManager.activeFile = null;\n\t\t\tnew EditorFile();\n\t\t} else {\n\t\t\tfiles[files.length - 1].makeActive();\n\t\t}\n\t\teditorManager.onupdate(\"remove-file\");\n\t\teditorManager.emit(\"remove-file\", this);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Saves the file.\n\t * @returns {Promise<boolean>} true if file is saved, false if not.\n\t */\n\tsave() {\n\t\tif (this.type !== \"editor\") return Promise.resolve(false);\n\t\treturn this.#save(false);\n\t}\n\n\t/**\n\t * Saves the file to a new location.\n\t * @returns {Promise<boolean>} true if file is saved, false if not.\n\t */\n\tsaveAs() {\n\t\tif (this.type !== \"editor\") return Promise.resolve(false);\n\t\treturn this.#save(true);\n\t}\n\n\tsetReadOnly(value) {\n\t\ttry {\n\t\t\tconst { editor, readOnlyCompartment } = editorManager;\n\t\t\tif (!editor) return;\n\t\t\tif (!readOnlyCompartment) return;\n\t\t\teditor.dispatch({\n\t\t\t\teffects: readOnlyCompartment.reconfigure(\n\t\t\t\t\tEditorState.readOnly.of(!!value),\n\t\t\t\t),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to update read-only state for ${this.filename || this.uri}`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\n\t\t// Sync internal flags and header\n\t\tthis.readOnly = !!value;\n\t\tthis.#editable = !this.readOnly;\n\t\tif (editorManager.activeFile?.id === this.id) {\n\t\t\teditorManager.header.subText = this.#getTitle();\n\t\t}\n\t}\n\n\t/**\n\t * Sets syntax highlighting of the file.\n\t * @param {string} [mode]\n\t */\n\tsetMode(mode) {\n\t\tif (this.type !== \"editor\") return;\n\t\tconst event = createFileEvent(this);\n\t\tthis.#emit(\"changemode\", event);\n\t\tif (event.defaultPrevented) return;\n\n\t\tif (!mode) {\n\t\t\tconst ext = Path.extname(this.filename);\n\t\t\tconst modes = helpers.parseJSON(localStorage.modeassoc);\n\t\t\tif (modes?.[ext]) {\n\t\t\t\tmode = modes[ext];\n\t\t\t}\n\t\t}\n\n\t\tlet modeInfo = mode ? getMode(mode) : null;\n\t\tif (!modeInfo) {\n\t\t\tmodeInfo = getModeForPath(this.filename);\n\t\t}\n\t\tmode = modeInfo?.name || String(mode || \"text\").toLowerCase();\n\n\t\t// Store mode info for later use when creating editor view\n\t\tthis.currentMode = mode;\n\t\tthis.currentLanguageExtension = modeInfo?.getExtension() || null;\n\n\t\t// sets file icon\n\t\tthis.#tab.lead(\n\t\t\t<span className={this.icon} style={{ paddingRight: \"5px\" }}></span>,\n\t\t);\n\t}\n\n\t/**\n\t * Makes this file active\n\t */\n\tmakeActive() {\n\t\tconst { activeFile, editor, switchFile } = editorManager;\n\n\t\tif (activeFile) {\n\t\t\tif (activeFile.id === this.id) return;\n\t\t\tactiveFile.focusedBefore = activeFile.focused;\n\t\t\tactiveFile.removeActive();\n\n\t\t\t// Hide previous content if it exists\n\t\t\tif (activeFile.type !== \"editor\" && activeFile.content) {\n\t\t\t\tactiveFile.content.style.display = \"none\";\n\t\t\t}\n\t\t}\n\n\t\tswitchFile(this.id);\n\n\t\t// Show/hide appropriate content\n\t\tif (this.type === \"editor\") {\n\t\t\teditorManager.container.style.display = \"block\";\n\t\t\tif (this.focused) {\n\t\t\t\teditor.focus();\n\t\t\t} else {\n\t\t\t\teditor.contentDOM.blur();\n\t\t\t\t// Ensure any native DOM selection is cleared on blur to avoid sticky selection handles\n\t\t\t\ttry {\n\t\t\t\t\tdocument.getSelection()?.removeAllRanges();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(\"Failed to clear native text selection.\", error);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\teditorManager.container.style.display = \"none\";\n\t\t\tif (this.content) {\n\t\t\t\tthis.content.style.display = \"block\";\n\t\t\t\tif (!this.content.parentElement) {\n\t\t\t\t\teditorManager.container.parentElement.appendChild(this.content);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (activeFile && activeFile.type === \"editor\") {\n\t\t\t\tclearSelection(editorManager.editor);\n\t\t\t}\n\t\t}\n\n\t\tthis.#tab.classList.add(\"active\");\n\t\tthis.#tab.scrollIntoView();\n\n\t\tif (this.type === \"editor\" && !this.loaded && !this.loading) {\n\t\t\tthis.#loadText();\n\t\t}\n\n\t\t// Handle quicktools visibility based on hideQuickTools property\n\t\tif (this.hideQuickTools) {\n\t\t\troot.classList.add(\"hide-floating-button\");\n\t\t\tactions(\"set-height\", { height: 0, save: false });\n\t\t} else {\n\t\t\troot.classList.remove(\"hide-floating-button\");\n\t\t\tconst quickToolsHeight =\n\t\t\t\tappSettings.value.quickTools !== undefined\n\t\t\t\t\t? appSettings.value.quickTools\n\t\t\t\t\t: 1;\n\t\t\tactions(\"set-height\", { height: quickToolsHeight, save: false });\n\t\t}\n\n\t\teditorManager.header.subText = this.#getTitle();\n\n\t\tthis.#emit(\"focus\", createFileEvent(this));\n\t}\n\n\tremoveActive() {\n\t\tthis.#emit(\"blur\", createFileEvent(this));\n\t}\n\n\topenWith() {\n\t\tthis.#fileAction(\"VIEW\");\n\t}\n\n\teditWith() {\n\t\tthis.#fileAction(\"EDIT\", \"text/plain\");\n\t}\n\n\tshare() {\n\t\tthis.#fileAction(\"SEND\");\n\t}\n\n\trunAction() {\n\t\tthis.#fileAction(\"RUN\");\n\t}\n\n\trun() {\n\t\tthis.#run(false);\n\t}\n\n\trunFile() {\n\t\tthis.#run(true);\n\t}\n\n\trender() {\n\t\tthis.makeActive();\n\n\t\tif (this.id !== constants.DEFAULT_FILE_SESSION) {\n\t\t\tconst defaultFile = editorManager.getFile(\n\t\t\t\tconstants.DEFAULT_FILE_SESSION,\n\t\t\t\t\"id\",\n\t\t\t);\n\t\t\tdefaultFile?.remove();\n\t\t}\n\n\t\t// Show/hide editor based on content type\n\t\tif (this.#type === \"editor\") {\n\t\t\teditorManager.container.style.display = \"block\";\n\t\t\tif (this.#content) this.#content.style.display = \"none\";\n\t\t} else {\n\t\t\teditorManager.container.style.display = \"none\";\n\t\t\tif (this.#content) {\n\t\t\t\tthis.#content.style.display = \"block\";\n\t\t\t\teditorManager.container.parentElement.appendChild(this.#content);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Add event listener\n\t * @param {string} event\n\t * @param {(this:File, e:Event)=>void} callback\n\t */\n\ton(event, callback) {\n\t\tthis.#events[event.toLowerCase()]?.push(callback);\n\t}\n\n\t/**\n\t * Remove event listener\n\t * @param {string} event\n\t * @param {(this:File, e:Event)=>void} callback\n\t */\n\toff(event, callback) {\n\t\tconst events = this.#events[event.toLowerCase()];\n\t\tif (!events) return;\n\t\tconst index = events.indexOf(callback);\n\t\tif (index > -1) events.splice(index, 1);\n\t}\n\n\t/**\n\t * Add custom stylesheets to shadow DOM\n\t * @param {string|string[]} styles URLs or CSS strings\n\t * @param {ShadowRoot} shadow Shadow DOM root\n\t */\n\t#addCustomStyles(styles, shadow) {\n\t\tif (typeof styles === \"string\") {\n\t\t\tstyles = [styles];\n\t\t}\n\n\t\tstyles.forEach((style) => {\n\t\t\tif (style.startsWith(\"http\") || style.startsWith(\"/\")) {\n\t\t\t\t// External stylesheet\n\t\t\t\tconst link = tag(\"link\", {\n\t\t\t\t\trel: \"stylesheet\",\n\t\t\t\t\thref: style,\n\t\t\t\t});\n\t\t\t\tshadow.appendChild(link);\n\t\t\t} else {\n\t\t\t\t// Inline CSS\n\t\t\t\tconst styleElement = tag(\"style\", {\n\t\t\t\t\ttextContent: style,\n\t\t\t\t});\n\t\t\t\tshadow.appendChild(styleElement);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Add stylesheet to tab's shadow DOM\n\t * @param {string} style URL or CSS string\n\t */\n\taddStyle(style) {\n\t\tif (this.#type === \"editor\" || !this.#content) return;\n\n\t\tconst shadow = this.#content.shadowRoot;\n\t\tthis.#addCustomStyles(style, shadow);\n\t}\n\n\t/**\n\t * Set custom title function for special tab types\n\t * @param {function} titleFn Function that returns the title string\n\t */\n\tsetCustomTitle(titleFn) {\n\t\tthis.#customTitleFn = titleFn;\n\t\t// Update header if this file is currently active\n\t\tif (editorManager.activeFile && editorManager.activeFile.id === this.id) {\n\t\t\teditorManager.header.subText = this.#getTitle();\n\t\t}\n\t}\n\n\tget headerSubtitle() {\n\t\treturn this.#getTitle();\n\t}\n\n\t/**\n\t *\n\t * @param {FileAction} action\n\t */\n\tasync #fileAction(action, mimeType) {\n\t\ttry {\n\t\t\tconst uri = await this.#getShareableUri();\n\t\t\tif (!mimeType) mimeType = mimeTypes.lookup(this.name) || \"text/plain\";\n\t\t\tsystem.fileAction(\n\t\t\t\turi,\n\t\t\t\tthis.filename,\n\t\t\t\taction,\n\t\t\t\tmimeType,\n\t\t\t\tthis.#showNoAppError,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\ttoast(strings.error);\n\t\t}\n\t}\n\n\tasync #getShareableUri() {\n\t\tif (!this.uri) return null;\n\n\t\tconst fs = fsOperation(this.uri);\n\n\t\tif (/^s?ftp:/.test(this.uri)) return fs.localName;\n\n\t\tconst { url } = await fs.stat();\n\t\treturn url;\n\t}\n\n\t/**\n\t * Rename cache file.\n\t * @param {String} newId\n\t */\n\tasync #renameCacheFile(newId) {\n\t\ttry {\n\t\t\tconst fs = fsOperation(this.cacheFile);\n\t\t\tif (!(await fs.exists())) return;\n\t\t\tfs.renameTo(newId);\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", \"renameCacheFile\");\n\t\t\twindow.log(\"error\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Removes cache file\n\t */\n\tasync #removeCache() {\n\t\ttry {\n\t\t\tconst fs = fsOperation(this.cacheFile);\n\t\t\tif (!(await fs.exists())) return;\n\t\t\tawait fs.delete();\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", error);\n\t\t}\n\t}\n\n\tasync #loadText() {\n\t\tif (this.#type !== \"editor\") return;\n\t\tlet value = \"\";\n\n\t\tconst { cursorPos, scrollLeft, scrollTop, folds, editable } =\n\t\t\tthis.#loadOptions;\n\t\tconst { editor } = editorManager;\n\n\t\tthis.#loadOptions = null;\n\n\t\tthis.setReadOnly(true);\n\t\tthis.loading = true;\n\t\tthis.markChanged = false;\n\t\tthis.#emit(\"loadstart\", createFileEvent(this));\n\t\tthis.session.setValue(strings[\"loading...\"]);\n\n\t\t// Immediately reflect \"loading...\" in the visible editor if this tab is active\n\t\ttry {\n\t\t\tconst { activeFile, emit } = editorManager;\n\t\t\tif (activeFile?.id === this.id) {\n\t\t\t\temit(\"file-loaded\", this);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to emit interim file-loaded event.\", error);\n\t\t}\n\n\t\ttry {\n\t\t\tconst cacheFs = fsOperation(this.cacheFile);\n\t\t\tconst cacheExists = await cacheFs.exists();\n\n\t\t\tif (cacheExists) {\n\t\t\t\tvalue = await cacheFs.readFile(this.encoding);\n\t\t\t}\n\n\t\t\tif (this.uri) {\n\t\t\t\tconst file = fsOperation(this.uri);\n\t\t\t\tconst fileExists = await file.exists();\n\t\t\t\tif (!fileExists && cacheExists) {\n\t\t\t\t\tthis.deletedFile = true;\n\t\t\t\t\tthis.isUnsaved = true;\n\t\t\t\t} else if (!cacheExists && fileExists) {\n\t\t\t\t\tvalue = await file.readFile(this.encoding);\n\t\t\t\t} else if (!cacheExists && !fileExists) {\n\t\t\t\t\twindow.log(\"error\", \"unable to load file\");\n\t\t\t\t\tthrow new Error(\"Unable to load file\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.markChanged = false;\n\t\t\tthis.session.setValue(value);\n\t\t\tthis.loaded = true;\n\t\t\tthis.loading = false;\n\n\t\t\tconst { activeFile, emit } = editorManager;\n\t\t\tif (activeFile.id === this.id) {\n\t\t\t\tthis.setReadOnly(false);\n\t\t\t}\n\n\t\t\tsetTimeout(() => {\n\t\t\t\tthis.#emit(\"load\", createFileEvent(this));\n\t\t\t\temit(\"file-loaded\", this);\n\t\t\t\tif (cursorPos) {\n\t\t\t\t\trestoreSelection(editor, cursorPos);\n\t\t\t\t}\n\t\t\t\tif (scrollTop || scrollLeft) {\n\t\t\t\t\tsetScrollPosition(editor, scrollTop, scrollLeft);\n\t\t\t\t}\n\t\t\t\tif (editable !== undefined) this.editable = editable;\n\t\t\t\trestoreFolds(editor, folds);\n\t\t\t}, 0);\n\t\t} catch (error) {\n\t\t\tthis.#emit(\"loaderror\", createFileEvent(this));\n\t\t\tthis.remove(false, { ignorePinned: true });\n\t\t\ttoast(`Unable to load: ${this.filename}`);\n\t\t\twindow.log(\"error\", \"Unable to load: \" + this.filename);\n\t\t\twindow.log(\"error\", error);\n\t\t} finally {\n\t\t\tthis.#emit(\"loadend\", createFileEvent(this));\n\t\t}\n\t}\n\n\t// TODO: Implement CodeMirror equivalents for folding and scroll events\n\t// static #onfold(e) {\n\t// \teditorManager.editor._emit(\"fold\", e);\n\t// }\n\n\t// static #onscrolltop(e) {\n\t// \teditorManager.editor._emit(\"scrolltop\", e);\n\t// }\n\n\t// static #onscrollleft(e) {\n\t// \teditorManager.editor._emit(\"scrollleft\", e);\n\t// }\n\n\t#save(as) {\n\t\tconst event = createFileEvent(this);\n\t\tthis.#emit(\"save\", event);\n\n\t\tif (event.defaultPrevented) return Promise.resolve(false);\n\t\treturn Promise.all([this.writeToCache(), saveFile(this, as)]);\n\t}\n\n\t#run(file) {\n\t\tconst event = createFileEvent(this);\n\t\tthis.#emit(\"run\", event);\n\t\tif (event.defaultPrevented) return;\n\t\trun(false, appSettings.value.previewMode, file);\n\t}\n\n\t#updateTab() {\n\t\tif (!this.#tab) return;\n\n\t\tif (this.#isUnsaved) {\n\t\t\tthis.tab.classList.add(\"notice\");\n\t\t} else {\n\t\t\tthis.tab.classList.remove(\"notice\");\n\t\t}\n\n\t\tthis.tab.classList.toggle(\"pinned\", this.#pinned);\n\t\tthis.#tab.tail(this.#createTabTail());\n\t}\n\n\t/**\n\t * Setup CodeMirror EditorState for the file\n\t */\n\t#setupSession() {\n\t\tif (this.type !== \"editor\") return;\n\t\t// CodeMirror configuration will be handled in the EditorView\n\t\t// Store settings for when the editor view is created\n\t\tthis.editorSettings = {\n\t\t\ttabSize: appSettings.value.tabSize,\n\t\t\tsoftTab: appSettings.value.softTab,\n\t\t\ttextWrap: appSettings.value.textWrap,\n\t\t};\n\t}\n\n\t#destroy() {\n\t\tthis.#emit(\"close\", createFileEvent(this));\n\t\tappSettings.off(\"update:openFileListPos\", this.#onFilePosChange);\n\t\tif (this.type === \"editor\") {\n\t\t\tthis.#removeCache();\n\t\t\t// CodeMirror EditorState doesn't need explicit cleanup\n\t\t\tthis.session = null;\n\t\t} else if (this.content) {\n\t\t\tthis.content.remove();\n\t\t}\n\n\t\tthis.#tab.remove();\n\t\tthis.#tab = null;\n\t}\n\n\t#showNoAppError() {\n\t\ttoast(strings[\"no app found to handle this file\"]);\n\t}\n\n\t#createTabTail() {\n\t\tif (!this.#pinned) {\n\t\t\treturn tag(\"span\", {\n\t\t\t\tclassName: \"icon cancel\",\n\t\t\t\tdataset: {\n\t\t\t\t\taction: \"close-file\",\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn tag(\"span\", {\n\t\t\tclassName: \"icon pin\",\n\t\t\ttitle: strings[\"unpin tab\"] || \"Unpin tab\",\n\t\t\tdataset: {\n\t\t\t\taction: \"toggle-pin\",\n\t\t\t},\n\t\t});\n\t}\n\n\t#getTitle() {\n\t\t// Use custom title function if provided\n\t\tif (this.#customTitleFn) {\n\t\t\treturn this.#customTitleFn();\n\t\t}\n\n\t\tlet text = this.location || this.uri;\n\n\t\tif (text && !this.readOnly) {\n\t\t\ttext = helpers.getVirtualPath(text);\n\t\t\tif (text.length > 30) text = \"...\" + text.slice(text.length - 27);\n\t\t} else if (this.readOnly) {\n\t\t\ttext = strings[\"read only\"];\n\t\t} else if (this.deletedFile) {\n\t\t\ttext = strings[\"deleted file\"];\n\t\t} else {\n\t\t\ttext = strings[\"new file\"];\n\t\t}\n\t\treturn text;\n\t}\n\n\t/**\n\t * Emits an event\n\t * @param {FileEvents} eventName\n\t * @param {FileEvent} event\n\t */\n\t#emit(eventName, event) {\n\t\tthis[`on${eventName}`]?.(event);\n\t\tif (!event.BUBBLING_PHASE) return;\n\t\tthis.#events[eventName]?.some((fn) => {\n\t\t\tfn(event);\n\t\t\treturn !event.BUBBLING_PHASE;\n\t\t});\n\t}\n}\n\n/**\n *\n * @param {MouseEvent} e\n * @returns\n */\nfunction tabOnclick(e) {\n\te.preventDefault();\n\tconst { action } = e.target.dataset;\n\tif (action === \"close-file\") {\n\t\tthis.remove();\n\t\treturn;\n\t}\n\tif (action === \"toggle-pin\") {\n\t\tthis.togglePinned();\n\t\treturn;\n\t}\n\tthis.makeActive();\n}\n\nfunction createFileEvent(file) {\n\treturn new FileEvent(file);\n}\n\nclass FileEvent {\n\t#bubblingPhase = true;\n\t#defaultPrevented = false;\n\ttarget;\n\tconstructor(file) {\n\t\tthis.target = file;\n\t}\n\tstopPropagation() {\n\t\tthis.#bubblingPhase = false;\n\t}\n\tpreventDefault() {\n\t\tthis.#defaultPrevented = true;\n\t}\n\tget BUBBLING_PHASE() {\n\t\treturn this.#bubblingPhase;\n\t}\n\tget defaultPrevented() {\n\t\treturn this.#defaultPrevented;\n\t}\n}\n"
  },
  {
    "path": "src/lib/editorManager.js",
    "content": "import sidebarApps from \"sidebarApps\";\nimport { indentUnit } from \"@codemirror/language\";\nimport { search } from \"@codemirror/search\";\nimport { Compartment, EditorState, Prec, StateEffect } from \"@codemirror/state\";\nimport { oneDark } from \"@codemirror/theme-one-dark\";\nimport {\n\tcloseHoverTooltips,\n\tEditorView,\n\thasHoverTooltips,\n\thighlightActiveLineGutter,\n\thighlightTrailingWhitespace,\n\thighlightWhitespace,\n\tkeymap,\n\tlineNumbers,\n} from \"@codemirror/view\";\nimport {\n\tabbreviationTracker,\n\tEmmetKnownSyntax,\n\temmetCompletionSource,\n\temmetConfig,\n\texpandAbbreviation,\n\twrapWithAbbreviation,\n} from \"@emmetio/codemirror6-plugin\";\nimport createBaseExtensions from \"cm/baseExtensions\";\nimport {\n\tsetKeyBindings as applyKeyBindings,\n\texecuteCommand,\n\tgetCommandKeymapExtension,\n\tgetRegisteredCommands,\n\trefreshCommandKeymap,\n\tregisterExternalCommand,\n\tremoveExternalCommand,\n} from \"cm/commandRegistry\";\nimport lspApi from \"cm/lsp/api\";\nimport lspClientManager from \"cm/lsp/clientManager\";\nimport {\n\tgetLspDiagnostics,\n\tLSP_DIAGNOSTICS_EVENT,\n\tlspDiagnosticsClientExtension,\n\tlspDiagnosticsUiExtension,\n} from \"cm/lsp/diagnostics\";\nimport { stopManagedServer } from \"cm/lsp/serverLauncher\";\nimport createMainEditorExtensions from \"cm/mainEditorExtensions\";\n// CodeMirror mode management\nimport {\n\tgetMode,\n\tgetModeForPath,\n\tgetModes,\n\tgetModesByName,\n\tinitModes,\n} from \"cm/modelist\";\nimport createTouchSelectionMenu from \"cm/touchSelectionMenu\";\nimport \"cm/supportedModes\";\nimport { autocompletion } from \"@codemirror/autocomplete\";\nimport colorView from \"cm/colorView\";\nimport {\n\tgetAllFolds,\n\trestoreFolds,\n\trestoreSelection,\n\tsetScrollPosition,\n} from \"cm/editorUtils\";\nimport indentGuides from \"cm/indentGuides\";\nimport rainbowBrackets, { getRainbowBracketColors } from \"cm/rainbowBrackets\";\nimport { getThemeConfig, getThemeExtensions } from \"cm/themes\";\nimport list from \"components/collapsableList\";\nimport quickTools from \"components/quickTools\";\nimport ScrollBar from \"components/scrollbar\";\nimport SideButton, { sideButtonContainer } from \"components/sideButton\";\nimport keyboardHandler, { keydownState } from \"handlers/keyboard\";\nimport EditorFile from \"./editorFile\";\nimport openFile from \"./openFile\";\nimport { addedFolder } from \"./openFolder\";\nimport appSettings from \"./settings\";\nimport {\n\tgetSystemConfiguration,\n\tHARDKEYBOARDHIDDEN_NO,\n} from \"./systemConfiguration\";\n\n/**\n * Represents an editor manager that handles multiple files and provides various editor configurations and event listeners.\n * @param {HTMLElement} $header - The header element.\n * @param {HTMLElement} $body - The body element.\n * @returns {Promise<Object>} A promise that resolves to the editor manager object.\n */\nasync function EditorManager($header, $body) {\n\t/**\n\t * @type {Collapsible & HTMLElement}\n\t */\n\tlet $openFileList;\n\tlet TIMEOUT_VALUE = 500;\n\tlet preventScrollbarV = false;\n\tlet preventScrollbarH = false;\n\tlet scrollBarVisibilityCount = 0;\n\tlet timeoutQuicktoolsToggler;\n\tlet timeoutHeaderToggler;\n\tlet isScrolling = false;\n\tlet lastScrollTop = 0;\n\tlet lastScrollLeft = 0;\n\n\t// Debounce timers for CodeMirror change handling\n\tlet checkTimeout = null;\n\tlet autosaveTimeout = null;\n\tlet touchSelectionController = null;\n\tlet touchSelectionSyncRaf = 0;\n\tlet nativeContextMenuDisabled = null;\n\tconst recoverableWarningKeys = new Set();\n\n\tfunction warnRecoverable(message, error, key) {\n\t\tif (key) {\n\t\t\tif (recoverableWarningKeys.has(key)) return;\n\t\t\trecoverableWarningKeys.add(key);\n\t\t}\n\t\tconsole.warn(message, error);\n\t}\n\n\tfunction isCoarsePointerDevice() {\n\t\tif (typeof window !== \"undefined\") {\n\t\t\ttry {\n\t\t\t\tif (window.matchMedia?.(\"(pointer: coarse)\").matches) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (_) {\n\t\t\t\t// Ignore matchMedia capability errors and fall through.\n\t\t\t}\n\t\t}\n\t\treturn (\n\t\t\ttypeof navigator !== \"undefined\" &&\n\t\t\tNumber(navigator.maxTouchPoints || 0) > 0\n\t\t);\n\t}\n\n\tconst setNativeContextMenuDisabled = (disabled) => {\n\t\tconst value = !!disabled;\n\t\tif (nativeContextMenuDisabled === value) return;\n\t\tnativeContextMenuDisabled = value;\n\t\tconst api = globalThis.system?.setNativeContextMenuDisabled;\n\t\tif (typeof api !== \"function\") return;\n\t\ttry {\n\t\t\tapi.call(globalThis.system, value);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to update native context menu state\", error);\n\t\t}\n\t};\n\n\tconst { scrollbarSize } = appSettings.value;\n\tconst events = {\n\t\t\"switch-file\": [],\n\t\t\"rename-file\": [],\n\t\t\"save-file\": [],\n\t\t\"file-loaded\": [],\n\t\t\"file-content-changed\": [],\n\t\t\"add-folder\": [],\n\t\t\"remove-folder\": [],\n\t\tupdate: [],\n\t\t\"new-file\": [],\n\t\t\"remove-file\": [],\n\t\t\"int-open-file-list\": [],\n\t\temit(event, ...args) {\n\t\t\tif (!events[event]) return;\n\t\t\tevents[event].forEach((fn) => fn(...args));\n\t\t},\n\t};\n\tconst $container = <div className=\"editor-container\"></div>;\n\t// Ensure the container participates well in flex layouts and can constrain the editor\n\t$container.style.flex = \"1 1 auto\";\n\t$container.style.minHeight = \"0\"; // allow child scroller to size correctly\n\t$container.style.height = \"100%\";\n\t$container.style.width = \"100%\";\n\tconst problemButton = SideButton({\n\t\ttext: strings.problems,\n\t\ticon: \"warningreport_problem\",\n\t\tbackgroundColor: \"var(--danger-color)\",\n\t\ttextColor: \"var(--danger-text-color)\",\n\t\tonclick() {\n\t\t\tacode.exec(\"open\", \"problems\");\n\t\t},\n\t});\n\n\tconst pointerCursorVisibilityExtension = EditorView.updateListener.of(\n\t\t(update) => {\n\t\t\tif (!update.selectionSet) return;\n\t\t\tconst pointerTriggered = update.transactions.some(\n\t\t\t\t(tr) =>\n\t\t\t\t\ttr.isUserEvent(\"pointer\") ||\n\t\t\t\t\ttr.isUserEvent(\"select\") ||\n\t\t\t\t\ttr.isUserEvent(\"select.pointer\") ||\n\t\t\t\t\ttr.isUserEvent(\"touch\") ||\n\t\t\t\t\ttr.isUserEvent(\"select.touch\"),\n\t\t\t);\n\t\t\tif (!pointerTriggered) return;\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (!isCursorVisible()) scrollCursorIntoView({ behavior: \"instant\" });\n\t\t\t});\n\t\t},\n\t);\n\n\tconst isShiftSelectionActive = (event) => {\n\t\tif (!appSettings.value.shiftClickSelection) return false;\n\t\treturn !!event?.shiftKey || quickTools?.$footer?.dataset?.shift != null;\n\t};\n\tconst shiftClickSelectionExtension = EditorView.domEventHandlers({\n\t\tclick(event) {\n\t\t\tif (!touchSelectionController?.consumePendingShiftSelectionClick(event)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tevent.preventDefault();\n\t\t\treturn true;\n\t\t},\n\t});\n\tconst touchSelectionUpdateExtension = EditorView.updateListener.of(\n\t\t(update) => {\n\t\t\tif (!touchSelectionController) return;\n\t\t\tconst pointerTriggered = update.transactions.some(\n\t\t\t\t(tr) =>\n\t\t\t\t\ttr.isUserEvent(\"pointer\") ||\n\t\t\t\t\ttr.isUserEvent(\"select\") ||\n\t\t\t\t\ttr.isUserEvent(\"select.pointer\") ||\n\t\t\t\t\ttr.isUserEvent(\"touch\") ||\n\t\t\t\t\ttr.isUserEvent(\"select.touch\"),\n\t\t\t);\n\t\t\tif (update.selectionSet || pointerTriggered) {\n\t\t\t\tcancelAnimationFrame(touchSelectionSyncRaf);\n\t\t\t\ttouchSelectionSyncRaf = requestAnimationFrame(() => {\n\t\t\t\t\ttouchSelectionController?.onStateChanged({\n\t\t\t\t\t\tpointerTriggered,\n\t\t\t\t\t\tselectionChanged: update.selectionSet,\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t);\n\n\t// Compartment to swap editor theme dynamically\n\tconst themeCompartment = new Compartment();\n\t// Compartments to control indentation, tab width, and font styling dynamically\n\tconst indentUnitCompartment = new Compartment();\n\tconst tabSizeCompartment = new Compartment();\n\tconst fontStyleCompartment = new Compartment();\n\t// Compartment for line wrapping\n\tconst wrapCompartment = new Compartment();\n\t// Compartment for line numbers\n\tconst lineNumberCompartment = new Compartment();\n\t// Compartment for text direction (RTL/LTR)\n\tconst rtlCompartment = new Compartment();\n\t// Compartment for whitespace visualization\n\tconst whitespaceCompartment = new Compartment();\n\t// Compartment for fold gutter theme (fade)\n\tconst foldThemeCompartment = new Compartment();\n\t// Compartment for autocompletion behavior\n\tconst completionCompartment = new Compartment();\n\t// Compartment for rainbow bracket colorizer\n\tconst rainbowCompartment = new Compartment();\n\t// Compartment for indent guides\n\tconst indentGuidesCompartment = new Compartment();\n\t// Compartment for read-only toggling\n\tconst readOnlyCompartment = new Compartment();\n\t// Compartment for language mode (allows async loading/reconfigure)\n\tconst languageCompartment = new Compartment();\n\t// Compartment for LSP extensions so we can swap per file\n\tconst lspCompartment = new Compartment();\n\tconst diagnosticsClientExt = lspDiagnosticsClientExtension();\n\tconst buildDiagnosticsUiExt = () =>\n\t\tlspDiagnosticsUiExtension(appSettings?.value?.lintGutter !== false);\n\tlet lspRequestToken = 0;\n\tlet lastLspUri = null;\n\tconst UNTITLED_URI_PREFIX = \"untitled://acode/\";\n\n\tfunction getEditorFontFamily() {\n\t\tconst font = appSettings?.value?.editorFont || \"Roboto Mono\";\n\t\treturn `${font}, Noto Mono, Monaco, monospace`;\n\t}\n\n\tfunction makeFontTheme() {\n\t\tconst fontSize = appSettings?.value?.fontSize || \"12px\";\n\t\tconst lineHeight = appSettings?.value?.lineHeight || 1.6;\n\t\tconst fontFamily = getEditorFontFamily();\n\t\treturn EditorView.theme({\n\t\t\t\"&\": { fontSize, lineHeight: String(lineHeight) },\n\t\t\t\".cm-content\": { fontFamily },\n\t\t\t\".cm-gutter\": { fontFamily },\n\t\t\t\".cm-tooltip, .cm-tooltip *\": { fontFamily },\n\t\t});\n\t}\n\n\tfunction makeWrapExtension() {\n\t\treturn appSettings?.value?.textWrap ? EditorView.lineWrapping : [];\n\t}\n\n\tfunction makeLineNumberExtension() {\n\t\tconst { linenumbers = true, relativeLineNumbers = false } =\n\t\t\tappSettings?.value || {};\n\t\tif (!linenumbers)\n\t\t\treturn EditorView.theme({\n\t\t\t\t\".cm-gutter\": {\n\t\t\t\t\tdisplay: \"none !important\",\n\t\t\t\t\twidth: \"0px !important\",\n\t\t\t\t\tminWidth: \"0px !important\",\n\t\t\t\t\tborder: \"none !important\",\n\t\t\t\t},\n\t\t\t});\n\t\tif (!relativeLineNumbers)\n\t\t\treturn Prec.highest([lineNumbers(), highlightActiveLineGutter()]);\n\t\treturn Prec.highest([\n\t\t\tlineNumbers({\n\t\t\t\tformatNumber: (lineNo, state) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cur = state.doc.lineAt(state.selection.main.head).number;\n\t\t\t\t\t\tconst diff = Math.abs(lineNo - cur);\n\t\t\t\t\t\treturn diff === 0 ? String(lineNo) : String(diff);\n\t\t\t\t\t} catch (_) {\n\t\t\t\t\t\treturn String(lineNo);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}),\n\t\t\thighlightActiveLineGutter(),\n\t\t]);\n\t}\n\n\tfunction makeIndentExtensions() {\n\t\tconst { softTab = true, tabSize = 2 } = appSettings?.value || {};\n\t\tconst unit = softTab ? \" \".repeat(Math.max(1, Number(tabSize) || 2)) : \"\\t\";\n\t\treturn {\n\t\t\tindentExt: indentUnit.of(unit),\n\t\t\ttabSizeExt: EditorState.tabSize.of(Math.max(1, Number(tabSize) || 2)),\n\t\t};\n\t}\n\n\tfunction makeRainbowBracketExtension() {\n\t\tconst enabled = appSettings?.value?.rainbowBrackets ?? true;\n\t\tif (!enabled) return [];\n\n\t\tconst themeId = appSettings?.value?.editorTheme || \"one_dark\";\n\t\treturn rainbowBrackets({\n\t\t\tcolors: getRainbowBracketColors(getThemeConfig(themeId)),\n\t\t});\n\t}\n\n\tfunction makeWhitespaceTheme() {\n\t\treturn EditorView.theme({\n\t\t\t\".cm-highlightSpace\": {\n\t\t\t\tbackgroundImage:\n\t\t\t\t\t\"radial-gradient(circle at 50% 54%, var(--cm-space-marker-color) 0.08em, transparent 0.1em)\",\n\t\t\t\tbackgroundPosition: \"center\",\n\t\t\t\tbackgroundRepeat: \"no-repeat\",\n\t\t\t\topacity: \"0.5\",\n\t\t\t},\n\t\t\t\".cm-highlightTab\": {\n\t\t\t\tbackgroundSize: \"auto 70%\",\n\t\t\t\tbackgroundPosition: \"right 60%\",\n\t\t\t\topacity: \"0.65\",\n\t\t\t},\n\t\t\t\".cm-trailingSpace\": {\n\t\t\t\tbackgroundColor: \"var(--cm-trailing-space-color)\",\n\t\t\t\tborderRadius: \"2px\",\n\t\t\t},\n\t\t\t\"&\": {\n\t\t\t\t\"--cm-space-marker-color\": \"rgba(127, 127, 127, 0.6)\",\n\t\t\t\t\"--cm-trailing-space-color\": \"rgba(255, 77, 77, 0.2)\",\n\t\t\t},\n\t\t});\n\t}\n\n\t// Centralised CodeMirror options registry for organized configuration\n\t// Each spec declares related settings keys, its compartment(s), and a builder returning extension(s)\n\tconst cmOptionSpecs = [\n\t\t{\n\t\t\tkeys: [\"linenumbers\", \"relativeLineNumbers\"],\n\t\t\tcompartments: [lineNumberCompartment],\n\t\t\tbuild() {\n\t\t\t\treturn makeLineNumberExtension();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"rainbowBrackets\"],\n\t\t\tcompartments: [rainbowCompartment],\n\t\t\tbuild() {\n\t\t\t\treturn makeRainbowBracketExtension();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"indentGuides\"],\n\t\t\tcompartments: [indentGuidesCompartment],\n\t\t\tbuild() {\n\t\t\t\tconst enabled = appSettings?.value?.indentGuides ?? true;\n\t\t\t\tif (!enabled) return [];\n\t\t\t\treturn indentGuides({\n\t\t\t\t\thighlightActiveGuide: true,\n\t\t\t\t\thideOnBlankLines: false,\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"fontSize\", \"editorFont\", \"lineHeight\"],\n\t\t\tcompartments: [fontStyleCompartment],\n\t\t\tbuild() {\n\t\t\t\treturn makeFontTheme();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"textWrap\"],\n\t\t\tcompartments: [wrapCompartment],\n\t\t\tbuild() {\n\t\t\t\treturn makeWrapExtension();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"softTab\", \"tabSize\"],\n\t\t\tcompartments: [indentUnitCompartment, tabSizeCompartment],\n\t\t\tbuild() {\n\t\t\t\tconst { indentExt, tabSizeExt } = makeIndentExtensions();\n\t\t\t\treturn [indentExt, tabSizeExt];\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"rtlText\"],\n\t\t\tcompartments: [rtlCompartment],\n\t\t\tbuild() {\n\t\t\t\tconst rtl = !!appSettings?.value?.rtlText;\n\t\t\t\treturn EditorView.theme({\n\t\t\t\t\t\"&\": { direction: rtl ? \"rtl\" : \"ltr\" },\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"showSpaces\"],\n\t\t\tcompartments: [whitespaceCompartment],\n\t\t\tbuild() {\n\t\t\t\tconst show = !!appSettings?.value?.showSpaces;\n\t\t\t\treturn show\n\t\t\t\t\t? [\n\t\t\t\t\t\t\thighlightWhitespace(),\n\t\t\t\t\t\t\thighlightTrailingWhitespace(),\n\t\t\t\t\t\t\tmakeWhitespaceTheme(),\n\t\t\t\t\t\t]\n\t\t\t\t\t: [];\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"fadeFoldWidgets\"],\n\t\t\tcompartments: [foldThemeCompartment],\n\t\t\tbuild() {\n\t\t\t\tconst fade = !!appSettings?.value?.fadeFoldWidgets;\n\t\t\t\tif (!fade) return [];\n\t\t\t\treturn EditorView.theme({\n\t\t\t\t\t\".cm-gutter.cm-foldGutter .cm-gutterElement\": {\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\ttransition: \"opacity .12s ease\",\n\t\t\t\t\t},\n\t\t\t\t\t\".cm-gutter.cm-foldGutter:hover .cm-gutterElement, .cm-gutter.cm-foldGutter .cm-gutterElement:hover\":\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\topacity: 1,\n\t\t\t\t\t\t\tpointerEvents: \"auto\",\n\t\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tkeys: [\"liveAutoCompletion\"],\n\t\t\tcompartments: [completionCompartment],\n\t\t\tbuild() {\n\t\t\t\tconst live = !!appSettings?.value?.liveAutoCompletion;\n\t\t\t\treturn autocompletion({\n\t\t\t\t\tactivateOnTyping: live,\n\t\t\t\t\tactivateOnTypingDelay: isCoarsePointerDevice() ? 220 : 100,\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t];\n\n\tfunction getBaseExtensionsFromOptions() {\n\t\t/** @type {import(\"@codemirror/state\").Extension[]} */\n\t\tconst exts = [];\n\t\tfor (const spec of cmOptionSpecs) {\n\t\t\tconst built = spec.build();\n\t\t\tif (spec.compartments.length === 1) {\n\t\t\t\texts.push(spec.compartments[0].of(built));\n\t\t\t} else {\n\t\t\t\tconst arr = Array.isArray(built) ? built : [built];\n\t\t\t\tfor (let i = 0; i < spec.compartments.length; i++) {\n\t\t\t\t\tconst comp = spec.compartments[i];\n\t\t\t\t\tconst ext = arr[i];\n\t\t\t\t\tif (ext !== undefined) exts.push(comp.of(ext));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn exts;\n\t}\n\n\tfunction createEmmetExtensionSet({\n\t\tsyntax,\n\t\ttracker = {},\n\t\tconfig: emmetOverrides = {},\n\t} = {}) {\n\t\tconst resolvedSyntax =\n\t\t\tsyntax === undefined ? EmmetKnownSyntax.html : syntax;\n\t\tif (!resolvedSyntax) return [];\n\t\tconst trackerExtension = abbreviationTracker({\n\t\t\tsyntax: resolvedSyntax,\n\t\t\t...tracker,\n\t\t});\n\t\tconst { autocompleteTab = [\"markup\", \"stylesheet\"], ...restOverrides } =\n\t\t\temmetOverrides || {};\n\t\tconst emmetConfigExtension = emmetConfig.of({\n\t\t\tsyntax: resolvedSyntax,\n\t\t\tautocompleteTab,\n\t\t\t...restOverrides,\n\t\t});\n\t\treturn [\n\t\t\tPrec.high(trackerExtension),\n\t\t\twrapWithAbbreviation(),\n\t\t\tkeymap.of([{ key: \"Mod-e\", run: expandAbbreviation }]),\n\t\t\temmetConfigExtension,\n\t\t];\n\t}\n\n\tfunction applyOptions(keys) {\n\t\tconst filter = keys ? new Set(keys) : null;\n\t\tfor (const spec of cmOptionSpecs) {\n\t\t\tif (filter && !spec.keys.some((k) => filter.has(k))) continue;\n\t\t\tconst built = spec.build();\n\t\t\tconst effects = [];\n\t\t\tif (spec.compartments.length === 1) {\n\t\t\t\teffects.push(spec.compartments[0].reconfigure(built));\n\t\t\t} else {\n\t\t\t\tconst arr = Array.isArray(built) ? built : [built];\n\t\t\t\tfor (let i = 0; i < spec.compartments.length; i++) {\n\t\t\t\t\tconst comp = spec.compartments[i];\n\t\t\t\t\tconst ext = arr[i] ?? [];\n\t\t\t\t\teffects.push(comp.reconfigure(ext));\n\t\t\t\t}\n\t\t\t}\n\t\t\teditor.dispatch({ effects });\n\t\t}\n\t}\n\n\tfunction buildLspMetadata(file) {\n\t\tif (!file || file.type !== \"editor\") return null;\n\t\tconst uri = getFileLspUri(file);\n\t\tif (!uri) return null;\n\t\tconst languageId = getFileLanguageId(file);\n\t\treturn {\n\t\t\turi,\n\t\t\tlanguageId,\n\t\t\tlanguageName: file.currentMode || file.mode || languageId,\n\t\t\tview: editor,\n\t\t\tfile,\n\t\t\trootUri: resolveRootUriForContext({ uri, file }),\n\t\t};\n\t}\n\n\tasync function configureLspForFile(file) {\n\t\tconst metadata = buildLspMetadata(file);\n\t\tconst token = ++lspRequestToken;\n\t\tif (!metadata) {\n\t\t\tdetachActiveLsp();\n\t\t\teditor.dispatch({ effects: lspCompartment.reconfigure([]) });\n\t\t\treturn;\n\t\t}\n\t\tif (metadata.uri !== lastLspUri) {\n\t\t\tdetachActiveLsp();\n\t\t}\n\t\ttry {\n\t\t\tconst extensions =\n\t\t\t\t(await lspClientManager.getExtensionsForFile(metadata)) || [];\n\t\t\tif (token !== lspRequestToken) return;\n\t\t\tif (!extensions.length) {\n\t\t\t\tlastLspUri = null;\n\t\t\t\teditor.dispatch({ effects: lspCompartment.reconfigure([]) });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlastLspUri = metadata.uri;\n\t\t\teditor.dispatch({\n\t\t\t\teffects: lspCompartment.reconfigure(extensions),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (token !== lspRequestToken) return;\n\t\t\tconsole.error(\"Failed to configure LSP\", error);\n\t\t\tlastLspUri = null;\n\t\t\teditor.dispatch({ effects: lspCompartment.reconfigure([]) });\n\t\t}\n\t}\n\n\tfunction detachLspForFile(file) {\n\t\tif (!file || file.type !== \"editor\") return;\n\t\tconst uri = getFileLspUri(file);\n\t\tif (!uri) return;\n\t\ttry {\n\t\t\tlspClientManager.detach(uri);\n\t\t} catch (error) {\n\t\t\tconsole.warn(`Failed to detach LSP client for ${uri}`, error);\n\t\t}\n\t\tif (uri === lastLspUri && manager.activeFile?.id === file.id) {\n\t\t\tlastLspUri = null;\n\t\t\teditor.dispatch({ effects: lspCompartment.reconfigure([]) });\n\t\t}\n\t}\n\n\t// Plugin already wires CSS completions; attach extras for related syntaxes.\n\tconst emmetCompletionSyntaxes = new Set([\n\t\tEmmetKnownSyntax.scss,\n\t\tEmmetKnownSyntax.less,\n\t\tEmmetKnownSyntax.sass,\n\t\tEmmetKnownSyntax.sss,\n\t\tEmmetKnownSyntax.stylus,\n\t\tEmmetKnownSyntax.postcss,\n\t]);\n\n\tfunction maybeAttachEmmetCompletions(targetExtensions, syntax) {\n\t\tif (emmetCompletionSyntaxes.has(syntax)) {\n\t\t\ttargetExtensions.push(\n\t\t\t\tEditorState.languageData.of(() => [\n\t\t\t\t\t{ autocomplete: emmetCompletionSource },\n\t\t\t\t]),\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction getFileLspUri(file) {\n\t\tif (!file) return null;\n\t\tif (file.uri) return file.uri;\n\t\treturn `${UNTITLED_URI_PREFIX}${file.id}`;\n\t}\n\n\tfunction getFileLanguageId(file) {\n\t\tif (!file) return \"plaintext\";\n\t\tconst mode = file.currentMode || file.mode;\n\t\tif (mode) {\n\t\t\tconst modeInfo = getMode(String(mode));\n\t\t\tif (modeInfo?.name) return String(modeInfo.name).toLowerCase();\n\t\t\treturn String(mode).toLowerCase();\n\t\t}\n\t\ttry {\n\t\t\tconst guess = getModeForPath(file.filename || file.name || \"\");\n\t\t\tif (guess?.name) return String(guess.name).toLowerCase();\n\t\t} catch (error) {\n\t\t\twarnRecoverable(\n\t\t\t\t`Failed to resolve language id for ${file.filename || file.name || \"untitled file\"}`,\n\t\t\t\terror,\n\t\t\t\t\"language-id-resolution\",\n\t\t\t);\n\t\t}\n\t\treturn \"plaintext\";\n\t}\n\n\tfunction resolveRootUriForContext(context = {}) {\n\t\tconst uri = context.uri || context.file?.uri;\n\t\tif (!uri) return null;\n\t\tfor (const folder of addedFolder) {\n\t\t\tconst base = typeof folder?.url === \"string\" ? folder.url : \"\";\n\t\t\tif (!base) continue;\n\t\t\tif (uri.startsWith(base)) return base;\n\t\t}\n\t\treturn uri;\n\t}\n\n\tfunction detachActiveLsp() {\n\t\tif (!lastLspUri) return;\n\t\ttry {\n\t\t\tlspClientManager.detach(lastLspUri, editor);\n\t\t} catch (error) {\n\t\t\tconsole.warn(`Failed to detach LSP session for ${lastLspUri}`, error);\n\t\t}\n\t\tlastLspUri = null;\n\t}\n\n\tfunction applyLspSettings() {\n\t\tconst { lsp } = appSettings.value || {};\n\t\tif (!lsp) return;\n\t\tconst overrides = lsp.servers || {};\n\t\tfor (const [id, config] of Object.entries(overrides)) {\n\t\t\tif (!config || typeof config !== \"object\") continue;\n\t\t\tconst key = String(id || \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\t\tif (!key) continue;\n\t\t\tconst existing = lspApi.servers.get(key);\n\t\t\tif (existing) {\n\t\t\t\tlspApi.servers.update(key, (current) => {\n\t\t\t\t\tconst next = { ...current };\n\t\t\t\t\tif (Array.isArray(config.languages) && config.languages.length) {\n\t\t\t\t\t\tnext.languages = config.languages.map((lang) =>\n\t\t\t\t\t\t\tString(lang).toLowerCase(),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (config.transport && typeof config.transport === \"object\") {\n\t\t\t\t\t\tnext.transport = { ...current.transport, ...config.transport };\n\t\t\t\t\t\tdelete next.transport.protocols;\n\t\t\t\t\t}\n\t\t\t\t\tif (config.clientConfig && typeof config.clientConfig === \"object\") {\n\t\t\t\t\t\tnext.clientConfig = {\n\t\t\t\t\t\t\t...current.clientConfig,\n\t\t\t\t\t\t\t...config.clientConfig,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\tconfig.initializationOptions &&\n\t\t\t\t\t\ttypeof config.initializationOptions === \"object\"\n\t\t\t\t\t) {\n\t\t\t\t\t\tnext.initializationOptions = {\n\t\t\t\t\t\t\t...current.initializationOptions,\n\t\t\t\t\t\t\t...config.initializationOptions,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (\n\t\t\t\t\t\ttypeof config.startupTimeout === \"number\" &&\n\t\t\t\t\t\tNumber.isFinite(config.startupTimeout) &&\n\t\t\t\t\t\tconfig.startupTimeout > 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tnext.startupTimeout = Math.floor(config.startupTimeout);\n\t\t\t\t\t}\n\t\t\t\t\tif (config.launcher && typeof config.launcher === \"object\") {\n\t\t\t\t\t\tnext.launcher = { ...current.launcher, ...config.launcher };\n\t\t\t\t\t}\n\t\t\t\t\tif (Object.prototype.hasOwnProperty.call(config, \"enabled\")) {\n\t\t\t\t\t\tnext.enabled = !!config.enabled;\n\t\t\t\t\t}\n\t\t\t\t\treturn next;\n\t\t\t\t});\n\t\t\t\tif (config.enabled === false) {\n\t\t\t\t\tstopManagedServer(key);\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tArray.isArray(config.languages) &&\n\t\t\t\tconfig.languages.length &&\n\t\t\t\tconfig.transport &&\n\t\t\t\ttypeof config.transport === \"object\"\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tlspApi.upsert({\n\t\t\t\t\t\tid: key,\n\t\t\t\t\t\tlabel: config.label || key,\n\t\t\t\t\t\tlanguages: config.languages,\n\t\t\t\t\t\ttransport: config.transport,\n\t\t\t\t\t\tclientConfig: config.clientConfig,\n\t\t\t\t\t\tinitializationOptions: config.initializationOptions,\n\t\t\t\t\t\tstartupTimeout: config.startupTimeout,\n\t\t\t\t\t\tlauncher: config.launcher,\n\t\t\t\t\t\tenabled: config.enabled !== false,\n\t\t\t\t\t});\n\t\t\t\t\tlspApi.servers.update(key, (current) => {\n\t\t\t\t\t\tif (current.transport?.protocols) {\n\t\t\t\t\t\t\tconst updated = { ...current };\n\t\t\t\t\t\t\tupdated.transport = { ...current.transport };\n\t\t\t\t\t\t\tdelete updated.transport.protocols;\n\t\t\t\t\t\t\treturn updated;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn current;\n\t\t\t\t\t});\n\t\t\t\t\tif (config.enabled === false) {\n\t\t\t\t\t\tstopManagedServer(key);\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`Failed to register LSP server override for ${key}`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Create minimal CodeMirror editor\n\tconst editorState = EditorState.create({\n\t\tdoc: \"\",\n\t\textensions: createMainEditorExtensions({\n\t\t\t// Emmet needs highest precedence so place before default keymaps\n\t\t\temmetExtensions: createEmmetExtensionSet({\n\t\t\t\tsyntax: EmmetKnownSyntax.html,\n\t\t\t}),\n\t\t\tbaseExtensions: createBaseExtensions(),\n\t\t\tcommandKeymapExtension: getCommandKeymapExtension(),\n\t\t\tthemeExtension: themeCompartment.of(oneDark),\n\t\t\tpointerCursorVisibilityExtension,\n\t\t\tshiftClickSelectionExtension,\n\t\t\ttouchSelectionUpdateExtension,\n\t\t\tsearchExtension: search(),\n\t\t\t// Ensure read-only can be toggled later via compartment\n\t\t\treadOnlyExtension: readOnlyCompartment.of(EditorState.readOnly.of(false)),\n\t\t\t// Editor options driven by settings via compartments\n\t\t\toptionExtensions: getBaseExtensionsFromOptions(),\n\t\t}),\n\t});\n\n\tconst editor = new EditorView({\n\t\tstate: editorState,\n\t\tparent: $container,\n\t});\n\n\tawait applyKeyBindings(editor);\n\n\teditor.execCommand = function (commandName, args) {\n\t\tif (!commandName) return false;\n\t\treturn executeCommand(String(commandName), editor, args);\n\t};\n\n\teditor.commands = {\n\t\taddCommand(descriptor) {\n\t\t\tconst command = registerExternalCommand(descriptor);\n\t\t\trefreshCommandKeymap(editor);\n\t\t\treturn command;\n\t\t},\n\t\tremoveCommand(name) {\n\t\t\tif (!name) return;\n\t\t\tremoveExternalCommand(name);\n\t\t\trefreshCommandKeymap(editor);\n\t\t},\n\t};\n\n\tObject.defineProperty(editor.commands, \"commands\", {\n\t\tget() {\n\t\t\tconst map = {};\n\t\t\tgetRegisteredCommands().forEach((cmd) => {\n\t\t\t\tmap[cmd.name] = cmd;\n\t\t\t});\n\t\t\treturn map;\n\t\t},\n\t});\n\n\t// Provide editor.session for Ace API compatibility\n\t// Returns the active file's session (Proxy with Ace-like methods)\n\tObject.defineProperty(editor, \"session\", {\n\t\tget() {\n\t\t\treturn manager.activeFile?.session ?? null;\n\t\t},\n\t});\n\n\ttouchSelectionController = createTouchSelectionMenu(editor, {\n\t\tcontainer: $container,\n\t\tgetActiveFile: () => manager?.activeFile || null,\n\t\tisShiftSelectionActive,\n\t});\n\n\t// Provide minimal Ace-like API compatibility used by plugins\n\t/**\n\t * Insert text at the current selection/cursor in the editor\n\t * @param {string} text\n\t * @returns {boolean} success\n\t */\n\teditor.insert = function (text) {\n\t\ttry {\n\t\t\tconst { from, to } = editor.state.selection.main;\n\t\t\tconst insertText = String(text ?? \"\");\n\t\t\t// Replace current selection and move cursor to end of inserted text\n\t\t\teditor.dispatch({\n\t\t\t\tchanges: { from, to, insert: insertText },\n\t\t\t\tselection: {\n\t\t\t\t\tanchor: from + insertText.length,\n\t\t\t\t\thead: from + insertText.length,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn true;\n\t\t} catch (_) {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\t// Set CodeMirror theme by id registered in our registry\n\teditor.setTheme = function (themeId) {\n\t\ttry {\n\t\t\tconst id = String(themeId || \"\");\n\t\t\tconst ext = getThemeExtensions(id, [oneDark]);\n\t\t\teditor.dispatch({ effects: themeCompartment.reconfigure(ext) });\n\t\t\treturn true;\n\t\t} catch (_) {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\t/**\n\t * Go to a specific line and column in the editor (CodeMirror implementation)\n\t * Supports multiple input formats:\n\t * - Simple line number: gotoLine(16) or gotoLine(16, 5)\n\t * - Relative offsets: gotoLine(\"+5\") or gotoLine(\"-3\")\n\t * - Percentages: gotoLine(\"50%\") or gotoLine(\"25%\")\n\t * - Line:column format: gotoLine(\"16:5\")\n\t * - Mixed formats: gotoLine(\"+5:10\") or gotoLine(\"50%:5\")\n\t *\n\t * @param {number|string} line - Line number (1-based), or string with special formats\n\t * @param {number} column - Column number (0-based) - only used with numeric line parameter\n\t * @param {boolean} animate - Whether to animate (not used in CodeMirror, for compatibility)\n\t * @returns {boolean} success\n\t */\n\teditor.gotoLine = function (line, column = 0, animate = false) {\n\t\ttry {\n\t\t\tconst { state } = editor;\n\t\t\tconst { doc } = state;\n\n\t\t\tlet targetLine,\n\t\t\t\ttargetColumn = column;\n\n\t\t\t// If line is a string, parse it for special formats\n\t\t\tif (typeof line === \"string\") {\n\t\t\t\tconst match = /^([+-])?(\\d+)?(:\\d+)?(%)?$/.exec(line.trim());\n\t\t\t\tif (!match) {\n\t\t\t\t\tconsole.warn(\"Invalid gotoLine format:\", line);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tconst currentLine = doc.lineAt(state.selection.main.head);\n\t\t\t\tconst [, sign, lineNum, colonColumn, percent] = match;\n\n\t\t\t\t// Parse column if specified in line:column format\n\t\t\t\tif (colonColumn) {\n\t\t\t\t\ttargetColumn = Math.max(0, +colonColumn.slice(1) - 1); // Convert to 0-based\n\t\t\t\t}\n\n\t\t\t\t// Parse line number\n\t\t\t\tlet parsedLine = lineNum ? +lineNum : currentLine.number;\n\n\t\t\t\tif (lineNum && percent) {\n\t\t\t\t\t// Percentage format: \"50%\" or \"+10%\"\n\t\t\t\t\tlet percentage = parsedLine / 100;\n\t\t\t\t\tif (sign) {\n\t\t\t\t\t\tpercentage =\n\t\t\t\t\t\t\tpercentage * (sign === \"-\" ? -1 : 1) +\n\t\t\t\t\t\t\tcurrentLine.number / doc.lines;\n\t\t\t\t\t}\n\t\t\t\t\ttargetLine = Math.round(doc.lines * percentage);\n\t\t\t\t} else if (lineNum && sign) {\n\t\t\t\t\t// Relative format: \"+5\" or \"-3\"\n\t\t\t\t\ttargetLine =\n\t\t\t\t\t\tparsedLine * (sign === \"-\" ? -1 : 1) + currentLine.number;\n\t\t\t\t} else if (lineNum) {\n\t\t\t\t\t// Absolute line number\n\t\t\t\t\ttargetLine = parsedLine;\n\t\t\t\t} else {\n\t\t\t\t\t// No line number specified, stay on current line\n\t\t\t\t\ttargetLine = currentLine.number;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Simple numeric line parameter\n\t\t\t\ttargetLine = line;\n\t\t\t}\n\n\t\t\t// Clamp line number to valid range\n\t\t\tconst lineNum = Math.max(1, Math.min(targetLine, doc.lines));\n\t\t\tconst docLine = doc.line(lineNum);\n\n\t\t\t// Clamp column to line length\n\t\t\tconst col = Math.max(0, Math.min(targetColumn, docLine.length));\n\t\t\tconst pos = docLine.from + col;\n\n\t\t\t// Move cursor and scroll into view\n\t\t\teditor.dispatch({\n\t\t\t\tselection: { anchor: pos, head: pos },\n\t\t\t\teffects: EditorView.scrollIntoView(pos, { y: \"center\" }),\n\t\t\t});\n\t\t\teditor.focus();\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error in gotoLine:\", error);\n\t\t\treturn false;\n\t\t}\n\t};\n\n\t/**\n\t * Get current cursor position)\n\t * @returns {{row: number, column: number}} Cursor position\n\t */\n\teditor.getCursorPosition = function () {\n\t\ttry {\n\t\t\tconst head = editor.state.selection.main.head;\n\t\t\tconst cursor = editor.state.doc.lineAt(head);\n\t\t\tconst line = cursor.number;\n\t\t\tconst col = head - cursor.from;\n\t\t\treturn { row: line, column: col };\n\t\t} catch (_) {\n\t\t\treturn { row: 1, column: 0 };\n\t\t}\n\t};\n\n\t/**\n\t * Ace-compatible selection range getter with 0-based rows.\n\t * @returns {{start: {row: number, column: number}, end: {row: number, column: number}}}\n\t */\n\teditor.getSelectionRange = function () {\n\t\ttry {\n\t\t\tconst { from, to } = editor.state.selection.main;\n\t\t\tconst fromLine = editor.state.doc.lineAt(from);\n\t\t\tconst toLine = editor.state.doc.lineAt(to);\n\t\t\treturn {\n\t\t\t\tstart: {\n\t\t\t\t\trow: Math.max(0, fromLine.number - 1),\n\t\t\t\t\tcolumn: from - fromLine.from,\n\t\t\t\t},\n\t\t\t\tend: {\n\t\t\t\t\trow: Math.max(0, toLine.number - 1),\n\t\t\t\t\tcolumn: to - toLine.from,\n\t\t\t\t},\n\t\t\t};\n\t\t} catch (_) {\n\t\t\treturn { start: { row: 0, column: 0 }, end: { row: 0, column: 0 } };\n\t\t}\n\t};\n\n\t/**\n\t * Ace-compatible row scrolling helper.\n\t * @param {number} row - 0-based row index, supports Infinity to jump to end.\n\t * @returns {boolean}\n\t */\n\teditor.scrollToRow = function (row) {\n\t\ttry {\n\t\t\tconst scroller = editor.scrollDOM;\n\t\t\tif (!scroller) return false;\n\n\t\t\tif (row === Number.POSITIVE_INFINITY) {\n\t\t\t\tscroller.scrollTop = Math.max(\n\t\t\t\t\tscroller.scrollHeight - scroller.clientHeight,\n\t\t\t\t\t0,\n\t\t\t\t);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst parsedRow = Number(row);\n\t\t\tif (!Number.isFinite(parsedRow)) return false;\n\t\t\tconst aceRow = Math.max(0, Math.floor(parsedRow));\n\t\t\tconst lineNum = Math.min(editor.state.doc.lines, aceRow + 1);\n\t\t\tconst line = editor.state.doc.line(lineNum);\n\t\t\teditor.dispatch({\n\t\t\t\teffects: EditorView.scrollIntoView(line.from, { y: \"start\" }),\n\t\t\t});\n\t\t\treturn true;\n\t\t} catch (_) {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\t/**\n\t * Move cursor to specific position\n\t * @param {{row: number, column: number}} pos - Position to move to\n\t */\n\teditor.moveCursorToPosition = function (pos) {\n\t\ttry {\n\t\t\tconst lineNum = Math.max(1, pos.row || 1);\n\t\t\tconst col = Math.max(0, pos.column || 0);\n\t\t\teditor.gotoLine(lineNum, col);\n\t\t} catch (_) {\n\t\t\t// ignore\n\t\t}\n\t};\n\n\t/**\n\t * Get the entire document value\n\t * @returns {string} Document content\n\t */\n\teditor.getValue = function () {\n\t\ttry {\n\t\t\treturn editor.state.doc.toString();\n\t\t} catch (_) {\n\t\t\treturn \"\";\n\t\t}\n\t};\n\n\t/**\n\t * Compatibility object for selection-related methods\n\t */\n\teditor.selection = {\n\t\t/**\n\t\t * Get current selection anchor\n\t\t * @returns {number} Anchor position\n\t\t */\n\t\tget anchor() {\n\t\t\ttry {\n\t\t\t\treturn editor.state.selection.main.anchor;\n\t\t\t} catch (_) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Get current selection range\n\t\t * @returns {{start: {row: number, column: number}, end: {row: number, column: number}}} Selection range\n\t\t */\n\t\tgetRange: function () {\n\t\t\ttry {\n\t\t\t\tconst { from, to } = editor.state.selection.main;\n\t\t\t\tconst fromLine = editor.state.doc.lineAt(from);\n\t\t\t\tconst toLine = editor.state.doc.lineAt(to);\n\t\t\t\treturn {\n\t\t\t\t\tstart: {\n\t\t\t\t\t\trow: fromLine.number,\n\t\t\t\t\t\tcolumn: from - fromLine.from,\n\t\t\t\t\t},\n\t\t\t\t\tend: {\n\t\t\t\t\t\trow: toLine.number,\n\t\t\t\t\t\tcolumn: to - toLine.from,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t} catch (_) {\n\t\t\t\treturn { start: { row: 1, column: 0 }, end: { row: 1, column: 0 } }; // Default to line 1\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Get cursor position\n\t\t * @returns {{row: number, column: number}} Cursor position\n\t\t */\n\t\tgetCursor: function () {\n\t\t\treturn editor.getCursorPosition();\n\t\t},\n\t};\n\n\t/**\n\t * Get selected text or text under cursor (CodeMirror implementation)\n\t * @returns {string} Selected text\n\t */\n\teditor.getCopyText = function () {\n\t\ttry {\n\t\t\tconst { from, to } = editor.state.selection.main;\n\t\t\tif (from === to) return \"\"; // No selection\n\t\t\treturn editor.state.doc.sliceString(from, to);\n\t\t} catch (_) {\n\t\t\treturn \"\";\n\t\t}\n\t};\n\n\teditor.setSelection = function (value) {\n\t\ttouchSelectionController?.setSelection(!!value);\n\t};\n\n\teditor.setMenu = function (value) {\n\t\ttouchSelectionController?.setMenu(!!value);\n\t};\n\n\t// Helper: apply a file's content and language to the editor view\n\tfunction applyFileToEditor(file) {\n\t\tif (!file || file.type !== \"editor\") return;\n\t\tconst syntax = getEmmetSyntaxForFile(file);\n\t\tconst baseExtensions = createMainEditorExtensions({\n\t\t\t// Emmet needs to precede default keymaps so tracker Tab wins over indent\n\t\t\temmetExtensions: createEmmetExtensionSet({ syntax }),\n\t\t\tbaseExtensions: createBaseExtensions(),\n\t\t\tcommandKeymapExtension: getCommandKeymapExtension(),\n\t\t\t// keep compartment in the state to allow dynamic theme changes later\n\t\t\tthemeExtension: themeCompartment.of(oneDark),\n\t\t\tpointerCursorVisibilityExtension,\n\t\t\tshiftClickSelectionExtension,\n\t\t\ttouchSelectionUpdateExtension,\n\t\t\tsearchExtension: search(),\n\t\t\t// Keep dynamic compartments across state swaps\n\t\t\toptionExtensions: getBaseExtensionsFromOptions(),\n\t\t});\n\t\tconst exts = [...baseExtensions];\n\t\tmaybeAttachEmmetCompletions(exts, syntax);\n\t\ttry {\n\t\t\tconst langExtFn = file.currentLanguageExtension;\n\t\t\tlet initialLang = [];\n\t\t\tif (typeof langExtFn === \"function\") {\n\t\t\t\tlet result;\n\t\t\t\ttry {\n\t\t\t\t\tresult = langExtFn();\n\t\t\t\t} catch (_) {\n\t\t\t\t\tresult = [];\n\t\t\t\t}\n\t\t\t\t// If the loader returns a Promise, reconfigure when it resolves\n\t\t\t\tif (result && typeof result.then === \"function\") {\n\t\t\t\t\tinitialLang = [];\n\t\t\t\t\tresult\n\t\t\t\t\t\t.then((ext) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\teditor.dispatch({\n\t\t\t\t\t\t\t\t\teffects: languageCompartment.reconfigure(ext || []),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\twarnRecoverable(\n\t\t\t\t\t\t\t\t\t\"Failed to apply async language extensions.\",\n\t\t\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t\t\t\t\"async-language-reconfigure\",\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t// ignore load errors; remain in plain text\n\t\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tinitialLang = result || [];\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Ensure language compartment is present (empty -> plain text)\n\t\t\texts.push(languageCompartment.of(initialLang));\n\t\t} catch (e) {\n\t\t\t// ignore language extension errors; fallback to plain text\n\t\t}\n\n\t\t// Color preview plugin when enabled\n\t\tif (appSettings.value.colorPreview) {\n\t\t\texts.push(colorView(true));\n\t\t}\n\n\t\t// Apply read-only state based on file.editable/loading using Compartment\n\t\ttry {\n\t\t\tconst ro = !file.editable || !!file.loading;\n\t\t\texts.push(readOnlyCompartment.of(EditorState.readOnly.of(ro)));\n\t\t} catch (e) {\n\t\t\t// safe to ignore; editor will remain editable by default\n\t\t}\n\n\t\t// Keep file.session in sync and handle caching/autosave\n\t\texts.push(getDocSyncListener());\n\t\texts.push(lspCompartment.of([]));\n\n\t\t// Preserve previous state for restoring selection/folds after swap\n\t\tconst prevState = file.session || null;\n\n\t\tconst doc = prevState ? prevState.doc.toString() : \"\";\n\t\tconst state = EditorState.create({ doc, extensions: exts });\n\t\tfile.session = state;\n\t\teditor.setState(state);\n\t\ttouchSelectionController?.onSessionChanged();\n\t\t// Re-apply selected theme after state replacement\n\t\tconst desiredTheme = appSettings?.value?.editorTheme;\n\t\tif (desiredTheme) editor.setTheme(desiredTheme);\n\n\t\t// Ensure dynamic compartments reflect current settings\n\t\tapplyOptions();\n\n\t\t// Restore selection from previous state if available\n\t\ttry {\n\t\t\tconst sel = prevState?.selection;\n\t\t\tif (sel && Array.isArray(sel.ranges)) {\n\t\t\t\tconst ranges = sel.ranges.map((r) => ({ from: r.from, to: r.to }));\n\t\t\t\tconst mainIndex = sel.mainIndex ?? 0;\n\t\t\t\trestoreSelection(editor, { ranges, mainIndex });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\twarnRecoverable(\n\t\t\t\t\"Failed to restore selection from previous session state.\",\n\t\t\t\terror,\n\t\t\t\t\"restore-selection\",\n\t\t\t);\n\t\t}\n\n\t\t// Restore folds from previous state if available\n\t\ttry {\n\t\t\tconst folds = prevState ? getAllFolds(prevState) : [];\n\t\t\tif (folds && folds.length) {\n\t\t\t\trestoreFolds(editor, folds);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\twarnRecoverable(\n\t\t\t\t\"Failed to restore folded regions from previous session state.\",\n\t\t\t\terror,\n\t\t\t\t\"restore-folds\",\n\t\t\t);\n\t\t}\n\n\t\t// Restore last known scroll position if present\n\t\tif (\n\t\t\ttypeof file.lastScrollTop === \"number\" ||\n\t\t\ttypeof file.lastScrollLeft === \"number\"\n\t\t) {\n\t\t\tsetScrollPosition(editor, file.lastScrollTop, file.lastScrollLeft);\n\t\t}\n\n\t\tvoid configureLspForFile(file);\n\t}\n\n\tfunction getEmmetSyntaxForFile(file) {\n\t\tconst mode = (file?.currentMode || \"\").toLowerCase();\n\t\tconst name = (file?.filename || \"\").toLowerCase();\n\t\tconst ext = name.includes(\".\") ? name.split(\".\").pop() : \"\";\n\t\tif (ext === \"tsx\" || mode.includes(\"tsx\")) return EmmetKnownSyntax.tsx;\n\t\tif (ext === \"jsx\" || mode.includes(\"jsx\")) return EmmetKnownSyntax.jsx;\n\t\tif (mode.includes(\"javascript\") && (ext === \"jsx\" || ext === \"tsx\")) {\n\t\t\treturn ext === \"tsx\" ? EmmetKnownSyntax.tsx : EmmetKnownSyntax.jsx;\n\t\t}\n\t\tif (ext === \"css\" || mode.includes(\"css\")) return EmmetKnownSyntax.css;\n\t\tif (ext === \"scss\" || mode.includes(\"scss\")) return EmmetKnownSyntax.scss;\n\t\tif (ext === \"sass\" || mode.includes(\"sass\")) return EmmetKnownSyntax.sass;\n\t\tif (ext === \"less\" || mode.includes(\"less\")) return EmmetKnownSyntax.less;\n\t\tif (ext === \"sss\" || mode.includes(\"sss\")) return EmmetKnownSyntax.sss;\n\t\tif (ext === \"styl\" || ext === \"stylus\" || mode.includes(\"styl\"))\n\t\t\treturn EmmetKnownSyntax.stylus;\n\t\tif (ext === \"postcss\" || mode.includes(\"postcss\"))\n\t\t\treturn EmmetKnownSyntax.postcss;\n\t\tif (ext === \"xml\" || mode.includes(\"xml\")) return EmmetKnownSyntax.xml;\n\t\tif (ext === \"xsl\" || mode.includes(\"xsl\")) return EmmetKnownSyntax.xsl;\n\t\tif (ext === \"haml\" || mode.includes(\"haml\")) return EmmetKnownSyntax.haml;\n\t\tif (\n\t\t\text === \"pug\" ||\n\t\t\text === \"jade\" ||\n\t\t\tmode.includes(\"pug\") ||\n\t\t\tmode.includes(\"jade\")\n\t\t)\n\t\t\treturn EmmetKnownSyntax.pug;\n\t\tif (ext === \"slim\" || mode.includes(\"slim\")) return EmmetKnownSyntax.slim;\n\t\tif (ext === \"vue\" || mode.includes(\"vue\")) return EmmetKnownSyntax.vue;\n\t\tif (ext === \"php\" || mode.includes(\"php\")) return EmmetKnownSyntax.html;\n\t\tif (\n\t\t\text === \"htm\" ||\n\t\t\text === \"html\" ||\n\t\t\text === \"xhtml\" ||\n\t\t\tmode.includes(\"html\")\n\t\t)\n\t\t\treturn EmmetKnownSyntax.html;\n\t\treturn null;\n\t}\n\n\tconst $vScrollbar = ScrollBar({\n\t\twidth: scrollbarSize,\n\t\tonscroll: onscrollV,\n\t\tonscrollend: onscrollVend,\n\t\tparent: $body,\n\t});\n\tconst $hScrollbar = ScrollBar({\n\t\twidth: scrollbarSize,\n\t\tonscroll: onscrollH,\n\t\tonscrollend: onscrollHEnd,\n\t\tparent: $body,\n\t\tplacement: \"bottom\",\n\t});\n\tconst manager = {\n\t\tfiles: [],\n\t\tonupdate: () => {},\n\t\tactiveFile: null,\n\t\tisCodeMirror: true,\n\t\taddFile,\n\t\teditor,\n\t\treadOnlyCompartment,\n\t\tgetFile,\n\t\tswitchFile,\n\t\tmoveFileByPinnedState,\n\t\tnormalizePinnedTabOrder,\n\t\tsyncOpenFileList,\n\t\thasUnsavedFiles,\n\t\tgetEditorHeight,\n\t\tgetEditorWidth,\n\t\theader: $header,\n\t\tcontainer: $container,\n\t\tgetLspMetadata: buildLspMetadata,\n\t\tget isScrolling() {\n\t\t\treturn isScrolling;\n\t\t},\n\t\tget openFileList() {\n\t\t\tif (!$openFileList) initFileTabContainer();\n\t\t\treturn $openFileList;\n\t\t},\n\t\tget TIMEOUT_VALUE() {\n\t\t\treturn TIMEOUT_VALUE;\n\t\t},\n\t\ton(types, callback) {\n\t\t\tif (!Array.isArray(types)) types = [types];\n\t\t\ttypes.forEach((type) => {\n\t\t\t\tif (!events[type]) events[type] = [];\n\t\t\t\tevents[type].push(callback);\n\t\t\t});\n\t\t},\n\t\toff(types, callback) {\n\t\t\tif (!Array.isArray(types)) types = [types];\n\t\t\ttypes.forEach((type) => {\n\t\t\t\tif (!events[type]) return;\n\t\t\t\tevents[type] = events[type].filter((c) => c !== callback);\n\t\t\t});\n\t\t},\n\t\temit(event, ...args) {\n\t\t\tlet detailedEvent;\n\t\t\tlet detailedEventArgs = args.slice(1);\n\t\t\tif (event === \"update\") {\n\t\t\t\tconst subEvent = args[0];\n\t\t\t\tif (subEvent) {\n\t\t\t\t\tdetailedEvent = `${event}:${subEvent}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\tevents.emit(event, ...args);\n\t\t\tif (detailedEvent) {\n\t\t\t\tevents.emit(detailedEvent, ...detailedEventArgs);\n\t\t\t}\n\t\t},\n\t\t/**\n\t\t * Restart LSP for the active file\n\t\t * Useful after stopping/restarting language servers\n\t\t */\n\t\trestartLsp() {\n\t\t\tconst activeFile = manager.activeFile;\n\t\t\tif (activeFile?.type === \"editor\") {\n\t\t\t\tvoid configureLspForFile(activeFile);\n\t\t\t}\n\t\t},\n\t};\n\n\tif (typeof document !== \"undefined\") {\n\t\tconst globalTarget =\n\t\t\ttypeof globalThis !== \"undefined\" ? globalThis : document;\n\t\tconst diagnosticsListenerKey = \"__acodeDiagnosticsListener\";\n\t\tconst existing = globalTarget?.[diagnosticsListenerKey];\n\t\tif (typeof existing === \"function\") {\n\t\t\tdocument.removeEventListener(LSP_DIAGNOSTICS_EVENT, existing);\n\t\t}\n\t\tlet diagnosticsButtonSyncRaf = 0;\n\t\tconst listener = () => {\n\t\t\tcancelAnimationFrame(diagnosticsButtonSyncRaf);\n\t\t\tdiagnosticsButtonSyncRaf = requestAnimationFrame(() => {\n\t\t\t\tdiagnosticsButtonSyncRaf = 0;\n\t\t\t\tconst active = manager.activeFile;\n\t\t\t\tif (active?.type === \"editor\") {\n\t\t\t\t\tactive.session = editor.state;\n\t\t\t\t}\n\t\t\t\ttoggleProblemButton();\n\t\t\t});\n\t\t};\n\t\tdocument.addEventListener(LSP_DIAGNOSTICS_EVENT, listener);\n\t\tif (globalTarget) {\n\t\t\tglobalTarget[diagnosticsListenerKey] = listener;\n\t\t}\n\t}\n\n\tlspClientManager.setOptions({\n\t\tresolveRoot: resolveRootUriForContext,\n\t\tonClientIdle: ({ server }) => {\n\t\t\tif (server?.id) stopManagedServer(server.id);\n\t\t},\n\t\tdisplayFile: async (targetUri) => {\n\t\t\tif (!targetUri) return null;\n\t\t\t// Decode URI components (e.g., %40 -> @) since LSP returns encoded URIs\n\t\t\tconst decodedUri = decodeURIComponent(targetUri);\n\t\t\tconst existing = manager.getFile(decodedUri, \"uri\");\n\t\t\tif (existing?.type === \"editor\") {\n\t\t\t\texisting.makeActive();\n\t\t\t\treturn editor;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait openFile(decodedUri, { render: true });\n\t\t\t\tconst opened = manager.getFile(decodedUri, \"uri\");\n\t\t\t\tif (opened?.type === \"editor\") {\n\t\t\t\t\topened.makeActive();\n\t\t\t\t\treturn editor;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[LSP] Failed to open file\", decodedUri, error);\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\topenFile: async (targetUri) => {\n\t\t\tif (!targetUri) return null;\n\t\t\t// Decode URI components (e.g., %40 -> @)\n\t\t\tconst decodedUri = decodeURIComponent(targetUri);\n\t\t\tconst existing = manager.getFile(decodedUri, \"uri\");\n\t\t\tif (existing?.type === \"editor\") {\n\t\t\t\texisting.makeActive();\n\t\t\t\treturn editor;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait openFile(decodedUri, { render: true });\n\t\t\t\tconst opened = manager.getFile(decodedUri, \"uri\");\n\t\t\t\tif (opened?.type === \"editor\") {\n\t\t\t\t\topened.makeActive();\n\t\t\t\t\treturn editor;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[LSP] Failed to open file\", decodedUri, error);\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\tresolveLanguageId: (uri) => {\n\t\t\tif (!uri) return \"plaintext\";\n\t\t\ttry {\n\t\t\t\tconst mode = getModeForPath(uri);\n\t\t\t\tif (mode?.name) return String(mode.name).toLowerCase();\n\t\t\t} catch (error) {\n\t\t\t\twarnRecoverable(\n\t\t\t\t\t`Failed to resolve language id for URI: ${uri}`,\n\t\t\t\t\terror,\n\t\t\t\t\t\"lsp-language-id-resolution\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn \"plaintext\";\n\t\t},\n\t\tclientExtensions: [diagnosticsClientExt],\n\t\tdiagnosticsUiExtension: buildDiagnosticsUiExt(),\n\t});\n\tapplyLspSettings();\n\n\t$body.append($container);\n\tinitModes(); // Initialize CodeMirror modes\n\tawait setupEditor();\n\n\t// Initialize theme from settings or fallback\n\ttry {\n\t\tconst desired = appSettings?.value?.editorTheme || \"one_dark\";\n\t\teditor.setTheme(desired);\n\t} catch (error) {\n\t\twarnRecoverable(\n\t\t\t\"Failed to apply configured editor theme. Falling back to one_dark.\",\n\t\t\terror,\n\t\t\t\"initial-editor-theme\",\n\t\t);\n\t\teditor.setTheme(\"one_dark\");\n\t}\n\n\t// Ensure initial options reflect settings\n\tapplyOptions();\n\n\t$hScrollbar.onshow = $vScrollbar.onshow = updateFloatingButton.bind(\n\t\t{},\n\t\tfalse,\n\t);\n\t$hScrollbar.onhide = $vScrollbar.onhide = updateFloatingButton.bind({}, true);\n\n\tappSettings.on(\"update:textWrap\", function () {\n\t\tupdateMargin();\n\t\tapplyOptions([\"textWrap\"]);\n\t});\n\n\tfunction updateEditorIndentationSettings() {\n\t\tapplyOptions([\"softTab\", \"tabSize\"]);\n\t}\n\n\tfunction updateEditorStyleFromSettings() {\n\t\tapplyOptions([\"fontSize\", \"editorFont\", \"lineHeight\"]);\n\t}\n\n\tfunction updateEditorWrapFromSettings() {\n\t\tapplyOptions([\"textWrap\"]);\n\t\tif (appSettings.value.textWrap) {\n\t\t\t$hScrollbar.hide();\n\t\t}\n\t}\n\n\tfunction updateEditorLineNumbersFromSettings() {\n\t\tapplyOptions([\"linenumbers\", \"relativeLineNumbers\"]);\n\t}\n\n\tappSettings.on(\"update:tabSize\", function () {\n\t\tupdateEditorIndentationSettings();\n\t});\n\n\tappSettings.on(\"update:softTab\", function () {\n\t\tupdateEditorIndentationSettings();\n\t});\n\n\t// Show spaces/tabs and trailing whitespace\n\tappSettings.on(\"update:showSpaces\", function () {\n\t\tapplyOptions([\"showSpaces\"]);\n\t});\n\n\t// Font size update for CodeMirror\n\tappSettings.on(\"update:fontSize\", function () {\n\t\tupdateEditorStyleFromSettings();\n\t});\n\n\t// Font family update for CodeMirror\n\tappSettings.on(\"update:editorFont\", function () {\n\t\tupdateEditorStyleFromSettings();\n\t});\n\n\tappSettings.on(\"update:lsp\", async function () {\n\t\tapplyLspSettings();\n\t\tconst active = manager.activeFile;\n\t\tif (active?.type === \"editor\") {\n\t\t\tvoid configureLspForFile(active);\n\t\t} else {\n\t\t\tdetachActiveLsp();\n\t\t\teditor.dispatch({ effects: lspCompartment.reconfigure([]) });\n\t\t\tawait lspClientManager.dispose();\n\t\t}\n\t});\n\n\tappSettings.on(\"update:openFileListPos\", function (value) {\n\t\tinitFileTabContainer();\n\t\t$vScrollbar.resize();\n\t});\n\n\t// appSettings.on(\"update:showPrintMargin\", function (value) {\n\t// \t// manager.editor.setOption(\"showPrintMargin\", value);\n\t// });\n\n\tappSettings.on(\"update:scrollbarSize\", function (value) {\n\t\t$vScrollbar.size = value;\n\t\t$hScrollbar.size = value;\n\t});\n\n\t// Live autocompletion (activateOnTyping)\n\tappSettings.on(\"update:liveAutoCompletion\", function () {\n\t\tapplyOptions([\"liveAutoCompletion\"]);\n\t});\n\n\tappSettings.on(\"update:linenumbers\", function () {\n\t\tupdateMargin(true);\n\t\tupdateEditorLineNumbersFromSettings();\n\t});\n\n\t// Line height update for CodeMirror\n\tappSettings.on(\"update:lineHeight\", function () {\n\t\tupdateEditorStyleFromSettings();\n\t});\n\n\tappSettings.on(\"update:relativeLineNumbers\", function () {\n\t\tupdateEditorLineNumbersFromSettings();\n\t});\n\n\tappSettings.on(\"update:editorTheme\", function () {\n\t\tconst desiredTheme = appSettings?.value?.editorTheme || \"one_dark\";\n\t\teditor.setTheme(desiredTheme);\n\t\tapplyOptions([\"rainbowBrackets\"]);\n\t});\n\n\tappSettings.on(\"update:lintGutter\", function (value) {\n\t\tlspClientManager.setOptions({\n\t\t\tdiagnosticsUiExtension: lspDiagnosticsUiExtension(value !== false),\n\t\t});\n\t\tconst active = manager.activeFile;\n\t\tif (active?.type === \"editor\") {\n\t\t\tvoid configureLspForFile(active);\n\t\t}\n\t});\n\n\t// appSettings.on(\"update:elasticTabstops\", function (_value) {\n\t// \t// Not applicable in CodeMirror (Ace-era). No-op for now.\n\t// });\n\n\tappSettings.on(\"update:rtlText\", function () {\n\t\tapplyOptions([\"rtlText\"]);\n\t});\n\n\t// appSettings.on(\"update:hardWrap\", function (_value) {\n\t// \t// Not applicable in CodeMirror (Ace-era). No-op for now.\n\t// });\n\n\t// appSettings.on(\"update:printMargin\", function (_value) {\n\t// \t// Not applicable in CodeMirror (Ace-era). No-op for now.\n\t// });\n\n\tappSettings.on(\"update:colorPreview\", function () {\n\t\tconst file = manager.activeFile;\n\t\tif (file?.type === \"editor\") applyFileToEditor(file);\n\t});\n\n\tappSettings.on(\"update:showSideButtons\", function () {\n\t\tupdateMargin();\n\t\tupdateSideButtonContainer();\n\t\ttoggleProblemButton();\n\t});\n\n\tappSettings.on(\"update:showAnnotations\", function () {\n\t\tupdateMargin(true);\n\t});\n\n\tappSettings.on(\"update:fadeFoldWidgets\", function () {\n\t\tapplyOptions([\"fadeFoldWidgets\"]);\n\t});\n\n\t// Toggle rainbow brackets\n\tappSettings.on(\"update:rainbowBrackets\", function () {\n\t\tapplyOptions([\"rainbowBrackets\"]);\n\t});\n\n\t// Toggle indent guides\n\tappSettings.on(\"update:indentGuides\", function () {\n\t\tapplyOptions([\"indentGuides\"]);\n\t});\n\n\t// Keep file.session and cache in sync on every edit\n\tfunction getDocSyncListener() {\n\t\treturn EditorView.updateListener.of((update) => {\n\t\t\tconst file = manager.activeFile;\n\t\t\tif (!file || file.type !== \"editor\") return;\n\n\t\t\t// Only run expensive work when the document actually changed\n\t\t\tif (!update.docChanged) return;\n\n\t\t\t// Mirror latest state only on doc changes to avoid clobbering async loads\n\t\t\tfile.session = update.state;\n\n\t\t\t// Debounced change handling (unsaved flag, cache, autosave)\n\t\t\tif (checkTimeout) clearTimeout(checkTimeout);\n\t\t\tif (autosaveTimeout) clearTimeout(autosaveTimeout);\n\n\t\t\tcheckTimeout = setTimeout(async () => {\n\t\t\t\tconst changed = await file.isChanged();\n\t\t\t\tfile.isUnsaved = changed;\n\t\t\t\ttry {\n\t\t\t\t\tawait file.writeToCache();\n\t\t\t\t} catch (error) {\n\t\t\t\t\twarnRecoverable(\n\t\t\t\t\t\t`Failed to write cache for ${file.filename || file.uri}`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t\t`cache-write-${file.id}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tevents.emit(\"file-content-changed\", file);\n\t\t\t\tmanager.onupdate(\"file-changed\");\n\t\t\t\tmanager.emit(\"update\", \"file-changed\");\n\t\t\t\ttoggleProblemButton();\n\n\t\t\t\tconst { autosave } = appSettings.value;\n\t\t\t\tif (file.uri && changed && autosave) {\n\t\t\t\t\tautosaveTimeout = setTimeout(() => {\n\t\t\t\t\t\tacode.exec(\"save\", false);\n\t\t\t\t\t}, autosave);\n\t\t\t\t}\n\n\t\t\t\tfile.markChanged = true;\n\t\t\t}, TIMEOUT_VALUE);\n\t\t});\n\t}\n\n\t// Register critical listeners\n\tmanager.on([\"file-loaded\"], (file) => {\n\t\tif (!file) return;\n\t\tif (manager.activeFile?.id === file.id && file.type === \"editor\") {\n\t\t\tapplyFileToEditor(file);\n\t\t}\n\t});\n\n\tmanager.on([\"update:read-only\"], () => {\n\t\tconst file = manager.activeFile;\n\t\tif (file?.type !== \"editor\") return;\n\t\ttry {\n\t\t\tconst ro = !file.editable || !!file.loading;\n\t\t\teditor.dispatch({\n\t\t\t\teffects: readOnlyCompartment.reconfigure(EditorState.readOnly.of(ro)),\n\t\t\t});\n\t\t\ttouchSelectionController?.onStateChanged();\n\t\t} catch (error) {\n\t\t\twarnRecoverable(\n\t\t\t\t\"Failed to apply read-only compartment update. Recreating editor state.\",\n\t\t\t\terror,\n\t\t\t\t\"readonly-reconfigure\",\n\t\t\t);\n\t\t\t// Fallback: full re-apply\n\t\t\tapplyFileToEditor(file);\n\t\t}\n\t});\n\n\tmanager.on([\"remove-file\"], (file) => {\n\t\tdetachLspForFile(file);\n\t\ttoggleProblemButton();\n\t});\n\n\tmanager.on([\"rename-file\"], (file) => {\n\t\tif (file?.type !== \"editor\") return;\n\t\tif (manager.activeFile?.id === file.id) {\n\t\t\t// Re-apply file to editor to update language/syntax highlighting\n\t\t\tapplyFileToEditor(file);\n\t\t\tvoid configureLspForFile(file);\n\t\t}\n\t});\n\n\t// Attach doc-sync listener to the current editor instance\n\ttry {\n\t\teditor.dispatch({\n\t\t\teffects: StateEffect.appendConfig.of(getDocSyncListener()),\n\t\t});\n\t} catch (error) {\n\t\twarnRecoverable(\n\t\t\t\"Failed to attach document sync listener to editor.\",\n\t\t\terror,\n\t\t\t\"doc-sync-listener\",\n\t\t);\n\t}\n\n\treturn manager;\n\n\t/**\n\t * Adds a file to the manager's file list and updates the UI.\n\t * @param {File} file - The file to be added.\n\t */\n\tfunction addFile(file) {\n\t\tif (manager.files.includes(file)) return;\n\t\tconst insertAt = file.pinned\n\t\t\t? getPinnedInsertIndex()\n\t\t\t: manager.files.length;\n\t\tmanager.files.splice(insertAt, 0, file);\n\t\tsyncOpenFileList();\n\t\t$header.text = file.name;\n\t\ttoggleProblemButton();\n\t}\n\n\tfunction getPinnedInsertIndex(skipFile = null) {\n\t\treturn manager.files.reduce((count, file) => {\n\t\t\tif (file === skipFile) return count;\n\t\t\treturn count + (file.pinned ? 1 : 0);\n\t\t}, 0);\n\t}\n\n\tfunction syncOpenFileList() {\n\t\tconst $list = manager.openFileList;\n\t\tmanager.files.forEach((file) => {\n\t\t\t$list.append(file.tab);\n\t\t});\n\t}\n\n\tfunction moveFileByPinnedState(file) {\n\t\tif (!manager.files.includes(file)) return;\n\t\tif (manager.activeFile?.id === file.id) {\n\t\t\tfile.tab.scrollIntoView();\n\t\t}\n\t}\n\n\tfunction normalizePinnedTabOrder(nextFiles = manager.files) {\n\t\tconst pinnedFiles = [];\n\t\tconst regularFiles = [];\n\n\t\tnextFiles.forEach((file) => {\n\t\t\tif (file.pinned) {\n\t\t\t\tpinnedFiles.push(file);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tregularFiles.push(file);\n\t\t});\n\n\t\tmanager.files = [...pinnedFiles, ...regularFiles];\n\t\tsyncOpenFileList();\n\n\t\treturn manager.files;\n\t}\n\n\t/**\n\t * Sets up the editor with various configurations and event listeners.\n\t * @returns {Promise<void>} A promise that resolves once the editor is set up.\n\t */\n\tasync function setupEditor() {\n\t\tconst settings = appSettings.value;\n\t\tconst { leftMargin, textWrap, colorPreview, fontSize, lineHeight } =\n\t\t\tappSettings.value;\n\t\tconst scrollMarginTop = 0;\n\t\tconst scrollMarginLeft = 0;\n\t\tconst scrollMarginRight = textWrap ? 0 : leftMargin;\n\t\tconst scrollMarginBottom = 0;\n\n\t\tlet checkTimeout = null;\n\t\tlet autosaveTimeout;\n\t\tlet scrollTimeout;\n\t\tlet scrollSyncRaf = 0;\n\t\tconst scroller = editor.scrollDOM;\n\n\t\tfunction syncScrollUi() {\n\t\t\tscrollSyncRaf = 0;\n\t\t\tonscrolltop();\n\t\t\tonscrollleft();\n\t\t}\n\n\t\tfunction handleEditorScroll() {\n\t\t\tif (!scroller) return;\n\t\t\tif (!isScrolling) {\n\t\t\t\tisScrolling = true;\n\t\t\t\tif (hasHoverTooltips(editor.state)) {\n\t\t\t\t\teditor.dispatch({ effects: closeHoverTooltips });\n\t\t\t\t}\n\t\t\t\ttouchSelectionController?.onScrollStart();\n\t\t\t}\n\t\t\tif (!scrollSyncRaf) {\n\t\t\t\tscrollSyncRaf = requestAnimationFrame(syncScrollUi);\n\t\t\t}\n\t\t\tclearTimeout(scrollTimeout);\n\t\t\tscrollTimeout = setTimeout(() => {\n\t\t\t\tisScrolling = false;\n\t\t\t\ttouchSelectionController?.onScrollEnd();\n\t\t\t}, 100);\n\t\t}\n\n\t\tscroller?.addEventListener(\"scroll\", handleEditorScroll, { passive: true });\n\t\tsyncScrollUi();\n\n\t\tkeyboardHandler.on(\"keyboardShowStart\", () => {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tscrollCursorIntoView({ behavior: \"instant\" });\n\t\t\t});\n\t\t});\n\t\tkeyboardHandler.on(\"keyboardShow\", scrollCursorIntoView);\n\n\t\t// Attach native DOM event listeners directly to the editor's contentDOM\n\t\tconst contentDOM = editor.contentDOM;\n\t\tconst isFocused =\n\t\t\tcontentDOM === document.activeElement ||\n\t\t\tcontentDOM.contains(document.activeElement);\n\t\tsetNativeContextMenuDisabled(isFocused);\n\n\t\tcontentDOM.addEventListener(\"focus\", (_event) => {\n\t\t\tsetNativeContextMenuDisabled(true);\n\t\t\tconst { activeFile } = manager;\n\t\t\tif (activeFile) {\n\t\t\t\tactiveFile.focused = true;\n\t\t\t}\n\t\t\ttouchSelectionController?.onStateChanged();\n\t\t});\n\n\t\tcontentDOM.addEventListener(\"blur\", async (_event) => {\n\t\t\tsetNativeContextMenuDisabled(false);\n\t\t\ttouchSelectionController?.setMenu(false);\n\t\t\tconst { hardKeyboardHidden, keyboardHeight } =\n\t\t\t\tawait getSystemConfiguration();\n\t\t\tconst blur = () => {\n\t\t\t\tconst { activeFile } = manager;\n\t\t\t\tif (activeFile) {\n\t\t\t\t\tactiveFile.focused = false;\n\t\t\t\t\tactiveFile.focusedBefore = false;\n\t\t\t\t}\n\t\t\t};\n\t\t\tif (\n\t\t\t\thardKeyboardHidden === HARDKEYBOARDHIDDEN_NO &&\n\t\t\t\tkeyboardHeight < 100\n\t\t\t) {\n\t\t\t\t// external keyboard - blur immediately\n\t\t\t\tblur();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// soft keyboard - wait for keyboard to hide\n\t\t\tconst onKeyboardHide = () => {\n\t\t\t\tkeyboardHandler.off(\"keyboardHide\", onKeyboardHide);\n\t\t\t\tblur();\n\t\t\t};\n\t\t\tkeyboardHandler.on(\"keyboardHide\", onKeyboardHide);\n\t\t});\n\n\t\tcontentDOM.addEventListener(\"keydown\", (event) => {\n\t\t\tif (event.key === \"Escape\") {\n\t\t\t\tkeydownState.esc = { value: true, target: contentDOM };\n\t\t\t}\n\t\t});\n\n\t\tupdateMargin(true);\n\t\tupdateSideButtonContainer();\n\t\ttoggleProblemButton();\n\t}\n\n\t/**\n\t * Scrolls the cursor into view if it is not currently visible.\n\t */\n\tfunction scrollCursorIntoView(options = {}) {\n\t\tconst view = editor;\n\t\tconst scroller = view?.scrollDOM;\n\t\tif (!view || !scroller) return;\n\n\t\tconst { behavior = \"smooth\" } = options;\n\t\tconst { head } = view.state.selection.main;\n\t\tconst caret = safeCoordsAtPos(view, head);\n\t\tif (!caret) return;\n\n\t\tconst scrollerRect = scroller.getBoundingClientRect();\n\t\tconst relativeTop = caret.top - scrollerRect.top + scroller.scrollTop;\n\t\tconst relativeBottom = caret.bottom - scrollerRect.top + scroller.scrollTop;\n\t\tconst topMargin = 16;\n\t\tconst bottomMargin = 24;\n\n\t\tconst scrollTop = scroller.scrollTop;\n\t\tconst visibleTop = scrollTop + topMargin;\n\t\tconst visibleBottom = scrollTop + scroller.clientHeight - bottomMargin;\n\n\t\tif (relativeTop < visibleTop) {\n\t\t\tconst nextTop = Math.max(relativeTop - topMargin, 0);\n\t\t\tscroller.scrollTo({ top: nextTop, behavior });\n\t\t} else if (relativeBottom > visibleBottom) {\n\t\t\tconst delta = relativeBottom - visibleBottom;\n\t\t\tscroller.scrollTo({ top: scrollTop + delta, behavior });\n\t\t}\n\t}\n\n\t/**\n\t * Checks if the cursor is visible within the CodeMirror viewport.\n\t * @returns {boolean} - True if the cursor is visible, false otherwise.\n\t */\n\tfunction isCursorVisible() {\n\t\tconst view = editor;\n\t\tconst scroller = view?.scrollDOM;\n\t\tif (!view || !scroller) return true;\n\n\t\tconst { head } = view.state.selection.main;\n\t\tconst caret = safeCoordsAtPos(view, head);\n\t\tif (!caret) return true;\n\n\t\tconst scrollerRect = scroller.getBoundingClientRect();\n\t\treturn caret.top >= scrollerRect.top && caret.bottom <= scrollerRect.bottom;\n\t}\n\n\tfunction safeCoordsAtPos(view, pos) {\n\t\ttry {\n\t\t\treturn view.coordsAtPos(pos);\n\t\t} catch (_) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the vertical scroll value of the editor. This is called when the editor is scrolled horizontally using the scrollbar.\n\t * @param {Number} value\n\t */\n\tfunction onscrollV(value) {\n\t\tconst scroller = editor?.scrollDOM;\n\t\tif (!scroller) return;\n\t\tconst normalized = clamp01(value);\n\t\tconst maxScroll = Math.max(\n\t\t\tscroller.scrollHeight - scroller.clientHeight,\n\t\t\t0,\n\t\t);\n\t\tpreventScrollbarV = true;\n\t\tscroller.scrollTop = normalized * maxScroll;\n\t\tlastScrollTop = scroller.scrollTop;\n\t}\n\n\t/**\n\t * Handles the onscroll event for the vend element.\n\t */\n\tfunction onscrollVend() {\n\t\tpreventScrollbarV = false;\n\t\tsetVScrollValue();\n\t}\n\n\t/**\n\t * Sets the horizontal scroll value of the editor. This is called when the editor is scrolled vertically using the scrollbar.\n\t * @param {number} value - The scroll value.\n\t */\n\tfunction onscrollH(value) {\n\t\tif (appSettings.value.textWrap) return;\n\t\tconst scroller = editor?.scrollDOM;\n\t\tif (!scroller) return;\n\t\tconst normalized = clamp01(value);\n\t\tconst maxScroll = Math.max(scroller.scrollWidth - scroller.clientWidth, 0);\n\t\tpreventScrollbarH = true;\n\t\tscroller.scrollLeft = normalized * maxScroll;\n\t\tlastScrollLeft = scroller.scrollLeft;\n\t}\n\n\t/**\n\t * Handles the event when the horizontal scrollbar reaches the end.\n\t */\n\tfunction onscrollHEnd() {\n\t\tpreventScrollbarH = false;\n\t\tsetHScrollValue();\n\t}\n\n\t/**\n\t * Sets scrollbars value based on the editor's scroll position.\n\t */\n\tfunction setHScrollValue() {\n\t\tif (appSettings.value.textWrap || preventScrollbarH) return;\n\t\tconst scroller = editor?.scrollDOM;\n\t\tif (!scroller) return;\n\t\tconst maxScroll = Math.max(scroller.scrollWidth - scroller.clientWidth, 0);\n\t\tif (maxScroll <= 0) {\n\t\t\tlastScrollLeft = 0;\n\t\t\t$hScrollbar.value = 0;\n\t\t\treturn;\n\t\t}\n\t\tconst scrollLeft = scroller.scrollLeft;\n\t\tif (scrollLeft === lastScrollLeft) return;\n\t\tlastScrollLeft = scrollLeft;\n\t\tconst factor = scrollLeft / maxScroll;\n\t\t$hScrollbar.value = clamp01(factor);\n\t}\n\n\t/**\n\t * Handles the scroll left event.\n\t * Updates the horizontal scroll value and renders the horizontal scrollbar.\n\t */\n\tfunction onscrollleft() {\n\t\tif (appSettings.value.textWrap) {\n\t\t\t$hScrollbar.hide();\n\t\t\treturn;\n\t\t}\n\t\tconst scroller = editor?.scrollDOM;\n\t\tif (!scroller) return;\n\t\tconst maxScroll = Math.max(scroller.scrollWidth - scroller.clientWidth, 0);\n\t\tif (maxScroll <= 0) {\n\t\t\t$hScrollbar.hide();\n\t\t\tlastScrollLeft = 0;\n\t\t\t$hScrollbar.value = 0;\n\t\t\treturn;\n\t\t}\n\t\tsetHScrollValue();\n\t\t$hScrollbar.render();\n\t}\n\n\t/**\n\t * Sets scrollbars value based on the editor's scroll position.\n\t */\n\tfunction setVScrollValue() {\n\t\tif (preventScrollbarV) return;\n\t\tconst scroller = editor?.scrollDOM;\n\t\tif (!scroller) return;\n\t\tconst maxScroll = Math.max(\n\t\t\tscroller.scrollHeight - scroller.clientHeight,\n\t\t\t0,\n\t\t);\n\t\tif (maxScroll <= 0) {\n\t\t\tlastScrollTop = 0;\n\t\t\t$vScrollbar.value = 0;\n\t\t\treturn;\n\t\t}\n\t\tconst scrollTop = scroller.scrollTop;\n\t\tif (scrollTop === lastScrollTop) return;\n\t\tlastScrollTop = scrollTop;\n\t\tconst factor = scrollTop / maxScroll;\n\t\t$vScrollbar.value = clamp01(factor);\n\t}\n\n\t/**\n\t * Handles the scroll top event.\n\t * Updates the vertical scroll value and renders the vertical scrollbar.\n\t */\n\tfunction onscrolltop() {\n\t\tconst scroller = editor?.scrollDOM;\n\t\tif (!scroller) return;\n\t\tconst maxScroll = Math.max(\n\t\t\tscroller.scrollHeight - scroller.clientHeight,\n\t\t\t0,\n\t\t);\n\t\tif (maxScroll <= 0) {\n\t\t\t$vScrollbar.hide();\n\t\t\tlastScrollTop = 0;\n\t\t\t$vScrollbar.value = 0;\n\t\t\treturn;\n\t\t}\n\t\tsetVScrollValue();\n\t\t$vScrollbar.render();\n\t}\n\n\tfunction clamp01(value) {\n\t\tif (value <= 0) return 0;\n\t\tif (value >= 1) return 1;\n\t\treturn value;\n\t}\n\n\t/**\n\t * Updates the floating button visibility based on the provided show parameter.\n\t * @param {boolean} [show=false] - Indicates whether to show the floating button.\n\t */\n\tfunction updateFloatingButton(show = false) {\n\t\tconst { $headerToggler } = acode;\n\t\tconst { $toggler } = quickTools;\n\n\t\tif (show) {\n\t\t\tif (scrollBarVisibilityCount) --scrollBarVisibilityCount;\n\n\t\t\tif (!scrollBarVisibilityCount) {\n\t\t\t\tclearTimeout(timeoutHeaderToggler);\n\t\t\t\tclearTimeout(timeoutQuicktoolsToggler);\n\n\t\t\t\tif (appSettings.value.floatingButton) {\n\t\t\t\t\t$toggler.classList.remove(\"hide\");\n\t\t\t\t\troot.appendOuter($toggler);\n\t\t\t\t}\n\n\t\t\t\t$headerToggler.classList.remove(\"hide\");\n\t\t\t\troot.appendOuter($headerToggler);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (!scrollBarVisibilityCount) {\n\t\t\tif ($toggler.isConnected) {\n\t\t\t\t$toggler.classList.add(\"hide\");\n\t\t\t\ttimeoutQuicktoolsToggler = setTimeout(() => $toggler.remove(), 300);\n\t\t\t}\n\t\t\tif ($headerToggler.isConnected) {\n\t\t\t\t$headerToggler.classList.add(\"hide\");\n\t\t\t\ttimeoutHeaderToggler = setTimeout(() => $headerToggler.remove(), 300);\n\t\t\t}\n\t\t}\n\n\t\t++scrollBarVisibilityCount;\n\t}\n\n\t/**\n\t * Toggles the visibility of the problem button based on the presence of annotations in the files.\n\t */\n\tfunction fileHasProblems(file) {\n\t\tconst state = getDiagnosticStateForFile(file);\n\t\tif (!state) return false;\n\n\t\tconst session = file.session;\n\t\tif (session && typeof session.getAnnotations === \"function\") {\n\t\t\ttry {\n\t\t\t\tconst annotations = session.getAnnotations() || [];\n\t\t\t\tif (annotations.length) return true;\n\t\t\t} catch (error) {\n\t\t\t\twarnRecoverable(\n\t\t\t\t\t\"Failed to read editor annotations while checking problems.\",\n\t\t\t\t\terror,\n\t\t\t\t\t\"read-annotations\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (typeof state.field !== \"function\") return false;\n\t\ttry {\n\t\t\tconst diagnostics = getLspDiagnostics(state);\n\t\t\treturn diagnostics.length > 0;\n\t\t} catch (error) {\n\t\t\twarnRecoverable(\n\t\t\t\t\"Failed to read LSP diagnostics while checking problems.\",\n\t\t\t\terror,\n\t\t\t\t\"read-lsp-diagnostics\",\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tfunction toggleProblemButton() {\n\t\tconst { showSideButtons } = appSettings.value;\n\t\tif (!showSideButtons) {\n\t\t\tproblemButton.hide();\n\t\t\treturn;\n\t\t}\n\n\t\tconst hasProblems = manager.files.some((file) => fileHasProblems(file));\n\t\tif (hasProblems) {\n\t\t\tproblemButton.show();\n\t\t} else {\n\t\t\tproblemButton.hide();\n\t\t}\n\t}\n\n\tfunction getDiagnosticStateForFile(file) {\n\t\tif (!file || file.type !== \"editor\") return null;\n\t\tif (manager.activeFile?.id === file.id && editor?.state) {\n\t\t\treturn editor.state;\n\t\t}\n\t\treturn file.session || null;\n\t}\n\n\t/**\n\t * Updates the side button container based on the value of `showSideButtons` in `appSettings`.\n\t * If `showSideButtons` is `false`, the side button container is removed from the DOM.\n\t * If `showSideButtons` is `true`, the side button container is appended to the body element.\n\t */\n\tfunction updateSideButtonContainer() {\n\t\tconst { showSideButtons } = appSettings.value;\n\t\tif (!showSideButtons) {\n\t\t\tsideButtonContainer.remove();\n\t\t\treturn;\n\t\t}\n\n\t\t$body.append(sideButtonContainer);\n\t}\n\n\t/**\n\t * Updates the margin of the editor and optionally updates the gutter settings.\n\t * @param {boolean} [updateGutter=false] - Whether to update the gutter settings.\n\t */\n\tfunction updateMargin(updateGutter = false) {\n\t\tconst { showSideButtons, linenumbers, showAnnotations } = appSettings.value;\n\t\tconst top = 0;\n\t\tconst bottom = 0;\n\t\tconst right = showSideButtons ? 15 : 0;\n\t\tconst left = linenumbers ? (showAnnotations ? 0 : -16) : 0;\n\t\t// TODO\n\t\t//editor.renderer.setMargin(top, bottom, left, right);\n\n\t\tif (!updateGutter) return;\n\n\t\t// editor.setOptions({\n\t\t// \tshowGutter: linenumbers || showAnnotations,\n\t\t// \tshowLineNumbers: linenumbers,\n\t\t// });\n\t}\n\n\t/**\n\t * Switches the active file in the editor.\n\t * @param {string} id - The ID of the file to switch to.\n\t */\n\tfunction switchFile(id) {\n\t\tconst { id: activeFileId } = manager.activeFile || {};\n\t\tif (activeFileId === id) return;\n\n\t\tconst file = manager.getFile(id);\n\t\tif (!file) return;\n\n\t\tmanager.activeFile?.tab.classList.remove(\"active\");\n\n\t\t// Hide previous content if it was non-editor\n\t\tif (manager.activeFile?.type !== \"editor\" && manager.activeFile?.content) {\n\t\t\tmanager.activeFile.content.style.display = \"none\";\n\t\t}\n\n\t\t// Persist the previous editor's state before switching away\n\t\tconst prev = manager.activeFile;\n\t\tif (prev?.type === \"editor\") {\n\t\t\tprev.session = editor.state;\n\t\t\tprev.lastScrollTop = editor.scrollDOM?.scrollTop || 0;\n\t\t\tprev.lastScrollLeft = editor.scrollDOM?.scrollLeft || 0;\n\t\t}\n\n\t\tmanager.activeFile = file;\n\n\t\tif (file.type === \"editor\") {\n\t\t\ttouchSelectionController?.setEnabled(true);\n\t\t\t// Apply active file content and language to CodeMirror\n\t\t\tapplyFileToEditor(file);\n\t\t\t$container.style.display = \"block\";\n\n\t\t\t$hScrollbar.hideImmediately();\n\t\t\t$vScrollbar.hideImmediately();\n\n\t\t\tsetVScrollValue();\n\t\t\tif (!appSettings.value.textWrap) {\n\t\t\t\tsetHScrollValue();\n\t\t\t}\n\t\t} else {\n\t\t\ttouchSelectionController?.setEnabled(false);\n\t\t\t$container.style.display = \"none\";\n\t\t\tif (file.content) {\n\t\t\t\tfile.content.style.display = \"block\";\n\t\t\t\tif (!file.content.parentElement) {\n\t\t\t\t\t$container.parentElement.appendChild(file.content);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfile.tab.classList.add(\"active\");\n\t\tfile.tab.scrollIntoView();\n\n\t\t$header.text = file.filename;\n\t\t$header.subText = file.headerSubtitle || \"\";\n\t\tmanager.onupdate(\"switch-file\");\n\t\tevents.emit(\"switch-file\", file);\n\n\t\ttoggleProblemButton();\n\t}\n\n\t/**\n\t * Initializes the file tab container.\n\t */\n\tfunction initFileTabContainer() {\n\t\tlet $list;\n\n\t\tif ($openFileList) {\n\t\t\tif ($openFileList.classList.contains(\"collapsible\")) {\n\t\t\t\t$list = Array.from($openFileList.$ul.children);\n\t\t\t} else {\n\t\t\t\t$list = Array.from($openFileList.children);\n\t\t\t}\n\t\t\t$openFileList.remove();\n\t\t}\n\n\t\t// show open file list in header\n\t\tconst { openFileListPos } = appSettings.value;\n\t\tif (\n\t\t\topenFileListPos === appSettings.OPEN_FILE_LIST_POS_HEADER ||\n\t\t\topenFileListPos === appSettings.OPEN_FILE_LIST_POS_BOTTOM\n\t\t) {\n\t\t\tif (!$openFileList?.classList.contains(\"open-file-list\")) {\n\t\t\t\t$openFileList = <ul className=\"open-file-list\"></ul>;\n\t\t\t}\n\t\t\tif ($list) $openFileList.append(...$list);\n\n\t\t\tif (openFileListPos === appSettings.OPEN_FILE_LIST_POS_BOTTOM) {\n\t\t\t\t$container.parentElement.insertAdjacentElement(\n\t\t\t\t\t\"afterend\",\n\t\t\t\t\t$openFileList,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t$header.insertAdjacentElement(\"afterend\", $openFileList);\n\t\t\t}\n\n\t\t\troot.classList.add(\"top-bar\");\n\n\t\t\tconst oldAppend = $openFileList.append;\n\t\t\t$openFileList.append = (...args) => {\n\t\t\t\toldAppend.apply($openFileList, args);\n\t\t\t};\n\t\t} else {\n\t\t\t$openFileList = list(strings[\"active files\"]);\n\t\t\t$openFileList.classList.add(\"file-list\");\n\t\t\tif ($list) $openFileList.$ul.append(...$list);\n\t\t\t$openFileList.expand();\n\n\t\t\tconst oldAppend = $openFileList.$ul.append;\n\t\t\t$openFileList.append = (...args) => {\n\t\t\t\toldAppend.apply($openFileList.$ul, args);\n\t\t\t};\n\n\t\t\tconst files = sidebarApps.get(\"files\");\n\t\t\tfiles.insertBefore($openFileList, files.firstElementChild);\n\t\t\troot.classList.remove(\"top-bar\");\n\t\t}\n\n\t\troot.setAttribute(\"open-file-list-pos\", openFileListPos);\n\t\tmanager.emit(\"int-open-file-list\", openFileListPos);\n\t}\n\n\t/**\n\t * Checks if there are any unsaved files in the manager.\n\t * @returns {number} The number of unsaved files.\n\t */\n\tfunction hasUnsavedFiles() {\n\t\tconst unsavedFiles = manager.files.filter((file) => file.isUnsaved);\n\t\treturn unsavedFiles.length;\n\t}\n\n\t/**\n\t * Gets a file from the file manager\n\t * @param {string|number} checkFor\n\t * @param {\"id\"|\"name\"|\"uri\"} [type]\n\t * @returns {File}\n\t */\n\tfunction getFile(checkFor, type = \"id\") {\n\t\treturn manager.files.find((file) => {\n\t\t\tswitch (type) {\n\t\t\t\tcase \"id\":\n\t\t\t\t\tif (file.id === checkFor) return true;\n\t\t\t\t\treturn false;\n\t\t\t\tcase \"name\":\n\t\t\t\t\tif (file.filename === checkFor) return true;\n\t\t\t\t\treturn false;\n\t\t\t\tcase \"uri\":\n\t\t\t\t\tif (file.uri === checkFor) return true;\n\t\t\t\t\treturn false;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Gets the height of the editor\n\t * @param {object} editor\n\t * @returns\n\t */\n\tfunction getEditorHeight(editor) {\n\t\ttry {\n\t\t\tconst view = editor;\n\t\t\tif (!view || !view.scrollDOM) return 0;\n\n\t\t\tconst total = view.scrollDOM.scrollHeight || 0;\n\t\t\tconst viewport = view.scrollDOM.clientHeight || 0;\n\t\t\treturn Math.max(total - viewport, 0);\n\t\t} catch (_) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Gets the height of the editor\n\t * @param {object} editor\n\t * @returns\n\t */\n\tfunction getEditorWidth(editor) {\n\t\ttry {\n\t\t\tconst view = editor;\n\t\t\tif (!view || !view.scrollDOM) return 0;\n\n\t\t\tconst total = view.scrollDOM.scrollWidth || 0;\n\t\t\tconst viewport = view.scrollDOM.clientWidth || 0;\n\t\t\tlet width = Math.max(total - viewport, 0);\n\t\t\tif (!appSettings.value.textWrap) {\n\t\t\t\tconst { leftMargin = 0 } = appSettings.value;\n\t\t\t\twidth += leftMargin || 0;\n\t\t\t}\n\t\t\treturn width;\n\t\t} catch (_) {\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n\nexport default EditorManager;\n"
  },
  {
    "path": "src/lib/fileList.js",
    "content": "import fsOperation from \"fileSystem\";\nimport toast from \"components/toast\";\nimport picomatch from \"picomatch/posix\";\nimport Url from \"utils/Url\";\nimport { addedFolder } from \"./openFolder\";\nimport settings from \"./settings\";\n\n/**\n * @typedef {import('fileSystem').File} File\n */\n\nconst filesTree = {};\nconst events = {\n\t\"add-file\": [],\n\t\"remove-file\": [],\n\t\"add-folder\": [],\n\t\"remove-folder\": [],\n\trefresh: [],\n};\n\nexport function initFileList() {\n\tif (editorManager?.activeFile.loading) {\n\t\teditorManager.activeFile.on(\"loadend\", initFileList);\n\t\treturn;\n\t}\n\t// editorManager.on('add-folder', onAddFolder);\n\teditorManager.on(\"remove-folder\", onRemoveFolder);\n\tsettings.on(\"update:excludeFolders:after\", refresh);\n}\n\n/**\n * Add a file to the list\n * @param {string} parent file directory\n * @param {string} child file url\n */\nexport async function append(parent, child) {\n\tconst tree = getTree(Object.values(filesTree), parent);\n\tif (!tree || !tree.children) return;\n\n\tconst childTree = await Tree.create(child);\n\ttree.children.push(childTree);\n\tgetAllFiles(childTree);\n\temit(\"add-file\", childTree);\n}\n\n/**\n * Remove a file from the list\n * @param {string} item url\n */\nexport function remove(item) {\n\tif (filesTree[item]) {\n\t\tdelete filesTree[item];\n\t\temit(\"remove-file\", item);\n\t\treturn;\n\t}\n\n\tconst tree = getTree(Object.values(filesTree), item);\n\tif (!tree) return;\n\tconst { parent } = tree;\n\tconst index = parent.children.indexOf(tree);\n\tparent.children.splice(index, 1);\n\temit(\"remove-file\", tree);\n}\n\n/**\n * Refresh file list\n */\nexport async function refresh() {\n\tObject.keys(filesTree).forEach((key) => {\n\t\tdelete filesTree[key];\n\t});\n\n\tawait Promise.all(\n\t\taddedFolder.map(async ({ url, title }) => {\n\t\t\tconst tree = await Tree.createRoot(url, title);\n\t\t\tfilesTree[url] = tree;\n\t\t\tgetAllFiles(tree);\n\t\t}),\n\t);\n\n\temit(\"refresh\", filesTree);\n}\n\n/**\n * Renames a tree\n * @param {string} oldUrl\n * @param {string} newUrl\n * @returns\n */\nexport function rename(oldUrl, newUrl) {\n\tconst tree = getTree(Object.values(filesTree), oldUrl);\n\tif (!tree) return;\n\n\ttree.update(newUrl);\n}\n\n/**\n * Get all files in a folder\n * @param {string|()=>object} dir\n * @returns {Tree[]}\n */\nexport default function files(dir) {\n\tconst listedDirs = [];\n\tlet transform = (item) => item;\n\tif (typeof dir === \"string\") {\n\t\treturn Object.values(filesTree).find((item) => getFile(dir, item));\n\t} else if (typeof dir === \"function\") {\n\t\ttransform = dir;\n\t}\n\n\tconst allFiles = [];\n\tObject.values(filesTree).forEach((item) => {\n\t\tallFiles.push(...flattenTree(item, transform, listedDirs));\n\t});\n\treturn allFiles;\n}\n\n/**\n * @typedef {'add-file'|'remove-file'|'add-folder'|'remove-folder'|'refresh'} FileListEvent\n */\n\n/**\n * Adds event listener for file list\n * @param {FileListEvent} event - Event name\n * @param {(tree:Tree)=>void} callback - Callback function\n */\nfiles.on = function (event, callback) {\n\tif (!events[event]) events[event] = [];\n\tevents[event].push(callback);\n};\n\n/**\n * Removes event listener for file list\n * @param {FileListEvent} event - Event name\n * @param {(tree:Tree)=>void} callback - Callback function\n */\nfiles.off = function (event, callback) {\n\tif (!events[event]) return;\n\tevents[event] = events[event].filter((cb) => cb !== callback);\n};\n\n/**\n * Get directory tree\n * @param {Tree[]} treeList list of tree\n * @param {string} dir path to find\n * @returns {Tree}\n */\nfunction getTree(treeList, dir) {\n\tif (!treeList) return;\n\tlet tree = treeList.find(({ url }) => url === dir);\n\tif (tree) return tree;\n\tfor (const item of treeList) {\n\t\ttree = getTree(item.children, dir);\n\t\tif (tree) return tree;\n\t}\n\n\treturn null;\n}\n\n/**\n * Get all files in a folder\n * e.g /dir1/dir2/dir3\n * This function will first test if dir1 exists in the tree,\n * if not, it will return null, otherwise it will traverse the tree\n * and return the files in dir3\n * @param {string} path - Folder path\n * @param {Tree} tree - Files tree\n */\nfunction getFile(path, tree) {\n\tconst { children } = tree;\n\tlet { url } = tree;\n\tif (url === path) return tree;\n\tif (!children) return null;\n\tconst len = children.length;\n\tfor (let i = 0; i < len; i++) {\n\t\tconst item = children[i];\n\t\tconst result = getFile(path, item);\n\t\tif (result) return result;\n\t}\n\treturn null;\n}\n\n/**\n * Get all files\n * @param {Tree} tree\n * @param {(item:Tree)=>object} transform\n */\nfunction flattenTree(tree, transform, listedDirs) {\n\tconst list = [];\n\tconst { children } = tree;\n\tif (!children) {\n\t\treturn [transform(tree)];\n\t}\n\n\tif (listedDirs.includes(tree.url)) return list;\n\n\tlistedDirs.push(tree.url);\n\n\tchildren.forEach((item) => {\n\t\tif (item.children) list.push(...flattenTree(item, transform, listedDirs));\n\t\telse list.push(transform(item));\n\t});\n\treturn list;\n}\n\n/**\n * Called when a folder is added\n * @param {{url: string, name: string}} folder - Folder path\n */\nexport async function addRoot({ url, name }) {\n\ttry {\n\t\tconst TERMUX_STORAGE =\n\t\t\t\"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome::/data/data/com.termux/files/home/storage\";\n\t\tconst TERMUX_SHARED =\n\t\t\t\"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome::/data/data/com.termux/files/home/storage/shared\";\n\t\tif (url === TERMUX_STORAGE) return;\n\t\tif (url === TERMUX_SHARED) return;\n\n\t\tconst tree = await Tree.createRoot(url, name);\n\t\tfilesTree[url] = tree;\n\t\tgetAllFiles(tree);\n\t\temit(\"add-folder\", tree);\n\t} catch (error) {\n\t\t// ignore\n\t\twindow.log(\"error\", error);\n\t}\n}\n\n/**\n * Called when a folder is removed\n * @param {{url: string, name: string}} folder - Folder path\n */\nfunction onRemoveFolder({ url }) {\n\tconst tree = filesTree[url];\n\tif (!tree) return;\n\tdelete filesTree[url];\n\temit(\"remove-folder\", tree);\n}\n\n/**\n * Get all file recursively\n * @param {Tree} parent - An array to store files\n * @param {Tree} [root] - Root path\n */\nasync function getAllFiles(parent, root) {\n\troot = root || parent.root;\n\tif (!parent.children || !root.isConnected) return;\n\n\ttry {\n\t\tconst entries = await fsOperation(parent.url).lsDir();\n\t\tconst promises = [];\n\n\t\tfor (const item of entries) {\n\t\t\tpromises.push(createChildTree(parent, item, root));\n\t\t}\n\n\t\tawait Promise.all(promises);\n\t} catch (error) {\n\t\t// retry after 3s\n\t\tparent.retriedCount += 1;\n\t\tif (parent.retriedCount > settings.value.maxRetryCount) return;\n\t\tif (settings.value.showRetryToast) {\n\t\t\ttoast(`retrying: ${parent.path}`);\n\t\t}\n\n\t\tsetTimeout(() => {\n\t\t\t// why not outside? because parent may be removed\n\t\t\tif (!root.isConnected) return;\n\t\t\tparent.children.length = 0;\n\t\t\tgetAllFiles(parent);\n\t\t}, 3000);\n\t}\n}\n\n/**\n * Emit an event\n * @param {string} event\n * @param  {...any} args\n */\nfunction emit(event, ...args) {\n\tconst list = events[event];\n\tif (!list) return;\n\tlist.forEach((fn) => fn(...args));\n}\n\n/**\n * Create a child tree\n * @param {Tree} parent\n * @param {File} item\n * @param {Tree} root\n */\nasync function createChildTree(parent, item, root) {\n\tif (!root.isConnected) return;\n\tconst { name, url, isDirectory } = item;\n\tconst exists = parent.children.findIndex(({ value }) => value === url);\n\tif (exists > -1) {\n\t\treturn;\n\t}\n\n\tconst file = await Tree.create(url, name, isDirectory);\n\tif (!root.isConnected) return;\n\n\tconst existingTree = getTree(Object.values(filesTree), file.url);\n\n\tif (existingTree) {\n\t\tfile.children = existingTree.children;\n\t\tparent.children.push(file);\n\t\treturn;\n\t}\n\n\tparent.children.push(file);\n\tif (isDirectory) {\n\t\tconst ignore = picomatch.isMatch(\n\t\t\tUrl.join(file.path, \"\"),\n\t\t\tsettings.value.excludeFolders,\n\t\t\t{ matchBase: true },\n\t\t);\n\t\tif (ignore) return;\n\n\t\tgetAllFiles(file, root);\n\t\treturn;\n\t}\n\n\temit(\"push-file\", file);\n}\n\nexport class Tree {\n\t/**@type {string}*/\n\t#name;\n\t/**@type {string}*/\n\t#url;\n\t/**@type {string}*/\n\t#path;\n\t/**@type {Array<Tree>}*/\n\t#children;\n\t/**@type {Tree}*/\n\t#parent;\n\n\tretriedCount = 0;\n\n\t/**\n\t * Create a tree using constructor\n\t * @param {string} name\n\t * @param {string} root\n\t * @param {string} url\n\t * @param {boolean} isDirectory\n\t */\n\tconstructor(name, url, isDirectory) {\n\t\tthis.#name = name;\n\t\tthis.#url = url;\n\t\tthis.#children = isDirectory ? this.#childrenArray() : null;\n\t\tthis.#parent = null;\n\t}\n\n\t#childrenArray() {\n\t\tconst ar = [];\n\t\tconst oldPush = ar.push;\n\t\tar.push = (...args) => {\n\t\t\targs.forEach((item) => {\n\t\t\t\tif (!(item instanceof Tree)) throw new Error(\"Invalid tree\");\n\t\t\t\titem.parent = this;\n\t\t\t\toldPush.call(ar, item);\n\t\t\t});\n\t\t};\n\t\treturn ar;\n\t}\n\n\t/**\n\t * Create a tree\n\t * @param {string} url file url\n\t * @param {string} [name] file name\n\t * @param {boolean} [isDirectory] if the file is a directory\n\t */\n\tstatic async create(url, name, isDirectory) {\n\t\tif (!name && !isDirectory) {\n\t\t\tconst stat = await fsOperation(url).stat();\n\t\t\tname = stat.name;\n\t\t\tisDirectory = stat.isDirectory;\n\t\t}\n\n\t\treturn new Tree(name, url, isDirectory);\n\t}\n\n\t/**\n\t * Create a root tree\n\t * @param {string} url\n\t * @param {string} name\n\t * @returns\n\t */\n\tstatic async createRoot(url, name) {\n\t\tconst tree = await Tree.create(url, name, true);\n\t\ttree.#path = name;\n\t\treturn tree;\n\t}\n\n\t/**@returns {string} */\n\tget name() {\n\t\treturn this.#name;\n\t}\n\n\t/**@returns {string} */\n\tget url() {\n\t\treturn this.#url;\n\t}\n\n\t/**@returns {string} */\n\tget path() {\n\t\treturn this.#path;\n\t}\n\n\t/**@returns {Array<Tree>} */\n\tget children() {\n\t\treturn this.#children;\n\t}\n\n\tset children(value) {\n\t\tif (!Array.isArray(value)) throw new Error(\"Invalid children\");\n\t\tthis.#children = value;\n\t}\n\n\t/**@returns {Tree} */\n\tget parent() {\n\t\treturn this.#parent;\n\t}\n\n\t/**@param {Tree} value */\n\tset parent(value) {\n\t\tif (!(value instanceof Tree)) throw new Error(\"Invalid parent\");\n\t\tthis.#parent = value;\n\t\tif (this.#parent) {\n\t\t\tthis.#path = Url.join(this.#parent.path, this.#name);\n\t\t}\n\t}\n\n\t/**\n\t * Check if the root of the tree is added to the open folder list.\n\t * @returns {boolean}\n\t */\n\tget isConnected() {\n\t\tconst root = this.root;\n\t\treturn !!addedFolder.find(({ url }) => url === root.url);\n\t}\n\n\t/**\n\t * Get the root of the tree\n\t * @returns {Tree}\n\t */\n\tget root() {\n\t\tlet root = this;\n\t\twhile (root.parent) {\n\t\t\troot = root.parent;\n\t\t}\n\t\treturn root;\n\t}\n\n\t/**\n\t * Update tree name and url\n\t * @param {string} url\n\t * @param {string} [name]\n\t */\n\tupdate(url, name) {\n\t\tif (!name) name = Url.basename(url);\n\t\tthis.#url = url;\n\t\tthis.#name = name;\n\t\tthis.#path = Url.join(this.#parent.path, name);\n\t\tgetAllFiles(this);\n\t}\n\n\t/**\n\t * @typedef {object} TreeJson\n\t * @property {string} name\n\t * @property {string} url\n\t * @property {string} path\n\t * @property {string} parent\n\t * @property {boolean} isDirectory\n\t */\n\n\t/**\n\t * To tree object to json\n\t * @returns {TreeJson}\n\t */\n\ttoJSON() {\n\t\treturn {\n\t\t\tname: this.#name,\n\t\t\turl: this.#url,\n\t\t\tpath: this.#path,\n\t\t\tparent: this.#parent?.url,\n\t\t\tisDirectory: !!this.#children,\n\t\t};\n\t}\n\n\t/**\n\t * Create a tree from json\n\t * @param {TreeJson} json\n\t * @returns {Tree}\n\t */\n\tstatic fromJSON(json) {\n\t\tconst { name, url, path, parent, isDirectory } = json;\n\t\tconst tree = new Tree(name, url, isDirectory);\n\t\ttree.#parent = getTree(Object.values(filesTree), parent);\n\t\ttree.#path = path;\n\t\treturn tree;\n\t}\n}\n"
  },
  {
    "path": "src/lib/fileTypeHandler.js",
    "content": "/**\n * @typedef {Object} FileTypeHandler\n * @property {string} id - Unique identifier for the handler\n * @property {string[]} extensions - File extensions this handler supports (without dots)\n * @property {function} handleFile - Function that handles the file\n */\n\n/**\n * @typedef {Object} FileInfo\n * @property {string} name - File name\n * @property {string} uri - File URI\n * @property {Object} stats - File stats\n * @property {boolean} readOnly - Whether the file is read-only\n * @property {Object} options - Additional options passed during file open\n */\n\nclass FileTypeHandlerRegistry {\n\t#handlers = new Map();\n\n\t/**\n\t * Register a file type handler\n\t * @param {string} id - Unique identifier for the handler\n\t * @param {Object} options - Handler options\n\t * @param {string[]} options.extensions - File extensions to handle (without dots)\n\t * @param {function(FileInfo): Promise<void>} options.handleFile - Async function to handle the file\n\t * @throws {Error} If id is already registered or required options are missing\n\t */\n\tregisterFileHandler(id, { extensions, handleFile }) {\n\t\tif (this.#handlers.has(id)) {\n\t\t\tthrow new Error(`Handler with id '${id}' is already registered`);\n\t\t}\n\n\t\tif (!extensions?.length) {\n\t\t\tthrow new Error(\"extensions array is required\");\n\t\t}\n\n\t\tif (typeof handleFile !== \"function\") {\n\t\t\tthrow new Error(\"handleFile function is required\");\n\t\t}\n\n\t\t// Normalize extensions (remove dots if present, convert to lowercase)\n\t\tconst normalizedExts = extensions.map((ext) =>\n\t\t\text.toLowerCase().replace(/^\\./, \"\"),\n\t\t);\n\n\t\tthis.#handlers.set(id, {\n\t\t\textensions: normalizedExts,\n\t\t\thandleFile,\n\t\t});\n\t}\n\n\t/**\n\t * Unregister a file type handler\n\t * @param {string} id - The handler id to remove\n\t */\n\tunregisterFileHandler(id) {\n\t\tthis.#handlers.delete(id);\n\t}\n\n\t/**\n\t * Get a file handler for a given filename\n\t * @param {string} filename\n\t * @returns {Object|null} The matching handler or null if none found\n\t */\n\tgetFileHandler(filename) {\n\t\tconst ext = filename.split(\".\").pop().toLowerCase();\n\n\t\tfor (const [id, handler] of this.#handlers) {\n\t\t\tif (\n\t\t\t\thandler.extensions.includes(ext) ||\n\t\t\t\thandler.extensions.includes(\"*\")\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tid,\n\t\t\t\t\t...handler,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get all registered handlers\n\t * @returns {Map} Map of all registered handlers\n\t */\n\tgetHandlers() {\n\t\treturn new Map(this.#handlers);\n\t}\n}\n\nexport const fileTypeHandler = new FileTypeHandlerRegistry();\nexport default fileTypeHandler;\n"
  },
  {
    "path": "src/lib/fonts.js",
    "content": "import fsOperation from \"fileSystem\";\nimport loader from \"dialogs/loader\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport firaCode from \"../res/fonts/FiraCode.ttf\";\nimport MesloLGSNFRegular from \"../res/fonts/MesloLGSNFRegular.ttf\";\nimport robotoMono from \"../res/fonts/RobotoMono.ttf\";\n\nconst fonts = new Map();\nconst customFontNames = new Set();\nconst CUSTOM_FONTS_KEY = \"custom_fonts\";\nconst FONT_FACE_STYLE_ID = \"font-face-style\";\nconst EDITOR_STYLE_ID = \"editor-font-style\";\nconst APP_STYLE_ID = \"app-font-style\";\nconst DEFAULT_EDITOR_FONT = \"Roboto Mono\";\nconst DEFAULT_APP_FONT_STACK = `\"Roboto\", sans-serif`;\n\nadd(\n\t\"Fira Code\",\n\t`@font-face {\n  font-family: 'Fira Code';\n  src: url(${firaCode}) format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"Roboto Mono\",\n\t`@font-face {\n  font-family: 'Roboto Mono';\n  font-style: normal;\n  font-weight: 400;\n  font-display: swap;\n  src: url(${robotoMono}) format('truetype');\n  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F,\n    U+FE2E-FE2F;\n}`,\n);\n\nadd(\n\t\"MesloLGS NF Regular\",\n\t`@font-face {\n  font-family: 'MesloLGS NF Regular';\n  font-style: normal;\n  font-weight: normal;\n  src: url(${MesloLGSNFRegular}) format('truetype');\n}`,\n);\n\nadd(\n\t\"Source Code\",\n\t`@font-face {\n  font-family: 'Source Code';\n  src: url(https://acode.app/SourceCodePro.ttf) format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"Victor Mono Italic\",\n\t`@font-face {\n  font-family: 'Victor Mono Italic';\n  src: url(https://acode.app/VictorMono-Italic.otf) format('truetype');\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"Victor Mono Medium\",\n\t`@font-face {\n  font-family: 'Victor Mono Medium';\n  src: url(https://acode.app/VictorMono-Medium.otf) format('truetype');\n  font-weight: medium;\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"Cascadia Code\",\n\t`@font-face {\n  font-family: 'Cascadia Code';\n  src: url(https://acode.app/CascadiaCode.ttf) format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"Proggy Clean\",\n\t`@font-face {\n  font-family: 'Proggy Clean';\n  src: url(https://acode.app/ProggyClean.ttf) format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"JetBrains Mono Bold\",\n\t`@font-face {\n  font-family: 'JetBrains Mono Bold';\n  src: url(https://acode.app/JetBrainsMono-Bold.ttf) format('truetype');\n  font-weight: bold;\n}`,\n);\n\nadd(\n\t\"JetBrains Mono Regular\",\n\t`@font-face {\n  font-family: 'JetBrains Mono Regular';\n  src: url(https://acode.app/JetBrainsMono-Regular.ttf) format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}`,\n);\n\nadd(\n\t\"Noto Mono\",\n\t`@font-face {\n  font-display: swap;\n  font-family: 'Noto Mono';\n  src: url(https://acode.app/NotoMono-Regular.woff) format(\"woff\");\n  font-weight: 400;\n  font-style: normal;\n  unicode-range: U+0590-06FF;\n}`,\n);\n\n// Load custom fonts on module initialization\nloadCustomFonts();\n\nfunction add(name, css) {\n\tfonts.set(name, css);\n}\n\nfunction addCustom(name, css) {\n\tfonts.set(name, css);\n\tcustomFontNames.add(name);\n\tsaveCustomFonts();\n}\n\nfunction saveCustomFonts() {\n\tconst customFonts = {};\n\n\tfor (const name of customFontNames) {\n\t\tconst css = fonts.get(name);\n\t\tif (css) {\n\t\t\tcustomFonts[name] = css;\n\t\t}\n\t}\n\n\tlocalStorage.setItem(CUSTOM_FONTS_KEY, JSON.stringify(customFonts));\n}\n\nfunction loadCustomFonts() {\n\ttry {\n\t\tconst customFonts = localStorage.getItem(CUSTOM_FONTS_KEY);\n\t\tif (customFonts) {\n\t\t\tconst parsed = JSON.parse(customFonts);\n\t\t\tfor (const [name, css] of Object.entries(parsed)) {\n\t\t\t\tfonts.set(name, css);\n\t\t\t\tcustomFontNames.add(name);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\"Failed to load custom fonts:\", error);\n\t}\n}\n\nfunction get(name) {\n\treturn fonts.get(name);\n}\n\nfunction getNames() {\n\treturn [...fonts.keys()];\n}\n\nfunction remove(name) {\n\tconst result = fonts.delete(name);\n\tif (result) {\n\t\tcustomFontNames.delete(name);\n\t\tsaveCustomFonts();\n\t}\n\treturn result;\n}\n\nfunction has(name) {\n\treturn fonts.has(name);\n}\n\nfunction isCustom(name) {\n\treturn customFontNames.has(name);\n}\n\nasync function setEditorFont(name) {\n\tloader.showTitleLoader();\n\ttry {\n\t\tawait loadFont(name);\n\t\tconst $style = ensureStyleElement(EDITOR_STYLE_ID);\n\t\t$style.textContent = `.editor-container.ace_editor{\n    font-family: \"${name}\", NotoMono, Monaco, MONOSPACE !important;\n  }\n  .ace_text{\n    font-family: inherit !important;\n  }`;\n\t} catch (error) {\n\t\ttoast(`${name} font not found`, \"error\");\n\t\tsetEditorFont(DEFAULT_EDITOR_FONT);\n\t} finally {\n\t\tloader.removeTitleLoader();\n\t}\n}\n\nasync function setAppFont(name) {\n\tconst $style = ensureStyleElement(APP_STYLE_ID);\n\n\tif (!name) {\n\t\t$style.textContent = `:root {\n  --app-font-family: ${DEFAULT_APP_FONT_STACK};\n}`;\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait loadFont(name);\n\t\t$style.textContent = `:root {\n  --app-font-family: \"${name}\", ${DEFAULT_APP_FONT_STACK};\n}`;\n\t} catch (error) {\n\t\ttoast(`${name} font not found`, \"error\");\n\t\t$style.textContent = `:root {\n  --app-font-family: ${DEFAULT_APP_FONT_STACK};\n}`;\n\t}\n}\n\nasync function downloadFont(name, link) {\n\tconst FONT_DIR = Url.join(DATA_STORAGE, \"fonts\");\n\tconst FONT_FILE = Url.join(FONT_DIR, name);\n\n\tconst fs = fsOperation(FONT_FILE);\n\tif (await fs.exists()) return FONT_FILE;\n\n\tif (!(await fsOperation(FONT_DIR).exists())) {\n\t\tawait fsOperation(DATA_STORAGE).createDirectory(\"fonts\");\n\t}\n\n\tconst font = await fsOperation(link).readFile();\n\tconsole.log(\"fonts content : \", font);\n\tawait fsOperation(FONT_DIR).createFile(name, font);\n\n\treturn FONT_FILE;\n}\n\nasync function loadFont(name) {\n\tconst $style = ensureStyleElement(FONT_FACE_STYLE_ID);\n\tlet css = get(name);\n\n\tif (!css) {\n\t\tthrow new Error(`Font ${name} not found`);\n\t}\n\n\t// Get all URL font references\n\tconst urls = [...css.matchAll(/url\\((.*?)\\)/g)].map((match) => match[1]);\n\n\t// Download and replace URLs\n\tfor (const url of urls) {\n\t\tif (!/^https?/.test(url)) continue;\n\t\tif (/^https?:\\/\\/localhost/.test(url)) continue;\n\t\tconst fontFile = await downloadFont(name, url);\n\t\tconst internalUrl = await helpers.toInternalUri(fontFile);\n\t\tcss = css.replace(url, internalUrl);\n\t}\n\n\t// Add font face to document if not already present\n\tif (!$style.textContent.includes(`font-family: '${name}'`)) {\n\t\t$style.textContent = `${$style.textContent}\\n${css}`;\n\t}\n\n\treturn css;\n}\n\nfunction ensureStyleElement(id) {\n\tconst selector = `style#${id}`;\n\tconst $style = tag.get(selector) ?? <style id={id}></style>;\n\tif (!$style.isConnected) {\n\t\tdocument.head.append($style);\n\t}\n\treturn $style;\n}\n\nexport default {\n\tadd,\n\taddCustom,\n\tget,\n\tgetNames,\n\tremove,\n\thas,\n\tisCustom,\n\tsetFont: setEditorFont,\n\tsetEditorFont,\n\tsetAppFont,\n\tloadFont,\n};\n"
  },
  {
    "path": "src/lib/installPlugin.js",
    "content": "import fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport purchaseListener from \"handlers/purchase\";\nimport JSZip from \"jszip\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\nimport InstallState from \"./installState\";\nimport loadPlugin from \"./loadPlugin\";\n\n/** @type {import(\"dialogs/loader\").Loader} */\nlet loaderDialog;\n/** @type {Array<() => Promise<void>>} */\nlet depsLoaders;\n\n/**\n * Installs a plugin.\n * @param {string} id\n * @param {string} name\n * @param {string} purchaseToken\n * @param {boolean} isDependency\n */\nexport default async function installPlugin(\n\tid,\n\tname,\n\tpurchaseToken,\n\tisDependency,\n) {\n\tif (!isDependency) {\n\t\tloaderDialog = loader.create(name || \"Plugin\", strings.installing, {\n\t\t\ttimeout: 6000,\n\t\t});\n\t\tdepsLoaders = [];\n\t}\n\n\tlet pluginDir;\n\tlet pluginUrl;\n\tlet state;\n\n\ttry {\n\t\tif (!(await fsOperation(PLUGIN_DIR).exists())) {\n\t\t\tawait fsOperation(DATA_STORAGE).createDirectory(\"plugins\");\n\t\t}\n\t} catch (error) {\n\t\twindow.log(\"error\", error);\n\t}\n\n\tif (!/^(https?|file|content):/.test(id)) {\n\t\tpluginUrl = Url.join(\n\t\t\tconstants.API_BASE,\n\t\t\t\"plugin/download/\",\n\t\t\t`${id}?device=${device.uuid}`,\n\t\t);\n\t\tif (purchaseToken) pluginUrl += `&token=${purchaseToken}`;\n\t\tpluginUrl += `&package=${BuildInfo.packageName}`;\n\t\tpluginUrl += `&version=${device.version}`;\n\n\t\tpluginDir = Url.join(PLUGIN_DIR, id);\n\t} else {\n\t\tpluginUrl = id;\n\t}\n\n\ttry {\n\t\tif (!isDependency) loaderDialog.show();\n\n\t\tlet plugin;\n\t\tif (\n\t\t\tpluginUrl.includes(constants.API_BASE) ||\n\t\t\tpluginUrl.startsWith(\"file:\") ||\n\t\t\tpluginUrl.startsWith(\"content:\")\n\t\t) {\n\t\t\t// Use fsOperation for Acode registry URL\n\t\t\tplugin = await fsOperation(pluginUrl).readFile(\n\t\t\t\tundefined,\n\t\t\t\t(loaded, total) => {\n\t\t\t\t\tloaderDialog.setMessage(\n\t\t\t\t\t\t`${strings.loading} ${((loaded / total) * 100).toFixed(2)}%`,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t} else {\n\t\t\t// cordova http plugin for others\n\t\t\tplugin = await new Promise((resolve, reject) => {\n\t\t\t\tcordova.plugin.http.sendRequest(\n\t\t\t\t\tpluginUrl,\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\tresponseType: \"arraybuffer\",\n\t\t\t\t\t},\n\t\t\t\t\t(response) => {\n\t\t\t\t\t\tresolve(response.data);\n\t\t\t\t\t\tloaderDialog.setMessage(`${strings.loading} 100%`);\n\t\t\t\t\t},\n\t\t\t\t\t(error) => {\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\tif (plugin) {\n\t\t\tconst zip = new JSZip();\n\t\t\tawait zip.loadAsync(plugin);\n\n\t\t\tif (!zip.files[\"plugin.json\"]) {\n\t\t\t\tthrow new Error(strings[\"invalid plugin\"]);\n\t\t\t}\n\n\t\t\t/** @type {{ dependencies: string[] }} */\n\t\t\tconst pluginJson = JSON.parse(\n\t\t\t\tawait zip.files[\"plugin.json\"].async(\"text\"),\n\t\t\t);\n\n\t\t\t/** patch main in manifest */\n\t\t\tif (!zip.files[pluginJson.main]) {\n\t\t\t\tpluginJson.main = \"main.js\";\n\t\t\t}\n\n\t\t\t/** patch icon in manifest */\n\t\t\tif (!zip.files[pluginJson.icon]) {\n\t\t\t\tpluginJson.icon = \"icon.png\";\n\t\t\t}\n\n\t\t\t/** patch readme in manifest */\n\t\t\tif (!zip.files[pluginJson.readme]) {\n\t\t\t\tpluginJson.readme = \"readme.md\";\n\t\t\t}\n\n\t\t\tif (!zip.files[pluginJson.main]) {\n\t\t\t\tthrow new Error(strings[\"invalid plugin\"]);\n\t\t\t}\n\n\t\t\tif (!isDependency && pluginJson.dependencies) {\n\t\t\t\tconst manifests = await resolveDepsManifest(pluginJson.dependencies);\n\n\t\t\t\tlet titleText;\n\t\t\t\tif (manifests.length > 1) {\n\t\t\t\t\ttitleText = \"Acode wants to install the following dependencies:\";\n\t\t\t\t} else {\n\t\t\t\t\ttitleText = \"Acode wants to install the following dependency:\";\n\t\t\t\t}\n\n\t\t\t\tconst shouldInstall = await confirm(\n\t\t\t\t\t\"Installer Notice\",\n\t\t\t\t\ttitleText +\n\t\t\t\t\t\t\"<br /><br />\" +\n\t\t\t\t\t\tmanifests.map((value) => value.name).join(\", \"),\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\n\t\t\t\tif (shouldInstall) {\n\t\t\t\t\tfor (const manifest of manifests) {\n\t\t\t\t\t\tconst hasError = await resolveDep(manifest);\n\t\t\t\t\t\tif (hasError) throw new Error(strings.failed);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!pluginDir) {\n\t\t\t\tpluginJson.source = pluginUrl;\n\t\t\t\tid = pluginJson.id;\n\t\t\t\tpluginDir = Url.join(PLUGIN_DIR, id);\n\t\t\t}\n\n\t\t\tstate = await InstallState.new(id);\n\n\t\t\tif (!(await fsOperation(pluginDir).exists())) {\n\t\t\t\tawait fsOperation(PLUGIN_DIR).createDirectory(id);\n\t\t\t}\n\n\t\t\t// Track unsafe absolute entries to skip\n\t\t\tconst ignoredUnsafeEntries = new Set();\n\n\t\t\tconst files = Object.keys(zip.files);\n\t\t\tconst limit = 2;\n\n\t\t\tasync function processFile(file) {\n\t\t\t\ttry {\n\t\t\t\t\tconst entry = zip.files[file];\n\n\t\t\t\t\tlet correctFile = file.replace(/\\\\/g, \"/\");\n\t\t\t\t\tconst isDirEntry = entry.dir || correctFile.endsWith(\"/\");\n\n\t\t\t\t\tif (isUnsafeAbsolutePath(file)) {\n\t\t\t\t\t\tignoredUnsafeEntries.add(file);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tcorrectFile = sanitizeZipPath(correctFile, isDirEntry);\n\t\t\t\t\tif (!correctFile) return;\n\n\t\t\t\t\tconst fileUrl = Url.join(pluginDir, correctFile);\n\n\t\t\t\t\t// Handle directory entries\n\t\t\t\t\tif (isDirEntry) {\n\t\t\t\t\t\tawait createFileRecursive(pluginDir, correctFile, true);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Ensure parent directory exists\n\t\t\t\t\tconst lastSlash = correctFile.lastIndexOf(\"/\");\n\t\t\t\t\tif (lastSlash !== -1) {\n\t\t\t\t\t\tconst parentRel = correctFile.slice(0, lastSlash + 1);\n\t\t\t\t\t\tawait createFileRecursive(pluginDir, parentRel, true);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!state.exists(correctFile)) {\n\t\t\t\t\t\tawait createFileRecursive(pluginDir, correctFile, false);\n\t\t\t\t\t}\n\n\t\t\t\t\tlet data = await entry.async(\"ArrayBuffer\");\n\n\t\t\t\t\tif (file === \"plugin.json\") {\n\t\t\t\t\t\tdata = JSON.stringify(pluginJson);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!(await state.isUpdated(correctFile, data))) return;\n\n\t\t\t\t\tawait fsOperation(fileUrl).writeFile(data);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Error processing file ${file}:`, error);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Process in batches\n\t\t\tfor (let i = 0; i < files.length; i += limit) {\n\t\t\t\tconst batch = files.slice(i, i + limit);\n\t\t\t\tawait Promise.allSettled(batch.map(processFile));\n\n\t\t\t\t// Allow UI thread to breathe\n\t\t\t\tawait new Promise((r) => setTimeout(r, 0));\n\t\t\t}\n\t\t\t// Emit a non-blocking warning if any unsafe entries were skipped\n\t\t\tif (!isDependency && ignoredUnsafeEntries.size) {\n\t\t\t\tconst sample = Array.from(ignoredUnsafeEntries).slice(0, 3).join(\", \");\n\t\t\t\tloaderDialog.setMessage(\n\t\t\t\t\t`Skipped ${ignoredUnsafeEntries.size} unsafe archive entr${\n\t\t\t\t\t\tignoredUnsafeEntries.size === 1 ? \"y\" : \"ies\"\n\t\t\t\t\t} (e.g., ${sample})`,\n\t\t\t\t);\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"Plugin installer: skipped unsafe absolute paths in archive:\",\n\t\t\t\t\tArray.from(ignoredUnsafeEntries),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (isDependency) {\n\t\t\t\tdepsLoaders.push(async () => {\n\t\t\t\t\tawait loadPlugin(id, true);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tfor (const loader of depsLoaders) {\n\t\t\t\t\tawait loader();\n\t\t\t\t}\n\t\t\t\tawait loadPlugin(id, true);\n\t\t\t}\n\n\t\t\tawait state.save();\n\t\t\tdeleteRedundantFiles(pluginDir, state);\n\t\t}\n\t} catch (err) {\n\t\ttry {\n\t\t\t// Clear the install state if installation fails\n\t\t\tif (state) await state.clear();\n\n\t\t\t// Delete the plugin directory if it was created\n\t\t\tif (pluginDir && (await fsOperation(pluginDir).exists())) {\n\t\t\t\tawait fsOperation(pluginDir).delete();\n\t\t\t}\n\t\t} catch (cleanupError) {\n\t\t\tconsole.error(\"Cleanup failed:\", cleanupError);\n\t\t}\n\t\tthrow err;\n\t} finally {\n\t\tif (!isDependency) {\n\t\t\tloaderDialog.destroy();\n\t\t}\n\t}\n}\n\n/**\n * Create directory recursively\n * @param {string} parent\n * @param {Array<string> | string} dir\n */\nasync function createFileRecursive(parent, dir, shouldBeDirAtEnd) {\n\tlet wantDirEnd = !!shouldBeDirAtEnd;\n\t/** @type {string[]} */\n\tlet parts;\n\tif (typeof dir === \"string\") {\n\t\tif (dir.endsWith(\"/\")) wantDirEnd = true;\n\t\tdir = dir.replace(/\\\\/g, \"/\");\n\t\tparts = dir.split(\"/\");\n\t} else {\n\t\tparts = dir;\n\t}\n\tparts = parts.filter((d) => d);\n\tconst cd = parts.shift();\n\tif (!cd) return;\n\tconst newParent = Url.join(parent, cd);\n\n\tconst isLast = parts.length === 0;\n\tconst needDir = !isLast || wantDirEnd;\n\tif (!(await fsOperation(newParent).exists())) {\n\t\tif (needDir) {\n\t\t\ttry {\n\t\t\t\tawait fsOperation(parent).createDirectory(cd);\n\t\t\t} catch (e) {\n\t\t\t\t// If another concurrent task created it, consider it fine\n\t\t\t\tif (!(await fsOperation(newParent).exists())) throw e;\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tawait fsOperation(parent).createFile(cd);\n\t\t\t} catch (e) {\n\t\t\t\tif (!(await fsOperation(newParent).exists())) throw e;\n\t\t\t}\n\t\t}\n\t}\n\tif (parts.length) {\n\t\tawait createFileRecursive(newParent, parts, wantDirEnd);\n\t}\n}\n\n/**\n * Sanitize zip entry path to ensure it's relative and safe under pluginDir\n * - Normalizes separators to '/'\n * - Strips leading slashes and Windows drive prefixes (e.g., C:/)\n * - Resolves '.' and '..' segments\n * - Preserves trailing slash for directory entries\n * @param {string} p\n * @param {boolean} isDir\n * @returns {string} sanitized relative path\n */\nfunction sanitizeZipPath(p, isDir) {\n\tif (!p) return \"\";\n\tlet path = String(p);\n\t// Normalize separators\n\tpath = path.replace(/\\\\/g, \"/\");\n\t// Remove URL-like scheme if present accidentally\n\tpath = path.replace(/^[a-zA-Z]+:\\/\\//, \"\");\n\t// Strip leading slashes\n\tpath = path.replace(/^\\/+/, \"\");\n\t// Strip Windows drive letter, e.g., C:/\n\tpath = path.replace(/^[A-Za-z]:\\//, \"\");\n\n\tconst parts = path.split(\"/\");\n\tconst stack = [];\n\tfor (const part of parts) {\n\t\tif (!part || part === \".\") continue;\n\t\tif (part === \"..\") {\n\t\t\tif (stack.length) stack.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tstack.push(part);\n\t}\n\tlet safe = stack.join(\"/\");\n\tif (isDir && safe && !safe.endsWith(\"/\")) safe += \"/\";\n\treturn safe;\n}\n\n/**\n * Detects unsafe absolute paths in zip entries that should be ignored.\n * Treats leading '/' as absolute, Windows drive roots like 'C:/' as absolute,\n * and common Android/Linux device roots like '/data', '/root', '/system'.\n * @param {string} p\n */\nfunction isUnsafeAbsolutePath(p) {\n\tif (!p) return false;\n\tconst s = String(p);\n\tif (/^[A-Za-z]:[\\\\\\/]/.test(s)) return true; // Windows drive root\n\tif (s.startsWith(\"//\")) return true; // network path\n\tif (s.startsWith(\"/\")) {\n\t\treturn (\n\t\t\ts.startsWith(\"/data\") ||\n\t\t\ts.startsWith(\"/system\") ||\n\t\t\ts.startsWith(\"/vendor\") ||\n\t\t\ts.startsWith(\"/storage\") ||\n\t\t\ts.startsWith(\"/sdcard\") ||\n\t\t\ts.startsWith(\"/root\") ||\n\t\t\ttrue // any leading slash is unsafe\n\t\t);\n\t}\n\treturn false;\n}\n\n/**\n * Resolves Dependencies Manifest with given ids.\n * @param {string[]} deps dependencies\n */\nasync function resolveDepsManifest(deps) {\n\tconst resolved = [];\n\tfor (const dependency of deps) {\n\t\tconst remoteDependency = await fsOperation(\n\t\t\tconstants.API_BASE,\n\t\t\t`plugin/${dependency}`,\n\t\t)\n\t\t\t.readFile(\"json\")\n\t\t\t.catch(() => null);\n\n\t\tif (!remoteDependency)\n\t\t\tthrow new Error(`Unknown plugin dependency: ${dependency}`);\n\n\t\tconst version = await getInstalledPluginVersion(remoteDependency.id);\n\t\tif (remoteDependency?.version === version) continue;\n\n\t\tif (remoteDependency.dependencies) {\n\t\t\tconst manifests = await resolveDepsManifest(\n\t\t\t\tremoteDependency.dependencies,\n\t\t\t);\n\t\t\tresolved.push(manifests);\n\t\t}\n\n\t\tresolved.push(remoteDependency);\n\t}\n\n\t/**\n\t *\n\t * @param {string} id\n\t * @returns {Promise<string>} plugin version\n\t */\n\tasync function getInstalledPluginVersion(id) {\n\t\tif (await fsOperation(PLUGIN_DIR, id).exists()) {\n\t\t\tconst plugin = await fsOperation(PLUGIN_DIR, id, \"plugin.json\").readFile(\n\t\t\t\t\"json\",\n\t\t\t);\n\t\t\treturn plugin.version;\n\t\t}\n\t}\n\n\treturn resolved;\n}\n\n/** Resolve dependency\n * @param {object} manifest\n * @returns {Promise<boolean>} has error\n */\nasync function resolveDep(manifest) {\n\tlet purchaseToken;\n\tlet product;\n\tlet isPaid = false;\n\n\tisPaid = manifest.price > 0;\n\t[product] = await helpers.promisify(iap.getProducts, [manifest.sku]);\n\tif (product) {\n\t\tconst purchase = await getPurchase(product.productId);\n\t\tpurchaseToken = purchase?.purchaseToken;\n\t}\n\n\tif (isPaid && !purchaseToken) {\n\t\tif (!product) throw new Error(\"Product not found\");\n\t\tconst apiStatus = await helpers.checkAPIStatus();\n\n\t\tif (!apiStatus) {\n\t\t\talert(strings.error, strings.api_error);\n\t\t\treturn true;\n\t\t}\n\n\t\tiap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, onerror));\n\t\tloaderDialog.setMessage(strings[\"loading...\"]);\n\t\tawait helpers.promisify(iap.purchase, product.productId);\n\n\t\tasync function onpurchase(e) {\n\t\t\tconst purchase = await getPurchase(product.productId);\n\t\t\tawait ajax.post(Url.join(constants.API_BASE, \"plugin/order\"), {\n\t\t\t\tdata: {\n\t\t\t\t\tid: manifest.id,\n\t\t\t\t\ttoken: purchase?.purchaseToken,\n\t\t\t\t\tpackage: BuildInfo.packageName,\n\t\t\t\t},\n\t\t\t});\n\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t}\n\n\t\tasync function onerror(error) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tloaderDialog.setMessage(\n\t\t`${strings.installing.replace(\"...\", \"\")} ${manifest.name}...`,\n\t);\n\tawait installPlugin(manifest.id, undefined, purchaseToken, true);\n\n\tasync function getPurchase(sku) {\n\t\tconst purchases = await helpers.promisify(iap.getPurchases);\n\t\tconst purchase = purchases.find((p) => p.productIds.includes(sku));\n\t\treturn purchase;\n\t}\n}\n\n/**\n *\n * @param {string} dir\n * @param {Array<string>} files\n */\nasync function listFileRecursive(dir, files) {\n\tfor (const child of await fsOperation(dir).lsDir()) {\n\t\tconst fileUrl = Url.join(dir, child.name);\n\t\tif (child.isDirectory) {\n\t\t\tawait listFileRecursive(fileUrl, files);\n\t\t} else {\n\t\t\tfiles.push(fileUrl);\n\t\t}\n\t}\n}\n\n/**\n *\n * @param {Record<string, boolean>} files\n */\nasync function deleteRedundantFiles(pluginDir, state) {\n\t/** @type {string[]} */\n\tlet files = [];\n\tawait listFileRecursive(pluginDir, files);\n\n\tfor (const file of files) {\n\t\tif (!state.exists(file.replace(`${pluginDir}/`, \"\"))) {\n\t\t\tfsOperation(file).delete();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/lib/installState.js",
    "content": "import fsOperation from \"fileSystem\";\nimport Url from \"utils/Url\";\n\nconst INSTALL_STATE_STORAGE = Url.join(DATA_STORAGE, \".install-state\");\n\nexport default class InstallState {\n\t/** @type Record<string, string> */\n\tstore;\n\t/** @type Record<string, string> */\n\tupdatedStore;\n\n\t/**\n\t *\n\t * @param {string} id\n\t * @returns\n\t */\n\tstatic async new(id) {\n\t\ttry {\n\t\t\tconst state = new InstallState();\n\t\t\tstate.id = await checksumText(id);\n\t\t\tstate.updatedStore = {};\n\n\t\t\tif (!(await fsOperation(INSTALL_STATE_STORAGE).exists())) {\n\t\t\t\tawait fsOperation(DATA_STORAGE).createDirectory(\".install-state\");\n\t\t\t}\n\n\t\t\tstate.storeUrl = Url.join(INSTALL_STATE_STORAGE, state.id);\n\t\t\tif (await fsOperation(state.storeUrl).exists()) {\n\t\t\t\tlet raw = \"{}\";\n\t\t\t\ttry {\n\t\t\t\t\traw = await fsOperation(state.storeUrl).readFile(\"utf-8\");\n\t\t\t\t\tstate.store = JSON.parse(raw);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"InstallState: Failed to parse state file, deleting:\",\n\t\t\t\t\t\terr,\n\t\t\t\t\t);\n\t\t\t\t\t// Delete corrupted state file to avoid parse errors such as 'Unexpected end of JSON'\n\t\t\t\t\tstate.store = {};\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait fsOperation(state.storeUrl).delete();\n\t\t\t\t\t\t// Recreate a fresh empty file to keep invariant\n\t\t\t\t\t\tawait fsOperation(INSTALL_STATE_STORAGE).createFile(state.id);\n\t\t\t\t\t} catch (writeErr) {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\"InstallState: Failed to recreate state file:\",\n\t\t\t\t\t\t\twriteErr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst patchedStore = {};\n\t\t\t\tfor (const [key, value] of Object.entries(state.store)) {\n\t\t\t\t\tpatchedStore[key.toLowerCase()] = value;\n\t\t\t\t}\n\n\t\t\t\tstate.store = patchedStore;\n\t\t\t} else {\n\t\t\t\tstate.store = {};\n\t\t\t\tawait fsOperation(INSTALL_STATE_STORAGE).createFile(state.id);\n\t\t\t}\n\n\t\t\treturn state;\n\t\t} catch (e) {\n\t\t\tconsole.error(e);\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {string} url\n\t * @param {ArrayBuffer | string} content\n\t * @param {boolean} isString\n\t * @returns\n\t */\n\tasync isUpdated(url, content) {\n\t\turl = url.toLowerCase();\n\t\tconst current = this.store[url];\n\t\tconst update =\n\t\t\ttypeof content === \"string\"\n\t\t\t\t? await checksumText(content)\n\t\t\t\t: await checksum(content);\n\t\tthis.updatedStore[url] = update;\n\n\t\tif (current === update) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/**\n\t *\n\t * @param {string} url\n\t * @returns\n\t */\n\texists(url) {\n\t\tif (typeof this.store[url.toLowerCase()] !== \"undefined\") {\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync save() {\n\t\tthis.store = this.updatedStore;\n\t\tawait fsOperation(this.storeUrl).writeFile(\n\t\t\tJSON.stringify(this.updatedStore),\n\t\t);\n\t}\n\n\tasync delete(url) {\n\t\turl = url.toLowerCase();\n\t\tif (await fsOperation(url).exists()) {\n\t\t\tawait fsOperation(url).delete();\n\t\t}\n\t}\n\n\tasync clear() {\n\t\ttry {\n\t\t\tthis.store = {};\n\t\t\tthis.updatedStore = {};\n\t\t\t// Delete the state file entirely to avoid corrupted/partial JSON issues\n\t\t\tif (await fsOperation(this.storeUrl).exists()) {\n\t\t\t\ttry {\n\t\t\t\t\tawait fsOperation(this.storeUrl).delete();\n\t\t\t\t} catch (delErr) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"InstallState: Failed to delete state file during clear:\",\n\t\t\t\t\t\tdelErr,\n\t\t\t\t\t);\n\t\t\t\t\t// As a fallback, overwrite with a valid empty JSON\n\t\t\t\t\tawait fsOperation(this.storeUrl).writeFile(\"{}\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to clear install state:\", error);\n\t\t}\n\t}\n}\n\n/**\n * Derives the checksum of a Buffer\n * @param {BufferSource} data\n * @returns the derived checksum\n */\nasync function checksum(data) {\n\tconst hashBuffer = await window.crypto.subtle.digest(\"SHA-256\", data);\n\tconst hashArray = Array.from(new Uint8Array(hashBuffer));\n\tconst hashHex = hashArray\n\t\t.map((byte) => byte.toString(16).padStart(2, \"0\"))\n\t\t.join(\"\");\n\treturn hashHex;\n}\n\n/**\n *\n * @param {string} text\n * @returns\n */\nasync function checksumText(text) {\n\treturn new Promise((resolve, reject) => {\n\t\tcordova.exec(\n\t\t\t(hash) => resolve(hash),\n\t\t\t(error) => reject(error),\n\t\t\t\"System\",\n\t\t\t\"checksumText\",\n\t\t\t[text],\n\t\t);\n\t});\n}\n"
  },
  {
    "path": "src/lib/keyBindings.js",
    "content": "import * as cmCommands from \"@codemirror/commands\";\nimport {\n\tdefaultKeymap,\n\temacsStyleKeymap,\n\thistoryKeymap,\n\tindentWithTab,\n\tstandardKeymap,\n} from \"@codemirror/commands\";\n\nconst MODIFIER_ORDER = [\"Ctrl\", \"Alt\", \"Shift\", \"Cmd\"];\nconst KEYMAP_SOURCES = [\n\t...standardKeymap,\n\t...defaultKeymap,\n\t...historyKeymap,\n\t...emacsStyleKeymap,\n\tindentWithTab,\n];\n\nconst APP_BINDING_CONFIG = [\n\t{\n\t\tname: \"focusEditor\",\n\t\tdescription: \"Focus editor\",\n\t\tkey: \"Ctrl-1\",\n\t\treadOnly: false,\n\t},\n\t{\n\t\tname: \"findFile\",\n\t\tdescription: \"Find a file\",\n\t\tkey: \"Ctrl-P\",\n\t\taction: \"find-file\",\n\t},\n\t{\n\t\tname: \"closeCurrentTab\",\n\t\tdescription: \"Close current tab.\",\n\t\tkey: \"Ctrl-Q\",\n\t\taction: \"close-current-tab\",\n\t\treadOnly: false,\n\t},\n\t{\n\t\tname: \"closeAllTabs\",\n\t\tdescription: \"Close all tabs.\",\n\t\tkey: \"Ctrl-Shift-Q\",\n\t\taction: \"close-all-tabs\",\n\t\treadOnly: false,\n\t},\n\t{\n\t\tname: \"newFile\",\n\t\tdescription: \"Create new file\",\n\t\tkey: \"Ctrl-N\",\n\t\taction: \"new-file\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"openFile\",\n\t\tdescription: \"Open a file\",\n\t\tkey: \"Ctrl-O\",\n\t\taction: \"open-file\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"openFolder\",\n\t\tdescription: \"Open a folder\",\n\t\tkey: \"Ctrl-Shift-O\",\n\t\taction: \"open-folder\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"saveFile\",\n\t\tdescription: \"Save current file\",\n\t\tkey: \"Ctrl-S\",\n\t\taction: \"save\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"saveFileAs\",\n\t\tdescription: \"Save as current file\",\n\t\tkey: \"Ctrl-Shift-S\",\n\t\taction: \"save-as\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"saveAllChanges\",\n\t\tdescription: \"Save all changes\",\n\t\tkey: null,\n\t\taction: \"save-all-changes\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"nextFile\",\n\t\tdescription: \"Open next file tab\",\n\t\tkey: \"Ctrl-Tab\",\n\t\taction: \"next-file\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"prevFile\",\n\t\tdescription: \"Open previous file tab\",\n\t\tkey: \"Ctrl-Shift-Tab\",\n\t\taction: \"prev-file\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"showSettingsMenu\",\n\t\tdescription: \"Show settings menu\",\n\t\tkey: \"Ctrl-,\",\n\t\treadOnly: false,\n\t},\n\t{\n\t\tname: \"renameFile\",\n\t\tdescription: \"Rename current file\",\n\t\tkey: \"F2\",\n\t\taction: \"rename\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"run\",\n\t\tdescription: \"Run current file\",\n\t\tkey: \"F5\",\n\t\taction: \"run\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"openInAppBrowser\",\n\t\tdescription: \"Open in-app browser\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"toggleFullscreen\",\n\t\tdescription: \"Toggle full screen mode\",\n\t\tkey: \"F11\",\n\t\taction: \"toggle-fullscreen\",\n\t\treadOnly: false,\n\t},\n\t{\n\t\tname: \"toggleSidebar\",\n\t\tdescription: \"Toggle sidebar\",\n\t\tkey: \"Ctrl-B\",\n\t\taction: \"toggle-sidebar\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"toggleMenu\",\n\t\tdescription: \"Toggle menu\",\n\t\tkey: \"F3\",\n\t\taction: \"toggle-menu\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"toggleEditMenu\",\n\t\tdescription: \"Toggle edit menu\",\n\t\tkey: \"F4\",\n\t\taction: \"toggle-editmenu\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"selectall\",\n\t\tdescription: \"Select all\",\n\t\tkey: \"Ctrl-A\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"gotoline\",\n\t\tdescription: \"Go to line\",\n\t\tkey: \"Ctrl-G\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"find\",\n\t\tdescription: \"Find\",\n\t\tkey: \"Ctrl-F\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"copy\",\n\t\tdescription: \"Copy\",\n\t\tkey: \"Ctrl-C\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"cut\",\n\t\tdescription: \"Cut\",\n\t\tkey: \"Ctrl-X\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"paste\",\n\t\tdescription: \"Paste\",\n\t\tkey: \"Ctrl-V\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"problems\",\n\t\tdescription: \"Show problems\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"replace\",\n\t\tdescription: \"Replace\",\n\t\tkey: \"Ctrl-R\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"openCommandPalette\",\n\t\tdescription: \"Open command palette\",\n\t\tkey: \"Ctrl-Shift-P\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"modeSelect\",\n\t\tdescription: \"Change language mode\",\n\t\tkey: \"Ctrl-M\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"toggleQuickTools\",\n\t\tdescription: \"Toggle quick tools\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"selectWord\",\n\t\tdescription: \"Select current word\",\n\t\tkey: \"Ctrl-D\",\n\t\taction: \"select-word\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"openLogFile\",\n\t\tdescription: \"Open log file\",\n\t\tkey: null,\n\t\taction: \"open-log-file\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"openPluginsPage\",\n\t\tdescription: \"Open plugins page\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"openFileExplorer\",\n\t\tdescription: \"Open file explorer\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"copyDeviceInfo\",\n\t\tdescription: \"Copy device info\",\n\t\tkey: null,\n\t\taction: \"copy-device-info\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"changeAppTheme\",\n\t\tdescription: \"Change app theme\",\n\t\tkey: null,\n\t\taction: \"change-app-theme\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"changeEditorTheme\",\n\t\tdescription: \"Change editor theme\",\n\t\tkey: null,\n\t\taction: \"change-editor-theme\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"openTerminal\",\n\t\tdescription: \"Open terminal\",\n\t\tkey: \"Ctrl-`\",\n\t\taction: \"new-terminal\",\n\t\treadOnly: true,\n\t},\n\t{\n\t\tname: \"documentSymbols\",\n\t\tdescription: \"Go to symbol in document\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"duplicateSelection\",\n\t\tdescription: \"Duplicate selection\",\n\t\tkey: \"Ctrl-Shift-D\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"copylinesdown\",\n\t\tdescription: \"Copy lines down\",\n\t\tkey: \"Alt-Shift-Down\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"copylinesup\",\n\t\tdescription: \"Copy lines up\",\n\t\tkey: \"Alt-Shift-Up\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"movelinesdown\",\n\t\tdescription: \"Move lines down\",\n\t\tkey: \"Alt-Down\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"movelinesup\",\n\t\tdescription: \"Move lines up\",\n\t\tkey: \"Alt-Up\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"removeline\",\n\t\tdescription: \"Remove line\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"insertlineafter\",\n\t\tdescription: \"Insert line after\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"selectline\",\n\t\tdescription: \"Select line\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"selectlinesdown\",\n\t\tdescription: \"Select lines down\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"selectlinesup\",\n\t\tdescription: \"Select lines up\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"selectlinestart\",\n\t\tdescription: \"Select line start\",\n\t\tkey: \"Shift-Home\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"selectlineend\",\n\t\tdescription: \"Select line end\",\n\t\tkey: \"Shift-End\",\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"indent\",\n\t\tdescription: \"Indent\",\n\t\tkey: \"Tab\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"outdent\",\n\t\tdescription: \"Outdent\",\n\t\tkey: \"Shift-Tab\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"indentselection\",\n\t\tdescription: \"Indent selection\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"newline\",\n\t\tdescription: \"Insert newline\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"joinlines\",\n\t\tdescription: \"Join lines\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"deletetolinestart\",\n\t\tdescription: \"Delete to line start\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"deletetolineend\",\n\t\tdescription: \"Delete to line end\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"togglecomment\",\n\t\tdescription: \"Toggle comment\",\n\t\tkey: \"Ctrl-/\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"comment\",\n\t\tdescription: \"Add line comment\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"uncomment\",\n\t\tdescription: \"Remove line comment\",\n\t\tkey: null,\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"toggleBlockComment\",\n\t\tdescription: \"Toggle block comment\",\n\t\tkey: \"Ctrl-Shift-/\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"undo\",\n\t\tdescription: \"Undo\",\n\t\tkey: \"Ctrl-Z\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"redo\",\n\t\tdescription: \"Redo\",\n\t\tkey: \"Ctrl-Shift-Z|Ctrl-Y\",\n\t\treadOnly: false,\n\t\teditorOnly: true,\n\t},\n\t{\n\t\tname: \"simplifySelection\",\n\t\tdescription: \"Simplify selection\",\n\t\tkey: null,\n\t\treadOnly: true,\n\t\teditorOnly: true,\n\t},\n];\n\nconst APP_KEY_BINDINGS = buildAppBindings(APP_BINDING_CONFIG);\nconst APP_CUSTOM_COMMANDS = new Set(\n\tAPP_BINDING_CONFIG.filter((config) => !config.action).map(\n\t\t(config) => config.name,\n\t),\n);\n\nconst FORCE_READ_ONLY = new Set([\n\t\"toggleTabFocusMode\",\n\t\"temporarilySetTabFocusMode\",\n]);\nconst MUTATING_COMMAND_PATTERN =\n\t/^(delete|insert|indent|move|copy|split|transpose|toggle|undo|redo|line|block)/i;\n\nconst CODEMIRROR_COMMAND_NAMES = new Set(\n\tObject.entries(cmCommands)\n\t\t.filter(([, value]) => typeof value === \"function\")\n\t\t.map(([name]) => name),\n);\n\nconst CODEMIRROR_KEY_BINDINGS = buildCodemirrorKeyBindings(APP_KEY_BINDINGS);\n\nconst keyBindings = Object.fromEntries(\n\tObject.entries({ ...CODEMIRROR_KEY_BINDINGS, ...APP_KEY_BINDINGS })\n\t\t.filter(\n\t\t\t([name, binding]) =>\n\t\t\t\tbinding &&\n\t\t\t\t(binding.action ||\n\t\t\t\t\tAPP_CUSTOM_COMMANDS.has(name) ||\n\t\t\t\t\tCODEMIRROR_COMMAND_NAMES.has(name)),\n\t\t)\n\t\t.sort((a, b) => a[0].localeCompare(b[0])),\n);\n\nexport default keyBindings;\n\nfunction buildAppBindings(configs) {\n\treturn Object.fromEntries(\n\t\tconfigs.map(\n\t\t\t({\n\t\t\t\tname,\n\t\t\t\tdescription,\n\t\t\t\tkey = null,\n\t\t\t\taction,\n\t\t\t\treadOnly = true,\n\t\t\t\teditorOnly,\n\t\t\t}) => [\n\t\t\t\tname,\n\t\t\t\t{\n\t\t\t\t\tdescription: description ?? humanizeCommandName(name),\n\t\t\t\t\tkey,\n\t\t\t\t\treadOnly,\n\t\t\t\t\t...(editorOnly !== undefined ? { editorOnly } : {}),\n\t\t\t\t\t...(action ? { action } : {}),\n\t\t\t\t},\n\t\t\t],\n\t\t),\n\t);\n}\n\nfunction buildCodemirrorKeyBindings(appBindings) {\n\tconst commandEntries = Object.entries(cmCommands).filter(\n\t\t([, value]) => typeof value === \"function\",\n\t);\n\tconst commandNameByFunction = new Map(\n\t\tcommandEntries.map(([name, fn]) => [fn, name]),\n\t);\n\tconst comboMap = new Map();\n\n\tfor (const binding of KEYMAP_SOURCES) {\n\t\tconst baseCombos = new Set();\n\n\t\tpushCommandCombo(binding.run, binding.key, \"win\", baseCombos);\n\t\tpushCommandCombo(binding.run, binding.win, \"win\", baseCombos);\n\t\tpushCommandCombo(binding.run, binding.linux, \"win\", baseCombos);\n\t\tpushCommandCombo(binding.run, binding.mac, \"mac\", baseCombos);\n\n\t\tif (binding.shift) {\n\t\t\tconst shiftName = commandNameByFunction.get(binding.shift);\n\t\t\tif (shiftName && !appBindings[shiftName]) {\n\t\t\t\tconst combos = baseCombos.size\n\t\t\t\t\t? Array.from(baseCombos)\n\t\t\t\t\t: [\n\t\t\t\t\t\t\tnormalizeKey(binding.key, \"win\"),\n\t\t\t\t\t\t\tnormalizeKey(binding.win, \"win\"),\n\t\t\t\t\t\t\tnormalizeKey(binding.linux, \"win\"),\n\t\t\t\t\t\t\tnormalizeKey(binding.mac, \"mac\"),\n\t\t\t\t\t\t].filter(Boolean);\n\t\t\t\tfor (const combo of combos) {\n\t\t\t\t\taddCommandCombo(comboMap, shiftName, ensureModifier(combo, \"Shift\"));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconst result = {};\n\tfor (const [name, combos] of comboMap.entries()) {\n\t\tif (!combos.size || appBindings[name]) continue;\n\t\tresult[name] = {\n\t\t\tdescription: humanizeCommandName(name),\n\t\t\tkey: Array.from(combos)\n\t\t\t\t.sort((a, b) => a.localeCompare(b))\n\t\t\t\t.join(\"|\"),\n\t\t\treadOnly: inferReadOnly(name),\n\t\t\teditorOnly: true,\n\t\t};\n\t}\n\treturn result;\n\n\tfunction pushCommandCombo(commandFn, key, platform, baseCombos) {\n\t\tif (!commandFn) return;\n\t\tconst name = commandNameByFunction.get(commandFn);\n\t\tif (!name || appBindings[name]) return;\n\t\tconst normalized = normalizeKey(key, platform);\n\t\tif (!normalized) return;\n\t\taddCommandCombo(comboMap, name, normalized);\n\t\tbaseCombos.add(normalized);\n\t}\n}\n\nfunction addCommandCombo(map, name, combo) {\n\tif (!combo) return;\n\tlet entry = map.get(name);\n\tif (!entry) {\n\t\tentry = new Set();\n\t\tmap.set(name, entry);\n\t}\n\tentry.add(combo);\n}\n\nfunction normalizeKey(key, platform = \"win\") {\n\tif (!key) return null;\n\tconst replaced = key.replace(/Mod/g, platform === \"mac\" ? \"Cmd\" : \"Ctrl\");\n\tconst { modifiers, baseKey } = parseKeyParts(replaced);\n\tif (!baseKey) return [...modifiers].join(\"-\") || null;\n\tconst ordered = MODIFIER_ORDER.filter((mod) => modifiers.has(mod));\n\treturn [...ordered, baseKey].join(\"-\");\n}\n\nfunction ensureModifier(combo, modifier) {\n\tif (!combo) return null;\n\tconst { modifiers, baseKey } = parseKeyParts(combo);\n\tif (!baseKey) return combo;\n\tmodifiers.add(modifier);\n\tconst ordered = MODIFIER_ORDER.filter((mod) => modifiers.has(mod));\n\treturn [...ordered, baseKey].join(\"-\");\n}\n\nfunction parseKeyParts(combo) {\n\tconst modifiers = new Set();\n\tlet baseKey = \"\";\n\tif (!combo) return { modifiers, baseKey };\n\tfor (const rawPart of combo.split(\"-\")) {\n\t\tconst part = rawPart.trim();\n\t\tif (!part) continue;\n\t\tconst normalized = part.charAt(0).toUpperCase() + part.slice(1);\n\t\tif (MODIFIER_ORDER.includes(normalized)) {\n\t\t\tmodifiers.add(normalized);\n\t\t} else {\n\t\t\tbaseKey = part;\n\t\t}\n\t}\n\treturn { modifiers, baseKey };\n}\n\nfunction humanizeCommandName(name) {\n\treturn name\n\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n\t\t.replace(/_/g, \" \")\n\t\t.replace(/^./, (char) => char.toUpperCase());\n}\n\nfunction inferReadOnly(name) {\n\tif (FORCE_READ_ONLY.has(name)) return true;\n\treturn !MUTATING_COMMAND_PATTERN.test(name);\n}\n"
  },
  {
    "path": "src/lib/lang.js",
    "content": "const langMap = {\n\t\"en-us\": {\n\t\tname: \"English\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/en-us.json\");\n\t\t},\n\t},\n\t\"es-sv\": {\n\t\tname: \"Español\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/es-sv.json\");\n\t\t},\n\t},\n\t\"fr-fr\": {\n\t\tname: \"Francais\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/fr-fr.json\");\n\t\t},\n\t},\n\t\"tl-ph\": {\n\t\tname: \"Tagalog\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/tl-ph.json\");\n\t\t},\n\t},\n\t\"de-de\": {\n\t\tname: \"Deutsch\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/de-de.json\");\n\t\t},\n\t},\n\t\"id-id\": {\n\t\tname: \"Indonesian\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/id-id.json\");\n\t\t},\n\t},\n\t\"uz-uz\": {\n\t\tname: \"O'zbekcha\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/uz-uz.json\");\n\t\t},\n\t},\n\t\"ru-ru\": {\n\t\tname: \"Русский\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/ru-ru.json\");\n\t\t},\n\t},\n\t\"pl-pl\": {\n\t\tname: \"Polski\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/pl-pl.json\");\n\t\t},\n\t},\n\t\"pt-br\": {\n\t\tname: \"Português\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/pt-br.json\");\n\t\t},\n\t},\n\t\"pu-in\": {\n\t\tname: \"ਪੰਜਾਬੀ\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/pu-in.json\");\n\t\t},\n\t},\n\t\"tr-tr\": {\n\t\tname: \"Türkçe\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/tr-tr.json\");\n\t\t},\n\t},\n\t\"uk-ua\": {\n\t\tname: \"Українська\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/uk-ua.json\");\n\t\t},\n\t},\n\t\"hi-in\": {\n\t\tname: \"हिंदी\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/hi-in.json\");\n\t\t},\n\t},\n\t\"zh-cn\": {\n\t\tname: \"中文简体\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/zh-cn.json\");\n\t\t},\n\t},\n\t\"zh-hant\": {\n\t\tname: \"繁體中文\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/zh-hant.json\");\n\t\t},\n\t},\n\t\"zh-tw\": {\n\t\tname: \"繁體中文 (台灣)\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/zh-tw.json\");\n\t\t},\n\t},\n\t\"ir-fa\": {\n\t\tname: \"فارسی\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/ir-fa.json\");\n\t\t},\n\t},\n\t\"ar-ye\": {\n\t\tname: \"العربية\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/ar-ye.json\");\n\t\t},\n\t},\n\t\"ja-jp\": {\n\t\tname: \"日本語\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/ja-jp.json\");\n\t\t},\n\t},\n\t\"bn-bd\": {\n\t\tname: \"বাংলা\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/bn-bd.json\");\n\t\t},\n\t},\n\t\"cs-cz\": {\n\t\tname: \"Čeština\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/cs-cz.json\");\n\t\t},\n\t},\n\t\"vi-vn\": {\n\t\tname: \"Tiếng Việt\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/vi-vn.json\");\n\t\t},\n\t},\n\t\"be-by\": {\n\t\tname: \"Беларуская\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/be-by.json\");\n\t\t},\n\t},\n\t\"hu-hu\": {\n\t\tname: \"Magyar\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/hu-hu.json\");\n\t\t},\n\t},\n\t\"ml-in\": {\n\t\tname: \"മലയാളം\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/ml-in.json\");\n\t\t},\n\t},\n\t\"mm-unicode\": {\n\t\tname: \"ဗမာစာ(Unicode)\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/mm-unicode.json\");\n\t\t},\n\t},\n\t\"mm-zawgyi\": {\n\t\tname: \"ဗမာစာ(Zawgyi)\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/mm-zawgyi.json\");\n\t\t},\n\t},\n\t\"ko-kr\": {\n\t\tname: \"한국어\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/ko-kr.json\");\n\t\t},\n\t},\n\t\"it-it\": {\n\t\tname: \"Italiano\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/it-it.json\");\n\t\t},\n\t},\n\t\"he-il\": {\n\t\tname: \"Hebrew\",\n\t\tasync strings() {\n\t\t\treturn await import(\"../lang/he-il.json\");\n\t\t},\n\t},\n};\n\nexport default {\n\tasync set(code) {\n\t\tcode = code?.toLowerCase();\n\t\tconst lang = langMap[code] || langMap[\"en-us\"];\n\t\tconst strings = await lang.strings();\n\t\twindow.strings = strings.default;\n\t},\n\tlist: Object.keys(langMap).map((code) => [code, langMap[code].name]),\n\tgetName(code) {\n\t\tcode = code?.toLowerCase();\n\t\tcode = code in langMap ? code : \"en-us\";\n\t\treturn langMap[code].name;\n\t},\n};\n"
  },
  {
    "path": "src/lib/loadPlugin.js",
    "content": "import fsOperation from \"fileSystem\";\nimport Page from \"components/page\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport actionStack from \"./actionStack\";\n\nexport default async function loadPlugin(pluginId, justInstalled = false) {\n\tconst baseUrl = await helpers.toInternalUri(Url.join(PLUGIN_DIR, pluginId));\n\tconst cacheFile = Url.join(CACHE_STORAGE, pluginId);\n\n\t// Unmount the old version before loading the new one.\n\t// This MUST be done here by the framework, not by the new plugin code itself,\n\t// because once the new script loads, it calls acode.setPluginUnmount(id, newDestroy)\n\t// which overwrites the old version's destroy callback. At that point the old\n\t// destroy — which holds references to the old sidebar app, commands, event\n\t// listeners, etc. — is lost and can never be called. Letting the framework\n\t// invoke unmountPlugin() first ensures the OLD destroy() runs while it still\n\t// exists, so all old-version resources are properly cleaned up.\n\ttry {\n\t\tacode.unmountPlugin(pluginId);\n\t} catch (e) {\n\t\t// unmountPlugin() itself is safe when no callback is registered (it no-ops),\n\t\t// but a plugin's destroy() callback may throw. We catch here so a faulty\n\t\t// cleanup in the old version does not block reloading the new one.\n\t\tconsole.error(`Error while unmounting plugin \"${pluginId}\":`, e);\n\t}\n\n\t// Remove the old <script> tag so the browser fetches the new source.\n\tconst oldScript = document.getElementById(`${pluginId}-mainScript`);\n\tif (oldScript) oldScript.remove();\n\n\tconst pluginJson = await fsOperation(\n\t\tUrl.join(PLUGIN_DIR, pluginId, \"plugin.json\"),\n\t).readFile(\"json\");\n\n\tlet mainUrl;\n\tif (\n\t\tawait fsOperation(Url.join(PLUGIN_DIR, pluginId, pluginJson.main)).exists()\n\t) {\n\t\tmainUrl = Url.join(baseUrl, pluginJson.main);\n\t} else {\n\t\tmainUrl = Url.join(baseUrl, \"main.js\");\n\t}\n\n\treturn new Promise((resolve, reject) => {\n\t\tconst $script = (\n\t\t\t<script id={`${pluginId}-mainScript`} src={mainUrl}></script>\n\t\t);\n\n\t\t$script.onerror = (error) => {\n\t\t\treject(\n\t\t\t\tnew Error(\n\t\t\t\t\t`Failed to load script for plugin ${pluginId}: ${error.message || error}`,\n\t\t\t\t),\n\t\t\t);\n\t\t};\n\n\t\t$script.onload = async () => {\n\t\t\tconst $page = Page(\"Plugin\");\n\t\t\t$page.show = () => {\n\t\t\t\tactionStack.push({\n\t\t\t\t\tid: pluginId,\n\t\t\t\t\taction: $page.hide,\n\t\t\t\t});\n\n\t\t\t\tapp.append($page);\n\t\t\t};\n\n\t\t\t$page.onhide = function () {\n\t\t\t\tactionStack.remove(pluginId);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tif (!(await fsOperation(cacheFile).exists())) {\n\t\t\t\t\tawait fsOperation(CACHE_STORAGE).createFile(pluginId);\n\t\t\t\t}\n\n\t\t\t\tawait acode.initPlugin(pluginId, baseUrl, $page, {\n\t\t\t\t\tcacheFileUrl: await helpers.toInternalUri(cacheFile),\n\t\t\t\t\tcacheFile: fsOperation(cacheFile),\n\t\t\t\t\tfirstInit: justInstalled,\n\t\t\t\t\tctx: await PluginContext.generate(\n\t\t\t\t\t\tpluginId,\n\t\t\t\t\t\tJSON.stringify(pluginJson),\n\t\t\t\t\t),\n\t\t\t\t});\n\n\t\t\t\tresolve();\n\t\t\t} catch (error) {\n\t\t\t\treject(error);\n\t\t\t}\n\t\t};\n\n\t\tdocument.head.append($script);\n\t});\n}\n"
  },
  {
    "path": "src/lib/loadPlugins.js",
    "content": "import fsOperation from \"../fileSystem\";\nimport Url from \"../utils/Url\";\nimport loadPlugin from \"./loadPlugin\";\nimport settings from \"./settings\";\n\n// theme-related keywords for determining theme plugins\nconst THEME_IDENTIFIERS = new Set([\n\t\"theme\",\n\t\"catppuccin\",\n\t\"pine\",\n\t\"githubdark\",\n\t\"radiant\",\n\t\"rdtheme\",\n\t\"ayumirage\",\n\t\"dust\",\n\t\"synthwave\",\n\t\"dragon\",\n\t\"mint\",\n\t\"monokai\",\n\t\"lumina_code\",\n\t\"sweet\",\n\t\"moonlight\",\n\t\"bluloco\",\n\t\"acode.plugin.extra_syntax_highlights\",\n\t\"documentsviewer\",\n]);\n\nexport const onPluginLoadCallback = Symbol(\"onPluginLoadCallback\");\nexport const onPluginsLoadCompleteCallback = Symbol(\n\t\"onPluginsLoadCompleteCallback\",\n);\n\nexport const LOADED_PLUGINS = new Set();\nexport const BROKEN_PLUGINS = new Map();\n\nexport default async function loadPlugins(loadOnlyTheme = false) {\n\tconst plugins = await fsOperation(PLUGIN_DIR).lsDir();\n\tconst results = [];\n\tconst failedPlugins = [];\n\n\tif (plugins.length > 0) {\n\t\ttoast(strings[\"loading plugins\"]);\n\t}\n\n\tlet pluginsToLoad = [];\n\tconst currentTheme = settings.value.appTheme;\n\tconst enabledMap = settings.value.pluginsDisabled || {};\n\n\tif (loadOnlyTheme) {\n\t\t// Only load theme plugins matching current theme\n\t\tpluginsToLoad = plugins.filter((pluginDir) => {\n\t\t\tconst pluginId = Url.basename(pluginDir.url);\n\t\t\t// Skip already loaded and plugins that were previously marked broken\n\t\t\treturn (\n\t\t\t\tisThemePlugin(pluginId) &&\n\t\t\t\t!LOADED_PLUGINS.has(pluginId) &&\n\t\t\t\t!BROKEN_PLUGINS.has(pluginId)\n\t\t\t);\n\t\t});\n\t} else {\n\t\t// Load non-theme plugins that aren't loaded yet and are enabled\n\t\tpluginsToLoad = plugins.filter((pluginDir) => {\n\t\t\tconst pluginId = Url.basename(pluginDir.url);\n\t\t\t// Skip theme plugins, already loaded, disabled or previously marked broken\n\t\t\treturn (\n\t\t\t\t!isThemePlugin(pluginId) &&\n\t\t\t\t!LOADED_PLUGINS.has(pluginId) &&\n\t\t\t\tenabledMap[pluginId] !== true &&\n\t\t\t\t!BROKEN_PLUGINS.has(pluginId)\n\t\t\t);\n\t\t});\n\t}\n\n\t// Load plugins concurrently\n\tconst LOAD_TIMEOUT = 15000; // ms per plugin\n\tconst loadPromises = pluginsToLoad.map(async (pluginDir) => {\n\t\tconst pluginId = Url.basename(pluginDir.url);\n\n\t\tif (loadOnlyTheme && currentTheme) {\n\t\t\tconst pluginIdLower = pluginId.toLowerCase();\n\t\t\tconst currentThemeLower = currentTheme.toLowerCase();\n\t\t\tconst matchFound = pluginIdLower.includes(currentThemeLower);\n\t\t\t// Skip if:\n\t\t\t// 1. No match found with current theme AND\n\t\t\t// 2. It's not a theme plugin at all\n\t\t\tif (!matchFound && !isThemePlugin(pluginId)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// ensure loadPlugin doesn't hang: timeout wrapper\n\t\t\tawait Promise.race([\n\t\t\t\tloadPlugin(pluginId),\n\t\t\t\tnew Promise((_, rej) =>\n\t\t\t\t\tsetTimeout(() => rej(new Error(\"Plugin load timeout\")), LOAD_TIMEOUT),\n\t\t\t\t),\n\t\t\t]);\n\t\t\tLOADED_PLUGINS.add(pluginId);\n\n\t\t\tacode[onPluginLoadCallback](pluginId);\n\n\t\t\tresults.push(true);\n\t\t\t// clear broken mark if present\n\t\t\tif (BROKEN_PLUGINS.has(pluginId)) {\n\t\t\t\tBROKEN_PLUGINS.delete(pluginId);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error loading plugin ${pluginId}:`, error);\n\t\t\t// mark plugin as broken to avoid repeated attempts until user intervenes\n\t\t\tBROKEN_PLUGINS.set(pluginId, {\n\t\t\t\terror: String(error.message || error),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t});\n\t\t\tfailedPlugins.push(pluginId);\n\t\t\tresults.push(false);\n\t\t}\n\t});\n\n\tawait Promise.allSettled(loadPromises);\n\n\tacode[onPluginsLoadCompleteCallback]();\n\n\tif (failedPlugins.length > 0) {\n\t\tsetTimeout(() => {\n\t\t\tcleanupFailedPlugins(failedPlugins).catch((error) => {\n\t\t\t\tconsole.error(\"Failed to cleanup plugins:\", error);\n\t\t\t});\n\t\t}, 1000);\n\t}\n\treturn results.filter(Boolean).length;\n}\n\nfunction isThemePlugin(pluginId) {\n\t// Convert to lowercase for case-insensitive matching\n\tconst id = pluginId.toLowerCase();\n\t// Check if any theme identifier is present in the plugin ID\n\treturn Array.from(THEME_IDENTIFIERS).some((theme) => id.includes(theme));\n}\n\nasync function cleanupFailedPlugins(pluginIds) {\n\tfor (const pluginId of pluginIds) {\n\t\ttry {\n\t\t\tconst pluginDir = Url.join(PLUGIN_DIR, pluginId);\n\t\t\tif (await fsOperation(pluginDir).exists()) {\n\t\t\t\tawait fsOperation(pluginDir).delete();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(`Failed to cleanup plugin ${pluginId}:`, error);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/lib/logger.js",
    "content": "import fsOperation from \"fileSystem\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\n\n/*\n/**\n * Logger class for handling application logging with buffer and file output.\n * @class\n */\n\n/**\n * Creates a new Logger instance.\n * @constructor\n * @param {number} [maxBufferSize=1000] - Maximum number of log entries to keep in buffer.\n * @param {string} [logLevel=\"info\"] - Minimum log level to record (\"error\", \"warn\", \"info\", \"debug\").\n * @param {number} [flushInterval=30000] - Interval in milliseconds for automatic log flushing.\n */\nclass Logger {\n\t#logBuffer;\n\t#maxBufferSize;\n\t#logLevel;\n\t#logFileName;\n\t#flushInterval;\n\t#autoFlushInterval;\n\t#maxFileSize;\n\n\tconstructor(\n\t\tmaxBufferSize = 1000,\n\t\tlogLevel = \"info\",\n\t\tflushInterval = 30000,\n\t\tmaxFileSize = 10 * 1024 * 1024,\n\t) {\n\t\tthis.#logBuffer = new Map();\n\t\tthis.#maxBufferSize = maxBufferSize;\n\t\tthis.#logLevel = logLevel;\n\t\tthis.#logFileName = constants.LOG_FILE_NAME;\n\t\tthis.#flushInterval = flushInterval;\n\t\tthis.#maxFileSize = maxFileSize;\n\t\tthis.#startAutoFlush(); // Automatically flush logs at intervals\n\t\tthis.#setupAppLifecycleHandlers(); // Handle app lifecycle events for safe log saving\n\t}\n\n\t/**\n\t * Logs a message with the specified log level.\n\t * @param {'error' | 'warn' | 'info' | 'debug'} level - The log level.\n\t * @param {string} message - The message to be logged.\n\t */\n\tlog(level, message) {\n\t\tconst levels = [\"error\", \"warn\", \"info\", \"debug\"];\n\t\tif (levels.indexOf(level) <= levels.indexOf(this.#logLevel)) {\n\t\t\tlet logEntry;\n\n\t\t\t// Check if the message is an instance of Error\n\t\t\tif (message instanceof Error) {\n\t\t\t\tlogEntry = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message.name}: ${message.message}\\nStack trace: ${message.stack}`;\n\t\t\t} else {\n\t\t\t\tlogEntry = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message}`;\n\t\t\t}\n\t\t\t// LRU Mechanism for efficient log buffer management\n\t\t\tif (this.#logBuffer.size >= this.#maxBufferSize) {\n\t\t\t\t// Remove oldest entry\n\t\t\t\tconst oldestKey = this.#logBuffer.keys().next().value;\n\t\t\t\tthis.#logBuffer.delete(oldestKey);\n\t\t\t}\n\t\t\tthis.#logBuffer.set(Date.now(), logEntry); // Using timestamp as key\n\t\t}\n\t}\n\n\tflushLogs() {\n\t\tif (this.#logBuffer.size > 0) {\n\t\t\tconst logContent = Array.from(this.#logBuffer.values()).join(\"\\n\");\n\t\t\tthis.#writeLogToFile(logContent);\n\t\t\tthis.#logBuffer.clear(); // Clear the buffer after flushing\n\t\t}\n\t}\n\n\t#writeLogToFile = async (logContent) => {\n\t\ttry {\n\t\t\tconst logFilePath = Url.join(DATA_STORAGE, constants.LOG_FILE_NAME);\n\t\t\tif (!(await fsOperation(logFilePath).exists())) {\n\t\t\t\tawait fsOperation(window.DATA_STORAGE).createFile(\n\t\t\t\t\tconstants.LOG_FILE_NAME,\n\t\t\t\t\tlogContent,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlet existingData = await fsOperation(logFilePath).readFile(\"utf8\");\n\t\t\t\tlet newData = existingData + \"\\n\" + logContent;\n\t\t\t\t// Check if the new data exceeds the maximum file size\n\t\t\t\tif (new Blob([newData]).size > this.#maxFileSize) {\n\t\t\t\t\tconst lines = newData.split(\"\\n\");\n\t\t\t\t\twhile (\n\t\t\t\t\t\tnew Blob([lines.join(\"\\n\")]).size > this.#maxFileSize &&\n\t\t\t\t\t\tlines.length > 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tlines.shift();\n\t\t\t\t\t}\n\t\t\t\t\tnewData = lines.join(\"\\n\");\n\t\t\t\t}\n\n\t\t\t\tawait fsOperation(logFilePath).writeFile(newData);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\n\t\t\t\t\"Error in handling fs operation on log file. Error:\",\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t};\n\n\t#startAutoFlush = () => {\n\t\tthis.#autoFlushInterval = setInterval(() => {\n\t\t\tthis.flushLogs();\n\t\t}, this.#flushInterval);\n\t};\n\n\tstopAutoFlush() {\n\t\tclearInterval(this.#autoFlushInterval);\n\t}\n\n\t#setupAppLifecycleHandlers = () => {\n\t\tdocument.addEventListener(\n\t\t\t\"pause\",\n\t\t\t() => {\n\t\t\t\tthis.flushLogs(); // Flush logs when app is paused (background)\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t};\n}\n\nexport default Logger;\n"
  },
  {
    "path": "src/lib/notificationManager.js",
    "content": "import sidebarApps from \"sidebarApps\";\nimport DOMPurify from \"dompurify\";\n\n// Singleton instance\nlet instance = null;\n\nexport default class NotificationManager {\n\tDEFAULT_ICON = `<i class=\"icon notifications\"></i>`;\n\tMAX_NOTIFICATIONS = 20;\n\tnotifications = [];\n\tREFRESH_INTERVAL = 60000; // 1 minute refresh interval\n\ttimeUpdateInterval = null;\n\n\tconstructor() {\n\t\tif (instance) {\n\t\t\treturn instance;\n\t\t}\n\t\tthis.notifications = [];\n\t\tinstance = this;\n\t}\n\n\tinit() {\n\t\tdocument.body.appendChild(\n\t\t\t<div className=\"notification-toast-container\"></div>,\n\t\t);\n\t\tthis.renderNotifications();\n\t\tthis.startTimeUpdates();\n\n\t\tsidebarApps\n\t\t\t.get(\"notification\")\n\t\t\t?.querySelector(\".notifications-container\")\n\t\t\t.addEventListener(\"click\", this.handleClick.bind(this));\n\t}\n\n\tstartTimeUpdates() {\n\t\tif (this.timeUpdateInterval) {\n\t\t\tclearInterval(this.timeUpdateInterval);\n\t\t}\n\n\t\tthis.timeUpdateInterval = setInterval(() => {\n\t\t\tthis.updateNotificationTimes();\n\t\t}, this.REFRESH_INTERVAL);\n\t}\n\n\tupdateNotificationTimes() {\n\t\tconst container = sidebarApps\n\t\t\t.get(\"notification\")\n\t\t\t?.querySelector(\".notifications-container\");\n\n\t\tif (!container) return;\n\n\t\tcontainer.querySelectorAll(\".notification-time\").forEach((timeElement) => {\n\t\t\tconst notificationItem = timeElement.closest(\".notification-item\");\n\t\t\tconst id = notificationItem?.dataset.id;\n\t\t\tif (!id) return;\n\n\t\t\tconst notification = this.notifications.find(\n\t\t\t\t(n) => n.id === Number.parseInt(id),\n\t\t\t);\n\t\t\tif (notification) {\n\t\t\t\ttimeElement.textContent = this.formatTime(notification.time);\n\t\t\t}\n\t\t});\n\t}\n\n\trenderNotifications() {\n\t\tconst container = sidebarApps\n\t\t\t.get(\"notification\")\n\t\t\t?.querySelector(\".notifications-container\");\n\t\tif (!container) return;\n\n\t\tif (this.notifications.length === 0) {\n\t\t\tcontainer.innerHTML = `<div class='empty-state'>${strings[\"no_unread_notifications\"]}</div>`;\n\t\t\treturn;\n\t\t}\n\n\t\tcontainer.innerHTML = \"\";\n\t\tthis.notifications.forEach((notification) => {\n\t\t\tcontainer.appendChild(this.createNotificationElement(notification));\n\t\t});\n\t}\n\n\thandleClick(e) {\n\t\tconst dismissButton = e.target.closest(\".action-button\");\n\t\tif (!dismissButton) return;\n\n\t\te.stopPropagation();\n\t\tconst notificationElement = dismissButton.closest(\".notification-item\");\n\t\tif (!notificationElement) return;\n\n\t\tconst id = notificationElement.dataset.id;\n\t\tif (id) {\n\t\t\tconst index = this.notifications.findIndex(\n\t\t\t\t(n) => n.id === Number.parseInt(id),\n\t\t\t);\n\t\t\tif (index > -1) {\n\t\t\t\tnotificationElement.remove();\n\t\t\t\tthis.notifications.splice(index, 1);\n\t\t\t\tthis.renderNotifications();\n\t\t\t}\n\t\t}\n\t}\n\n\tcreateNotificationElement(notification) {\n\t\tconst element = (\n\t\t\t<div\n\t\t\t\tclassName={`notification-item ${notification.type}`}\n\t\t\t\tdata-id={notification.id}\n\t\t\t></div>\n\t\t);\n\t\tconst safeIcon = this.sanitizeIcon(this.parseIcon(notification.icon));\n\t\tconst safeTitle = this.sanitizeText(notification.title);\n\t\tconst safeMessage = this.sanitizeText(notification.message);\n\t\telement.innerHTML = `\n\t\t\t\t\t\t\t<div class=\"notification-icon\">\n\t\t\t\t\t\t\t${safeIcon}\n\t\t\t\t</div>\n\t\t\t\t<div class=\"notification-content\">\n\t\t\t\t\t<div class=\"notification-title\">\n\t\t\t\t\t\t${safeTitle}\n\t\t\t\t\t\t<span class=\"notification-time\">${this.formatTime(notification.time)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"notification-message\">${safeMessage}</div>\n\t\t\t\t\t<div class=\"notification-actions\">\n\t\t\t\t\t\t\t<div class=\"action-button\">Dismiss</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\t\t\t`;\n\t\tif (notification.action) {\n\t\t\telement.addEventListener(\"click\", (e) => {\n\t\t\t\tif (e.target.closest(\".action-button\")) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnotification.action(notification);\n\t\t\t});\n\t\t}\n\t\treturn element;\n\t}\n\n\tcreateToastNotification(notification) {\n\t\tconst element = (\n\t\t\t<div\n\t\t\t\tclassName={`notification-toast ${notification.type}`}\n\t\t\t\tdata-id={notification.id}\n\t\t\t></div>\n\t\t);\n\t\tconst safeIcon = this.sanitizeIcon(this.parseIcon(notification.icon));\n\t\tconst safeTitle = this.sanitizeText(notification.title);\n\t\tconst safeMessage = this.sanitizeText(notification.message);\n\t\telement.innerHTML = `\n\t\t\t\t\t\t\t<div class=\"notification-icon\">${safeIcon}</div>\n\t\t\t\t\t\t\t<div class=\"notification-content\">\n\t\t\t\t\t<div class=\"notification-title\">\n\t\t\t\t\t\t${safeTitle}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"notification-message\">${safeMessage}</div>\n\t\t\t\t</div>\n\t\t\t\t${notification.autoClose ? \"\" : `<span class=\"close-icon icon clearclose\"></span>`}\n\t\t\t\t`;\n\n\t\tconst closeIcon = element.querySelector(\".close-icon\");\n\t\tif (closeIcon) {\n\t\t\tcloseIcon.addEventListener(\"click\", (event) => {\n\t\t\t\tevent.stopPropagation();\n\t\t\t\telement.remove();\n\t\t\t});\n\t\t}\n\t\tif (notification.action) {\n\t\t\telement.addEventListener(\"click\", () =>\n\t\t\t\tnotification.action(notification),\n\t\t\t);\n\t\t}\n\t\tif (notification.autoClose) {\n\t\t\tsetTimeout(() => {\n\t\t\t\telement.classList.add(\"hiding\");\n\t\t\t\tsetTimeout(() => element.remove(), 300);\n\t\t\t}, 5000);\n\t\t}\n\t\treturn element;\n\t}\n\n\taddNotification(notification) {\n\t\tthis.notifications.unshift(notification);\n\n\t\t// Remove oldest if exceeding limit\n\t\tif (this.notifications.length > this.MAX_NOTIFICATIONS) {\n\t\t\tthis.notifications.pop();\n\t\t}\n\n\t\tthis.renderNotifications();\n\n\t\t// show toast notification\n\t\tdocument\n\t\t\t.querySelector(\".notification-toast-container\")\n\t\t\t?.appendChild(this.createToastNotification(notification));\n\t}\n\n\tpushNotification({\n\t\ttitle,\n\t\tmessage,\n\t\ticon,\n\t\tautoClose = true,\n\t\taction = null,\n\t\ttype = \"info\",\n\t}) {\n\t\tconst notification = {\n\t\t\tid: Date.now(),\n\t\t\ttitle,\n\t\t\tmessage,\n\t\t\ticon,\n\t\t\taction,\n\t\t\tautoClose,\n\t\t\ttype,\n\t\t\ttime: new Date(),\n\t\t};\n\t\tthis.addNotification(notification);\n\t}\n\n\tparseIcon(icon) {\n\t\tif (typeof icon !== \"string\" || !icon) return this.DEFAULT_ICON;\n\t\tif (icon.startsWith(\"<svg\")) return icon;\n\t\tif (icon.startsWith(\"data:\") || icon.startsWith(\"http\"))\n\t\t\treturn `<img src=\"${icon}\" alt=\"notification\" width=\"16\" height=\"16\">`;\n\t\treturn `<i class=\"icon ${icon}\"></i>`;\n\t}\n\n\tsanitizeText(text) {\n\t\treturn DOMPurify.sanitize(String(text ?? \"\"), {\n\t\t\tALLOWED_TAGS: [],\n\t\t\tALLOWED_ATTR: [],\n\t\t});\n\t}\n\n\tsanitizeIcon(iconMarkup) {\n\t\treturn DOMPurify.sanitize(iconMarkup, {\n\t\t\tUSE_PROFILES: { html: true, svg: true },\n\t\t\tALLOW_DATA_ATTR: false,\n\t\t});\n\t}\n\n\tformatTime(date) {\n\t\tconst now = new Date();\n\t\tconst diff = Math.floor((now - date) / 1000);\n\n\t\tif (diff < 60) return \"Just now\";\n\t\tif (diff < 3600) return `${Math.floor(diff / 60)}m`;\n\t\tif (diff < 86400) return `${Math.floor(diff / 3600)}h`;\n\t\tif (diff < 604800) return `${Math.floor(diff / 86400)}d`;\n\n\t\treturn date.toLocaleDateString();\n\t}\n\n\tclearAll() {\n\t\tthis.notifications = [];\n\t\tthis.renderNotifications();\n\t\tif (this.timeUpdateInterval) {\n\t\t\tclearInterval(this.timeUpdateInterval);\n\t\t\tthis.timeUpdateInterval = null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/lib/openFile.js",
    "content": "import fsOperation from \"fileSystem\";\nimport AudioPlayer from \"components/audioPlayer\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport { reopenWithNewEncoding } from \"palettes/changeEncoding\";\nimport { decode, detectEncoding } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport EditorFile from \"./editorFile\";\nimport fileTypeHandler from \"./fileTypeHandler\";\nimport recents from \"./recents\";\nimport appSettings from \"./settings\";\n\n/**\n * @typedef {object} FileOptions\n * @property {string} text\n * @property {{ row: number, column: number }} cursorPos\n * @property {boolean} render\n * @property {function} onsave\n * @property {string} encoding\n * @property {string} mode\n * @property {string} uri\n */\n\n/**\n * Opens a editor file\n * @param {String & FileOptions} file\n * @param {FileOptions} options\n */\n\nexport default async function openFile(file, options = {}) {\n\ttry {\n\t\tlet uri = typeof file === \"string\" ? file : file.uri;\n\t\tif (!uri) return;\n\n\t\t/**@type {EditorFile} */\n\t\tconst existingFile = editorManager.getFile(uri, \"uri\");\n\t\tconst { cursorPos, render, onsave, text, mode, encoding } = options;\n\n\t\tif (existingFile) {\n\t\t\t// If file is already opened and new text is provided\n\t\t\tconst existingText = existingFile.session.doc.toString() ?? \"\";\n\n\t\t\t// If file is already opened\n\t\t\texistingFile.makeActive();\n\n\t\t\tconst { editor } = editorManager;\n\n\t\t\tif (onsave) {\n\t\t\t\texistingFile.onsave = onsave;\n\t\t\t}\n\n\t\t\tif (text && existingText !== text) {\n\t\t\t\t// let confirmation = true;\n\t\t\t\t// if (existingFile.isUnsaved) {\n\t\t\t\t//   const message = strings['reopen file'].replace('{file}', existingFile.filename);\n\t\t\t\t//   confirmation = await confirm(strings.warning, message);\n\t\t\t\t// }\n\t\t\t\t// if (confirmation) {\n\t\t\t\t// }\n\t\t\t\teditor.dispatch({\n\t\t\t\t\tchanges: {\n\t\t\t\t\t\tfrom: 0,\n\t\t\t\t\t\tto: editor.state.doc.length,\n\t\t\t\t\t\tinsert: String(text),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Move cursor if requested and different\n\t\t\ttry {\n\t\t\t\tif (cursorPos) {\n\t\t\t\t\tconst cur = editor.getCursorPosition();\n\t\t\t\t\tif (cur.row !== cursorPos.row || cur.column !== cursorPos.column) {\n\t\t\t\t\t\teditor.gotoLine(cursorPos.row, cursorPos.column);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Failed to move cursor for ${existingFile.filename || existingFile.uri}`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (encoding && existingFile.encoding !== encoding) {\n\t\t\t\treopenWithNewEncoding(encoding);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tloader.showTitleLoader();\n\t\tconst settings = appSettings.value;\n\t\tconst fs = fsOperation(uri);\n\t\tconst fileInfo = await fs.stat();\n\t\tconst name = fileInfo.name || file.filename || uri;\n\t\tconst readOnly = fileInfo.canWrite ? false : true;\n\t\tconst createEditor = (isUnsaved, text, detectedEncoding) => {\n\t\t\tnew EditorFile(name, {\n\t\t\t\turi,\n\t\t\t\ttext,\n\t\t\t\tcursorPos,\n\t\t\t\tisUnsaved,\n\t\t\t\trender,\n\t\t\t\tonsave,\n\t\t\t\treadOnly,\n\t\t\t\tencoding: detectedEncoding || encoding,\n\t\t\t\tSAFMode: mode,\n\t\t\t});\n\t\t};\n\n\t\t// Check for registered file handlers\n\t\tconst customHandler = fileTypeHandler.getFileHandler(name);\n\t\tif (customHandler) {\n\t\t\ttry {\n\t\t\t\tawait customHandler.handleFile({\n\t\t\t\t\tname,\n\t\t\t\t\turi,\n\t\t\t\t\tstats: fileInfo,\n\t\t\t\t\treadOnly,\n\t\t\t\t\toptions: {\n\t\t\t\t\t\tcursorPos,\n\t\t\t\t\t\trender,\n\t\t\t\t\t\tonsave,\n\t\t\t\t\t\tencoding,\n\t\t\t\t\t\tmode,\n\t\t\t\t\t\tcreateEditor,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`File handler '${customHandler.id}' failed:`, error);\n\t\t\t\t// Continue with default handling if custom handler fails\n\t\t\t}\n\t\t}\n\n\t\tif (text) {\n\t\t\t// If file is not opened and has unsaved text\n\t\t\tcreateEditor(true, text);\n\t\t\treturn;\n\t\t}\n\n\t\tconst videoRegex = /\\.(mp4|webm|ogg|mov|avi|wmv|flv|mkv|3gp)$/i;\n\t\tconst imageRegex = /\\.(jpe?g|png|gif|webp|bmp|ico|avif|apng|tiff?)$/i;\n\t\tconst audioRegex = /\\.(mp3|wav|ogg|m4a|aac|wma|flac|opus|3gp|mid|midi)$/i;\n\n\t\tif (videoRegex.test(name)) {\n\t\t\tconst objectUrl = await fileToDataUrl(uri);\n\t\t\tconst videoContainer = (\n\t\t\t\t<div\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\t\theight: \"100%\",\n\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\t\tjustifyContent: \"center\",\n\t\t\t\t\t}}\n\t\t\t\t></div>\n\t\t\t);\n\n\t\t\tconst videoEl = (\n\t\t\t\t<video\n\t\t\t\t\tsrc={objectUrl}\n\t\t\t\t\tcontrols\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tmaxWidth: \"100%\",\n\t\t\t\t\t\tmaxHeight: \"100%\",\n\t\t\t\t\t}}\n\t\t\t\t></video>\n\t\t\t);\n\n\t\t\tvideoContainer.append(videoEl);\n\n\t\t\tnew EditorFile(name, {\n\t\t\t\turi,\n\t\t\t\ttype: \"video\",\n\t\t\t\ttabIcon: \"file file_type_video\",\n\t\t\t\tcontent: videoContainer,\n\t\t\t\trender: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (imageRegex.test(name)) {\n\t\t\tconst objectUrl = await fileToDataUrl(uri);\n\t\t\tconst imageContainer = (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"image-container\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\t\theight: \"100%\",\n\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\t\tjustifyContent: \"center\",\n\t\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\tposition: \"relative\",\n\t\t\t\t\t}}\n\t\t\t\t></div>\n\t\t\t);\n\n\t\t\tconst imgEl = (\n\t\t\t\t<img\n\t\t\t\t\tsrc={objectUrl}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tmaxWidth: \"100%\",\n\t\t\t\t\t\tmaxHeight: \"100%\",\n\t\t\t\t\t\ttransformOrigin: \"center\",\n\t\t\t\t\t\ttransform: \"scale(1) translate(0px, 0px)\",\n\t\t\t\t\t\ttransition: \"none\",\n\t\t\t\t\t\tcursor: \"move\",\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t);\n\n\t\t\tlet scale = 1;\n\t\t\tlet startX = 0;\n\t\t\tlet startY = 0;\n\t\t\tlet translateX = 0;\n\t\t\tlet translateY = 0;\n\t\t\tlet lastX = 0;\n\t\t\tlet lastY = 0;\n\n\t\t\tfunction getBoundaries() {\n\t\t\t\tconst containerRect = imageContainer.getBoundingClientRect();\n\t\t\t\tconst imgRect = imgEl.getBoundingClientRect();\n\n\t\t\t\tconst maxX =\n\t\t\t\t\t(imgRect.width * scale - containerRect.width) / (2 * scale);\n\t\t\t\tconst maxY =\n\t\t\t\t\t(imgRect.height * scale - containerRect.height) / (2 * scale);\n\n\t\t\t\treturn {\n\t\t\t\t\tmaxX: Math.max(0, maxX),\n\t\t\t\t\tmaxY: Math.max(0, maxY),\n\t\t\t\t\tminX: -Math.max(0, maxX),\n\t\t\t\t\tminY: -Math.max(0, maxY),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tfunction constrainTranslation() {\n\t\t\t\tconst bounds = getBoundaries();\n\t\t\t\ttranslateX = Math.min(Math.max(translateX, bounds.minX), bounds.maxX);\n\t\t\t\ttranslateY = Math.min(Math.max(translateY, bounds.minY), bounds.maxY);\n\t\t\t}\n\n\t\t\t// Zoom with mouse wheel\n\t\t\timageContainer.addEventListener(\"wheel\", (e) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst delta = e.deltaY > 0 ? -0.1 : 0.1;\n\t\t\t\tconst oldScale = scale;\n\t\t\t\tscale = Math.max(0.1, Math.min(5, scale + delta));\n\n\t\t\t\t// Adjust translation to zoom toward mouse position\n\t\t\t\tconst rect = imgEl.getBoundingClientRect();\n\t\t\t\tconst mouseX = e.clientX - rect.left;\n\t\t\t\tconst mouseY = e.clientY - rect.top;\n\n\t\t\t\tconst scaleChange = scale / oldScale;\n\t\t\t\ttranslateX = mouseX - (mouseX - translateX) * scaleChange;\n\t\t\t\ttranslateY = mouseY - (mouseY - translateY) * scaleChange;\n\n\t\t\t\tconstrainTranslation();\n\t\t\t\timgEl.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;\n\t\t\t});\n\n\t\t\t// Pan image with mouse drag or touch\n\t\t\timageContainer.addEventListener(\"mousedown\", startDrag);\n\t\t\timageContainer.addEventListener(\"touchstart\", (e) => {\n\t\t\t\tif (e.touches.length === 1) {\n\t\t\t\t\tstartDrag(e.touches[0]);\n\t\t\t\t} else if (e.touches.length === 2) {\n\t\t\t\t\tconst touch1 = e.touches[0];\n\t\t\t\t\tconst touch2 = e.touches[1];\n\t\t\t\t\tstartX = Math.abs(touch1.clientX - touch2.clientX);\n\t\t\t\t\tstartY = Math.abs(touch1.clientY - touch2.clientY);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tfunction startDrag(e) {\n\t\t\t\tlastX = e.clientX;\n\t\t\t\tlastY = e.clientY;\n\t\t\t\tdocument.addEventListener(\"mousemove\", onDrag);\n\t\t\t\tdocument.addEventListener(\"mouseup\", stopDrag);\n\t\t\t\tdocument.addEventListener(\"touchmove\", onTouchDrag);\n\t\t\t\tdocument.addEventListener(\"touchend\", stopDrag);\n\t\t\t}\n\n\t\t\tfunction onDrag(e) {\n\t\t\t\tconst deltaX = e.clientX - lastX;\n\t\t\t\tconst deltaY = e.clientY - lastY;\n\t\t\t\ttranslateX += deltaX / scale;\n\t\t\t\ttranslateY += deltaY / scale;\n\t\t\t\tlastX = e.clientX;\n\t\t\t\tlastY = e.clientY;\n\t\t\t\tconstrainTranslation();\n\t\t\t\timgEl.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;\n\t\t\t}\n\n\t\t\tfunction onTouchDrag(e) {\n\t\t\t\tif (e.touches.length === 1) {\n\t\t\t\t\tconst touch = e.touches[0];\n\t\t\t\t\tconst deltaX = touch.clientX - lastX;\n\t\t\t\t\tconst deltaY = touch.clientY - lastY;\n\t\t\t\t\ttranslateX += deltaX / scale;\n\t\t\t\t\ttranslateY += deltaY / scale;\n\t\t\t\t\tlastX = touch.clientX;\n\t\t\t\t\tlastY = touch.clientY;\n\t\t\t\t\tconstrainTranslation();\n\t\t\t\t\timgEl.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;\n\t\t\t\t} else if (e.touches.length === 2) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tconst touch1 = e.touches[0];\n\t\t\t\t\tconst touch2 = e.touches[1];\n\t\t\t\t\tconst currentX = Math.abs(touch1.clientX - touch2.clientX);\n\t\t\t\t\tconst currentY = Math.abs(touch1.clientY - touch2.clientY);\n\n\t\t\t\t\tconst startDist = Math.sqrt(startX * startX + startY * startY);\n\t\t\t\t\tconst currentDist = Math.sqrt(\n\t\t\t\t\t\tcurrentX * currentX + currentY * currentY,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst delta = (currentDist - startDist) / 100;\n\t\t\t\t\tscale = Math.max(0.1, Math.min(5, scale + delta));\n\t\t\t\t\tconstrainTranslation();\n\t\t\t\t\timgEl.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;\n\n\t\t\t\t\tstartX = currentX;\n\t\t\t\t\tstartY = currentY;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction stopDrag() {\n\t\t\t\tdocument.removeEventListener(\"mousemove\", onDrag);\n\t\t\t\tdocument.removeEventListener(\"mouseup\", stopDrag);\n\t\t\t\tdocument.removeEventListener(\"touchmove\", onTouchDrag);\n\t\t\t\tdocument.removeEventListener(\"touchend\", stopDrag);\n\t\t\t}\n\n\t\t\timageContainer.append(imgEl);\n\n\t\t\tnew EditorFile(name, {\n\t\t\t\turi,\n\t\t\t\ttype: \"image\",\n\t\t\t\ttabIcon: \"file file_type_image\",\n\t\t\t\tcontent: imageContainer,\n\t\t\t\trender: true,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (audioRegex.test(name)) {\n\t\t\tconst objectUrl = await fileToDataUrl(uri);\n\t\t\tconst audioContainer = (\n\t\t\t\t<div\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\t\theight: \"100%\",\n\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\t\tjustifyContent: \"center\",\n\t\t\t\t\t}}\n\t\t\t\t></div>\n\t\t\t);\n\n\t\t\tconst audioPlayer = new AudioPlayer(audioContainer);\n\t\t\taudioPlayer.loadTrack(objectUrl);\n\n\t\t\tconst audioTab = new EditorFile(name, {\n\t\t\t\turi,\n\t\t\t\ttype: \"audio\",\n\t\t\t\ttabIcon: \"file file_type_audio\",\n\t\t\t\tcontent: audioPlayer.container,\n\t\t\t\trender: true,\n\t\t\t});\n\t\t\taudioTab.onclose = () => {\n\t\t\t\taudioPlayer.cleanup();\n\t\t\t};\n\t\t\treturn;\n\t\t}\n\n\t\t// Else open a new file\n\t\t// Checks for valid file\n\t\tif (fileInfo.length * 0.000001 > settings.maxFileSize) {\n\t\t\treturn alert(\n\t\t\t\tstrings.error.toUpperCase(),\n\t\t\t\tstrings[\"file too large\"].replace(\n\t\t\t\t\t\"{size}\",\n\t\t\t\t\tsettings.maxFileSize + \"MB\",\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\tif (helpers.isBinary(uri)) {\n\t\t\tconst confirmation = await confirm(strings.info, strings[\"binary file\"]);\n\t\t\tif (!confirmation) return;\n\t\t}\n\n\t\tconst binData = await fs.readFile();\n\n\t\t// Determine encoding: if explicit provided use it, otherwise\n\t\t// if settings.defaultFileEncoding === 'auto' then detect; else use the default as-is\n\t\tlet detectedEncoding = file.encoding || encoding;\n\t\tif (!detectedEncoding) {\n\t\t\tconst defaultSetting = appSettings.value.defaultFileEncoding;\n\t\t\tif (defaultSetting === \"auto\") {\n\t\t\t\ttry {\n\t\t\t\t\tdetectedEncoding = await detectEncoding(binData);\n\t\t\t\t\tif (detectedEncoding === \"auto\") detectedEncoding = \"UTF-8\";\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(\"Encoding detection failed, using UTF-8:\", error);\n\t\t\t\t\tdetectedEncoding = \"UTF-8\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdetectedEncoding = defaultSetting || \"UTF-8\";\n\t\t\t}\n\t\t}\n\n\t\tconst fileContent = await decode(binData, detectedEncoding);\n\n\t\tcreateEditor(false, fileContent, detectedEncoding);\n\t\tif (mode !== \"single\") recents.addFile(uri);\n\t\treturn;\n\t} catch (error) {\n\t\tconsole.error(error);\n\t} finally {\n\t\tloader.removeTitleLoader();\n\t}\n}\n\n/**\n * Converts file to data url\n * @param {string} file file url\n */\nasync function fileToDataUrl(file) {\n\tconst fs = fsOperation(file);\n\tconst fileInfo = await fs.stat();\n\tconst binData = await fs.readFile();\n\treturn URL.createObjectURL(new Blob([binData], { type: fileInfo.mime }));\n}\n"
  },
  {
    "path": "src/lib/openFolder.js",
    "content": "import fsOperation from \"fileSystem\";\nimport sidebarApps from \"sidebarApps\";\nimport collapsableList from \"components/collapsableList\";\nimport FileTree from \"components/fileTree\";\nimport Sidebar from \"components/sidebar\";\nimport { TerminalManager } from \"components/terminal\";\nimport tile from \"components/tile\";\nimport toast from \"components/toast\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport escapeStringRegexp from \"escape-string-regexp\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport helpers from \"utils/helpers\";\nimport Path from \"utils/Path\";\nimport Uri from \"utils/Uri\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\nimport * as FileList from \"./fileList\";\nimport openFile from \"./openFile\";\nimport recents from \"./recents\";\nimport appSettings from \"./settings\";\n\nconst isTermuxSafUri = (value = \"\") =>\n\tvalue.startsWith(\"content://com.termux.documents/tree/\");\nconst isAcodeTerminalPublicSafUri = (value = \"\") =>\n\tvalue.startsWith(\"content://com.foxdebug.acode.documents/tree/\");\nconst isTerminalSafUri = (value = \"\") =>\n\tisTermuxSafUri(value) || isAcodeTerminalPublicSafUri(value);\n\nconst getTerminalPaths = () => {\n\tconst packageName = window.BuildInfo?.packageName || \"com.foxdebug.acode\";\n\tconst dataDir = `/data/user/0/${packageName}`;\n\tconst alpineRoot = `${dataDir}/files/alpine`;\n\tconst publicDir = `${dataDir}/files/public`;\n\treturn { alpineRoot, publicDir, dataDir };\n};\n\nconst isTerminalAccessiblePath = (url = \"\") => {\n\tif (isAcodeTerminalPublicSafUri(url)) return true;\n\tconst { alpineRoot, publicDir } = getTerminalPaths();\n\tconst cleanUrl = url.replace(/^file:\\/\\//, \"\");\n\tif (cleanUrl.startsWith(alpineRoot) || cleanUrl.startsWith(publicDir)) {\n\t\treturn true;\n\t}\n\treturn false;\n};\n\nconst convertToProotPath = (url = \"\") => {\n\tconst { alpineRoot, publicDir } = getTerminalPaths();\n\tif (isAcodeTerminalPublicSafUri(url)) {\n\t\ttry {\n\t\t\tconst { docId } = Uri.parse(url);\n\t\t\tconst cleanDocId = /::/.test(url)\n\t\t\t\t? decodeURIComponent(docId || \"\")\n\t\t\t\t: docId || \"\";\n\t\t\tif (!cleanDocId) return \"/public\";\n\t\t\tif (cleanDocId.startsWith(publicDir)) {\n\t\t\t\treturn cleanDocId.replace(publicDir, \"/public\") || \"/public\";\n\t\t\t}\n\t\t\tif (cleanDocId.startsWith(\"/public\")) {\n\t\t\t\treturn cleanDocId;\n\t\t\t}\n\t\t\tif (cleanDocId.startsWith(\"public:\")) {\n\t\t\t\tconst relativePath = cleanDocId.slice(\"public:\".length);\n\t\t\t\treturn relativePath ? Path.join(\"/public\", relativePath) : \"/public\";\n\t\t\t}\n\t\t\tconst relativePath = cleanDocId\n\t\t\t\t.replace(/^\\/+/, \"\")\n\t\t\t\t.replace(/^public\\//, \"\");\n\t\t\treturn relativePath ? Path.join(\"/public\", relativePath) : \"/public\";\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to parse public SAF URI for terminal conversion: ${url}`,\n\t\t\t);\n\t\t\treturn \"/public\";\n\t\t}\n\t}\n\tconst cleanUrl = url.replace(/^file:\\/\\//, \"\");\n\tif (cleanUrl.startsWith(publicDir)) {\n\t\treturn cleanUrl.replace(publicDir, \"/public\");\n\t}\n\tif (cleanUrl.startsWith(alpineRoot)) {\n\t\treturn cleanUrl.replace(alpineRoot, \"\") || \"/\";\n\t}\n\tconsole.warn(`Unrecognized path for terminal conversion: ${url}`);\n\treturn cleanUrl;\n};\n\n/**\n * @typedef {import('../components/collapsableList').Collapsible} Collapsible\n */\n\n/**\n * @typedef {object} ClipBoard\n * @property {string} url\n * @property {HTMLElement} $el\n * @property {\"cut\"|\"copy\"} action\n */\n\n/**\n * @typedef {object} Folder\n * @property {string} id\n * @property {string} url\n * @property {string} title\n * @property {boolean} listFiles Weather to list all files recursively\n * @property {boolean} saveState\n * @property {Collapsible} $node\n * @property {ClipBoard} clipBoard\n * @property {function(): void} remove\n * @property {function(): void} reload\n * @property {Map<string, boolean>} listState\n */\n\n/**@type {Folder[]} */\nexport const addedFolder = [];\nconst ACODE_PLUGIN_MANIFEST_FILE = \"plugin.json\";\n/**\n * Open a folder in the sidebar\n * @param {string} _path\n * @param {object} opts\n * @param {string} opts.name\n * @param {string} [opts.id]\n * @param {boolean} [opts.saveState]\n * @param {boolean} [opts.listFiles]\n * @param {Map<string, boolean>} [opts.listState]\n */\nfunction openFolder(_path, opts = {}) {\n\tif (addedFolder.find((folder) => folder.url === _path)) {\n\t\treturn;\n\t}\n\n\tconst saveState = opts.saveState ?? true;\n\tconst listState = opts.listState || {};\n\tconst title = opts.name;\n\tlet listFiles = opts.listFiles;\n\n\tif (!title) {\n\t\tthrow new Error(\"Folder name is required\");\n\t}\n\n\tconst $root = collapsableList(title, \"folder\", {\n\t\tallCaps: true,\n\t\tontoggle: () => expandList($root),\n\t});\n\tconst $text = $root.$title.get(\":scope>span.text\");\n\n\t$root.id = \"r\" + _path.hashCode();\n\t$text.style.overflow = \"hidden\";\n\t$text.style.whiteSpace = \"nowrap\";\n\t$text.style.textOverflow = \"ellipsis\";\n\t$root.$title.dataset.type = \"root\";\n\t$root.$title.dataset.url = _path;\n\t$root.$title.dataset.name = title;\n\n\t$root.$ul.onclick =\n\t\t$root.$ul.oncontextmenu =\n\t\t$root.$title.onclick =\n\t\t$root.$title.oncontextmenu =\n\t\t\thandleItems;\n\n\trecents.addFolder(_path, opts);\n\tsidebarApps.get(\"files\").append($root);\n\n\tconst event = {\n\t\turl: _path,\n\t\tname: title,\n\t};\n\n\tconst folder = {\n\t\ttitle,\n\t\tremove,\n\t\tlistFiles,\n\t\tsaveState,\n\t\tlistState,\n\t\turl: _path,\n\t\t$node: $root,\n\t\tid: opts.id,\n\t\tclipBoard: {},\n\t\treload() {\n\t\t\t$root.collapse();\n\t\t\t$root.expand();\n\t\t},\n\t};\n\n\teditorManager.emit(\"update\", \"add-folder\");\n\teditorManager.onupdate(\"add-folder\", event);\n\teditorManager.emit(\"add-folder\", event);\n\n\t(async () => {\n\t\tif (typeof listFiles !== \"boolean\") {\n\t\t\tconst protocol = Url.getProtocol(_path).slice(0, -1);\n\t\t\tconst type = /^(content|file)$/.test(protocol) ? \"\" : ` (${protocol})`;\n\t\t\tconst message = strings[\"list files\"].replace(\n\t\t\t\t\"{name}\",\n\t\t\t\t`${title}${type}`,\n\t\t\t);\n\t\t\tlistFiles = await confirm(strings.confirm, message, true);\n\t\t}\n\n\t\tif (listFiles) {\n\t\t\tFileList.addRoot({ url: _path, name: title });\n\t\t}\n\n\t\tfolder.listFiles = listFiles;\n\t\taddedFolder.push(folder);\n\t})();\n\n\tif (listState[_path]) {\n\t\t$root.expand();\n\t}\n\n\tfunction remove(e) {\n\t\tif (e) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tif ($root.parentElement) {\n\t\t\t$root.remove();\n\t\t}\n\n\t\tconst index = addedFolder.findIndex((folder) => folder.url === _path);\n\t\tif (index !== -1) addedFolder.splice(index, 1);\n\t\teditorManager.emit(\"update\", \"remove-folder\");\n\t\teditorManager.onupdate(\"remove-folder\", event);\n\t\teditorManager.emit(\"remove-folder\", event);\n\t}\n}\n\n/**\n * Expand the list\n * @param {Collapsible} $list\n */\nasync function expandList($list) {\n\tconst { $ul, $title } = $list;\n\tconst { url } = $title.dataset;\n\n\tconst { saveState, listState, $node } = openFolder.find(url);\n\tconst startLoading = () => $node.$title.classList.add(\"loading\");\n\tconst stopLoading = () => $node.$title.classList.remove(\"loading\");\n\n\tif (!$ul) return;\n\n\t// Cleanup existing file tree\n\tif ($ul._fileTree) {\n\t\t$ul._fileTree.destroy();\n\t\t$ul._fileTree = null;\n\t}\n\t$ul.innerHTML = \"\";\n\n\tif (saveState) listState[url] = $list.unclasped;\n\tif (!$list.unclasped) return;\n\n\ttry {\n\t\tstartLoading();\n\n\t\tconst fileTree = new FileTree($ul, {\n\t\t\tgetEntries: (dirUrl) => fsOperation(dirUrl).lsDir(),\n\t\t\texpandedState: listState,\n\t\t\tonExpandedChange: (folderUrl, isExpanded) => {\n\t\t\t\tif (saveState) listState[folderUrl] = isExpanded;\n\t\t\t},\n\t\t\tonFileClick: (fileUrl) => {\n\t\t\t\thandleClick(\"file\", fileUrl);\n\t\t\t},\n\t\t\tonContextMenu: (type, itemUrl, name, $target) => {\n\t\t\t\thandleContextmenu(type, itemUrl, name, $target);\n\t\t\t},\n\t\t});\n\n\t\tawait fileTree.load(url);\n\t\t$ul._fileTree = fileTree;\n\t} catch (err) {\n\t\t$list.collapse();\n\t\tif (err?.includes?.(\"Invalid message length\")) {\n\t\t\tconsole.error(err);\n\t\t\ttoast(\"SFTP connection broken. Restart the app\");\n\t\t\treturn;\n\t\t}\n\t\thelpers.error(err);\n\t} finally {\n\t\tstopLoading();\n\t}\n}\n\n/**\n * Gets weather the folder is collapsed or not\n * @param {HTMLElement} $el\n * @param {boolean} isFile\n * @returns\n */\nfunction collapsed($el, isFile) {\n\tif (!$el.isConnected) return true;\n\t$el = $el.parentElement;\n\tif (!isFile) {\n\t\t$el = $el.parentElement;\n\t}\n\n\treturn $el.previousElementSibling.collapsed;\n}\n\n/**\n * Handle click event\n * @param {Event} e\n */\nfunction handleItems(e) {\n\tconst mode = e.type;\n\tconst $target = e.target;\n\tif (!($target instanceof HTMLElement)) return;\n\tconst type = $target.dataset.type;\n\tif (!type) return;\n\tconst url = $target.dataset.url;\n\tconst name = $target.dataset.name;\n\n\tif (mode === \"click\") {\n\t\thandleClick(type, url, name, $target);\n\t} else if (mode === \"contextmenu\") {\n\t\thandleContextmenu(type, url, name, $target);\n\t}\n}\n\n/**\n * Handle contextmenu\n * @param {\"file\"|\"dir\"|\"root\"} type\n * @param {string} url\n * @param {string} name\n * @param {HTMLElement} $target\n */\nasync function handleContextmenu(type, url, name, $target) {\n\tif (appSettings.value.vibrateOnTap) {\n\t\tnavigator.vibrate(constants.VIBRATION_TIME);\n\t}\n\tconst { clipBoard, $node } = openFolder.find(url);\n\tconst cancel = `${strings.cancel}${clipBoard ? ` (${strings[clipBoard.action]})` : \"\"}`;\n\tconst COPY = [\"copy\", strings.copy, \"copy\"];\n\tconst CUT = [\"cut\", strings.cut, \"cut\"];\n\tconst COPY_RELATIVE_PATH = [\n\t\t\"copy-relative-path\",\n\t\tstrings[\"copy relative path\"],\n\t\t\"attach_file\",\n\t];\n\tconst REMOVE = [\"delete\", strings.delete, \"delete\"];\n\tconst RENAME = [\"rename\", strings.rename, \"edit\"];\n\tconst PASTE = [\"paste\", strings.paste, \"paste\", !!clipBoard];\n\tconst NEW_FILE = [\"new file\", strings[\"new file\"], \"document-add\"];\n\tconst NEW_FOLDER = [\"new folder\", strings[\"new folder\"], \"folder-add\"];\n\tconst CANCEL = [\"cancel\", cancel, \"clearclose\"];\n\tconst OPEN_FOLDER = [\"open-folder\", strings[\"open folder\"], \"folder\"];\n\tconst INSERT_FILE = [\"insert-file\", strings[\"insert file\"], \"file_copy\"];\n\tconst CLOSE_FOLDER = [\"close\", strings[\"close\"], \"folder-remove\"];\n\tconst INSTALL_PLUGIN = [\n\t\t\"install-plugin\",\n\t\tstrings[\"install as plugin\"] || \"Install as Plugin\",\n\t\t\"extension\",\n\t];\n\n\tlet options;\n\n\tif (helpers.isFile(type)) {\n\t\toptions = [COPY, CUT, COPY_RELATIVE_PATH, RENAME, REMOVE];\n\t\tif (\n\t\t\turl.toLowerCase().endsWith(\".zip\") &&\n\t\t\t(await fsOperation(\n\t\t\t\tUrl.dirname(url) + ACODE_PLUGIN_MANIFEST_FILE,\n\t\t\t).exists())\n\t\t) {\n\t\t\toptions.push(INSTALL_PLUGIN);\n\t\t}\n\t} else if (helpers.isDir(type)) {\n\t\toptions = [COPY, CUT, COPY_RELATIVE_PATH, REMOVE, RENAME];\n\n\t\tif (clipBoard.url != null) {\n\t\t\toptions.push(PASTE);\n\t\t}\n\n\t\toptions.push(NEW_FILE, NEW_FOLDER, OPEN_FOLDER, INSERT_FILE);\n\n\t\tif (isTerminalAccessiblePath(url)) {\n\t\t\tconst OPEN_IN_TERMINAL = [\n\t\t\t\t\"open-in-terminal\",\n\t\t\t\tstrings[\"open in terminal\"] || \"Open in Terminal\",\n\t\t\t\t\"terminal\",\n\t\t\t];\n\t\t\toptions.push(OPEN_IN_TERMINAL);\n\t\t}\n\t} else if (type === \"root\") {\n\t\toptions = [];\n\n\t\tif (clipBoard.url != null) {\n\t\t\toptions.push(PASTE);\n\t\t}\n\n\t\toptions.push(NEW_FILE, NEW_FOLDER, INSERT_FILE);\n\n\t\tif (isTerminalAccessiblePath(url)) {\n\t\t\tconst OPEN_IN_TERMINAL = [\n\t\t\t\t\"open-in-terminal\",\n\t\t\t\tstrings[\"open in terminal\"] || \"Open in Terminal\",\n\t\t\t\t\"terminal\",\n\t\t\t];\n\t\t\toptions.push(OPEN_IN_TERMINAL);\n\t\t}\n\n\t\toptions.push(CLOSE_FOLDER);\n\t}\n\n\tif (clipBoard.action) options.push(CANCEL);\n\n\ttry {\n\t\tconst option = await select(name, options);\n\t\tawait execOperation(type, option, url, $target, name);\n\t} catch (error) {\n\t\tconsole.error(error);\n\t\thelpers.error(error);\n\t} finally {\n\t\t$node.$title.classList.remove(\"loading\");\n\t}\n}\n\n/**\n * @param {\"dir\"|\"file\"|\"root\"} type\n * @param {\"copy\"|\"cut\"|\"delete\"|\"rename\"|\"paste\"|\"new file\"|\"new folder\"|\"cancel\"|\"open-folder\"|\"install-plugin\"} action\n * @param {string} url target url\n * @param {HTMLElement} $target target element\n * @param {string} name Name of file or folder\n */\nfunction execOperation(type, action, url, $target, name) {\n\tconst { clipBoard, $node, remove, url: rootUrl } = openFolder.find(url);\n\tconst startLoading = () => $node.$title.classList.add(\"loading\");\n\tconst stopLoading = () => $node.$title.classList.remove(\"loading\");\n\n\tswitch (action) {\n\t\tcase \"copy\":\n\t\tcase \"cut\":\n\t\t\treturn clipBoardAction();\n\n\t\tcase \"delete\":\n\t\t\treturn deleteFile();\n\n\t\tcase \"rename\":\n\t\t\treturn renameFile();\n\n\t\tcase \"paste\":\n\t\t\treturn paste();\n\n\t\tcase \"new file\":\n\t\tcase \"new folder\":\n\t\t\treturn createNew();\n\n\t\tcase \"cancel\":\n\t\t\treturn cancelAction();\n\n\t\tcase \"open-folder\":\n\t\t\treturn open();\n\n\t\tcase \"insert-file\":\n\t\t\treturn insertFile();\n\n\t\tcase \"close\":\n\t\t\treturn remove();\n\n\t\tcase \"install-plugin\":\n\t\t\treturn installPlugin();\n\n\t\tcase \"open-in-terminal\":\n\t\t\treturn openInTerminal();\n\n\t\tcase \"copy-relative-path\":\n\t\t\treturn copyRelativePath();\n\t}\n\n\tasync function installPlugin() {\n\t\ttry {\n\t\t\tconst manifest = JSON.parse(\n\t\t\t\tawait fsOperation(\n\t\t\t\t\tUrl.dirname(url) + ACODE_PLUGIN_MANIFEST_FILE,\n\t\t\t\t).readFile(\"utf8\"),\n\t\t\t);\n\t\t\tconst { default: installPlugin } = await import(\"lib/installPlugin\");\n\t\t\tawait installPlugin(url, manifest.name);\n\t\t\ttoast(strings[\"success\"], 3000);\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t\tconsole.error(error);\n\t\t}\n\t}\n\n\tasync function copyRelativePath() {\n\t\ttry {\n\t\t\t// Validate inputs\n\t\t\tif (!url) {\n\t\t\t\tconsole.error(\"File path not available\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!rootUrl) {\n\t\t\t\tconsole.error(\"Root folder not found\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet relativePath;\n\n\t\t\t// Try using Url.pathname for protocol-based URLs\n\t\t\tconst rootPath = Url.pathname(rootUrl);\n\t\t\tconst targetPath = Url.pathname(url);\n\n\t\t\tif (rootPath && targetPath) {\n\t\t\t\t// Both pathnames extracted successfully\n\t\t\t\trelativePath = Path.convertToRelative(rootPath, targetPath);\n\t\t\t} else {\n\t\t\t\t// Fallback: Use simple string comparison for URIs where pathname extraction fails\n\t\t\t\tconst cleanRoot = rootUrl.endsWith(\"/\")\n\t\t\t\t\t? rootUrl.slice(0, -1)\n\t\t\t\t\t: rootUrl;\n\t\t\t\tconst cleanTarget = url.endsWith(\"/\") ? url.slice(0, -1) : url;\n\n\t\t\t\t// Check if target URL starts with root URL\n\t\t\t\tif (cleanTarget.startsWith(cleanRoot)) {\n\t\t\t\t\trelativePath = cleanTarget.slice(cleanRoot.length + 1);\n\t\t\t\t} else {\n\t\t\t\t\t// If not a child path, just use basename\n\t\t\t\t\trelativePath = Url.basename(url);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!relativePath) {\n\t\t\t\tconsole.error(\"Unable to calculate relative path\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (cordova.plugins.clipboard) {\n\t\t\t\tcordova.plugins.clipboard.copy(relativePath);\n\t\t\t\ttoast(strings.success || \"Relative path copied to clipboard\");\n\t\t\t} else {\n\t\t\t\tconsole.error(\"Clipboard not available\");\n\t\t\t\ttoast(\"Clipboard not available\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to copy relative path:\", error);\n\t\t}\n\t}\n\n\tasync function openInTerminal() {\n\t\ttry {\n\t\t\tconst prootPath = convertToProotPath(url);\n\t\t\tconst terminal = await TerminalManager.createTerminal({\n\t\t\t\tname: `Terminal - ${name}`,\n\t\t\t\trender: true,\n\t\t\t});\n\t\t\tif (terminal?.component) {\n\t\t\t\tconst waitForConnection = (timeoutMs = 5000) =>\n\t\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\t\tconst startTime = Date.now();\n\t\t\t\t\t\tconst check = () => {\n\t\t\t\t\t\t\tif (terminal.component.isConnected) {\n\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t} else if (Date.now() - startTime > timeoutMs) {\n\t\t\t\t\t\t\t\treject(new Error(\"Terminal connection timeout\"));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsetTimeout(check, 50);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t\tcheck();\n\t\t\t\t\t});\n\t\t\t\tawait waitForConnection();\n\t\t\t\tterminal.component.write(`cd ${JSON.stringify(prootPath)}\\n`);\n\t\t\t\tSidebar.hide();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to open terminal:\", error);\n\t\t\tconst errorMsg = error.message || \"Unknown error occurred\";\n\t\t\ttoast(`Failed to open terminal: ${errorMsg}`);\n\t\t}\n\t}\n\n\tasync function deleteFile() {\n\t\tconst msg = strings[\"delete entry\"].replace(\"{name}\", name);\n\t\tconst confirmation = await confirm(strings.warning, msg);\n\t\tif (!confirmation) return;\n\t\tstartLoading();\n\t\tif (!(await fsOperation(url).exists())) return;\n\t\t// await fsOperation(url).delete();\n\t\trecents.removeFile(url);\n\t\tif (helpers.isFile(type)) {\n\t\t\tawait fsOperation(url).delete();\n\t\t\t$target.remove();\n\t\t\tconst file = editorManager.getFile(url, \"uri\");\n\t\t\tif (file) file.uri = null;\n\t\t\teditorManager.onupdate(\"delete-file\");\n\t\t\teditorManager.emit(\"update\", \"delete-file\");\n\t\t} else {\n\t\t\tif (isTerminalSafUri(url)) {\n\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\tconst entries = await fs.lsDir();\n\t\t\t\tif (entries.length === 0) {\n\t\t\t\t\tawait fs.delete();\n\t\t\t\t} else {\n\t\t\t\t\tconst deleteRecursively = async (currentUrl) => {\n\t\t\t\t\t\tconst currentFs = fsOperation(currentUrl);\n\t\t\t\t\t\tconst currentEntries = await currentFs.lsDir();\n\t\t\t\t\t\tfor (const entry of currentEntries) {\n\t\t\t\t\t\t\tif (entry.isDirectory) {\n\t\t\t\t\t\t\t\tawait deleteRecursively(entry.url);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tawait fsOperation(entry.url).delete();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait currentFs.delete();\n\t\t\t\t\t};\n\t\t\t\t\tawait deleteRecursively(url);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tawait fsOperation(url).delete();\n\t\t\t}\n\t\t\trecents.removeFolder(url);\n\t\t\thelpers.updateUriOfAllActiveFiles(url, null);\n\t\t\t$target.parentElement.remove();\n\t\t\teditorManager.onupdate(\"delete-folder\");\n\t\t\teditorManager.emit(\"update\", \"delete-folder\");\n\t\t}\n\n\t\ttoast(strings.success);\n\t\tFileList.remove(url);\n\t}\n\n\tasync function renameFile() {\n\t\tif (isTermuxSafUri(url) && !helpers.isFile(type)) {\n\t\t\talert(strings.warning, strings[\"rename not supported\"]);\n\t\t\treturn;\n\t\t}\n\t\tlet newName = await prompt(strings.rename, name, \"text\", {\n\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\trequired: true,\n\t\t});\n\n\t\tnewName = helpers.fixFilename(newName);\n\t\tif (!newName || newName === name) return;\n\n\t\tstartLoading();\n\t\tconst fs = fsOperation(url);\n\t\tlet newUrl;\n\n\t\tif (isTermuxSafUri(url) && helpers.isFile(type)) {\n\t\t\t// Special handling for Termux SAF content files\n\t\t\tconst newFilePath = Url.join(Url.dirname(url), newName);\n\t\t\tconst content = await fs.readFile();\n\t\t\tawait fsOperation(Url.dirname(url)).createFile(newName, content);\n\t\t\tawait fs.delete();\n\t\t\tnewUrl = newFilePath;\n\t\t} else {\n\t\t\tnewUrl = await fs.renameTo(newName);\n\t\t}\n\n\t\tnewName = Url.basename(newUrl);\n\t\t$target.querySelector(\":scope>.text\").textContent = newName;\n\t\t$target.dataset.url = newUrl;\n\t\t$target.dataset.name = newName;\n\t\tif (helpers.isFile(type)) {\n\t\t\t$target.querySelector(\":scope>span\").className =\n\t\t\t\thelpers.getIconForFile(newName);\n\t\t\tlet file = editorManager.getFile(url, \"uri\");\n\t\t\tif (file) {\n\t\t\t\tfile.uri = newUrl;\n\t\t\t\tfile.filename = newName;\n\t\t\t}\n\t\t} else {\n\t\t\thelpers.updateUriOfAllActiveFiles(url, newUrl);\n\t\t\t//Reloading the folder by collapsing and expanding the folder\n\t\t\t$target.click(); //collapse\n\t\t\t$target.click(); //expand\n\t\t}\n\t\ttoast(strings.success);\n\t\tFileList.rename(url, newUrl);\n\t}\n\n\tasync function createNew() {\n\t\tconst msg =\n\t\t\taction === \"new file\"\n\t\t\t\t? strings[\"enter file name\"]\n\t\t\t\t: strings[\"enter folder name\"];\n\n\t\tlet newName = await prompt(msg, \"\", \"text\", {\n\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\trequired: true,\n\t\t});\n\n\t\tnewName = helpers.fixFilename(newName);\n\t\tif (!newName) return;\n\t\tstartLoading();\n\t\ttry {\n\t\t\tconst isNestedPath = newName.split(\"/\").filter(Boolean).length > 1;\n\t\t\tlet newUrl;\n\n\t\t\tif (action === \"new file\") {\n\t\t\t\tnewUrl = await helpers.createFileStructure(url, newName);\n\t\t\t} else {\n\t\t\t\tnewUrl = await helpers.createFileStructure(url, newName, false);\n\t\t\t}\n\t\t\tif (!newUrl.created) return;\n\n\t\t\tif (isNestedPath) {\n\t\t\t\tawait refreshOpenFolder(url);\n\t\t\t\tawait FileList.refresh();\n\t\t\t\ttoast(strings.success);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tnewName = Url.basename(newUrl.uri);\n\t\t\tappendEntryToOpenFolder(url, newUrl.uri, newUrl.type);\n\n\t\t\tFileList.append(url, newUrl.uri);\n\t\t\ttoast(strings.success);\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t} finally {\n\t\t\tstopLoading();\n\t\t}\n\t}\n\n\tasync function paste() {\n\t\tif (clipBoard.url == null) {\n\t\t\talert(strings.warning, \"Nothing to paste\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Prevent pasting a folder into itself or its subdirectories\n\t\tif (helpers.isDir(clipBoard.$el.dataset.type)) {\n\t\t\tconst sourceUrl = Url.parse(clipBoard.url).url;\n\t\t\tconst targetUrl = Url.parse(url).url;\n\n\t\t\t// Check if trying to paste folder into itself\n\t\t\tif (sourceUrl === targetUrl) {\n\t\t\t\talert(strings.warning, \"Cannot paste a folder into itself\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check if trying to paste folder into one of its subdirectories\n\t\t\tif (\n\t\t\t\ttargetUrl.startsWith(sourceUrl + \"/\") ||\n\t\t\t\ttargetUrl.startsWith(sourceUrl + \"\\\\\")\n\t\t\t) {\n\t\t\t\talert(strings.warning, \"Cannot paste a folder into its subdirectory\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet CASE = \"\";\n\t\tconst $src = clipBoard.$el;\n\t\tconst srcType = $src.dataset.type;\n\t\tconst IS_FILE = helpers.isFile(srcType);\n\t\tconst IS_DIR = helpers.isDir(srcType);\n\t\tconst srcCollapsed = collapsed($src, IS_FILE);\n\n\t\tCASE += IS_FILE ? 1 : 0;\n\t\tCASE += srcCollapsed ? 1 : 0;\n\t\tCASE += $target.collapsed ? 1 : 0;\n\n\t\tstartLoading();\n\t\ttry {\n\t\t\tconst fs = fsOperation(clipBoard.url);\n\t\t\tconst itemName = Url.basename(clipBoard.url);\n\t\t\tconst possibleConflictUrl = Url.join(url, itemName);\n\t\t\tconst doesExist = await fsOperation(possibleConflictUrl).exists();\n\t\t\tif (doesExist) {\n\t\t\t\tlet confirmation = await confirm(\n\t\t\t\t\tstrings.warning,\n\t\t\t\t\tstrings[\"already exists\"]\n\t\t\t\t\t\t? strings[\"already exists\"].replace(\"{name}\", itemName)\n\t\t\t\t\t\t: `\"${itemName}\" already exists in this location.`,\n\t\t\t\t);\n\t\t\t\tif (!confirmation) return;\n\t\t\t}\n\t\t\tlet newUrl;\n\t\t\tif (clipBoard.action === \"cut\") {\n\t\t\t\t// Special handling for SAF folders backed by terminal providers - move manually due to SAF limitations\n\t\t\t\tif (isTerminalSafUri(clipBoard.url) && IS_DIR) {\n\t\t\t\t\tconst moveRecursively = async (sourceUrl, targetParentUrl) => {\n\t\t\t\t\t\tconst sourceFs = fsOperation(sourceUrl);\n\t\t\t\t\t\tconst sourceName = Url.basename(sourceUrl);\n\t\t\t\t\t\tconst targetUrl = Url.join(targetParentUrl, sourceName);\n\n\t\t\t\t\t\t// Create target folder\n\t\t\t\t\t\tawait fsOperation(targetParentUrl).createDirectory(sourceName);\n\n\t\t\t\t\t\t// Get all entries in source folder\n\t\t\t\t\t\tconst entries = await sourceFs.lsDir();\n\n\t\t\t\t\t\t// Move all files and folders recursively\n\t\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\t\tif (entry.isDirectory) {\n\t\t\t\t\t\t\t\tawait moveRecursively(entry.url, targetUrl);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst fileContent = await fsOperation(entry.url).readFile();\n\t\t\t\t\t\t\t\tconst fileName = entry.name || Url.basename(entry.url);\n\t\t\t\t\t\t\t\tawait fsOperation(targetUrl).createFile(fileName, fileContent);\n\t\t\t\t\t\t\t\tawait fsOperation(entry.url).delete();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Delete the now-empty source folder\n\t\t\t\t\t\tawait sourceFs.delete();\n\t\t\t\t\t\treturn targetUrl;\n\t\t\t\t\t};\n\n\t\t\t\t\tnewUrl = await moveRecursively(clipBoard.url, url);\n\t\t\t\t} else {\n\t\t\t\t\tnewUrl = await fs.moveTo(url);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnewUrl = await fs.copyTo(url);\n\t\t\t}\n\t\t\tconst { name: newName } = await fsOperation(newUrl).stat();\n\t\t\tstopLoading();\n\t\t\t/**\n\t\t\t * CASES:\n\t\t\t * CASE 111: src is file and parent is collapsed where target is also collapsed\n\t\t\t * CASE 110: src is file and parent is collapsed where target is unclasped\n\t\t\t * CASE 101: src is file and parent is unclasped where target is collapsed\n\t\t\t * CASE 100: src is file and parent is unclasped where target is also unclasped\n\t\t\t * CASE 011: src is directory and parent is collapsed where target is also collapsed\n\t\t\t * CASE 001: src is directory and parent is unclasped where target is also collapsed\n\t\t\t * CASE 010: src is directory and parent is collapsed where target is also unclasped\n\t\t\t * CASE 000: src is directory and parent is unclasped where target is also unclasped\n\t\t\t */\n\n\t\t\tif (clipBoard.action === \"cut\") {\n\t\t\t\t//move\n\n\t\t\t\tif (IS_FILE) {\n\t\t\t\t\tconst file = editorManager.getFile(clipBoard.url, \"uri\");\n\t\t\t\t\tif (file) file.uri = newUrl;\n\t\t\t\t} else if (IS_DIR) {\n\t\t\t\t\thelpers.updateUriOfAllActiveFiles(clipBoard.url, newUrl);\n\t\t\t\t}\n\n\t\t\t\tswitch (CASE) {\n\t\t\t\t\tcase \"111\":\n\t\t\t\t\tcase \"011\":\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"110\":\n\t\t\t\t\t\tappendTile($target, createFileTile(newName, newUrl));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"101\":\n\t\t\t\t\t\t$src.remove();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"100\":\n\t\t\t\t\t\tappendTile($target, createFileTile(newName, newUrl));\n\t\t\t\t\t\t$src.remove();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"001\":\n\t\t\t\t\t\t$src.parentElement.remove();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"010\":\n\t\t\t\t\t\tappendList($target, createFolderTile(newName, newUrl));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"000\":\n\t\t\t\t\t\tappendList($target, createFolderTile(newName, newUrl));\n\t\t\t\t\t\t$src.parentElement.remove();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tFileList.remove(clipBoard.url);\n\t\t\t} else {\n\t\t\t\t//copy\n\n\t\t\t\tswitch (CASE) {\n\t\t\t\t\tcase \"111\":\n\t\t\t\t\tcase \"101\":\n\t\t\t\t\tcase \"011\":\n\t\t\t\t\tcase \"001\":\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"110\":\n\t\t\t\t\tcase \"100\":\n\t\t\t\t\t\tappendTile($target, createFileTile(newName, newUrl));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"010\":\n\t\t\t\t\tcase \"000\":\n\t\t\t\t\t\tappendList($target, createFolderTile(newName, newUrl));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tFileList.append(url, newUrl);\n\t\t\ttoast(strings.success);\n\t\t\tclearClipboard();\n\t\t} catch (error) {\n\t\t\tconsole.error(error);\n\t\t\thelpers.error(error);\n\t\t} finally {\n\t\t\tstopLoading();\n\t\t}\n\t}\n\n\tasync function insertFile() {\n\t\tstartLoading();\n\t\ttry {\n\t\t\tconst file = await FileBrowser(\"file\", strings[\"insert file\"]);\n\t\t\tconst sourceFs = fsOperation(file.url);\n\t\t\tconst data = await sourceFs.readFile();\n\t\t\tconst sourceStats = await sourceFs.stat();\n\t\t\tconst insertedFile = await fsOperation(url).createFile(\n\t\t\t\tsourceStats.name,\n\t\t\t\tdata,\n\t\t\t);\n\t\t\tappendTile($target, createFileTile(sourceStats.name, insertedFile));\n\t\t\tFileList.append(url, insertedFile);\n\t\t} catch (error) {\n\t\t} finally {\n\t\t\tstopLoading();\n\t\t}\n\t}\n\n\tasync function clipBoardAction() {\n\t\tclipBoard.url = url;\n\t\tclipBoard.action = action;\n\t\tclipBoard.$el = $target;\n\n\t\tif (action === \"cut\") $target.classList.add(\"cut\");\n\t\telse $target.classList.remove(\"cut\");\n\t}\n\n\tasync function open() {\n\t\tFileBrowser.openFolder({\n\t\t\turl,\n\t\t\tname,\n\t\t});\n\t}\n\n\tfunction cancelAction() {\n\t\tclipBoard.$el.classList.remove(\"cut\");\n\t\tclearClipboard();\n\t}\n\n\tfunction clearClipboard() {\n\t\tclipBoard.$el = null;\n\t\tclipBoard.url = null;\n\t\tclipBoard.action = null;\n\t}\n}\n\n/**\n *\n * @param {\"file\"|\"dir\"|\"root\"} type\n * @param {string} url\n */\nfunction handleClick(type, uri) {\n\tif (!helpers.isFile(type)) return;\n\topenFile(uri, { render: true });\n\tSidebar.hide();\n}\n\n/**\n * Insert a file into the list\n * @param {HTMLElement} $target\n * @param {HTMLElement} $tile\n */\nfunction appendTile($target, $tile) {\n\t$target = $target.nextElementSibling;\n\tconst $firstTile = $target.get(\":scope>[type=file]\");\n\tif ($firstTile) $target.insertBefore($tile, $firstTile);\n\telse $target.append($tile);\n}\n\n/**\n * Insert folder into the list\n * @param {HTMLElement} $target The target element\n * @param {HTMLElement} $list The tile to be inserted\n */\nfunction appendList($target, $list) {\n\t$target = $target.nextElementSibling;\n\tconst $firstList = $target.firstElementChild;\n\tif ($firstList) $target.insertBefore($list, $firstList);\n\telse $target.append($list);\n}\n\n/**\n * Get the active file tree for a folder element, if it has been loaded.\n * @param {HTMLElement} $el\n * @returns {FileTree|null}\n */\nfunction getLoadedFileTree($el) {\n\treturn (\n\t\t$el?.$ul?._fileTree || $el?.fileTree || $el?.nextElementSibling?._fileTree\n\t);\n}\n\n/**\n * Update matching expanded folder views with a new entry.\n * @param {string} parentUrl\n * @param {string} entryUrl\n * @param {\"file\"|\"folder\"} type\n */\nfunction appendEntryToOpenFolder(parentUrl, entryUrl, type) {\n\tconst filesApp = sidebarApps.get(\"files\");\n\tconst $els = filesApp.getAll(`[data-url=\"${parentUrl}\"]`);\n\tconst isDirectory = type === \"folder\";\n\tconst name = Url.basename(entryUrl);\n\n\tArray.from($els).forEach(($el) => {\n\t\tif (!(helpers.isDir($el.dataset.type) || $el.dataset.type === \"root\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!$el.unclasped) return;\n\n\t\tconst fileTree = getLoadedFileTree($el);\n\t\tif (fileTree) {\n\t\t\tfileTree.appendEntry(name, entryUrl, isDirectory);\n\t\t\treturn;\n\t\t}\n\n\t\tif (isDirectory) {\n\t\t\tappendList($el, createFolderTile(name, entryUrl));\n\t\t} else {\n\t\t\tappendTile($el, createFileTile(name, entryUrl));\n\t\t}\n\t});\n}\n\n/**\n * Refresh matching expanded folder views.\n * @param {string} folderUrl\n */\nasync function refreshOpenFolder(folderUrl) {\n\tconst filesApp = sidebarApps.get(\"files\");\n\tconst $els = filesApp.getAll(`[data-url=\"${folderUrl}\"]`);\n\n\tawait Promise.all(\n\t\tArray.from($els).map(async ($el) => {\n\t\t\tif (!(helpers.isDir($el.dataset.type) || $el.dataset.type === \"root\")) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst fileTree = getLoadedFileTree($el);\n\t\t\tif (fileTree) {\n\t\t\t\tawait fileTree.refresh();\n\t\t\t}\n\t\t}),\n\t);\n}\n\n/**\n * Create a folder tile\n * @param {string} name\n * @param {string} url\n * @returns {HTMLElement}\n */\nfunction createFolderTile(name, url) {\n\tconst $list = collapsableList(name, \"folder\", {\n\t\tontoggle: () => expandList($list),\n\t});\n\tconst { $title } = $list;\n\t$title.dataset.url = url;\n\t$title.dataset.name = name;\n\t$title.dataset.type = \"dir\";\n\n\treturn $list;\n}\n\n/**\n * Create a file tile\n * @param {string} name\n * @param {string} url\n * @returns {HTMLElement}\n */\nfunction createFileTile(name, url) {\n\tconst $tile = tile({\n\t\tlead: <span className={helpers.getIconForFile(name)}></span>,\n\t\ttext: name,\n\t});\n\t$tile.dataset.url = url;\n\t$tile.dataset.name = name;\n\t$tile.dataset.type = \"file\";\n\n\treturn $tile;\n}\n\n/**\n * Add file or folder to the list if expanded\n * @param {string} url Url of file or folder to add\n * @param {'file'|'folder'} type is file or folder\n */\nopenFolder.add = async (url, type) => {\n\tconst { url: parent } = await fsOperation(Url.dirname(url)).stat();\n\tFileList.append(parent, url);\n\tappendEntryToOpenFolder(parent, url, type);\n};\n\nopenFolder.renameItem = (oldFile, newFile, newFilename) => {\n\tFileList.rename(oldFile, newFile);\n\n\thelpers.updateUriOfAllActiveFiles(oldFile, newFile);\n\n\tconst filesApp = sidebarApps.get(\"files\");\n\tconst $els = filesApp.getAll(`[data-url=\"${oldFile}\"]`);\n\tArray.from($els).forEach(($el) => {\n\t\tif ($el.dataset.type === \"dir\") {\n\t\t\t$el = $el.$title;\n\t\t\tsetTimeout(() => {\n\t\t\t\t$el.collapse();\n\t\t\t\t$el.expand();\n\t\t\t}, 0);\n\t\t} else {\n\t\t\t$el.querySelector(\":scope>span\").className =\n\t\t\t\thelpers.getIconForFile(newFilename);\n\t\t}\n\n\t\t$el.dataset.url = newFile;\n\t\t$el.dataset.name = newFilename;\n\t\t$el.querySelector(\":scope>.text\").textContent = newFilename;\n\t});\n};\n\nopenFolder.removeItem = (url) => {\n\tFileList.remove(url);\n\tconst folder = addedFolder.find(({ url: fUrl }) => url === fUrl);\n\n\tif (folder) {\n\t\tfolder.remove();\n\t\treturn;\n\t}\n\n\tconst filesApp = sidebarApps.get(\"files\");\n\tconst $el = filesApp.getAll(`[data-url=\"${url}\"]`);\n\tArray.from($el).forEach(($el) => {\n\t\tconst type = $el.dataset.type;\n\t\tif (helpers.isFile(type)) {\n\t\t\t$el.remove();\n\t\t} else {\n\t\t\t$el.parentElement.remove();\n\t\t}\n\t});\n};\n\nopenFolder.removeFolders = (url) => {\n\t({ url } = Url.parse(url));\n\tconst regex = new RegExp(\"^\" + escapeStringRegexp(url));\n\taddedFolder.forEach((folder) => {\n\t\tif (regex.test(folder.url)) {\n\t\t\tfolder.remove();\n\t\t}\n\t});\n};\n\n/**\n * Find the folder that contains the url\n * @param {String} url\n * @returns {Folder}\n */\nopenFolder.find = (url) => {\n\tconst found = addedFolder.find((folder) => folder.url === url);\n\tif (found) return found;\n\treturn addedFolder.find((folder) => {\n\t\tconst { url: furl } = Url.parse(folder.url);\n\t\tconst regex = new RegExp(\"^\" + escapeStringRegexp(furl));\n\t\treturn regex.test(url);\n\t});\n};\n\nexport default openFolder;\n"
  },
  {
    "path": "src/lib/polyfill.js",
    "content": "// polyfill for prepend\n\n(function (arr) {\n\tarr.forEach(function (item) {\n\t\tif (item.hasOwnProperty(\"prepend\")) {\n\t\t\treturn;\n\t\t}\n\t\tObject.defineProperty(item, \"prepend\", {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: true,\n\t\t\twritable: true,\n\t\t\tvalue: function prepend() {\n\t\t\t\tvar argArr = Array.prototype.slice.call(arguments),\n\t\t\t\t\tdocFrag = document.createDocumentFragment();\n\n\t\t\t\targArr.forEach(function (argItem) {\n\t\t\t\t\tvar node =\n\t\t\t\t\t\targItem instanceof Node\n\t\t\t\t\t\t\t? argItem\n\t\t\t\t\t\t\t: document.createTextNode(String(argItem));\n\t\t\t\t\tdocFrag.appendChild(node);\n\t\t\t\t});\n\n\t\t\t\tthis.insertBefore(docFrag, this.firstChild);\n\t\t\t},\n\t\t});\n\t});\n})([Element.prototype, Document.prototype, DocumentFragment.prototype]);\n\n// polyfill for closest\n\n(function (arr) {\n\tarr.forEach(function (item) {\n\t\tif (item.hasOwnProperty(\"closest\")) {\n\t\t\treturn;\n\t\t}\n\t\tObject.defineProperty(item, \"closest\", {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: true,\n\t\t\twritable: true,\n\t\t\tvalue: function closest(s) {\n\t\t\t\tvar matches = (this.document || this.ownerDocument).querySelectorAll(s),\n\t\t\t\t\ti,\n\t\t\t\t\tel = this;\n\t\t\t\tdo {\n\t\t\t\t\ti = matches.length;\n\t\t\t\t\twhile (--i >= 0 && matches.item(i) !== el) {}\n\t\t\t\t} while (i < 0 && (el = el.parentElement));\n\t\t\t\treturn el;\n\t\t\t},\n\t\t});\n\t});\n})([Element.prototype]);\n\n// polyfill for replaceWith\n\n(function (arr) {\n\tarr.forEach(function (item) {\n\t\tif (item.hasOwnProperty(\"replaceWith\")) {\n\t\t\treturn;\n\t\t}\n\t\tObject.defineProperty(item, \"replaceWith\", {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: true,\n\t\t\twritable: true,\n\t\t\tvalue: function replaceWith() {\n\t\t\t\tvar parent = this.parentNode,\n\t\t\t\t\ti = arguments.length,\n\t\t\t\t\tcurrentNode;\n\t\t\t\tif (!parent) return;\n\t\t\t\tif (!i)\n\t\t\t\t\t// if there are no arguments\n\t\t\t\t\tparent.removeChild(this);\n\t\t\t\twhile (i--) {\n\t\t\t\t\t// i-- decrements i and returns the value of i before the decrement\n\t\t\t\t\tcurrentNode = arguments[i];\n\t\t\t\t\tif (typeof currentNode !== \"object\") {\n\t\t\t\t\t\tcurrentNode = this.ownerDocument.createTextNode(currentNode);\n\t\t\t\t\t} else if (currentNode.parentNode) {\n\t\t\t\t\t\tcurrentNode.parentNode.removeChild(currentNode);\n\t\t\t\t\t}\n\t\t\t\t\t// the value of \"i\" below is after the decrement\n\t\t\t\t\tif (!i)\n\t\t\t\t\t\t// if currentNode is the first argument (currentNode === arguments[0])\n\t\t\t\t\t\tparent.replaceChild(currentNode, this);\n\t\t\t\t\t// if currentNode isn't the first\n\t\t\t\t\telse parent.insertBefore(this.previousSibling, currentNode);\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t});\n})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);\n\n// polyfill for toggleAttribute\n\n(function (arr) {\n\tarr.forEach(function (item) {\n\t\tif (item.hasOwnProperty(\"toggleAttribute\")) {\n\t\t\treturn;\n\t\t}\n\t\tObject.defineProperty(item, \"toggleAttribute\", {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: true,\n\t\t\twritable: true,\n\t\t\tvalue: function toggleAttribute() {\n\t\t\t\tvar attr = arguments[0];\n\t\t\t\tif (this.hasAttribute(attr)) {\n\t\t\t\t\tthis.removeAttribute(attr);\n\t\t\t\t} else {\n\t\t\t\t\tthis.setAttribute(attr, arguments[1] || \"\");\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t});\n})([Element.prototype]);\n\n// polyfill for performance.now\n\n(function () {\n\tif (\"performance\" in window === false) {\n\t\twindow.performance = {};\n\t}\n\n\tDate.now =\n\t\tDate.now ||\n\t\tfunction () {\n\t\t\t// thanks IE8\n\t\t\treturn new Date().getTime();\n\t\t};\n\n\tif (\"now\" in window.performance === false) {\n\t\tvar nowOffset = Date.now();\n\n\t\tif (performance.timing && performance.timing.navigationStart) {\n\t\t\tnowOffset = performance.timing.navigationStart;\n\t\t}\n\n\t\twindow.performance.now = function now() {\n\t\t\treturn Date.now() - nowOffset;\n\t\t};\n\t}\n})();\n"
  },
  {
    "path": "src/lib/prettierFormatter.js",
    "content": "import fsOperation from \"fileSystem\";\nimport { parse } from \"acorn\";\nimport toast from \"components/toast\";\nimport appSettings from \"lib/settings\";\nimport prettierPluginBabel from \"prettier/plugins/babel\";\nimport prettierPluginEstree from \"prettier/plugins/estree\";\nimport prettierPluginGraphql from \"prettier/plugins/graphql\";\nimport prettierPluginHtml from \"prettier/plugins/html\";\nimport prettierPluginMarkdown from \"prettier/plugins/markdown\";\nimport prettierPluginPostcss from \"prettier/plugins/postcss\";\nimport prettierPluginTypescript from \"prettier/plugins/typescript\";\nimport prettierPluginYaml from \"prettier/plugins/yaml\";\nimport prettier from \"prettier/standalone\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nconst PRETTIER_ID = \"prettier\";\nconst PRETTIER_NAME = \"Prettier\";\nconst CONFIG_FILENAMES = [\n\t\".prettierrc\",\n\t\".prettierrc.json\",\n\t\".prettierrc.json5\",\n\t\".prettierrc.js\",\n\t\".prettierrc.cjs\",\n\t\".prettierrc.mjs\",\n\t\".prettierrc.config.cjs\",\n\t\".prettierrc.config.mjs\",\n\t\".prettier.config.js\",\n\t\".prettier.config.cjs\",\n\t\".prettier.config.mjs\",\n\t\"prettier.config.json\",\n\t\"prettier.config.js\",\n\t\"prettier.config.cjs\",\n\t\"prettier.config.mjs\",\n];\nconst PRETTIER_PLUGINS = [\n\tprettierPluginEstree,\n\tprettierPluginBabel,\n\tprettierPluginHtml,\n\tprettierPluginMarkdown,\n\tprettierPluginPostcss,\n\tprettierPluginTypescript,\n\tprettierPluginYaml,\n\tprettierPluginGraphql,\n];\n\n/**\n * Supported parser mapping keyed by CodeMirror mode name\n * @type {Record<string, string>}\n */\nconst MODE_TO_PARSER = {\n\tangular: \"angular\",\n\tgfm: \"markdown\",\n\tcss: \"css\",\n\tgraphql: \"graphql\",\n\thtml: \"html\",\n\tjson: \"json\",\n\tjson5: \"json\",\n\tjsx: \"babel\",\n\tless: \"less\",\n\tmarkdown: \"markdown\",\n\tmd: \"markdown\",\n\tmdx: \"mdx\",\n\tscss: \"scss\",\n\tstyled_jsx: \"babel\",\n\ttypescript: \"typescript\",\n\ttsx: \"typescript\",\n\tjsonc: \"json\",\n\tyaml: \"yaml\",\n\tyml: \"yaml\",\n\tvue: \"vue\",\n\tjavascript: \"babel\",\n};\n\nconst SUPPORTED_EXTENSIONS = [\n\t\"js\",\n\t\"cjs\",\n\t\"mjs\",\n\t\"jsx\",\n\t\"ts\",\n\t\"tsx\",\n\t\"json\",\n\t\"json5\",\n\t\"css\",\n\t\"scss\",\n\t\"less\",\n\t\"html\",\n\t\"htm\",\n\t\"vue\",\n\t\"md\",\n\t\"markdown\",\n\t\"mdx\",\n\t\"yaml\",\n\t\"yml\",\n\t\"graphql\",\n\t\"gql\",\n];\n\n/**\n * Register Prettier formatter with Acode instance\n */\nexport function registerPrettierFormatter() {\n\tif (!window?.acode) return;\n\tconst alreadyRegistered = acode.formatters.some(\n\t\t({ id }) => id === PRETTIER_ID,\n\t);\n\tif (alreadyRegistered) return;\n\tacode.registerFormatter(\n\t\tPRETTIER_ID,\n\t\tSUPPORTED_EXTENSIONS,\n\t\t() => formatActiveFileWithPrettier(),\n\t\tPRETTIER_NAME,\n\t);\n}\n\nasync function formatActiveFileWithPrettier() {\n\tconst file = editorManager?.activeFile;\n\tconst editor = editorManager?.editor;\n\tif (!file || file.type !== \"editor\" || !editor) return false;\n\n\tconst modeName = (file.currentMode || \"text\").toLowerCase();\n\tconst parser = getParserForMode(modeName);\n\tif (!parser) {\n\t\ttoast(\"Prettier does not support this file type yet\");\n\t\treturn false;\n\t}\n\n\tconst doc = editor.state.doc;\n\tconst source = doc.toString();\n\tconst filepath = file.uri || file.filename || \"\";\n\ttry {\n\t\tconst config = await resolvePrettierConfig(file);\n\t\tconst formatted = await prettier.format(source, {\n\t\t\t...config,\n\t\t\tparser,\n\t\t\tplugins: PRETTIER_PLUGINS,\n\t\t\tfilepath,\n\t\t\toverrideEditorconfig: true,\n\t\t});\n\n\t\tif (formatted === source) return true;\n\n\t\teditor.dispatch({\n\t\t\tchanges: {\n\t\t\t\tfrom: 0,\n\t\t\t\tto: doc.length,\n\t\t\t\tinsert: formatted,\n\t\t\t},\n\t\t});\n\t\treturn true;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\ttoast(message);\n\t\treturn false;\n\t}\n}\n\nfunction getParserForMode(modeName) {\n\tif (MODE_TO_PARSER[modeName]) return MODE_TO_PARSER[modeName];\n\tif (modeName.includes(\"javascript\")) return \"babel\";\n\tif (modeName.includes(\"typescript\")) return \"typescript\";\n\treturn null;\n}\n\nasync function resolvePrettierConfig(file) {\n\tconst overrides = appSettings?.value?.prettier || {};\n\tconst projectConfig = await loadProjectConfig(file);\n\tconst result = { ...overrides, ...(projectConfig || {}) };\n\tif (file?.eol && result.endOfLine == null) {\n\t\tresult.endOfLine = file.eol === \"windows\" ? \"crlf\" : \"lf\";\n\t}\n\tif (result.useTabs == null) {\n\t\tresult.useTabs = !appSettings?.value?.softTab;\n\t}\n\tif (\n\t\tresult.tabWidth == null &&\n\t\ttypeof appSettings?.value?.tabSize === \"number\"\n\t) {\n\t\tresult.tabWidth = appSettings.value.tabSize;\n\t}\n\treturn result;\n}\n\nasync function loadProjectConfig(file) {\n\tconst uri = file?.uri;\n\tif (!uri) return null;\n\n\tconst projectRoot = findProjectRoot(uri);\n\tconst directories = collectCandidateDirectories(uri, projectRoot);\n\n\tfor (const directory of directories) {\n\t\tconst config = await readConfigFromDirectory(directory);\n\t\tif (config) return config;\n\t}\n\n\treturn null;\n}\n\nfunction findProjectRoot(uri) {\n\tconst folders = Array.isArray(globalThis.addedFolder)\n\t\t? globalThis.addedFolder\n\t\t: [];\n\tconst target = normalizePath(uri);\n\tlet match = null;\n\tlet matchLength = -1;\n\n\tfor (const folder of folders) {\n\t\tconst folderUrl = folder?.url;\n\t\tif (!folderUrl) continue;\n\t\tconst normalized = normalizePath(folderUrl);\n\t\tif (!normalized) continue;\n\t\tif (target === normalized || target.startsWith(`${normalized}/`)) {\n\t\t\tif (normalized.length > matchLength) {\n\t\t\t\tmatch = folderUrl;\n\t\t\t\tmatchLength = normalized.length;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn match;\n}\n\nfunction collectCandidateDirectories(fileUri, projectRoot) {\n\tconst directories = [];\n\tconst visited = new Set();\n\tlet currentDir = safeDirname(fileUri);\n\n\twhile (currentDir) {\n\t\tconst normalized = normalizePath(currentDir);\n\t\tif (visited.has(normalized)) break;\n\t\tdirectories.push(currentDir);\n\t\tvisited.add(normalized);\n\t\tif (projectRoot && pathsAreSame(currentDir, projectRoot)) break;\n\t\tconst parent = safeDirname(currentDir);\n\t\tif (!parent || parent === currentDir) break;\n\t\tcurrentDir = parent;\n\t}\n\n\tif (\n\t\tprojectRoot &&\n\t\t!directories.some((dir) => pathsAreSame(dir, projectRoot))\n\t) {\n\t\tdirectories.push(projectRoot);\n\t}\n\n\treturn directories;\n}\n\nfunction safeDirname(path) {\n\ttry {\n\t\treturn Url.dirname(path);\n\t} catch (_) {\n\t\treturn null;\n\t}\n}\n\nasync function readConfigFromDirectory(directory) {\n\tif (!directory) return null;\n\n\tfor (const name of CONFIG_FILENAMES) {\n\t\tconst config = await loadConfigFile(directory, name);\n\t\tif (config) return config;\n\t}\n\n\treturn loadPrettierFromPackageJson(directory);\n}\n\nasync function loadConfigFile(directory, basename) {\n\ttry {\n\t\tconst filePath = Url.join(directory, basename);\n\t\tconst fs = fsOperation(filePath);\n\t\tif (!(await fs.exists())) return null;\n\t\tconst text = await fs.readFile(\"utf8\");\n\n\t\tswitch (basename) {\n\t\t\tcase \".prettierrc\":\n\t\t\tcase \".prettierrc.json\":\n\t\t\tcase \".prettierrc.json5\":\n\t\t\tcase \"prettier.config.json\":\n\t\t\t\treturn parseJsonLike(text);\n\t\t\tcase \".prettierrc.js\":\n\t\t\tcase \".prettier.config.js\":\n\t\t\tcase \"prettier.config.js\":\n\t\t\t\treturn parseJsConfig(directory, text, filePath);\n\t\t\tcase \".prettierrc.mjs\":\n\t\t\tcase \".prettierrc.config.mjs\":\n\t\t\tcase \".prettier.config.mjs\":\n\t\t\tcase \"prettier.config.mjs\":\n\t\t\t\treturn parseJsConfig(directory, text, filePath);\n\t\t\tcase \".prettierrc.cjs\":\n\t\t\tcase \".prettierrc.config.cjs\":\n\t\t\tcase \".prettier.config.cjs\":\n\t\t\tcase \"prettier.config.cjs\":\n\t\t\t\treturn parseJsConfig(directory, text, filePath);\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t} catch (_) {\n\t\treturn null;\n\t}\n}\n\nasync function loadPrettierFromPackageJson(directory) {\n\ttry {\n\t\tconst pkgPath = Url.join(directory, \"package.json\");\n\t\tconst fs = fsOperation(pkgPath);\n\t\tif (!(await fs.exists())) return null;\n\t\tconst pkg = await fs.readFile(\"json\");\n\t\tconst config = pkg?.prettier;\n\t\tif (config && typeof config === \"object\") return config;\n\t} catch (_) {\n\t\treturn null;\n\t}\n\treturn null;\n}\n\nfunction parseJsonLike(text) {\n\tconst trimmed = text?.trim();\n\tif (!trimmed) return null;\n\tconst parsed = helpers.parseJSON(trimmed);\n\tif (parsed) return parsed;\n\ttry {\n\t\treturn parseSafeExpression(trimmed);\n\t} catch (_) {\n\t\treturn null;\n\t}\n}\n\nfunction parseJsConfig(directory, source, absolutePath) {\n\tif (!source) return null;\n\tvoid directory;\n\tvoid absolutePath;\n\ttry {\n\t\treturn extractConfigFromProgram(source);\n\t} catch (_) {\n\t\treturn null;\n\t}\n}\n\nfunction parseProgram(source) {\n\ttry {\n\t\treturn parse(source, {\n\t\t\tecmaVersion: \"latest\",\n\t\t\tsourceType: \"module\",\n\t\t\tallowHashBang: true,\n\t\t});\n\t} catch (_) {\n\t\treturn parse(source, {\n\t\t\tecmaVersion: \"latest\",\n\t\t\tsourceType: \"script\",\n\t\t\tallowHashBang: true,\n\t\t});\n\t}\n}\n\nfunction extractConfigFromProgram(source) {\n\tconst ast = parseProgram(source);\n\tconst scope = new Map();\n\n\tfor (const statement of ast.body) {\n\t\tconst declared = readVariableDeclaration(statement, scope);\n\t\tif (declared) {\n\t\t\tfor (const [name, value] of declared) {\n\t\t\t\tscope.set(name, value);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst exported = readCommonJsExport(statement, scope);\n\t\tif (exported !== undefined) return exported;\n\n\t\tconst esmExported = readEsmExport(statement, scope);\n\t\tif (esmExported !== undefined) return esmExported;\n\t}\n\n\treturn null;\n}\n\nfunction parseSafeExpression(text) {\n\tconst wrapped = `(${text})`;\n\tconst ast = parse(wrapped, {\n\t\tecmaVersion: \"latest\",\n\t\tsourceType: \"module\",\n\t\tallowHashBang: true,\n\t});\n\tconst statement = ast.body[0];\n\tif (statement?.type !== \"ExpressionStatement\") return null;\n\treturn evaluateNode(statement.expression, new Map());\n}\n\nfunction readVariableDeclaration(statement, scope) {\n\tif (statement?.type !== \"VariableDeclaration\") return null;\n\tconst values = new Map();\n\tconst lookupScope = new Map(scope);\n\n\tfor (const decl of statement.declarations || []) {\n\t\tif (!decl || decl.type !== \"VariableDeclarator\") continue;\n\t\tif (decl.id?.type !== \"Identifier\") continue;\n\t\tif (!decl.init) continue;\n\t\ttry {\n\t\t\tconst value = evaluateNode(decl.init, lookupScope);\n\t\t\tvalues.set(decl.id.name, value);\n\t\t\tlookupScope.set(decl.id.name, value);\n\t\t} catch (_) {\n\t\t\t// Ignore unsupported declarations\n\t\t}\n\t}\n\n\treturn values.size ? values : null;\n}\n\nfunction readCommonJsExport(statement, scope) {\n\tif (statement?.type !== \"ExpressionStatement\") return undefined;\n\tconst expr = statement.expression;\n\tif (expr?.type !== \"AssignmentExpression\" || expr.operator !== \"=\") {\n\t\treturn undefined;\n\t}\n\n\tif (!isModuleExports(expr.left)) return undefined;\n\treturn evaluateNode(expr.right, scope);\n}\n\nfunction readEsmExport(statement, scope) {\n\tif (statement?.type !== \"ExportDefaultDeclaration\") return undefined;\n\treturn evaluateNode(statement.declaration, scope);\n}\n\nfunction isModuleExports(node) {\n\treturn (\n\t\tnode?.type === \"MemberExpression\" &&\n\t\t!node.computed &&\n\t\tnode.object?.type === \"Identifier\" &&\n\t\tnode.object.name === \"module\" &&\n\t\tnode.property?.type === \"Identifier\" &&\n\t\tnode.property.name === \"exports\"\n\t);\n}\n\nfunction evaluateNode(node, scope) {\n\tif (!node) return null;\n\n\tswitch (node.type) {\n\t\tcase \"ObjectExpression\":\n\t\t\treturn evaluateObjectExpression(node, scope);\n\t\tcase \"ArrayExpression\":\n\t\t\treturn node.elements.map((entry) => evaluateNode(entry, scope));\n\t\tcase \"Literal\":\n\t\t\treturn node.value;\n\t\tcase \"TemplateLiteral\":\n\t\t\tif (node.expressions.length) {\n\t\t\t\tthrow new Error(\"Template expressions are not supported\");\n\t\t\t}\n\t\t\treturn node.quasis.map((part) => part.value.cooked ?? \"\").join(\"\");\n\t\tcase \"Identifier\":\n\t\t\tif (scope.has(node.name)) return scope.get(node.name);\n\t\t\tif (node.name === \"undefined\") return undefined;\n\t\t\tthrow new Error(`Unsupported identifier: ${node.name}`);\n\t\tcase \"UnaryExpression\":\n\t\t\treturn evaluateUnaryExpression(node, scope);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported node type: ${node.type}`);\n\t}\n}\n\nfunction evaluateObjectExpression(node, scope) {\n\tconst output = {};\n\tfor (const property of node.properties || []) {\n\t\tif (!property || property.type !== \"Property\") {\n\t\t\tthrow new Error(\"Unsupported object property\");\n\t\t}\n\t\tif (property.kind !== \"init\" || property.method || property.shorthand) {\n\t\t\tthrow new Error(\"Unsupported object property kind\");\n\t\t}\n\t\tconst key = property.computed\n\t\t\t? evaluateNode(property.key, scope)\n\t\t\t: getPropertyKey(property.key);\n\t\tconst normalizedKey =\n\t\t\ttypeof key === \"string\" || typeof key === \"number\" ? String(key) : null;\n\t\tif (!normalizedKey) {\n\t\t\tthrow new Error(\"Unsupported object key\");\n\t\t}\n\t\toutput[normalizedKey] = evaluateNode(property.value, scope);\n\t}\n\treturn output;\n}\n\nfunction getPropertyKey(node) {\n\tif (node?.type === \"Identifier\") return node.name;\n\tif (node?.type === \"Literal\") return node.value;\n\tthrow new Error(\"Unsupported property key\");\n}\n\nfunction evaluateUnaryExpression(node, scope) {\n\tconst value = evaluateNode(node.argument, scope);\n\tswitch (node.operator) {\n\t\tcase \"+\":\n\t\t\treturn +value;\n\t\tcase \"-\":\n\t\t\treturn -value;\n\t\tcase \"!\":\n\t\t\treturn !value;\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported unary operator: ${node.operator}`);\n\t}\n}\n\nfunction normalizePath(path) {\n\tlet result = String(path || \"\").replace(/\\\\/g, \"/\");\n\twhile (result.length > 1 && result.endsWith(\"/\")) {\n\t\tconst prefix = result.slice(0, -1);\n\t\tif (/^[a-z]+:\\/{0,2}$/i.test(prefix)) break;\n\t\tresult = prefix;\n\t}\n\treturn result;\n}\n\nfunction pathsAreSame(a, b) {\n\tif (!a || !b) return false;\n\treturn normalizePath(a) === normalizePath(b);\n}\n"
  },
  {
    "path": "src/lib/projects.js",
    "content": "const projects = {\n\thtml() {\n\t\tacode.addIcon(\n\t\t\t\"html-project-icon\",\n\t\t\t\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABIUExURUdwTORPJuRPJuNOJeRPJuNQJ+RPJuNOJuNPJuROJeRPJuNOJuRPJuRQJONPJuNPJeVQI+NPJeROJuNPJuZPJ+NOJuRPJuNPJkmKsooAAAAXdFJOUwA6h5uxKGh/60/VE8BBll8izqXdDHT3jnqTYwAAAQRJREFUGBl9wY22azAURtGFhMS/Vvu9/5veHeGMMrhzAvoPkqBHgWTRo4XE6ZEjqfSoImn0qCGpZQYuBpmaJMpMXESZSFLIfLioZQoSLzMCzYmMJ+lkXsBbVx0bmR546YosSGqBUheBbJEUuFgkLWROpuMsSHJklYznTKYiK2WaHwWsMiXZRxceZpkP2SQzGO1mKGQmsigTwWvXQZSJZIVMDZ12K9QyBdks0wBDuUjvVw00MjNZJ1OxmWc2o0zHLkhynl9OUuDQyoS+jGx8PfZfSS2HXrvg6unVatdzcLrlOIy6NXIog26Ekj9+qlqdtNXkOSua/qvNt28Kbq1xfL/HuPLjH4f8MW+juHZUAAAAAElFTkSuQmCC\",\n\t\t);\n\t\treturn {\n\t\t\tasync files() {\n\t\t\t\treturn {\n\t\t\t\t\t\"index.html\":\n\t\t\t\t\t\t'<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n  <meta charset=\"UTF-8\">\\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\\n  <link rel=\"stylesheet\" href=\"css/index.css\">\\n  <script src=\"js/index.js\"></script>\\n  <title><%name%></title>\\n</head>\\n<body>\\n\\t<h1><%name%></h1>\\n</body>\\n</html>',\n\t\t\t\t\t\"css/index.css\": \"\",\n\t\t\t\t\t\"js/index.js\": \"\",\n\t\t\t\t};\n\t\t\t},\n\t\t\ticon: \"html-project-icon\",\n\t\t};\n\t},\n};\n\nexport default {\n\tlist() {\n\t\treturn Object.keys(projects).map((project) => ({\n\t\t\tname: project,\n\t\t\ticon: projects[project]().icon,\n\t\t}));\n\t},\n\tget(project) {\n\t\treturn projects[project]?.();\n\t},\n\t/**\n\t *\n\t * @param {string} project Project name\n\t * @param {()=>Promise<Map<string, string>>} files Async function that returns a map of files\n\t * @param {string} iconSrc Icon source (data url)\n\t */\n\tset(project, files, iconSrc) {\n\t\tconst icon = `${project}-project-icon`;\n\t\tacode.addIcon(`${project}-project-icon`, iconSrc);\n\t\tprojects[project] = () => ({ files, icon });\n\t},\n\tdelete(project) {\n\t\tdelete projects[project];\n\t},\n};\n"
  },
  {
    "path": "src/lib/recents.js",
    "content": "import select from \"dialogs/select\";\nimport escapeStringRegexp from \"escape-string-regexp\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nconst recents = {\n\t/**\n\t * @returns {Array<String>}\n\t */\n\tget files() {\n\t\tconst files = helpers.parseJSON(localStorage.recentFiles);\n\t\treturn Array.isArray(files) ? files : [];\n\t},\n\t/**\n\t * @returns {{url: String, opts: Map<String, String>}[]}\n\t */\n\tget folders() {\n\t\tconst folders = helpers.parseJSON(localStorage.recentFolders);\n\t\treturn Array.isArray(folders) ? folders : [];\n\t},\n\tset files(list) {\n\t\tif (Array.isArray(list)) localStorage.recentFiles = JSON.stringify(list);\n\t},\n\tset folders(list) {\n\t\tif (Array.isArray(list)) localStorage.recentFolders = JSON.stringify(list);\n\t},\n\tMAX: 10,\n\t/**\n\t *\n\t * @param {string} file\n\t */\n\taddFile(file) {\n\t\tlet files = this.files;\n\t\tif (files.length >= this.MAX) files.pop();\n\t\tfiles = files.filter((i) => i !== file);\n\t\tfiles.unshift(file);\n\t\tthis.files = files;\n\t},\n\taddFolder(url, opts) {\n\t\tif (url.slice(-1) === \"/\") {\n\t\t\turl = url.slice(0, -1);\n\t\t}\n\n\t\tlet folders = this.folders;\n\t\tif (folders.length >= this.MAX) folders.pop();\n\t\tfolders = folders.filter((i) => i.url !== url);\n\t\tfolders.unshift({\n\t\t\turl,\n\t\t\topts,\n\t\t});\n\t\tthis.folders = folders;\n\t},\n\n\tremoveFolder(url) {\n\t\t({ url } = Url.parse(url));\n\t\tthis.folders = this.folders.filter((folder) => {\n\t\t\treturn !new RegExp(\"^\" + escapeStringRegexp(folder.url)).test(url);\n\t\t});\n\t},\n\n\tremoveFile(url) {\n\t\t({ url } = Url.parse(url));\n\t\tthis.files = this.files.filter((file) => {\n\t\t\treturn !new RegExp(\"^\" + escapeStringRegexp(url)).test(file);\n\t\t});\n\t},\n\n\tclear() {\n\t\tthis.files = [];\n\t\tthis.folders = [];\n\t},\n\t/**\n\t *\n\t * @param {Array<Array<string, any, string>>} [extra]\n\t * @param {\"file\"|\"dir\"|\"all\"} [type]\n\t * @param {string} [title]\n\t * @returns {Promise<RecentPathData>}\n\t */\n\tselect(extra, type = \"all\", title = strings[\"open recent\"]) {\n\t\tconst all = [];\n\t\tconst MAX = 20;\n\t\tconst shortName = (name) => {\n\t\t\tname = helpers.getVirtualPath(name);\n\n\t\t\tif (name.length > MAX) {\n\t\t\t\treturn \"...\" + name.substr(-MAX - 3);\n\t\t\t}\n\t\t\treturn name;\n\t\t};\n\n\t\tif (type === \"dir\" || type === \"all\") {\n\t\t\tlet dirs = this.folders;\n\t\t\tfor (let dir of dirs) {\n\t\t\t\tconst { url } = dir;\n\n\t\t\t\tconst dirValue = {\n\t\t\t\t\ttype: \"dir\",\n\t\t\t\t\tval: dir,\n\t\t\t\t};\n\n\t\t\t\tconst tailElement = tag(\"span\", {\n\t\t\t\t\tclassName: \"icon clearclose\",\n\t\t\t\t\tdataset: {\n\t\t\t\t\t\taction: \"clear\",\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tall.push({\n\t\t\t\t\tvalue: dirValue,\n\t\t\t\t\ttext: shortName(url),\n\t\t\t\t\ticon: \"folder\",\n\t\t\t\t\ttailElement: tailElement,\n\t\t\t\t\tontailclick: (e) => {\n\t\t\t\t\t\tconst $item = e.currentTarget.closest(\".tile\");\n\t\t\t\t\t\tif ($item) $item.remove();\n\t\t\t\t\t\tthis.removeFolder(dir.url);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (type === \"file\" || type === \"all\") {\n\t\t\tlet files = this.files;\n\t\t\tfor (let file of files) {\n\t\t\t\tif (!file) continue;\n\t\t\t\tconst name = shortName(Url.parse(file).url);\n\n\t\t\t\tconst fileValue = {\n\t\t\t\t\ttype: \"file\",\n\t\t\t\t\tval: file,\n\t\t\t\t};\n\t\t\t\tconst tailElement = tag(\"span\", {\n\t\t\t\t\tclassName: \"icon clearclose\",\n\t\t\t\t\tdataset: {\n\t\t\t\t\t\taction: \"clear\",\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tall.push({\n\t\t\t\t\tvalue: fileValue,\n\t\t\t\t\ttext: name,\n\t\t\t\t\ticon: helpers.getIconForFile(name),\n\t\t\t\t\ttailElement: tailElement,\n\t\t\t\t\tontailclick: (e) => {\n\t\t\t\t\t\tconst $item = e.currentTarget.closest(\".tile\");\n\t\t\t\t\t\tif ($item) $item.remove();\n\t\t\t\t\t\tthis.removeFile(file);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (type === \"all\") all.push([\"clear\", strings.clear, \"icon clearclose\"]);\n\n\t\tif (extra) {\n\t\t\textra = extra.map((item) => {\n\t\t\t\titem[1] = shortName(item[1]);\n\t\t\t\treturn item;\n\t\t\t});\n\n\t\t\tall.push(...extra);\n\t\t}\n\n\t\treturn select(title, all, {\n\t\t\ttextTransform: false,\n\t\t});\n\t},\n};\n\nexport default recents;\n"
  },
  {
    "path": "src/lib/remoteStorage.js",
    "content": "import fsOperation from \"fileSystem\";\nimport Ftp from \"fileSystem/ftp\";\nimport Sftp from \"fileSystem/sftp\";\nimport loader from \"dialogs/loader\";\nimport multiPrompt from \"dialogs/multiPrompt\";\nimport URLParse from \"url-parse\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nexport default {\n\t/**\n\t *\n\t * @param  {...any} args [username, password, hostname, port, ftps, active, name]\n\t */\n\tasync addFtp(...args) {\n\t\tlet stopConnection = false;\n\t\tconst {\n\t\t\tusername, //\n\t\t\tpassword,\n\t\t\thostname,\n\t\t\tport,\n\t\t\tftps,\n\t\t\tactive,\n\t\t\talias,\n\t\t} = await prompt(...args);\n\t\tconst security = ftps ? \"ftps\" : \"ftp\";\n\t\tconst mode = active ? \"active\" : \"passive\";\n\t\tconst ftp = Ftp(hostname, username, password, port, security, mode);\n\t\ttry {\n\t\t\tloader.create(strings[\"add ftp\"], strings[\"connecting...\"], {\n\t\t\t\ttimeout: 10000,\n\t\t\t\tcallback() {\n\t\t\t\t\tstopConnection = true;\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst [home] = await Promise.all([ftp.getWorkingDirectory(), loadAd()]);\n\n\t\t\tif (stopConnection) {\n\t\t\t\tstopConnection = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst url = Url.formate({\n\t\t\t\tprotocol: \"ftp:\",\n\t\t\t\tusername,\n\t\t\t\tpassword,\n\t\t\t\thostname,\n\t\t\t\tport,\n\t\t\t\tpath: \"/\",\n\t\t\t\tquery: {\n\t\t\t\t\tmode,\n\t\t\t\t\tsecurity,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst res = {\n\t\t\t\turl,\n\t\t\t\talias,\n\t\t\t\tname: alias,\n\t\t\t\ttype: \"ftp\",\n\t\t\t\thome: null,\n\t\t\t};\n\n\t\t\tif (home !== \"/\") {\n\t\t\t\tres.home = home;\n\t\t\t}\n\t\t\tloader.destroy();\n\t\t\tawait helpers.showInterstitialIfReady();\n\t\t\treturn res;\n\t\t} catch (err) {\n\t\t\tif (stopConnection) {\n\t\t\t\tstopConnection = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tloader.destroy();\n\t\t\tawait helpers.error(err);\n\t\t\treturn await this.addFtp(\n\t\t\t\tusername,\n\t\t\t\tpassword,\n\t\t\t\thostname,\n\t\t\t\talias,\n\t\t\t\tport,\n\t\t\t\tsecurity,\n\t\t\t\tmode,\n\t\t\t);\n\t\t}\n\n\t\tfunction prompt(username, password, hostname, alias, port, security, mode) {\n\t\t\tport = port || 21;\n\t\t\tsecurity = security || \"ftp\";\n\t\t\tmode = mode || \"passive\";\n\t\t\treturn multiPrompt(strings[\"add ftp\"], [\n\t\t\t\t{\n\t\t\t\t\tid: \"alias\",\n\t\t\t\t\tplaceholder: strings.name,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tvalue: alias ? alias : \"\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"username\",\n\t\t\t\t\tplaceholder: `${strings.username} (${strings.optional})`,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tvalue: username,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"hostname\",\n\t\t\t\t\tplaceholder: strings.hostname,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tvalue: hostname,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"password\",\n\t\t\t\t\tplaceholder: `${strings.password} (${strings.optional})`,\n\t\t\t\t\ttype: \"password\",\n\t\t\t\t\tvalue: password,\n\t\t\t\t},\n\t\t\t\t[\n\t\t\t\t\t`${strings[\"security type\"]}: `,\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"ftp\",\n\t\t\t\t\t\tplaceholder: \"FTP\",\n\t\t\t\t\t\tname: \"type\",\n\t\t\t\t\t\ttype: \"radio\",\n\t\t\t\t\t\tvalue: security === \"ftp\" ? true : false,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"ftps\",\n\t\t\t\t\t\tplaceholder: \"FTPS\",\n\t\t\t\t\t\tname: \"type\",\n\t\t\t\t\t\ttype: \"radio\",\n\t\t\t\t\t\tvalue: security === \"ftps\" ? true : false,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\t[\n\t\t\t\t\t`${strings[\"connection mode\"]}: `,\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"active\",\n\t\t\t\t\t\tplaceholder: \"Active\",\n\t\t\t\t\t\tname: \"mode\",\n\t\t\t\t\t\ttype: \"radio\",\n\t\t\t\t\t\tvalue: mode === \"active\" ? true : false,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"passive\",\n\t\t\t\t\t\tplaceholder: \"Passive\",\n\t\t\t\t\t\tname: \"mode\",\n\t\t\t\t\t\ttype: \"radio\",\n\t\t\t\t\t\tvalue: mode === \"passive\" ? true : false,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\t{\n\t\t\t\t\tid: \"port\",\n\t\t\t\t\tplaceholder: `${strings.port} (${strings.optional})`,\n\t\t\t\t\ttype: \"number\",\n\t\t\t\t\tvalue: port,\n\t\t\t\t},\n\t\t\t]);\n\t\t}\n\t},\n\t/**\n\t * @param {...any} args [hostname, username, keyFile, password, passphrase, port, name]\n\t */\n\tasync addSftp(...args) {\n\t\tlet stopConnection = false;\n\n\t\tconst {\n\t\t\thostname,\n\t\t\tusername,\n\t\t\tkeyFile,\n\t\t\tpassword,\n\t\t\tpassPhrase,\n\t\t\tport,\n\t\t\talias,\n\t\t\tusePassword,\n\t\t} = await prompt(...args);\n\t\tconst authType = usePassword ? \"password\" : \"keyFile\";\n\n\t\tloader.create(strings[\"add sftp\"], strings[\"connecting...\"], {\n\t\t\ttimeout: 10000,\n\t\t\tcallback() {\n\t\t\t\tstopConnection = true;\n\t\t\t},\n\t\t});\n\t\tconst connection = Sftp(hostname, Number.parseInt(port), username, {\n\t\t\tpassword,\n\t\t\tkeyFile,\n\t\t\tpassPhrase,\n\t\t});\n\n\t\ttry {\n\t\t\tconst [home] = await Promise.all([connection.pwd(), loadAd()]);\n\n\t\t\tif (stopConnection) {\n\t\t\t\tstopConnection = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet localKeyFile = \"\";\n\t\t\tif (keyFile) {\n\t\t\t\tlet fs = fsOperation(keyFile);\n\t\t\t\tconst text = await fs.readFile(\"utf8\");\n\n\t\t\t\t//Original key file sometimes gives permission error\n\t\t\t\t//To solve permission error\n\t\t\t\tconst filename = keyFile.hashCode();\n\t\t\t\tlocalKeyFile = Url.join(DATA_STORAGE, filename);\n\t\t\t\tfs = fsOperation(localKeyFile);\n\t\t\t\tconst exists = await fs.exists();\n\t\t\t\tif (exists) {\n\t\t\t\t\tawait fs.writeFile(text);\n\t\t\t\t} else {\n\t\t\t\t\tlet fs = fsOperation(DATA_STORAGE);\n\t\t\t\t\tawait fs.createFile(filename, text);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst url = Url.formate({\n\t\t\t\tprotocol: \"sftp:\",\n\t\t\t\thostname,\n\t\t\t\tusername,\n\t\t\t\tpassword,\n\t\t\t\tport,\n\t\t\t\tpath: \"/\",\n\t\t\t\tquery: {\n\t\t\t\t\tkeyFile: localKeyFile,\n\t\t\t\t\tpassPhrase,\n\t\t\t\t},\n\t\t\t});\n\t\t\tloader.destroy();\n\t\t\tawait helpers.showInterstitialIfReady();\n\t\t\treturn {\n\t\t\t\talias,\n\t\t\t\tname: alias,\n\t\t\t\turl,\n\t\t\t\ttype: \"sftp\",\n\t\t\t\thome,\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (stopConnection) {\n\t\t\t\tstopConnection = false;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tloader.destroy();\n\t\t\tawait helpers.error(err);\n\t\t\treturn await this.addSftp(\n\t\t\t\thostname,\n\t\t\t\tusername,\n\t\t\t\tkeyFile,\n\t\t\t\tpassword,\n\t\t\t\tpassPhrase,\n\t\t\t\tport,\n\t\t\t\talias,\n\t\t\t\tauthType,\n\t\t\t);\n\t\t}\n\n\t\tfunction prompt(\n\t\t\thostname,\n\t\t\tusername,\n\t\t\tkeyFile,\n\t\t\tpassword,\n\t\t\tpassPhrase,\n\t\t\tport,\n\t\t\talias,\n\t\t\tauthType = \"password\",\n\t\t) {\n\t\t\tport = port || 22;\n\n\t\t\tconst MODE_PASS = authType === \"password\";\n\t\t\tconst inputs = [\n\t\t\t\t{\n\t\t\t\t\tid: \"alias\",\n\t\t\t\t\tplaceholder: strings.name,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tvalue: alias ? alias : \"\",\n\t\t\t\t\trequired: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"username\",\n\t\t\t\t\tplaceholder: `${strings.username} (${strings.optional})`,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tvalue: username,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"hostname\",\n\t\t\t\t\tplaceholder: strings.hostname,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tvalue: hostname,\n\t\t\t\t},\n\t\t\t\t[\n\t\t\t\t\t\"Authentication type: \",\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"usePassword\",\n\t\t\t\t\t\tplaceholder: strings.password,\n\t\t\t\t\t\tname: \"authType\",\n\t\t\t\t\t\ttype: \"radio\",\n\t\t\t\t\t\tvalue: MODE_PASS,\n\t\t\t\t\t\tonchange() {\n\t\t\t\t\t\t\tif (!!this.value) {\n\t\t\t\t\t\t\t\tthis.prompt.$body.get(\"#password\").hidden = false;\n\t\t\t\t\t\t\t\tthis.prompt.$body.get(\"#keyFile\").hidden = true;\n\t\t\t\t\t\t\t\tthis.prompt.$body.get(\"#passPhrase\").hidden = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"useKeyFile\",\n\t\t\t\t\t\tplaceholder: strings[\"key file\"],\n\t\t\t\t\t\tname: \"authType\",\n\t\t\t\t\t\ttype: \"radio\",\n\t\t\t\t\t\tvalue: !MODE_PASS,\n\t\t\t\t\t\tonchange() {\n\t\t\t\t\t\t\tif (!!this.value) {\n\t\t\t\t\t\t\t\tconst $password = this.prompt.$body.get(\"#password\");\n\t\t\t\t\t\t\t\t$password.hidden = true;\n\t\t\t\t\t\t\t\t$password.value = \"\";\n\t\t\t\t\t\t\t\tthis.prompt.$body.get(\"#keyFile\").hidden = false;\n\t\t\t\t\t\t\t\tthis.prompt.$body.get(\"#passPhrase\").hidden = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\t{\n\t\t\t\t\tid: \"password\",\n\t\t\t\t\tplaceholder: strings.password,\n\t\t\t\t\tname: \"password\",\n\t\t\t\t\ttype: \"password\",\n\t\t\t\t\tvalue: password,\n\t\t\t\t\thidden: !MODE_PASS,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"keyFile\",\n\t\t\t\t\tplaceholder: strings[\"select key file\"],\n\t\t\t\t\tname: \"keyFile\",\n\t\t\t\t\thidden: MODE_PASS,\n\t\t\t\t\tvalue: keyFile,\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\tonclick() {\n\t\t\t\t\t\tsdcard.openDocumentFile((res) => {\n\t\t\t\t\t\t\tthis.value = res.uri;\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"passPhrase\",\n\t\t\t\t\tplaceholder: `${strings.passphrase} (${strings.optional})`,\n\t\t\t\t\tname: \"passPhrase\",\n\t\t\t\t\ttype: \"password\",\n\t\t\t\t\thidden: MODE_PASS,\n\t\t\t\t\tvalue: passPhrase,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"port\",\n\t\t\t\t\tplaceholder: `${strings.port} (${strings.optional})`,\n\t\t\t\t\ttype: \"number\",\n\t\t\t\t\tvalue: port,\n\t\t\t\t},\n\t\t\t];\n\n\t\t\treturn multiPrompt(strings[\"add sftp\"], inputs);\n\t\t}\n\t},\n\tedit({ name, storageType, url }) {\n\t\tlet { username, password, hostname, port, query } = URLParse(url, true);\n\n\t\tif (username) {\n\t\t\tusername = decodeURIComponent(username);\n\t\t}\n\n\t\tif (password) {\n\t\t\tpassword = decodeURIComponent(password);\n\t\t}\n\n\t\tif (storageType === \"ftp\") {\n\t\t\tlet { security, mode } = query;\n\t\t\tif (security) {\n\t\t\t\tsecurity = decodeURIComponent(security);\n\t\t\t}\n\n\t\t\tif (mode) {\n\t\t\t\tmode = decodeURIComponent(mode);\n\t\t\t}\n\n\t\t\treturn this.addFtp(\n\t\t\t\tusername,\n\t\t\t\tpassword,\n\t\t\t\thostname,\n\t\t\t\tname,\n\t\t\t\tport,\n\t\t\t\tsecurity,\n\t\t\t\tmode,\n\t\t\t);\n\t\t}\n\n\t\tif (storageType === \"sftp\") {\n\t\t\tlet { passPhrase, keyFile } = query;\n\t\t\tif (passPhrase) {\n\t\t\t\tpassPhrase = decodeURIComponent(passPhrase);\n\t\t\t}\n\n\t\t\tif (keyFile) {\n\t\t\t\tkeyFile = decodeURIComponent(keyFile);\n\t\t\t}\n\n\t\t\treturn this.addSftp(\n\t\t\t\thostname,\n\t\t\t\tusername,\n\t\t\t\tkeyFile,\n\t\t\t\tpassword,\n\t\t\t\tpassPhrase,\n\t\t\t\tport,\n\t\t\t\tname,\n\t\t\t\tpassword ? \"password\" : \"key\",\n\t\t\t);\n\t\t}\n\n\t\treturn null;\n\t},\n};\n\nasync function loadAd() {\n\tif (!helpers.canShowAds()) return;\n\ttry {\n\t\tif (!(await window.iad?.isLoaded())) {\n\t\t\ttoast(strings.loading);\n\t\t\tawait window.iad.load();\n\t\t}\n\t} catch (error) {\n\t\tconsole.warn(\"Failed to load interstitial ad.\", error);\n\t}\n}\n"
  },
  {
    "path": "src/lib/removeAds.js",
    "content": "import purchaseListener from \"handlers/purchase\";\nimport { hideAd } from \"./startAd.js\";\n\n/**\n * Remove ads after purchase\n * @returns {Promise<void>}\n */\nexport default function removeAds() {\n\treturn new Promise((resolve, reject) => {\n\t\tiap.getProducts([\"acode_pro_new\"], (products) => {\n\t\t\tconst [product] = products;\n\n\t\t\tiap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, reject));\n\n\t\t\tiap.purchase(\n\t\t\t\tproduct.productId,\n\t\t\t\t(code) => {\n\t\t\t\t\t// ignore\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\talert(strings.error, err);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\n\t\tfunction onpurchase() {\n\t\t\tresolve(null);\n\t\t\thideAd(true);\n\t\t\tlocalStorage.setItem(\"acode_pro\", \"true\");\n\t\t\twindow.IS_FREE_VERSION = false;\n\t\t\ttoast(strings[\"thank you :)\"]);\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "src/lib/restoreFiles.js",
    "content": "import EditorFile from \"./editorFile\";\n\n/**\n *\n * @param {import('./editorFile').FileOptions[]} files\n * @param {(count: number)=>void} callback\n */\nexport default async function restoreFiles(files) {\n\tlet rendered = false;\n\n\tawait Promise.all(\n\t\tfiles.map(async (file, i) => {\n\t\t\trendered ||= !!file.render;\n\n\t\t\tif (i === files.length - 1 && !rendered) {\n\t\t\t\tfile.render = true;\n\t\t\t}\n\n\t\t\tconst { filename, render = false } = file;\n\t\t\tconst options = {\n\t\t\t\t...file,\n\t\t\t\trender,\n\t\t\t\temitUpdate: false,\n\t\t\t};\n\t\t\tnew EditorFile(filename, options);\n\t\t}),\n\t);\n}\n"
  },
  {
    "path": "src/lib/restoreTheme.js",
    "content": "import themes from \"theme/list\";\nimport Color from \"utils/color\";\nimport appSettings from \"./settings\";\n\nlet count = 0;\n\n/**\n * Restores the theme or darkens the status bar and navigation bar\n * Used when dialogs are opened which has mask that darkens the background\n * @param {boolean} darken Whether to darken the status bar and navigation bar\n * @returns\n */\nexport default function restoreTheme(darken = false) {\n\tif (!count && !darken) return;\n\tcount += darken ? 1 : -1;\n\tif (darken !== !!count) return;\n\tif (darken && document.body.classList.contains(\"loading\")) return;\n\n\tlet themeName = DOES_SUPPORT_THEME ? appSettings.value.appTheme : \"default\";\n\tlet theme = themes.get(themeName);\n\n\tif (theme?.version !== \"free\" && IS_FREE_VERSION) {\n\t\tthemeName = \"default\";\n\t\ttheme = themes.get(themeName);\n\t\tappSettings.value.appTheme = themeName;\n\t\tappSettings.update();\n\t}\n\n\tif (\n\t\t!theme.darkenedPrimaryColor ||\n\t\ttheme.darkenedPrimaryColor === theme.primaryColor\n\t) {\n\t\ttheme.darkenPrimaryColor();\n\t}\n\tconst color = darken ? theme.darkenedPrimaryColor : theme.primaryColor;\n\tconst hexColor = Color(color).hex.toString();\n\tsystem.setUiTheme(hexColor, theme.toJSON(\"hex\"));\n}\n"
  },
  {
    "path": "src/lib/run.js",
    "content": "import fsOperation from \"fileSystem\";\nimport tutorial from \"components/tutorial\";\nimport alert from \"dialogs/alert\";\nimport box from \"dialogs/box\";\nimport markdownIt from \"markdown-it\";\nimport anchor from \"markdown-it-anchor\";\nimport MarkdownItGitHubAlerts from \"markdown-it-github-alerts\";\nimport mimeType from \"mime-types\";\nimport mustache from \"mustache\";\nimport openMarkdownPreview from \"pages/markdownPreview\";\nimport browser from \"plugins/browser\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport $_console from \"views/console.hbs\";\nimport $_markdown from \"views/markdown.hbs\";\nimport constants from \"./constants\";\nimport EditorFile from \"./editorFile\";\nimport openFolder from \"./openFolder\";\nimport appSettings from \"./settings\";\n\n/**@type {Server} */\nlet webServer;\n\n/**\n * Starts the server and run the active file in browser\n * @param {Boolean} isConsole\n * @param {\"inapp\"|\"browser\"} target\n * @param {Boolean} runFile\n */\nasync function run(\n\tisConsole = false,\n\ttarget = appSettings.value.previewMode,\n\trunFile = false,\n) {\n\t/** @type {EditorFile} */\n\tconst activeFile = isConsole ? null : editorManager.activeFile;\n\n\tif (!isConsole && Url.extname(activeFile?.filename || \"\") === \".md\") {\n\t\tif (!(await activeFile?.canRun())) return;\n\t\tawait openMarkdownPreview(activeFile);\n\t\treturn;\n\t}\n\n\tif (!isConsole && !runFile) {\n\t\tconst { serverPort, previewPort, previewMode, disableCache, host } =\n\t\t\tappSettings.value;\n\t\tif (serverPort !== previewPort) {\n\t\t\tconst src = `http://${host}:${previewPort}`;\n\t\t\tif (previewMode === \"browser\") {\n\t\t\t\tsystem.openInBrowser(src);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbrowser.open(src);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (!isConsole && !(await activeFile?.canRun())) return;\n\n\tif (!isConsole && !localStorage.__init_runPreview) {\n\t\tlocalStorage.__init_runPreview = true;\n\t\ttutorial(\"run-preview\", strings[\"preview info\"]);\n\t}\n\n\tconst uuid = helpers.uuid();\n\n\tlet isLoading = false;\n\tlet filename, pathName, extension;\n\tlet port = appSettings.value.serverPort;\n\tlet EXECUTING_SCRIPT = uuid + \"_script.js\";\n\tconst MIMETYPE_HTML = mimeType.lookup(\"html\");\n\tconst CONSOLE_SCRIPT = uuid + \"_console.js\";\n\tconst MARKDOWN_STYLE = uuid + \"_md.css\";\n\tconst queue = [];\n\n\tif (activeFile) {\n\t\tfilename = activeFile.filename;\n\t\tpathName = activeFile.location;\n\t\textension = Url.extname(filename);\n\n\t\tif (!pathName && activeFile.uri) {\n\t\t\tpathName = Url.dirname(activeFile.uri);\n\t\t}\n\t}\n\n\tif (runFile && extension === \"svg\") {\n\t\ttry {\n\t\t\tconst fs = fsOperation(activeFile.uri);\n\t\t\tconst res = await fs.readFile();\n\t\t\tconst blob = new Blob([new Uint8Array(res)], {\n\t\t\t\ttype: mimeType.lookup(extension),\n\t\t\t});\n\n\t\t\tbox(filename, `<img src='${URL.createObjectURL(blob)}'>`);\n\t\t} catch (err) {\n\t\t\thelpers.error(err);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (!runFile && filename !== \"index.html\" && pathName) {\n\t\tconst folder = openFolder.find(activeFile.uri);\n\n\t\tif (folder) {\n\t\t\tconst { url } = folder;\n\t\t\tconst fs = fsOperation(Url.join(url, \"index.html\"));\n\n\t\t\ttry {\n\t\t\t\tif (await fs.exists()) {\n\t\t\t\t\tfilename = \"index.html\";\n\t\t\t\t\textension = \"html\";\n\t\t\t\t\tpathName = url;\n\t\t\t\t\tstart();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tnext();\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\thelpers.error(err);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tnext();\n\n\tfunction next() {\n\t\tif (extension === \".js\" || isConsole) startConsole();\n\t\telse start();\n\t}\n\n\tfunction startConsole() {\n\t\trunConsole();\n\t\tstart();\n\t}\n\n\tfunction runConsole() {\n\t\tif (!isConsole) EXECUTING_SCRIPT = activeFile.filename;\n\t\tisConsole = true;\n\t\ttarget = \"inapp\";\n\t\tfilename = \"console.html\";\n\t\tpathName = `${ASSETS_DIRECTORY}www/`;\n\t\tport = constants.CONSOLE_PORT;\n\t}\n\n\tfunction start() {\n\t\tif (target === \"browser\") {\n\t\t\tsystem.isPowerSaveMode((res) => {\n\t\t\t\tif (res) {\n\t\t\t\t\talert(strings.info, strings[\"powersave mode warning\"]);\n\t\t\t\t} else {\n\t\t\t\t\tstartServer();\n\t\t\t\t}\n\t\t\t}, startServer);\n\t\t} else {\n\t\t\tstartServer();\n\t\t}\n\t}\n\n\tfunction startServer() {\n\t\twebServer?.stop();\n\t\twebServer = CreateServer(port, openBrowser, onError);\n\t\twebServer.setOnRequestHandler(handleRequest);\n\n\t\tfunction onError(err) {\n\t\t\tif (err === \"Server already running\") {\n\t\t\t\topenBrowser();\n\t\t\t} else {\n\t\t\t\t++port;\n\t\t\t\tstart();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Requests handler\n\t * @param {object} req\n\t * @param {string} req.requestId\n\t * @param {string} req.path\n\t */\n\tfunction handleRequest(req) {\n\t\tconst reqId = req.requestId;\n\t\tlet reqPath = req.path.substring(1);\n\n\t\tif (!reqPath || reqPath.endsWith(\"/\")) {\n\t\t\treqPath += \"index.html\";\n\t\t}\n\n\t\tconst ext = Url.extname(reqPath);\n\t\tlet url = null;\n\n\t\tswitch (reqPath) {\n\t\t\tcase CONSOLE_SCRIPT:\n\t\t\t\tif (\n\t\t\t\t\tisConsole ||\n\t\t\t\t\tappSettings.value.console === appSettings.CONSOLE_LEGACY\n\t\t\t\t) {\n\t\t\t\t\turl = `${ASSETS_DIRECTORY}/build/console.js`;\n\t\t\t\t} else {\n\t\t\t\t\turl = `${DATA_STORAGE}/eruda.js`;\n\t\t\t\t}\n\t\t\t\tsendFileContent(url, reqId, \"application/javascript\");\n\t\t\t\tbreak;\n\n\t\t\tcase EXECUTING_SCRIPT: {\n\t\t\t\tconst text = activeFile?.session?.doc?.toString() || \"\";\n\t\t\t\tsendText(text, reqId, \"application/javascript\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MARKDOWN_STYLE:\n\t\t\t\turl = appSettings.value.markdownStyle;\n\t\t\t\tif (url) sendFileContent(url, reqId, \"text/css\");\n\t\t\t\telse sendText(\"img {max-width: 100%;}\", reqId, \"text/css\");\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tsendByExt();\n\t\t\t\tbreak;\n\t\t}\n\n\t\tasync function sendByExt() {\n\t\t\tif (isConsole) {\n\t\t\t\tif (reqPath === \"console.html\") {\n\t\t\t\t\tsendText(\n\t\t\t\t\t\tmustache.render($_console, {\n\t\t\t\t\t\t\tCONSOLE_SCRIPT,\n\t\t\t\t\t\t\tEXECUTING_SCRIPT,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\treqId,\n\t\t\t\t\t\tMIMETYPE_HTML,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (reqPath === \"favicon.ico\") {\n\t\t\t\t\tsendIco(ASSETS_DIRECTORY, reqId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (activeFile.mode === \"single\") {\n\t\t\t\tif (filename === reqPath) {\n\t\t\t\t\tsendText(\n\t\t\t\t\t\tactiveFile.session?.doc?.toString(),\n\t\t\t\t\t\treqId,\n\t\t\t\t\t\tmimeType.lookup(filename),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\terror(reqId);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet url = activeFile.uri;\n\t\t\tlet file = activeFile.SAFMode === \"single\" ? activeFile : null;\n\n\t\t\tif (pathName) {\n\t\t\t\turl = Url.join(pathName, reqPath);\n\t\t\t\tfile = editorManager.getFile(url, \"uri\");\n\t\t\t} else if (!activeFile.uri) {\n\t\t\t\tfile = activeFile;\n\t\t\t}\n\n\t\t\t// Handle extensionless URLs (e.g., \"about\" -> \"about.html\" or \"about/index.html\")\n\t\t\tif (!ext && pathName) {\n\t\t\t\t// Try exact match first for extensionless files (LICENSE, README, etc.)\n\t\t\t\tconst exactUrl = Url.join(pathName, reqPath);\n\t\t\t\tconst exactFs = fsOperation(exactUrl);\n\t\t\t\tif (await exactFs.exists()) {\n\t\t\t\t\tsendFile(exactUrl, reqId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Try path.html\n\t\t\t\tconst htmlUrl = Url.join(pathName, reqPath + \".html\");\n\t\t\t\tconst htmlFile = editorManager.getFile(htmlUrl, \"uri\");\n\t\t\t\tif (htmlFile?.loaded && htmlFile.isUnsaved) {\n\t\t\t\t\tsendHTML(htmlFile.session?.doc?.toString(), reqId);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst htmlFs = fsOperation(htmlUrl);\n\t\t\t\tif (await htmlFs.exists()) {\n\t\t\t\t\tsendFileContent(htmlUrl, reqId, MIMETYPE_HTML);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Try path/index.html\n\t\t\t\tconst indexUrl = Url.join(pathName, reqPath, \"index.html\");\n\t\t\t\tconst indexFs = fsOperation(indexUrl);\n\t\t\t\tif (await indexFs.exists()) {\n\t\t\t\t\tsendFileContent(indexUrl, reqId, MIMETYPE_HTML);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\terror(reqId);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tswitch (ext) {\n\t\t\t\tcase \".htm\":\n\t\t\t\tcase \".html\":\n\t\t\t\t\tif (file && file.loaded && file.isUnsaved) {\n\t\t\t\t\t\tsendHTML(file.session?.doc?.toString(), reqId);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsendFileContent(url, reqId, MIMETYPE_HTML);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \".md\":\n\t\t\t\t\tif (file) {\n\t\t\t\t\t\tconst html = markdownIt({ html: true })\n\t\t\t\t\t\t\t.use(MarkdownItGitHubAlerts)\n\t\t\t\t\t\t\t.use(anchor, {\n\t\t\t\t\t\t\t\tslugify: (s) =>\n\t\t\t\t\t\t\t\t\ts\n\t\t\t\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t\t\t\t.replace(/[^a-z0-9]+/g, \"-\"),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.render(file.session?.doc?.toString());\n\t\t\t\t\t\tconst doc = mustache.render($_markdown, {\n\t\t\t\t\t\t\thtml,\n\t\t\t\t\t\t\tfilename,\n\t\t\t\t\t\t\tMARKDOWN_STYLE,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tsendText(doc, reqId, MIMETYPE_HTML);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (file && file.loaded && file.isUnsaved) {\n\t\t\t\t\t\tsendText(\n\t\t\t\t\t\t\tfile.session?.doc?.toString(),\n\t\t\t\t\t\t\treqId,\n\t\t\t\t\t\t\tmimeType.lookup(file.filename),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else if (url) {\n\t\t\t\t\t\tif (reqPath === \"favicon.ico\") {\n\t\t\t\t\t\t\tsendIco(ASSETS_DIRECTORY, reqId);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsendFile(url, reqId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\terror(reqId);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sends 404 error\n\t * @param {string} id\n\t */\n\tfunction error(id) {\n\t\twebServer?.send(id, {\n\t\t\tstatus: 404,\n\t\t\tbody: \"File not found!\",\n\t\t});\n\t}\n\n\t/**\n\t * Sends favicon\n\t * @param {string} assets\n\t * @param {string} reqId\n\t */\n\tfunction sendIco(assets, reqId) {\n\t\tconst ico = Url.join(assets, \"res/logo/favicon.ico\");\n\t\tsendFile(ico, reqId);\n\t}\n\n\t/**\n\t * Sends HTML file\n\t * @param {string} text\n\t * @param {string} id\n\t */\n\tfunction sendHTML(text, id) {\n\t\tconst js = `<!-- Injected code, this is not present in original code --><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <script class=\"${uuid}\" src=\"/${CONSOLE_SCRIPT}\" crossorigin=\"anonymous\"></script>\n    <script class=\"${uuid}\">\n      if(window.eruda){\n        eruda.init({\n          theme: 'dark'\n        });\n\n        ${\n\t\t\t\t\ttarget === \"inapp\"\n\t\t\t\t\t\t? \"eruda._shadowRoot.querySelector('.eruda-entry-btn').style.display = 'none';\"\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n\n        sessionStorage.setItem('__console_available', true);\n        document.addEventListener('showconsole', function () {eruda.show()});\n        document.addEventListener('hideconsole', function () {eruda.hide()});\n      }else if(document.querySelector('c-toggler')){\n        ${\n\t\t\t\t\ttarget === \"inapp\" ||\n\t\t\t\t\t(target !== \"inapp\" && !appSettings.value.showConsoleToggler)\n\t\t\t\t\t\t? \"document.querySelector('c-toggler').style.display = 'none';\"\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n      }\n      setTimeout(function(){\n        var scripts = document.querySelectorAll('.${uuid}');\n        scripts.forEach(function(el){document.head.removeChild(el)});\n      }, 0);\n    </script><!-- Injected code, this is not present in original code -->`;\n\t\ttext = text.replace(/><\\/script>/g, ' crossorigin=\"anonymous\"></script>');\n\t\tconst part = text.split(\"<head>\");\n\t\tif (part.length === 2) {\n\t\t\ttext = `${part[0]}<head>${js}${part[1]}`;\n\t\t} else if (/<html>/i.test(text)) {\n\t\t\ttext = text.replace(\"<html>\", `<html><head>${js}</head>`);\n\t\t} else {\n\t\t\ttext = `<head>${js}</head>` + text;\n\t\t}\n\t\tsendText(text, id);\n\t}\n\n\t/**\n\t * Sends file\n\t * @param {string} path\n\t * @param {string} id\n\t * @returns\n\t */\n\tasync function sendFile(path, id) {\n\t\tif (isLoading) {\n\t\t\tqueue.push(() => {\n\t\t\t\tsendFile(path, id);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tisLoading = true;\n\t\tconst protocol = Url.getProtocol(path);\n\t\tconst ext = Url.extname(path);\n\t\tconst mimetype = mimeType.lookup(ext);\n\t\tif (/s?ftp:/.test(protocol)) {\n\t\t\tconst cacheFile = Url.join(\n\t\t\t\tCACHE_STORAGE,\n\t\t\t\tprotocol.slice(0, -1) + path.hashCode(),\n\t\t\t);\n\t\t\tconst fs = fsOperation(path);\n\t\t\ttry {\n\t\t\t\tawait fs.readFile(); // Because reading the remote file will create cache file\n\t\t\t\tpath = cacheFile;\n\t\t\t} catch (err) {\n\t\t\t\terror(id);\n\t\t\t\tisLoading = false;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (protocol === \"content:\") {\n\t\t\tpath = await new Promise((resolve, reject) => {\n\t\t\t\tsdcard.formatUri(path, resolve, reject);\n\t\t\t});\n\t\t} else if (!/^file:/.test(protocol)) {\n\t\t\tconst fileContent = await fsOperation(path).readFile();\n\t\t\tconst tempFileName = path.hashCode();\n\t\t\tconst tempFile = Url.join(CACHE_STORAGE, tempFileName);\n\t\t\tif (!(await fsOperation(tempFile).exists())) {\n\t\t\t\tawait fsOperation(CACHE_STORAGE).createFile(tempFileName, fileContent);\n\t\t\t} else {\n\t\t\t\tawait fsOperation(tempFile).writeFile(fileContent);\n\t\t\t}\n\t\t\tpath = tempFile;\n\t\t}\n\n\t\twebServer?.send(id, {\n\t\t\tstatus: 200,\n\t\t\tpath,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": mimetype,\n\t\t\t},\n\t\t});\n\n\t\tisLoading = false;\n\t\tconst action = queue.splice(-1, 1)[0];\n\t\tif (typeof action === \"function\") action();\n\t}\n\n\t/**\n\t * Sends file content\n\t * @param {string} url\n\t * @param {string} id\n\t * @param {string} mime\n\t * @param {(txt: string) => string} processText\n\t * @returns\n\t */\n\tasync function sendFileContent(url, id, mime, processText) {\n\t\tconst fs = fsOperation(url);\n\n\t\tif (!(await fs.exists())) {\n\t\t\terror(id);\n\t\t\treturn;\n\t\t}\n\n\t\tlet text = await fs.readFile(appSettings.value.defaultFileEncoding);\n\t\ttext = processText ? processText(text) : text;\n\t\tif (mime === MIMETYPE_HTML) {\n\t\t\tsendHTML(text, id);\n\t\t} else {\n\t\t\tsendText(text, id, mime);\n\t\t}\n\t}\n\n\t/**\n\t * Sends text\n\t * @param {string} text\n\t * @param {string} id\n\t * @param {string} mimeType\n\t * @param {(txt: string) => string} processText\n\t */\n\tfunction sendText(text, id, mimeType, processText) {\n\t\twebServer?.send(id, {\n\t\t\tstatus: 200,\n\t\t\tbody: processText ? processText(text) : text,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": mimeType || \"text/html\",\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Opens the preview in browser\n\t */\n\tfunction openBrowser() {\n\t\tconsole.count(\"openBrowser\");\n\t\tconst src = `http://localhost:${port}/${filename}`;\n\t\tif (target === \"browser\") {\n\t\t\tsystem.openInBrowser(src);\n\t\t\treturn;\n\t\t}\n\n\t\tbrowser.open(src, isConsole);\n\t}\n}\n\nexport default run;\n"
  },
  {
    "path": "src/lib/saveFile.js",
    "content": "import fsOperation from \"fileSystem\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport recents from \"lib/recents\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\nimport EditorFile from \"./editorFile\";\nimport openFolder from \"./openFolder\";\nimport appSettings from \"./settings\";\n\nlet saveTimeout;\n\nconst SELECT_FOLDER = \"select-folder\";\n\n/**\n * Saves a file to it's location, if file is new, it will ask for location\n * @param {EditorFile} file\n * @param {boolean} [isSaveAs]\n */\nasync function saveFile(file, isSaveAs = false) {\n\t// If file is loading, return\n\tif (file.loading) return;\n\n\t/**\n\t * If set, new file needs to be created\n\t * @type {string}\n\t */\n\tlet newUrl;\n\t/**\n\t * File operation object\n\t * @type {fsOperation}\n\t */\n\tlet fileOnDevice;\n\t/**\n\t * File name, can be changed by user\n\t * @type {string}\n\t */\n\tlet { filename } = file;\n\t/**\n\t * If file is new\n\t * @type {boolean}\n\t */\n\tlet isNewFile = false;\n\n\t/**\n\t * Encoding of file\n\t * @type {string}\n\t */\n\tconst { encoding } = file;\n\t/**\n\t * File data\n\t * @type {string}\n\t */\n\tconst data = file.session ? file.session.doc.toString() : \"\";\n\t/**\n\t * File tab bar text element, used to show saving status\n\t * @type {HTMLElement}\n\t */\n\tconst $text = file.tab.querySelector(\"span.text\");\n\n\tif (!file.uri) {\n\t\tisNewFile = true;\n\t} else {\n\t\tisSaveAs = isSaveAs ?? file.readOnly;\n\t}\n\n\tif (isSaveAs || isNewFile) {\n\t\tconst option = await recents.select(\n\t\t\t[[SELECT_FOLDER, strings[\"select folder\"], \"folder\"]], // options\n\t\t\t\"dir\", // type\n\t\t\tstrings[\"select folder\"], // title\n\t\t);\n\n\t\tif (option === SELECT_FOLDER) {\n\t\t\tnewUrl = await selectFolder();\n\t\t} else {\n\t\t\tnewUrl = option.val.url;\n\t\t}\n\n\t\tif (isSaveAs) {\n\t\t\tfilename = await getfilename(newUrl, file.filename);\n\t\t} else {\n\t\t\tfilename = await check(newUrl, file.filename);\n\t\t}\n\n\t\t// in case if user cancels the dialog\n\t\tif (!filename) return;\n\t}\n\n\tif (filename !== file.filename) {\n\t\tfile.filename = filename;\n\t}\n\n\t$text.textContent = strings.saving + \"...\";\n\tfile.isSaving = true;\n\n\ttry {\n\t\tif (isSaveAs || newUrl) {\n\t\t\t// if save as or new file\n\t\t\tconst fileUri = Url.join(newUrl, file.filename);\n\t\t\tfileOnDevice = fsOperation(fileUri);\n\n\t\t\tif (!(await fileOnDevice.exists())) {\n\t\t\t\tawait fsOperation(newUrl).createFile(file.filename);\n\t\t\t}\n\n\t\t\tconst openedFile = editorManager.getFile(fileUri, \"uri\");\n\t\t\tif (openedFile) openedFile.uri = null;\n\t\t\tfile.uri = fileUri;\n\t\t\trecents.addFile(fileUri);\n\n\t\t\tconst folder = openFolder.find(newUrl);\n\t\t\tif (folder) folder.reload();\n\t\t}\n\n\t\tif (!fileOnDevice) {\n\t\t\tfileOnDevice = fsOperation(file.uri);\n\t\t}\n\n\t\tif (appSettings.value.formatOnSave) {\n\t\t\teditorManager.activeFile.markChanged = false;\n\t\t\tacode.exec(\"format\", false);\n\t\t}\n\n\t\tawait fileOnDevice.writeFile(data, encoding);\n\n\t\tif (file.location) {\n\t\t\trecents.addFolder(file.location);\n\t\t}\n\n\t\tclearTimeout(saveTimeout);\n\t\tsaveTimeout = setTimeout(() => {\n\t\t\tfile.isSaving = false;\n\t\t\tfile.isUnsaved = false;\n\t\t\tif (newUrl) recents.addFile(file.uri);\n\t\t\teditorManager.onupdate(\"save-file\");\n\t\t\teditorManager.emit(\"update\", \"save-file\");\n\t\t\teditorManager.emit(\"save-file\", file);\n\t\t\tresetText();\n\t\t}, editorManager.TIMEOUT_VALUE + 100);\n\t} catch (err) {\n\t\thelpers.error(err);\n\t}\n\tresetText();\n\n\tfunction resetText() {\n\t\tsetTimeout(() => {\n\t\t\t$text.textContent = file.filename;\n\t\t}, editorManager.TIMEOUT_VALUE);\n\t}\n\n\tasync function selectFolder() {\n\t\tconst dir = await FileBrowser(\n\t\t\t\"folder\",\n\t\t\tstrings[`save file${isSaveAs ? \" as\" : \"\"}`],\n\t\t);\n\t\treturn dir.url;\n\t}\n\n\tasync function getfilename(url, name) {\n\t\tlet filename = await prompt(\n\t\t\tstrings[\"enter file name\"],\n\t\t\tname || \"\",\n\t\t\tstrings[\"new file\"],\n\t\t\t{\n\t\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t);\n\n\t\tfilename = helpers.fixFilename(filename);\n\t\tif (!filename) return null;\n\t\treturn await check(url, filename);\n\t}\n\n\tasync function check(url, filename) {\n\t\tconst pathname = Url.join(url, filename);\n\n\t\tconst fs = fsOperation(pathname);\n\t\tif (!(await fs.exists())) return filename;\n\n\t\tconst action = await select(strings[\"file already exists\"], [\n\t\t\t[\"overwrite\", strings.overwrite],\n\t\t\t[\"newname\", strings[\"enter file name\"]],\n\t\t]);\n\n\t\tif (action === \"newname\") {\n\t\t\tfilename = await getfilename(url, filename);\n\t\t}\n\n\t\treturn filename;\n\t}\n}\n\nexport default saveFile;\n"
  },
  {
    "path": "src/lib/saveState.js",
    "content": "import { getAllFolds, getScrollPosition, getSelection } from \"cm/editorUtils\";\nimport constants from \"./constants\";\nimport { addedFolder } from \"./openFolder\";\nimport appSettings from \"./settings\";\n\nexport default () => {\n\tif (!window.editorManager) return;\n\n\tconst filesToSave = [];\n\tconst folders = [];\n\tconst { editor, files, activeFile } = editorManager;\n\tconst { value: settings } = appSettings;\n\n\tfiles.forEach((file) => {\n\t\tif (file.type !== \"editor\") return;\n\t\tif (file.id === constants.DEFAULT_FILE_SESSION) return;\n\t\tif (file.SAFMode === \"single\") return;\n\n\t\t// Selection per file:\n\t\t// - Active file uses live EditorView selection\n\t\t// - Inactive files use their persisted EditorState selection\n\t\tlet cursorPos;\n\t\tif (activeFile?.id === file.id) {\n\t\t\tcursorPos = getSelection(editor);\n\t\t} else {\n\t\t\tconst sel = file.session?.selection;\n\t\t\tif (sel) {\n\t\t\t\tcursorPos = {\n\t\t\t\t\tranges: sel.ranges.map((r) => ({ from: r.from, to: r.to })),\n\t\t\t\t\tmainIndex: sel.mainIndex ?? 0,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tcursorPos = null;\n\t\t\t}\n\t\t}\n\n\t\t// Scroll per file:\n\t\t// - Active file uses live scroll from EditorView\n\t\t// - Inactive files use lastScrollTop/Left captured on tab switch\n\t\tlet scrollTop, scrollLeft;\n\t\tif (activeFile?.id === file.id) {\n\t\t\tconst sp = getScrollPosition(editor);\n\t\t\tscrollTop = sp.scrollTop;\n\t\t\tscrollLeft = sp.scrollLeft;\n\t\t} else {\n\t\t\tscrollTop =\n\t\t\t\ttypeof file.lastScrollTop === \"number\" ? file.lastScrollTop : 0;\n\t\t\tscrollLeft =\n\t\t\t\ttypeof file.lastScrollLeft === \"number\" ? file.lastScrollLeft : 0;\n\t\t}\n\n\t\tconst fileJson = {\n\t\t\tid: file.id,\n\t\t\turi: file.uri,\n\t\t\ttype: file.type,\n\t\t\tfilename: file.filename,\n\t\t\tpinned: file.pinned,\n\t\t\tisUnsaved: file.isUnsaved,\n\t\t\treadOnly: file.readOnly,\n\t\t\tSAFMode: file.SAFMode,\n\t\t\tdeletedFile: file.deletedFile,\n\t\t\tcursorPos,\n\t\t\tscrollTop,\n\t\t\tscrollLeft,\n\t\t\teditable: file.editable,\n\t\t\tencoding: file.encoding,\n\t\t\trender: activeFile?.id === file.id,\n\t\t\tfolds: getAllFolds(file.session),\n\t\t};\n\n\t\tif (settings.rememberFiles || fileJson.isUnsaved)\n\t\t\tfilesToSave.push(fileJson);\n\t});\n\n\tif (settings.rememberFolders) {\n\t\taddedFolder.forEach((folder) => {\n\t\t\tconst { url, saveState, title, listState, listFiles } = folder;\n\t\t\tfolders.push({\n\t\t\t\turl,\n\t\t\t\topts: {\n\t\t\t\t\tsaveState,\n\t\t\t\t\tname: title,\n\t\t\t\t\tlistState,\n\t\t\t\t\tlistFiles,\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\tlocalStorage.files = JSON.stringify(filesToSave);\n\tlocalStorage.folders = JSON.stringify(folders);\n};\n"
  },
  {
    "path": "src/lib/searchHistory.js",
    "content": "/**\n * Search and Replace History Manager\n * Manages search/replace history using localStorage\n */\n\nconst HISTORY_KEY = \"acode.searchreplace.history\";\nconst MAX_HISTORY_ITEMS = 20;\n\nclass SearchHistory {\n\tconstructor() {\n\t\tthis.history = this.loadHistory(HISTORY_KEY);\n\t\tthis.searchIndex = -1; // Current position in history for search input\n\t\tthis.replaceIndex = -1; // Current position in history for replace input\n\t\tthis.tempSearchValue = \"\"; // Temporary storage for current search input\n\t\tthis.tempReplaceValue = \"\"; // Temporary storage for current replace input\n\t}\n\n\t/**\n\t * Load history from localStorage\n\t * @param {string} key Storage key\n\t * @returns {Array<string>} History items\n\t */\n\tloadHistory(key) {\n\t\ttry {\n\t\t\tconst stored = localStorage.getItem(key);\n\t\t\treturn stored ? JSON.parse(stored) : [];\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to load search history:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Save history to localStorage\n\t */\n\tsaveHistory() {\n\t\ttry {\n\t\t\tlocalStorage.setItem(HISTORY_KEY, JSON.stringify(this.history));\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to save search history:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Add item to history\n\t * @param {string} item Item to add\n\t */\n\taddToHistory(item) {\n\t\tif (!item || typeof item !== \"string\" || item.trim().length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst trimmedItem = item.trim();\n\n\t\t// Remove existing item if present\n\t\tthis.history = this.history.filter((h) => h !== trimmedItem);\n\n\t\t// Add to beginning\n\t\tthis.history.unshift(trimmedItem);\n\n\t\t// Limit history size\n\t\tthis.history = this.history.slice(0, MAX_HISTORY_ITEMS);\n\n\t\tthis.saveHistory();\n\t}\n\n\t/**\n\t * Get history\n\t * @returns {Array<string>} History items\n\t */\n\tgetHistory() {\n\t\treturn [...this.history];\n\t}\n\n\t/**\n\t * Clear all history\n\t */\n\tclearHistory() {\n\t\tthis.history = [];\n\t\tthis.saveHistory();\n\t}\n\n\t/**\n\t * Navigate up in search history (terminal-like)\n\t * @param {string} currentValue Current input value\n\t * @returns {string} Previous history item or current value\n\t */\n\tnavigateSearchUp(currentValue) {\n\t\tif (this.history.length === 0) return currentValue;\n\n\t\t// Store current value if we're at the beginning\n\t\tif (this.searchIndex === -1) {\n\t\t\tthis.tempSearchValue = currentValue;\n\t\t\tthis.searchIndex = this.history.length - 1;\n\t\t} else if (this.searchIndex > 0) {\n\t\t\tthis.searchIndex--;\n\t\t}\n\n\t\treturn this.history[this.searchIndex] || currentValue;\n\t}\n\n\t/**\n\t * Navigate down in search history (terminal-like)\n\t * @param {string} currentValue Current input value\n\t * @returns {string} Next history item or original value\n\t */\n\tnavigateSearchDown(currentValue) {\n\t\tif (this.history.length === 0 || this.searchIndex === -1) {\n\t\t\treturn currentValue;\n\t\t}\n\n\t\tthis.searchIndex++;\n\n\t\t// If we've gone past the end, return to original value\n\t\tif (this.searchIndex >= this.history.length) {\n\t\t\tthis.searchIndex = -1;\n\t\t\treturn this.tempSearchValue;\n\t\t}\n\n\t\treturn this.history[this.searchIndex];\n\t}\n\n\t/**\n\t * Navigate up in replace history (terminal-like)\n\t * @param {string} currentValue Current input value\n\t * @returns {string} Previous history item or current value\n\t */\n\tnavigateReplaceUp(currentValue) {\n\t\tif (this.history.length === 0) return currentValue;\n\n\t\t// Store current value if we're at the beginning\n\t\tif (this.replaceIndex === -1) {\n\t\t\tthis.tempReplaceValue = currentValue;\n\t\t\tthis.replaceIndex = this.history.length - 1;\n\t\t} else if (this.replaceIndex > 0) {\n\t\t\tthis.replaceIndex--;\n\t\t}\n\n\t\treturn this.history[this.replaceIndex] || currentValue;\n\t}\n\n\t/**\n\t * Navigate down in replace history (terminal-like)\n\t * @param {string} currentValue Current input value\n\t * @returns {string} Next history item or original value\n\t */\n\tnavigateReplaceDown(currentValue) {\n\t\tif (this.history.length === 0 || this.replaceIndex === -1) {\n\t\t\treturn currentValue;\n\t\t}\n\n\t\tthis.replaceIndex++;\n\n\t\t// If we've gone past the end, return to original value\n\t\tif (this.replaceIndex >= this.history.length) {\n\t\t\tthis.replaceIndex = -1;\n\t\t\treturn this.tempReplaceValue;\n\t\t}\n\n\t\treturn this.history[this.replaceIndex];\n\t}\n\n\t/**\n\t * Reset search history navigation\n\t */\n\tresetSearchNavigation() {\n\t\tthis.searchIndex = -1;\n\t\tthis.tempSearchValue = \"\";\n\t}\n\n\t/**\n\t * Reset replace history navigation\n\t */\n\tresetReplaceNavigation() {\n\t\tthis.replaceIndex = -1;\n\t\tthis.tempReplaceValue = \"\";\n\t}\n\n\t/**\n\t * Reset all navigation state\n\t */\n\tresetAllNavigation() {\n\t\tthis.resetSearchNavigation();\n\t\tthis.resetReplaceNavigation();\n\t}\n}\n\nexport default new SearchHistory();\n"
  },
  {
    "path": "src/lib/secureAdRewardState.js",
    "content": "function execSystem(action, args = []) {\n\treturn new Promise((resolve, reject) => {\n\t\tif (!window.cordova?.exec) {\n\t\t\treject(new Error(\"Cordova exec is unavailable.\"));\n\t\t\treturn;\n\t\t}\n\n\t\tcordova.exec(resolve, reject, \"System\", action, args);\n\t});\n}\n\nexport default {\n\tasync getStatus() {\n\t\ttry {\n\t\t\tconst raw = await execSystem(\"getRewardStatus\");\n\t\t\tif (!raw) return null;\n\t\t\treturn typeof raw === \"string\" ? JSON.parse(raw) : raw;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to load secure rewarded ad status.\", error);\n\t\t\treturn null;\n\t\t}\n\t},\n\tasync redeem(offerId) {\n\t\ttry {\n\t\t\tconst raw = await execSystem(\"redeemReward\", [offerId]);\n\t\t\tif (!raw) return null;\n\t\t\treturn typeof raw === \"string\" ? JSON.parse(raw) : raw;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to redeem rewarded ad offer.\", error);\n\t\t\tthrow error;\n\t\t}\n\t},\n};\n"
  },
  {
    "path": "src/lib/selectionMenu.js",
    "content": "const exec = (command) => {\n\tconst { editor } = editorManager;\n\teditor.execCommand(command);\n\n\tif (command === \"selectall\") {\n\t\teditor.scrollToRow(Number.POSITIVE_INFINITY);\n\t\teditor.setSelection(true);\n\t\teditor.setMenu(true);\n\t}\n\teditor.focus();\n};\n\nconst showCodeActions = async () => {\n\tconst { editor } = editorManager;\n\tif (!editor) return;\n\n\ttry {\n\t\tconst { showCodeActionsMenu, supportsCodeActions } = await import(\"cm/lsp\");\n\t\tif (supportsCodeActions(editor)) {\n\t\t\tawait showCodeActionsMenu(editor);\n\t\t}\n\t} catch (error) {\n\t\tconsole.warn(\"[SelectionMenu] Code actions not available:\", error);\n\t}\n};\n\nconst items = [];\n\nexport default function selectionMenu() {\n\treturn [\n\t\titem(\n\t\t\t() => exec(\"copy\"),\n\t\t\t<span className=\"icon copy\"></span>,\n\t\t\t\"selected\",\n\t\t\ttrue,\n\t\t),\n\t\titem(() => exec(\"cut\"), <span className=\"icon cut\"></span>, \"selected\"),\n\t\titem(() => exec(\"paste\"), <span className=\"icon paste\"></span>, \"all\"),\n\t\titem(\n\t\t\t() => exec(\"selectall\"),\n\t\t\t<span className=\"icon text_format\"></span>,\n\t\t\t\"all\",\n\t\t\ttrue,\n\t\t),\n\t\titem(\n\t\t\t(color) => acode.exec(\"insert-color\", color),\n\t\t\t<span className=\"icon color_lenspalette\"></span>,\n\t\t\t\"all\",\n\t\t),\n\t\titem(\n\t\t\t() => showCodeActions(),\n\t\t\t<span className=\"icon lightbulb\" title=\"Code Actions\"></span>,\n\t\t\t\"all\",\n\t\t\ttrue,\n\t\t),\n\t\t...items,\n\t];\n}\n\n/**\n *\n * @param {function} onclick function to be called when the item is clicked\n * @param {string | HTMLElement} text content of the item\n * @param {'selected'|'all'} mode mode supported by the item\n * @param {boolean} readOnly whether to show the item in readOnly mode\n */\nselectionMenu.add = (onclick, text, mode, readOnly) => {\n\titems.push(item(onclick, text, mode, readOnly));\n};\n\nselectionMenu.exec = (command) => {\n\texec(command);\n};\n\nfunction item(onclick, text, mode = \"all\", readOnly = false) {\n\treturn { onclick, text, mode, readOnly };\n}\n"
  },
  {
    "path": "src/lib/settings.js",
    "content": "import fsOperation from \"fileSystem\";\nimport ThemeBuilder from \"theme/builder\";\nimport themes from \"theme/list\";\nimport { getSystemEditorTheme } from \"theme/preInstalled\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport constants from \"./constants\";\nimport lang from \"./lang\";\nimport { isDeviceDarkTheme } from \"./systemConfiguration\";\n\n/**\n * @typedef {object} fileBrowserSettings\n * @property {string} showHiddenFiles\n * @property {string} sortByName\n */\n\n/**\n * @typedef {object} searchAndFindSettings\n * @property {boolean} wrap\n * @property {boolean} caseSensitive\n * @property {boolean} regExp\n */\n\nclass Settings {\n\t#customTheme = new ThemeBuilder(\"Custom\").toJSON();\n\t#defaultSettings;\n\t#oldSettings;\n\t#initialized = false;\n\t#on = {\n\t\tupdate: [],\n\t\t\"update:after\": [],\n\t\treset: [],\n\t};\n\t#searchSettings = {\n\t\tcaseSensitive: false,\n\t\tregExp: false,\n\t\twholeWord: false,\n\t};\n\t#fileBrowserSettings = {\n\t\tshowHiddenFiles: false,\n\t\tsortByName: true,\n\t};\n\t#excludeFolders = [\n\t\t\"**/node_modules/**\",\n\t\t\"**/bower_components/**\",\n\t\t\"**/jspm_packages/**\",\n\t\t\"**/.npm/**\",\n\t\t\"**/flow-typed/**\",\n\t\t\"**/vendor/**\",\n\t\t\"**/composer/**\",\n\t\t\"**/venv/**\",\n\t\t\"**/.virtualenv/**\",\n\t\t\"**/__pycache__/**\",\n\t\t\"**/.pytest_cache/**\",\n\t\t\"**/.eggs/**\",\n\t\t\"**/*.egg-info/**\",\n\t\t\"**/.git/**\",\n\t\t\"**/.svn/**\",\n\t\t\"**/.hg/**\",\n\t\t\"**/.vscode/**\",\n\t\t\"**/.idea/**\",\n\t\t\"**/.vs/**\",\n\t\t\"**/.project/**\",\n\t\t\"**/.settings/**\",\n\t\t\"**/.classpath/**\",\n\t\t\"**/dist/**\",\n\t\t\"**/build/**\",\n\t\t\"**/out/**\",\n\t\t\"**/target/**\",\n\t\t\"**/bin/**\",\n\t\t\"**/obj/**\",\n\t\t\"**/coverage/**\",\n\t\t\"**/.nyc_output/**\",\n\t\t\"**/htmlcov/**\",\n\t\t\"**/temp/**\",\n\t\t\"**/tmp/**\",\n\t\t\"**/.cache/**\",\n\t\t\"**/logs/**\",\n\t\t\"**/.sass-cache/**\",\n\t\t\"**/.DS_Store/**\",\n\t\t\"**/Thumbs.db/**\",\n\t];\n\t#IS_TABLET = innerWidth > 768;\n\n\tQUICKTOOLS_ROWS = 2;\n\tQUICKTOOLS_GROUP_CAPACITY = 8;\n\tQUICKTOOLS_GROUPS = 2;\n\t#QUICKTOOLS_SIZE =\n\t\tthis.QUICKTOOLS_GROUP_CAPACITY * // items per group\n\t\tthis.QUICKTOOLS_GROUPS * // number of groups\n\t\tthis.QUICKTOOLS_ROWS; // number of rows\n\n\tQUICKTOOLS_TRIGGER_MODE_TOUCH = \"touch\";\n\tQUICKTOOLS_TRIGGER_MODE_CLICK = \"click\";\n\tOPEN_FILE_LIST_POS_HEADER = \"header\";\n\tOPEN_FILE_LIST_POS_SIDEBAR = \"sidebar\";\n\tOPEN_FILE_LIST_POS_BOTTOM = \"bottom\";\n\tKEYBOARD_MODE_NO_SUGGESTIONS = \"NO_SUGGESTIONS\";\n\tKEYBOARD_MODE_NO_SUGGESTIONS_AGGRESSIVE = \"NO_SUGGESTIONS_AGGRESSIVE\";\n\tKEYBOARD_MODE_NORMAL = \"NORMAL\";\n\tCONSOLE_ERUDA = \"eruda\";\n\tCONSOLE_LEGACY = \"legacy\";\n\tPREVIEW_MODE_INAPP = \"inapp\";\n\tPREVIEW_MODE_BROWSER = \"browser\";\n\n\t/**@type {{[key: string]: import('components/settingsPage').SettingsPage}} */\n\tuiSettings = {};\n\n\tconstructor() {\n\t\tthis.#defaultSettings = {\n\t\t\tanimation: \"system\",\n\t\t\tappTheme: \"dark\",\n\t\t\tautosave: 0,\n\t\t\tfileBrowser: this.#fileBrowserSettings,\n\t\t\tformatter: {},\n\t\t\tprettier: {},\n\t\t\tmaxFileSize: 12,\n\t\t\tserverPort: constants.SERVER_PORT,\n\t\t\tpreviewPort: constants.PREVIEW_PORT,\n\t\t\tshowConsoleToggler: true,\n\t\t\tpreviewMode: this.PREVIEW_MODE_INAPP,\n\t\t\tdisableCache: false,\n\t\t\tuseCurrentFileForPreview: false,\n\t\t\thost: \"localhost\",\n\t\t\tsearch: this.#searchSettings,\n\t\t\tlang: \"en-us\",\n\t\t\tfontSize: \"12px\",\n\t\t\teditorTheme: \"one_dark\",\n\t\t\ttextWrap: true,\n\t\t\tsoftTab: true,\n\t\t\ttabSize: 2,\n\t\t\tretryRemoteFsAfterFail: true,\n\t\t\tlinenumbers: true,\n\t\t\tformatOnSave: false,\n\t\t\tfadeFoldWidgets: false,\n\t\t\tautoCorrect: true,\n\t\t\topenFileListPos: this.OPEN_FILE_LIST_POS_HEADER,\n\t\t\tquickTools: this.#IS_TABLET ? 0 : 1,\n\t\t\tquickToolsTriggerMode: this.QUICKTOOLS_TRIGGER_MODE_TOUCH,\n\t\t\tappFont: \"\",\n\t\t\teditorFont: \"Roboto Mono\",\n\t\t\tvibrateOnTap: true,\n\t\t\tfullscreen: false,\n\t\t\tfloatingButton: !this.#IS_TABLET,\n\t\t\tliveAutoCompletion: true,\n\t\t\tshowPrintMargin: false,\n\t\t\tprintMargin: 80,\n\t\t\tscrollbarSize: 20,\n\t\t\tshowSpaces: false,\n\t\t\tconfirmOnExit: true,\n\t\t\tlineHeight: 2,\n\t\t\tleftMargin: 50,\n\t\t\tcheckFiles: true,\n\t\t\tcheckForAppUpdates: false,\n\t\t\tdesktopMode: false,\n\t\t\tconsole: this.CONSOLE_LEGACY,\n\t\t\tkeyboardMode: this.KEYBOARD_MODE_NO_SUGGESTIONS_AGGRESSIVE,\n\t\t\trememberFiles: true,\n\t\t\trememberFolders: true,\n\t\t\tdiagonalScrolling: false,\n\t\t\treverseScrolling: false,\n\t\t\tscrollSpeed: constants.SCROLL_SPEED_NORMAL,\n\t\t\tcustomTheme: this.#customTheme,\n\t\t\trelativeLineNumbers: false,\n\t\t\telasticTabstops: false,\n\t\t\trtlText: false,\n\t\t\thardWrap: false,\n\t\t\tuseTextareaForIME: false,\n\t\t\ttouchMoveThreshold: Math.round((1 / devicePixelRatio) * 10) / 20,\n\t\t\tquicktoolsItems: [...Array(this.#QUICKTOOLS_SIZE).keys()],\n\t\t\texcludeFolders: this.#excludeFolders,\n\t\t\tdefaultFileEncoding: \"UTF-8\",\n\t\t\tinlineAutoCompletion: true,\n\t\t\tcolorPreview: true,\n\t\t\tmaxRetryCount: 3,\n\t\t\tshowRetryToast: false,\n\t\t\tshowSideButtons: true,\n\t\t\tshowSponsorSidebarApp: true,\n\t\t\tshowAnnotations: false,\n\t\t\tlintGutter: true,\n\t\t\tindentGuides: true,\n\t\t\trainbowBrackets: true,\n\t\t\tpluginsDisabled: {}, // pluginId: true/false\n\t\t\tlsp: {\n\t\t\t\tservers: {},\n\t\t\t},\n\t\t\tdeveloperMode: false,\n\t\t\tshiftClickSelection: false,\n\t\t};\n\t\tthis.value = structuredClone(this.#defaultSettings);\n\t}\n\n\tasync init() {\n\t\tif (this.#initialized) return;\n\t\tthis.settingsFile = Url.join(DATA_STORAGE, \"settings.json\");\n\n\t\tthis.#defaultSettings.appTheme = \"system\";\n\t\tthis.#defaultSettings.editorTheme = getSystemEditorTheme(\n\t\t\tisDeviceDarkTheme(),\n\t\t);\n\n\t\tthis.#initialized = true;\n\n\t\tconst fs = fsOperation(this.settingsFile);\n\n\t\tif (!(await fs.exists())) {\n\t\t\tawait this.#save();\n\t\t\tthis.value = structuredClone(this.#defaultSettings);\n\t\t\tthis.#oldSettings = structuredClone(this.#defaultSettings);\n\t\t\tthis.value.lang = navigator.language || \"en-us\";\n\t\t\treturn;\n\t\t}\n\n\t\tconst settings = helpers.parseJSON(await fs.readFile(\"utf8\"));\n\t\tif (settings) {\n\t\t\t// make sure that all the settings are present\n\t\t\tObject.keys(this.#defaultSettings).forEach((setting) => {\n\t\t\t\tconst value = settings[setting];\n\t\t\t\tif (\n\t\t\t\t\tvalue === undefined ||\n\t\t\t\t\ttypeof value !== typeof this.#defaultSettings[setting]\n\t\t\t\t) {\n\t\t\t\t\tsettings[setting] = this.#defaultSettings[setting];\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis.value = structuredClone(settings);\n\t\t\tthis.#oldSettings = structuredClone(settings);\n\t\t\ttry {\n\t\t\t\tthemes.update(ThemeBuilder.fromJSON(this.value.customTheme));\n\t\t\t} catch (error) {\n\t\t\t\tthemes.update(new ThemeBuilder(\"Custom\").toJSON());\n\t\t\t}\n\n\t\t\t// Ensure pluginsDisabled exists\n\t\t\tif (!this.value.pluginsDisabled) this.value.pluginsDisabled = {};\n\n\t\t\treturn;\n\t\t}\n\n\t\tawait this.reset();\n\t}\n\n\tasync #save() {\n\t\tconst fs = fsOperation(this.settingsFile);\n\t\tconst settingsText = JSON.stringify(this.value, undefined, 4);\n\n\t\tif (!(await fs.exists())) {\n\t\t\tconst dirFs = fsOperation(DATA_STORAGE);\n\t\t\tawait dirFs.createFile(\"settings.json\");\n\t\t}\n\n\t\tawait fs.writeFile(settingsText);\n\t\tthis.#oldSettings = structuredClone(this.value);\n\t}\n\n\t/**\n\t *\n\t * @param {Object} [settings] - if provided, the settings will be updated\n\t * @param {Boolean} [showToast] - if false, the toast will not be shown\n\t * default is true\n\t * @param {Boolean} [saveFile] - if false, the settings will not be saved to the file,\n\t * default is true\n\t */\n\tasync update(settings, showToast = true, saveFile = true) {\n\t\tif (typeof settings === \"boolean\") {\n\t\t\tshowToast = settings;\n\t\t\tsettings = undefined;\n\t\t}\n\n\t\tconst onupdate = [...this.#on.update];\n\t\tconst onupdateAfter = [...this.#on[\"update:after\"]];\n\n\t\tif (settings) {\n\t\t\tObject.keys(settings).forEach((key) => {\n\t\t\t\tif (key in this.value) this.value[key] = settings[key];\n\t\t\t});\n\t\t}\n\n\t\tconst changedSettings = this.#getChangedKeys();\n\t\tchangedSettings.forEach((setting) => {\n\t\t\tthis.#applySettings(setting);\n\t\t\tconst listeners = this.#on[`update:${setting}`];\n\t\t\tif (Array.isArray(listeners)) {\n\t\t\t\tonupdate.push(...listeners);\n\t\t\t}\n\t\t\tonupdate.forEach((listener) => listener(this.value[setting]));\n\t\t});\n\n\t\tif (saveFile) await this.#save();\n\t\tif (showToast) toast(strings[\"settings saved\"]);\n\n\t\tchangedSettings.forEach((setting) => {\n\t\t\tconst listeners = this.#on[`update:${setting}:after`];\n\t\t\tif (Array.isArray(listeners)) {\n\t\t\t\tonupdateAfter.push(...listeners);\n\t\t\t}\n\t\t\tonupdateAfter.forEach((listener) => listener(this.value[setting]));\n\t\t});\n\t}\n\n\tasync reset(setting) {\n\t\tif (setting) {\n\t\t\tif (setting in this.#defaultSettings) {\n\t\t\t\tthis.value[setting] = this.#defaultSettings[setting];\n\t\t\t\tawait this.update();\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.value = this.#defaultSettings;\n\t\t\tawait this.update(false);\n\t\t}\n\n\t\tthis.#on.reset.forEach((onreset) => onreset(this.value));\n\t}\n\n\t/**\n\t * Adds a listener for the given event\n\t * @param {'update:<setting>' | 'update:<setting>:after' | 'reset'} event\n\t * @param {function():void} callback\n\t */\n\ton(event, callback) {\n\t\tif (!this.#on[event]) this.#on[event] = [];\n\t\tthis.#on[event].push(callback);\n\t}\n\n\t/**\n\t * Removes the given callback from the given event\n\t * @param {'update' | 'reset'} event\n\t * @param {function():void} callback\n\t */\n\toff(event, callback) {\n\t\tif (!this.#on[event]) this.#on[event] = [];\n\t\tthis.#on[event].splice(this.#on[event].indexOf(callback), 1);\n\t}\n\n\t/**\n\t * Gets a setting with the given key\n\t * @param {String} key\n\t * @returns\n\t */\n\tget(key) {\n\t\treturn this.value[key];\n\t}\n\n\t/**\n\t * Returns changed settings\n\t * @returns {Array<String>}\n\t */\n\t#getChangedKeys() {\n\t\tif (!this.#oldSettings) return [];\n\t\tconst keys = [];\n\t\tObject.keys(this.#oldSettings).forEach((key) => {\n\t\t\tconst value = this.#oldSettings[key];\n\t\t\tif (typeof value === \"object\") {\n\t\t\t\tif (!areEqual(value, this.value[key])) keys.push(key);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (value !== this.value[key]) keys.push(key);\n\t\t});\n\t\treturn keys;\n\t}\n\n\t#applySettings(setting) {\n\t\tswitch (setting) {\n\t\t\tcase \"animation\":\n\t\t\t\tthis.applyAnimationSetting();\n\t\t\t\tbreak;\n\n\t\t\tcase \"lang\":\n\t\t\t\tthis.applyLangSetting();\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tasync applyAnimationSetting() {\n\t\tlet value = this.value.animation;\n\t\tif (value === \"system\") {\n\t\t\tconst res = await new Promise((resolve, reject) => {\n\t\t\t\tsystem.getGlobalSetting(\"animator_duration_scale\", resolve, reject);\n\t\t\t});\n\t\t\tif (res) value = \"yes\";\n\t\t\telse value = \"no\";\n\t\t}\n\n\t\tif (value === \"yes\") {\n\t\t\tapp.classList.remove(\"no-animation\");\n\t\t} else if (value === \"no\") {\n\t\t\tapp.classList.add(\"no-animation\");\n\t\t}\n\t}\n\n\tasync applyLangSetting() {\n\t\tconst value = this.value.lang;\n\t\tlang.set(value);\n\t}\n}\n\n/**\n * Checks whether given objects are equal or not\n * @param {Object} obj1\n * @param {Object} obj2\n * @returns\n */\nfunction areEqual(obj1, obj2) {\n\tif (obj1 === obj2) return true;\n\tif (obj1 == null || obj2 == null) return false;\n\tif (obj1.constructor !== obj2.constructor) return false;\n\n\tfor (let key in obj1) {\n\t\tif (!obj2.hasOwnProperty(key)) return false;\n\t\tif (obj1[key] === obj2[key]) continue;\n\t\tif (typeof obj1[key] !== \"object\") return false;\n\t\tif (!areEqual(obj1[key], obj2[key])) return false;\n\t}\n\n\treturn true;\n}\n\nexport default new Settings();\n"
  },
  {
    "path": "src/lib/showFileInfo.js",
    "content": "import fsOperation from \"fileSystem\";\nimport box from \"dialogs/box\";\nimport { filesize } from \"filesize\";\nimport mustache from \"mustache\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport $_fileInfo from \"views/file-info.hbs\";\nimport settings from \"./settings\";\n\n/**\n * Shows file info\n * @param {String} [url]\n */\nexport default async function showFileInfo(url) {\n\tif (!url) url = editorManager.activeFile.uri;\n\tapp.classList.add(\"title-loading\");\n\ttry {\n\t\tconst fs = fsOperation(url);\n\t\tconst stats = await fs.stat();\n\n\t\tlet { name, lastModified, length, type } = stats;\n\t\tlength = filesize(length);\n\t\tlastModified = new Date(lastModified).toLocaleString();\n\n\t\tconst protocol = Url.getProtocol(url);\n\t\tconst fileType = type.toLowerCase();\n\t\tconst options = {\n\t\t\tname: name.slice(0, name.length - Url.extname(name).length),\n\t\t\textension: Url.extname(name),\n\t\t\tlastModified,\n\t\t\tlength,\n\t\t\ttype,\n\t\t\tlang: strings,\n\t\t\tshowUri: helpers.getVirtualPath(url),\n\t\t\tisEditor:\n\t\t\t\tfileType === \"text/plain\" || editorManager.activeFile.type === \"editor\",\n\t\t};\n\n\t\tif (editorManager.activeFile.type === \"editor\") {\n\t\t\tconst value = await fs.readFile(settings.value.defaultFileEncoding);\n\t\t\toptions.lineCount = value.split(/\\n+/).length;\n\t\t\toptions.wordCount = value.split(/\\s+|\\n+/).length;\n\n\t\t\tif (/s?ftp:/.test(protocol)) {\n\t\t\t\toptions.shareUri = Url.join(CACHE_STORAGE, name);\n\t\t\t\tconst fs = fsOperation(options.shareUri);\n\t\t\t\tif (await fs.exists()) {\n\t\t\t\t\tawait fs.delete();\n\t\t\t\t}\n\t\t\t\tawait fsOperation(CACHE_STORAGE).createFile(name, value);\n\t\t\t}\n\t\t}\n\n\t\tbox(\"\", mustache.render($_fileInfo, options), true).onclick((e) => {\n\t\t\tconst $target = e.target;\n\t\t\tif ($target instanceof HTMLElement) {\n\t\t\t\tconst action = $target.getAttribute(\"action\");\n\n\t\t\t\tif (action === \"copy\") {\n\t\t\t\t\tcordova.plugins.clipboard.copy($target.textContent);\n\t\t\t\t\ttoast(strings[\"copied to clipboard\"]);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t} catch (err) {\n\t\thelpers.error(err);\n\t}\n\n\tapp.classList.remove(\"title-loading\");\n}\n"
  },
  {
    "path": "src/lib/startAd.js",
    "content": "import tag from \"html-tag-js\";\n\nlet adUnitIdBanner = \"ca-app-pub-5911839694379275/9157899592\"; // Production\nlet adUnitIdInterstitial = \"ca-app-pub-5911839694379275/9570937608\"; // Production\nlet adUnitIdRewarded = \"ca-app-pub-5911839694379275/1633667633\"; // Production\nlet initialized = false;\n\nexport default async function startAd() {\n\tif (!IS_FREE_VERSION || !admob) return;\n\n\tif (!initialized) {\n\t\tinitialized = true;\n\n\t\tif (BuildInfo.type === \"debug\") {\n\t\t\tadUnitIdBanner = \"ca-app-pub-3940256099942544/6300978111\"; // Test\n\t\t\tadUnitIdInterstitial = \"ca-app-pub-3940256099942544/1033173712\"; // Test\n\t\t\tadUnitIdRewarded = \"ca-app-pub-3940256099942544/5224354917\"; // Test\n\t\t}\n\t}\n\n\tconst consentStatus = await consent.getConsentStatus();\n\tif (consentStatus === consent.ConsentStatus.Required) {\n\t\tawait consent.requestInfoUpdate();\n\t}\n\n\tconst formStatus = await consent.getFormStatus();\n\tif (formStatus === consent.FormStatus.Available) {\n\t\tconst form = await consent.loadForm();\n\t\tform.show();\n\t}\n\n\tawait admob.start();\n\n\tconst currentHour = new Date().getHours();\n\t//currentHour >= 22: Covers 10:00 PM to 11:59 PM.\n\t//currentHour < 4: Covers 12:00 AM to 3:59 AM.\n\tconst isQuietHours = currentHour >= 22 || currentHour < 4;\n\n\tawait admob.configure({\n\t\tappMuted: isQuietHours,\n\t\tappVolume: isQuietHours ? 0.0 : 1.0,\n\t});\n\n\tconst banner = new admob.BannerAd({\n\t\tadUnitId: adUnitIdBanner,\n\t\tposition: \"bottom\",\n\t});\n\n\tconst interstitial = new admob.InterstitialAd({\n\t\tadUnitId: adUnitIdInterstitial,\n\t});\n\n\tinterstitial.load();\n\n\tinterstitial.on(\"dismiss\", () => {\n\t\tinterstitial.load();\n\t});\n\twindow.ad = banner;\n\twindow.iad = interstitial;\n\twindow.adRewardedUnitId = adUnitIdRewarded;\n}\n\n/**\n * Hides the ad\n * @param {Boolean} [force=false]\n */\nexport function hideAd(force = false) {\n\tconst { ad } = window;\n\tif (ad?.active) {\n\t\tconst $pages = tag.getAll(\".page-replacement\");\n\t\tconst hide = $pages.length === 1;\n\n\t\tif (force || hide) {\n\t\t\tad.active = false;\n\t\t\tad.hide();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/lib/systemConfiguration.js",
    "content": "export const HARDKEYBOARDHIDDEN_NO = 1;\nexport const HARDKEYBOARDHIDDEN_YES = 2;\nexport const HARDKEYBOARDHIDDEN_UNDEFINED = 0;\n\nexport const KEYBOARDHIDDEN_NO = 1;\nexport const KEYBOARDHIDDEN_YES = 2;\nexport const KEYBOARDHIDDEN_UNDEFINED = 0;\n\nexport const KEYBOARD_12KEY = 3;\nexport const KEYBOARD_QWERTY = 2;\nexport const KEYBOARD_UNDEFINED = 0;\nexport const KEYBOARD_NOKEYS = 1;\n\nexport const NAVIGATIONHIDDEN_NO = 1;\nexport const NAVIGATIONHIDDEN_YES = 2;\nexport const NAVIGATIONHIDDEN_UNDEFINED = 0;\n\nexport const NAVIGATION_DPAD = 2;\nexport const NAVIGATION_TRACKBALL = 3;\nexport const NAVIGATION_WHEEL = 4;\nexport const NAVIGATION_UNDEFINED = 0;\n\nexport const ORIENTATION_LANDSCAPE = 2;\nexport const ORIENTATION_PORTRAIT = 1;\nexport const ORIENTATION_SQUARE = 3;\nexport const ORIENTATION_UNDEFINED = 0;\n\nexport const TOUCHSCREEN_FINGER = 3;\nexport const TOUCHSCREEN_NOTOUCH = 1;\nexport const TOUCHSCREEN_STYLUS = 2;\nexport const TOUCHSCREEN_UNDEFINED = 0;\n\n/**\n * @typedef {Object} SystemConfiguration\n * @property {number} hardKeyboardHidden\n * @property {number} navigationHidden\n * @property {number} keyboardHidden\n * @property {number} keyboardHeight\n * @property {number} orientation\n * @property {number} navigation\n * @property {number} fontScale\n * @property {number} keyboard\n * @property {string} locale\n */\n\n/**\n * Get the system configuration\n * @returns {Promise<SystemConfiguration>}\n */\nexport function getSystemConfiguration() {\n\treturn new Promise((resolve, reject) => {\n\t\tcordova.exec(resolve, reject, \"System\", \"get-configuration\", []);\n\t});\n}\n\nexport function isDeviceDarkTheme() {\n\treturn window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n}\n"
  },
  {
    "path": "src/main.js",
    "content": "import \"core-js/stable\";\nimport \"html-tag-js/dist/polyfill\";\n\nimport \"./main.scss\";\nimport \"res/icons/style.css\";\nimport \"res/file-icons/style.css\";\nimport \"styles/overrideAceStyle.scss\";\nimport \"styles/wideScreen.scss\";\n\nimport \"lib/polyfill\";\nimport \"cm/supportedModes\";\nimport \"components/WebComponents\";\n\nimport fsOperation from \"fileSystem\";\nimport sidebarApps from \"sidebarApps\";\nimport ajax from \"@deadlyjack/ajax\";\nimport { setKeyBindings } from \"cm/commandRegistry\";\nimport {\n\tgetModeForPath,\n\tgetModes,\n\tgetModesByName,\n\tinitModes,\n} from \"cm/modelist\";\nimport Contextmenu from \"components/contextmenu\";\nimport { hasConnectedServers } from \"components/lspInfoDialog\";\nimport Sidebar from \"components/sidebar\";\nimport { TerminalManager } from \"components/terminal\";\nimport tile from \"components/tile\";\nimport toast from \"components/toast\";\nimport tutorial from \"components/tutorial\";\nimport confirm from \"dialogs/confirm\";\nimport intentHandler, { processPendingIntents } from \"handlers/intent\";\nimport keyboardHandler, { keydownState } from \"handlers/keyboard\";\nimport quickToolsInit from \"handlers/quickToolsInit\";\nimport windowResize from \"handlers/windowResize\";\nimport Acode from \"lib/acode\";\nimport actionStack from \"lib/actionStack\";\nimport adRewards from \"lib/adRewards\";\nimport applySettings from \"lib/applySettings\";\nimport checkFiles from \"lib/checkFiles\";\nimport checkPluginsUpdate from \"lib/checkPluginsUpdate\";\nimport EditorFile from \"lib/editorFile\";\nimport EditorManager from \"lib/editorManager\";\nimport { initFileList } from \"lib/fileList\";\nimport lang from \"lib/lang\";\nimport loadPlugins from \"lib/loadPlugins\";\nimport Logger from \"lib/logger\";\nimport NotificationManager from \"lib/notificationManager\";\nimport openFolder, { addedFolder } from \"lib/openFolder\";\nimport { registerPrettierFormatter } from \"lib/prettierFormatter\";\nimport restoreFiles from \"lib/restoreFiles\";\nimport settings from \"lib/settings\";\nimport startAd from \"lib/startAd\";\nimport mustache from \"mustache\";\nimport plugins from \"pages/plugins\";\nimport openWelcomeTab from \"pages/welcome\";\nimport otherSettings from \"settings/appSettings\";\nimport themes from \"theme/list\";\nimport { initHighlighting } from \"utils/codeHighlight\";\nimport { getEncoding, initEncodings } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport loadPolyFill from \"utils/polyfill\";\nimport Url from \"utils/Url\";\nimport $_fileMenu from \"views/file-menu.hbs\";\nimport $_menu from \"views/menu.hbs\";\nimport auth, { loginEvents } from \"./lib/auth\";\n\nconst previousVersionCode = Number.parseInt(localStorage.versionCode, 10);\n\nwindow.onload = Main;\nconst logger = new Logger();\n\nfunction createAceModelistCompatModule() {\n\tconst toAceMode = (mode) => {\n\t\tconst resolved = mode || getModeForPath(\"\");\n\t\tif (!resolved) return null;\n\t\tconst name = resolved.name || \"text\";\n\t\tconst rawMode = String(resolved.mode || name);\n\t\tconst modePath = rawMode.startsWith(\"ace/mode/\")\n\t\t\t? rawMode\n\t\t\t: `ace/mode/${rawMode}`;\n\t\treturn {\n\t\t\t...resolved,\n\t\t\tname,\n\t\t\tcaption: resolved.caption || name,\n\t\t\tmode: modePath,\n\t\t};\n\t};\n\n\treturn {\n\t\tget modes() {\n\t\t\treturn getModes()\n\t\t\t\t.map((mode) => toAceMode(mode))\n\t\t\t\t.filter(Boolean);\n\t\t},\n\t\tget modesByName() {\n\t\t\tconst source = getModesByName();\n\t\t\tconst result = {};\n\t\t\tObject.keys(source).forEach((name) => {\n\t\t\t\tresult[name] = toAceMode(source[name]);\n\t\t\t});\n\t\t\treturn result;\n\t\t},\n\t\tgetModeForPath(path) {\n\t\t\treturn toAceMode(getModeForPath(String(path || \"\")));\n\t\t},\n\t};\n}\n\nfunction ensureAceCompatApi() {\n\tconst ace = window.ace || {};\n\tconst modelistModule = createAceModelistCompatModule();\n\tconst originalRequire =\n\t\ttypeof ace.require === \"function\" ? ace.require.bind(ace) : null;\n\n\tace.require = (moduleId) => {\n\t\tif (moduleId === \"ace/ext/modelist\" || moduleId === \"ace/ext/modelist.js\") {\n\t\t\treturn modelistModule;\n\t\t}\n\t\treturn originalRequire?.(moduleId);\n\t};\n\n\twindow.ace = ace;\n}\n\nasync function Main() {\n\tconst oldPreventDefault = TouchEvent.prototype.preventDefault;\n\n\tajax.response = (xhr) => {\n\t\treturn xhr.response;\n\t};\n\n\tloadPolyFill.apply(window);\n\n\tTouchEvent.prototype.preventDefault = function () {\n\t\tif (this.cancelable) {\n\t\t\toldPreventDefault.bind(this)();\n\t\t}\n\t};\n\n\twindow.addEventListener(\"resize\", windowResize);\n\tdocument.addEventListener(\"pause\", pauseHandler);\n\tdocument.addEventListener(\"resume\", resumeHandler);\n\tdocument.addEventListener(\"keydown\", keyboardHandler);\n\tdocument.addEventListener(\"deviceready\", onDeviceReady);\n\tdocument.addEventListener(\"backbutton\", backButtonHandler);\n\tdocument.addEventListener(\"menubutton\", menuButtonHandler);\n}\n\nasync function onDeviceReady() {\n\tawait initEncodings(); // important to load encodings before anything else\n\n\tconst isFreePackage = /(free)$/.test(BuildInfo.packageName);\n\tconst oldResolveURL = window.resolveLocalFileSystemURL;\n\tconst {\n\t\texternalCacheDirectory, //\n\t\texternalDataDirectory,\n\t\tcacheDirectory,\n\t\tdataDirectory,\n\t} = cordova.file;\n\n\twindow.app = document.body;\n\twindow.root = tag.get(\"#root\");\n\twindow.addedFolder = addedFolder;\n\twindow.editorManager = null;\n\twindow.toast = toast;\n\twindow.ASSETS_DIRECTORY = Url.join(cordova.file.applicationDirectory, \"www\");\n\twindow.DATA_STORAGE = externalDataDirectory || dataDirectory;\n\twindow.CACHE_STORAGE = externalCacheDirectory || cacheDirectory;\n\twindow.PLUGIN_DIR = Url.join(DATA_STORAGE, \"plugins\");\n\twindow.KEYBINDING_FILE = Url.join(DATA_STORAGE, \".key-bindings.json\");\n\twindow.IS_FREE_VERSION = isFreePackage;\n\twindow.log = logger.log.bind(logger);\n\n\t// Capture synchronous errors\n\twindow.addEventListener(\"error\", (event) => {\n\t\tconst errorMsg = `Error: ${event.message}, Source: ${event.filename}, Line: ${event.lineno}, Column: ${event.colno}, Stack: ${event.error?.stack || \"N/A\"}`;\n\t\twindow.log(\"error\", errorMsg);\n\t});\n\t// Capture unhandled promise rejections\n\twindow.addEventListener(\"unhandledrejection\", (event) => {\n\t\twindow.log(\n\t\t\t\"error\",\n\t\t\t`Unhandled rejection: ${event.reason ? event.reason.message : \"Unknown reason\"}\\nStack: ${event.reason ? event.reason.stack : \"No stack available\"}`,\n\t\t);\n\t});\n\n\tstartAd();\n\n\ttry {\n\t\tawait helpers.promisify(iap.startConnection).catch((e) => {\n\t\t\twindow.log(\"error\", \"connection error\");\n\t\t\twindow.log(\"error\", e);\n\t\t});\n\n\t\tif (localStorage.acode_pro === \"true\") {\n\t\t\twindow.IS_FREE_VERSION = false;\n\t\t}\n\n\t\tif (navigator.onLine) {\n\t\t\tconst purchases = await helpers.promisify(iap.getPurchases);\n\t\t\tconst isPro = purchases.find((p) =>\n\t\t\t\tp.productIds.includes(\"acode_pro_new\"),\n\t\t\t);\n\t\t\tif (isPro) {\n\t\t\t\twindow.IS_FREE_VERSION = false;\n\t\t\t} else {\n\t\t\t\twindow.IS_FREE_VERSION = isFreePackage;\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\twindow.log(\"error\", \"Purchase error\");\n\t\twindow.log(\"error\", error);\n\t}\n\n\ttry {\n\t\twindow.ANDROID_SDK_INT = await new Promise((resolve, reject) =>\n\t\t\tsystem.getAndroidVersion(resolve, reject),\n\t\t);\n\t} catch (error) {\n\t\twindow.ANDROID_SDK_INT = Number.parseInt(device.version);\n\t}\n\twindow.DOES_SUPPORT_THEME = (() => {\n\t\tconst $testEl = (\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\theight: \"var(--test-height)\",\n\t\t\t\t\twidth: \"var(--test-height)\",\n\t\t\t\t}}\n\t\t\t/>\n\t\t);\n\t\tdocument.body.append($testEl);\n\t\tconst client = $testEl.getBoundingClientRect();\n\n\t\t$testEl.remove();\n\n\t\tif (client.height === 0) return false;\n\t\treturn true;\n\t})();\n\twindow.acode = new Acode();\n\tawait adRewards.init();\n\tensureAceCompatApi();\n\n\tsystem.requestPermission(\"android.permission.READ_EXTERNAL_STORAGE\");\n\tsystem.requestPermission(\"android.permission.WRITE_EXTERNAL_STORAGE\");\n\tsystem.requestPermission(\"android.permission.POST_NOTIFICATIONS\");\n\n\tconst { versionCode } = BuildInfo;\n\n\tif (previousVersionCode !== versionCode) {\n\t\tsystem.clearCache();\n\t}\n\n\tif (!(await fsOperation(PLUGIN_DIR).exists())) {\n\t\tawait fsOperation(DATA_STORAGE).createDirectory(\"plugins\");\n\t}\n\n\tlocalStorage.versionCode = versionCode;\n\n\ttry {\n\t\tawait setDebugInfo();\n\t} catch (e) {\n\t\tconsole.error(e);\n\t}\n\n\tacode.setLoadingMessage(\"Loading settings...\");\n\n\twindow.resolveLocalFileSystemURL = function (url, ...args) {\n\t\toldResolveURL.call(this, Url.safe(url), ...args);\n\t};\n\n\tsetTimeout(async () => {\n\t\tif (document.body.classList.contains(\"loading\")) {\n\t\t\twindow.log(\"warn\", \"App is taking unexpectedly long time!\");\n\t\t\tdocument.body.setAttribute(\n\t\t\t\t\"data-small-msg\",\n\t\t\t\t\"This is taking unexpectedly long time!\",\n\t\t\t);\n\t\t}\n\t}, 1000 * 10);\n\n\tacode.setLoadingMessage(\"Loading settings...\");\n\tawait settings.init();\n\tthemes.init();\n\tinitHighlighting();\n\n\tregisterPrettierFormatter();\n\n\tacode.setLoadingMessage(\"Loading language...\");\n\tawait lang.set(settings.value.lang);\n\n\tif (settings.value.developerMode) {\n\t\ttry {\n\t\t\tconst devTools = (await import(\"lib/devTools\")).default;\n\t\t\tawait devTools.init(false);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to initialize developer tools\", error);\n\t\t}\n\t}\n\n\ttry {\n\t\tawait loadApp();\n\t} catch (error) {\n\t\twindow.log(\"error\", error);\n\t\ttoast(`Error: ${error.message}`);\n\t} finally {\n\t\tsetTimeout(async () => {\n\t\t\tdocument.body.removeAttribute(\"data-small-msg\");\n\t\t\tapp.classList.remove(\"loading\", \"splash\");\n\n\t\t\t// load plugins\n\t\t\ttry {\n\t\t\t\tawait loadPlugins();\n\t\t\t\t// Ensure at least one sidebar app is active after all plugins are loaded\n\t\t\t\t// This handles cases where the stored section was from an uninstalled plugin\n\t\t\t\tsidebarApps.ensureActiveApp();\n\n\t\t\t\t// Re-emit events for active file after plugins are loaded\n\t\t\t\tconst { activeFile } = editorManager;\n\t\t\t\tif (activeFile?.uri) {\n\t\t\t\t\t// Re-emit file-loaded event\n\t\t\t\t\teditorManager.emit(\"file-loaded\", activeFile);\n\t\t\t\t\t// Re-emit switch-file event\n\t\t\t\t\teditorManager.emit(\"switch-file\", activeFile);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\twindow.log(\"error\", \"Failed to load plugins!\");\n\t\t\t\twindow.log(\"error\", error);\n\t\t\t\ttoast(\"Failed to load plugins!\");\n\t\t\t}\n\t\t\tapplySettings.afterRender();\n\n\t\t\t// Check login status before emitting events\n\t\t\ttry {\n\t\t\t\tconst isLoggedIn = await auth.isLoggedIn();\n\t\t\t\tif (isLoggedIn) {\n\t\t\t\t\tloginEvents.emit();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error checking login status:\", error);\n\t\t\t\ttoast(\"Error checking login status\");\n\t\t\t}\n\t\t}, 500);\n\t}\n\n\tawait promptUpdateCheckConsent();\n\n\t// Check for app updates\n\tif (settings.value.checkForAppUpdates && navigator.onLine) {\n\t\tcordova.plugin.http.sendRequest(\n\t\t\t\"https://api.github.com/repos/Acode-Foundation/Acode/releases/latest\",\n\t\t\t{\n\t\t\t\tmethod: \"GET\",\n\t\t\t\tresponseType: \"json\",\n\t\t\t},\n\t\t\t(response) => {\n\t\t\t\tconst release = response.data;\n\t\t\t\t// assuming version is in format v1.2.3\n\t\t\t\tconst latestVersion = release.tag_name\n\t\t\t\t\t.replace(\"v\", \"\")\n\t\t\t\t\t.split(\".\")\n\t\t\t\t\t.map(Number);\n\t\t\t\tconst currentVersion = BuildInfo.version.split(\".\").map(Number);\n\n\t\t\t\tconst hasUpdate = latestVersion.some(\n\t\t\t\t\t(num, i) => num > currentVersion[i],\n\t\t\t\t);\n\n\t\t\t\tif (hasUpdate) {\n\t\t\t\t\tacode.pushNotification(\n\t\t\t\t\t\t\"Update Available\",\n\t\t\t\t\t\t`Acode ${release.tag_name} is now available! Click here to checkout.`,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ticon: \"update\",\n\t\t\t\t\t\t\ttype: \"warning\",\n\t\t\t\t\t\t\taction: () => {\n\t\t\t\t\t\t\t\tsystem.openInBrowser(release.html_url);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\t(err) => {\n\t\t\t\twindow.log(\"error\", \"Failed to check for updates\");\n\t\t\t\twindow.log(\"error\", err);\n\t\t\t},\n\t\t);\n\t}\n\tcheckPluginsUpdate()\n\t\t.then((updates) => {\n\t\t\tif (!updates.length) return;\n\t\t\tacode.pushNotification(\n\t\t\t\t\"Plugin Updates\",\n\t\t\t\t`${updates.length} plugin${updates.length > 1 ? \"s\" : \"\"} ${updates.length > 1 ? \"have\" : \"has\"} new version${updates.length > 1 ? \"s\" : \"\"} available.`,\n\t\t\t\t{\n\t\t\t\t\ticon: \"extension\",\n\t\t\t\t\taction: () => {\n\t\t\t\t\t\tplugins(updates);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t})\n\t\t.catch(console.error);\n}\n\nasync function setDebugInfo() {\n\tconst { version, versionCode } = BuildInfo;\n\n\tconst userAgent = navigator.userAgent;\n\tconst language = navigator.language;\n\n\t// Extract Android version\n\tconst androidMatch = userAgent.match(/Android\\s([0-9.]+)/);\n\tconst androidVersion = androidMatch ? androidMatch[1] : \"Unknown\";\n\n\t// Extract Chrome/WebView version\n\tconst chromeMatch = userAgent.match(/Chrome\\/([0-9.]+)/);\n\tconst webviewVersion = chromeMatch ? chromeMatch[1] : \"Unknown\";\n\n\tconst info = [\n\t\t`App: v${version} (${versionCode})`,\n\t\t`Android: ${androidVersion}`,\n\t\t`WebView: ${webviewVersion}`,\n\t\t`Language: ${language}`,\n\t].join(\"\\n\");\n\n\tdocument.body.setAttribute(\"data-version\", info);\n}\n\nasync function promptUpdateCheckConsent() {\n\ttry {\n\t\tif (Boolean(localStorage.getItem(\"checkForUpdatesPrompted\"))) return;\n\n\t\tif (settings.value.checkForAppUpdates) {\n\t\t\tlocalStorage.setItem(\"checkForUpdatesPrompted\", \"true\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst message = strings[\"prompt update check consent message\"];\n\t\tconst shouldEnable = await confirm(strings?.confirm, message);\n\t\tlocalStorage.setItem(\"checkForUpdatesPrompted\", \"true\");\n\t\tif (shouldEnable) {\n\t\t\tawait settings.update({ checkForAppUpdates: true }, false);\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\"Failed to prompt for update check consent\", error);\n\t}\n}\n\nasync function loadApp() {\n\tlet $mainMenu;\n\tlet $fileMenu;\n\tconst $editMenuToggler = (\n\t\t<span\n\t\t\tclassName=\"icon edit\"\n\t\t\tattr-action=\"toggle-edit-menu\"\n\t\t\tstyle={{ fontSize: \"1.2em\" }}\n\t\t/>\n\t);\n\tconst $navToggler = (\n\t\t<span className=\"icon menu\" attr-action=\"toggle-sidebar\" />\n\t);\n\tconst $menuToggler = (\n\t\t<span className=\"icon more_vert\" attr-action=\"toggle-menu\" />\n\t);\n\tconst $header = tile({\n\t\ttype: \"header\",\n\t\ttext: \"Acode\",\n\t\tlead: $navToggler,\n\t\ttail: $menuToggler,\n\t});\n\tconst $main = <main />;\n\tconst $sidebar = <Sidebar container={$main} toggler={$navToggler} />;\n\tconst $runBtn = (\n\t\t<span\n\t\t\tstyle={{ fontSize: \"1.2em\" }}\n\t\t\tclassName=\"icon play_arrow\"\n\t\t\tattr-action=\"run\"\n\t\t\tonclick={() => acode.exec(\"run\")}\n\t\t\toncontextmenu={() => acode.exec(\"run-file\")}\n\t\t/>\n\t);\n\tconst $floatingNavToggler = (\n\t\t<span\n\t\t\tid=\"sidebar-toggler\"\n\t\t\tclassName=\"floating icon menu\"\n\t\t\tonclick={() => acode.exec(\"toggle-sidebar\")}\n\t\t/>\n\t);\n\tconst $headerToggler = (\n\t\t<span className=\"floating icon keyboard_arrow_left\" id=\"header-toggler\" />\n\t);\n\tconst folders = helpers.parseJSON(localStorage.folders);\n\tconst files = helpers.parseJSON(localStorage.files) || [];\n\tconst editorManager = await EditorManager($header, $main);\n\n\tconst setMainMenu = () => {\n\t\tif ($mainMenu) {\n\t\t\t$mainMenu.removeEventListener(\"click\", handleMenu);\n\t\t\t$mainMenu.destroy();\n\t\t}\n\t\tconst { openFileListPos, fullscreen } = settings.value;\n\t\tif (openFileListPos === settings.OPEN_FILE_LIST_POS_BOTTOM && fullscreen) {\n\t\t\t$mainMenu = createMainMenu({ bottom: \"6px\", toggler: $menuToggler });\n\t\t} else {\n\t\t\t$mainMenu = createMainMenu({ top: \"6px\", toggler: $menuToggler });\n\t\t}\n\t\t$mainMenu.addEventListener(\"click\", handleMenu);\n\t};\n\n\tconst setFileMenu = () => {\n\t\tif ($fileMenu) {\n\t\t\t$fileMenu.removeEventListener(\"click\", handleMenu);\n\t\t\t$fileMenu.destroy();\n\t\t}\n\t\tconst { openFileListPos, fullscreen } = settings.value;\n\t\tif (openFileListPos === settings.OPEN_FILE_LIST_POS_BOTTOM && fullscreen) {\n\t\t\t$fileMenu = createFileMenu({ bottom: \"6px\", toggler: $editMenuToggler });\n\t\t} else {\n\t\t\t$fileMenu = createFileMenu({ top: \"6px\", toggler: $editMenuToggler });\n\t\t}\n\t\t$fileMenu.addEventListener(\"click\", handleMenu);\n\t};\n\n\tacode.$headerToggler = $headerToggler;\n\twindow.actionStack = actionStack.windowCopy();\n\twindow.editorManager = editorManager;\n\tsetMainMenu(settings.value.openFileListPos);\n\tsetFileMenu(settings.value.openFileListPos);\n\tactionStack.onCloseApp = () => acode.exec(\"save-state\");\n\t$headerToggler.onclick = function () {\n\t\troot.classList.toggle(\"show-header\");\n\t\tthis.classList.toggle(\"keyboard_arrow_left\");\n\t\tthis.classList.toggle(\"keyboard_arrow_right\");\n\t};\n\n\t//#region rendering\n\tapplySettings.beforeRender();\n\troot.appendOuter($header, $main, $floatingNavToggler, $headerToggler);\n\t//#endregion\n\n\t//#region Add event listeners\n\tinitModes();\n\tquickToolsInit();\n\tsidebarApps.init($sidebar);\n\tawait sidebarApps.loadApps();\n\teditorManager.onupdate = onEditorUpdate;\n\troot.on(\"show\", mainPageOnShow);\n\tapp.addEventListener(\"click\", onClickApp);\n\teditorManager.on(\"rename-file\", onFileUpdate);\n\teditorManager.on(\"switch-file\", onFileUpdate);\n\teditorManager.on(\"file-loaded\", onFileUpdate);\n\tnavigator.app.overrideButton(\"menubutton\", true);\n\tsystem.setIntentHandler(intentHandler, intentHandler.onError);\n\tsystem.getCordovaIntent(intentHandler, intentHandler.onError);\n\tsetTimeout(showTutorials, 1000);\n\tsettings.on(\"update:openFileListPos\", () => {\n\t\tsetMainMenu();\n\t\tsetFileMenu();\n\t});\n\tsettings.on(\"update:fullscreen\", () => {\n\t\tsetMainMenu();\n\t\tsetFileMenu();\n\t});\n\n\t$sidebar.onshow = () => {\n\t\tconst activeFile = editorManager.activeFile;\n\t\tif (activeFile) editorManager.editor.contentDOM.blur();\n\t};\n\tsdcard.watchFile(KEYBINDING_FILE, async () => {\n\t\tawait setKeyBindings(editorManager.editor);\n\t\ttoast(strings[\"key bindings updated\"]);\n\t});\n\t//#endregion\n\n\tconst notificationManager = new NotificationManager();\n\tnotificationManager.init();\n\n\twindow.log(\"info\", \"Started app and its services...\");\n\n\t// Show welcome tab on first launch, otherwise create default file\n\tconst isFirstLaunch = Number.isNaN(previousVersionCode);\n\tif (isFirstLaunch) {\n\t\topenWelcomeTab();\n\t} else {\n\t\tnew EditorFile();\n\t}\n\n\t// load theme plugins\n\ttry {\n\t\tawait loadPlugins(true);\n\t} catch (error) {\n\t\twindow.log(\"error\", \"Failed to load theme plugins!\");\n\t\twindow.log(\"error\", error);\n\t\ttoast(\"Failed to load theme plugins!\");\n\t}\n\n\tacode.setLoadingMessage(\"Loading folders...\");\n\tif (Array.isArray(folders)) {\n\t\tfor (const folder of folders) {\n\t\t\tfolder.opts.listFiles = !!folder.opts.listFiles;\n\t\t\topenFolder(folder.url, folder.opts);\n\t\t}\n\t}\n\n\tif (Array.isArray(files) && files.length) {\n\t\ttry {\n\t\t\tawait restoreFiles(files);\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", \"File loading failed!\");\n\t\t\twindow.log(\"error\", error);\n\t\t\ttoast(\"File loading failed!\");\n\t\t} finally {\n\t\t\t// Mark restoration complete even after a partial failure so\n\t\t\t// switch-file persistence and queued intents are not blocked.\n\t\t\tsessionStorage.setItem(\"isfilesRestored\", true);\n\t\t}\n\t\t// Process any pending intents that were queued before files were restored\n\t\tawait processPendingIntents();\n\t} else {\n\t\t// Even when no files need to be restored, mark as restored and process pending intents\n\t\tsessionStorage.setItem(\"isfilesRestored\", true);\n\t\tawait processPendingIntents();\n\t\tonEditorUpdate(undefined, false);\n\t}\n\n\tinitFileList();\n\n\tTerminalManager.restorePersistedSessions().catch((error) => {\n\t\tconsole.error(\"Terminal restoration failed:\", error);\n\t});\n\n\t/**\n\t *\n\t * @param {MouseEvent} e\n\t */\n\tfunction handleMenu(e) {\n\t\tconst $target = e.target;\n\t\tconst action = $target.getAttribute(\"action\");\n\t\tconst value = $target.getAttribute(\"value\") || undefined;\n\t\tif (!action) return;\n\n\t\tif ($mainMenu.contains($target)) $mainMenu.hide();\n\t\tif ($fileMenu.contains($target)) $fileMenu.hide();\n\t\tacode.exec(action, value);\n\t}\n\n\tfunction onEditorUpdate(mode, saveState = true) {\n\t\tconst { activeFile } = editorManager;\n\n\t\t// if (!$editMenuToggler.isConnected) {\n\t\t// \t$header.insertBefore($editMenuToggler, $header.lastChild);\n\t\t// }\n\t\tif (activeFile?.type === \"page\" || activeFile?.type === \"terminal\") {\n\t\t\t$editMenuToggler.remove();\n\t\t} else {\n\t\t\tif (!$editMenuToggler.isConnected) {\n\t\t\t\t$header.insertBefore($editMenuToggler, $header.lastChild);\n\t\t\t}\n\t\t}\n\n\t\tif (mode === \"switch-file\") {\n\t\t\tif (settings.value.rememberFiles && activeFile) {\n\t\t\t\tlocalStorage.setItem(\"lastfile\", activeFile.id);\n\t\t\t}\n\t\t\tif (saveState && sessionStorage.getItem(\"isfilesRestored\") === \"true\") {\n\t\t\t\tacode.exec(\"save-state\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (saveState) acode.exec(\"save-state\");\n\t}\n\n\tasync function onFileUpdate() {\n\t\ttry {\n\t\t\tconst { serverPort, previewPort } = settings.value;\n\t\t\tlet canRun = false;\n\t\t\tif (serverPort !== previewPort) {\n\t\t\t\tcanRun = true;\n\t\t\t} else {\n\t\t\t\tconst { activeFile } = editorManager;\n\t\t\t\tcanRun = await activeFile?.canRun();\n\t\t\t}\n\n\t\t\tif (canRun) {\n\t\t\t\t$header.insertBefore($runBtn, $header.lastChild);\n\t\t\t} else {\n\t\t\t\t$runBtn.remove();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t$runBtn.removeAttribute(\"run-file\");\n\t\t\t$runBtn.remove();\n\t\t}\n\t}\n}\n\nfunction onClickApp(e) {\n\tlet el = e.target;\n\tif (el instanceof HTMLAnchorElement || checkIfInsideAnchor()) {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\n\t\tsystem.openInBrowser(el.href);\n\t}\n\n\tfunction checkIfInsideAnchor() {\n\t\tconst allAs = [...document.body.getAll(\"a\")];\n\n\t\tfor (const a of allAs) {\n\t\t\tif (a.contains(el)) {\n\t\t\t\tel = a;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n}\n\nfunction mainPageOnShow() {\n\tconst { editor } = editorManager;\n\t// TODO : Codemirror\n\t//editor.resize(true);\n}\n\nfunction createMainMenu({ top, bottom, toggler }) {\n\treturn Contextmenu({\n\t\tright: \"6px\",\n\t\ttop,\n\t\tbottom,\n\t\ttoggler,\n\t\ttransformOrigin: top ? \"top right\" : \"bottom right\",\n\t\tinnerHTML: () => {\n\t\t\treturn mustache.render($_menu, strings);\n\t\t},\n\t});\n}\n\nfunction createFileMenu({ top, bottom, toggler }) {\n\tconst $menu = Contextmenu({\n\t\ttop,\n\t\tbottom,\n\t\ttoggler,\n\t\ttransformOrigin: top ? \"top right\" : \"bottom right\",\n\t\tinnerHTML: () => {\n\t\t\tconst file = window.editorManager.activeFile;\n\n\t\t\tif (file.type === \"page\") {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\tif (file.loading) {\n\t\t\t\t$menu.classList.add(\"disabled\");\n\t\t\t} else {\n\t\t\t\t$menu.classList.remove(\"disabled\");\n\t\t\t}\n\n\t\t\tconst { label: encoding } = getEncoding(file.encoding);\n\t\t\tconst isEditorFile = file.type === \"editor\";\n\t\t\tconst cmEditor = window.editorManager?.editor;\n\t\t\tconst hasSelection = !!cmEditor && !cmEditor.state.selection.main.empty;\n\t\t\treturn mustache.render($_fileMenu, {\n\t\t\t\t...strings,\n\t\t\t\ttoggle_pin_tab_text: file.pinned\n\t\t\t\t\t? strings[\"unpin tab\"] || \"Unpin tab\"\n\t\t\t\t\t: strings[\"pin tab\"] || \"Pin tab\",\n\t\t\t\ttoggle_pin_tab_icon: file.pinned ? \"icon pin-off\" : \"icon pin\",\n\t\t\t\t// Use CodeMirror mode stored on EditorFile (set in setMode)\n\t\t\t\tfile_mode: isEditorFile ? file.currentMode || \"\" : \"\",\n\t\t\t\tfile_encoding: isEditorFile ? encoding : \"\",\n\t\t\t\tfile_read_only: !file.editable,\n\t\t\t\tfile_on_disk: !!file.uri,\n\t\t\t\tfile_eol: isEditorFile ? file.eol : \"\",\n\t\t\t\tcopy_text: isEditorFile ? hasSelection : false,\n\t\t\t\tis_editor: isEditorFile,\n\t\t\t\thas_lsp_servers: isEditorFile && hasConnectedServers(),\n\t\t\t});\n\t\t},\n\t});\n\n\treturn $menu;\n}\n\nfunction showTutorials() {\n\tif (window.innerWidth > 750) {\n\t\ttutorial(\"quicktools-tutorials\", (hide) => {\n\t\t\tconst onclick = () => {\n\t\t\t\totherSettings();\n\t\t\t\thide();\n\t\t\t};\n\n\t\t\treturn (\n\t\t\t\t<p>\n\t\t\t\t\tQuicktools has been <strong>disabled</strong> because it seems like\n\t\t\t\t\tyou are on a bigger screen and probably using a keyboard. To enable\n\t\t\t\t\tit,{\" \"}\n\t\t\t\t\t<span className=\"link\" onclick={onclick}>\n\t\t\t\t\t\tclick here\n\t\t\t\t\t</span>{\" \"}\n\t\t\t\t\tor press <kbd>Ctrl + Shift + P</kbd> and search for{\" \"}\n\t\t\t\t\t<code>quicktools</code>.\n\t\t\t\t</p>\n\t\t\t);\n\t\t});\n\t}\n}\n\nfunction backButtonHandler() {\n\tif (keydownState.esc) {\n\t\tkeydownState.esc = false;\n\t\treturn;\n\t}\n\tactionStack.pop();\n}\n\nfunction menuButtonHandler() {\n\tconst { acode } = window;\n\tacode?.exec(\"toggle-sidebar\");\n}\n\nfunction pauseHandler() {\n\tconst { acode } = window;\n\tacode?.exec(\"save-state\");\n}\n\nfunction resumeHandler() {\n\tadRewards.handleResume();\n\tif (!settings.value.checkFiles) return;\n\tcheckFiles();\n}\n"
  },
  {
    "path": "src/main.scss",
    "content": "@use \"./styles/page.scss\";\n@use \"./styles/list.scss\";\n@use \"./styles/mixins.scss\";\n@use \"./styles/keyframes.scss\";\n@use \"./styles/fileInfo.scss\";\n@use \"./styles/markdown.scss\";\n@use \"./styles/codemirror.scss\";\n\n:root {\n  --scrollbar-width: 4px;\n  --app-font-family: \"Roboto\", sans-serif;\n}\n\n* {\n  margin: 0;\n  padding: 0;\n\n  &:focus {\n    outline: none;\n  }\n}\n\nhtml {\n  overflow: auto;\n}\n\nhtml,\nbody {\n  width: 100%;\n  height: 100%;\n  font-size: 14px;\n}\n\nbody {\n  user-select: none;\n  font-family: var(--app-font-family);\n  -webkit-tap-highlight-color: transparent;\n  background-color: #9999ff;\n  background-color: var(--primary-color);\n  color: #252525;\n  color: var(--secondary-text-color);\n\n  &.no-animation * {\n    animation: none !important;\n    transition: none !important;\n    box-shadow: none !important;\n  }\n\n  &:not(.loading).title-loading {\n    &.title-loading-hide {\n      &::after {\n        background-image: none;\n        transform: translateX(-50%) translateY(-100%) scale3d(0.5, 0.5, 1);\n        opacity: 0;\n        animation: hide-loader 100ms ease-in 1;\n      }\n    }\n\n    &::after {\n      content: \"\";\n      background-color: #3333ff;\n      background-color: var(--primary-color);\n      border-radius: 50%;\n      position: fixed;\n      height: 40px;\n      width: 40px;\n      top: 6px;\n      left: 50%;\n      transform: translateX(-50%);\n      background-image: url(res/tail-spin.svg);\n      background-repeat: no-repeat;\n      background-position: center;\n      background-size: 30px;\n      box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2);\n      box-shadow: 0 0 4px 0 var(--box-shadow-color);\n      border: solid 1px transparent;\n      border: solid 1px var(--popup-border-color);\n      animation: appear 100ms ease-out 1;\n      box-sizing: border-box;\n      z-index: 999;\n    }\n  }\n\n  .main {\n    position: relative;\n  }\n}\n\na {\n  color: #615efd;\n  color: var(--link-text-color);\n}\n\n.open-file-list {\n  position: relative;\n  height: 30px;\n  width: 100%;\n  background-color: #9999ff;\n  background-color: var(--primary-color);\n  overflow-x: auto !important;\n  overflow-y: hidden !important;\n  display: flex;\n  flex-direction: row !important;\n  color: white;\n  color: var(--primary-text-color);\n  z-index: 5;\n\n  li.tile {\n    $width: 120px;\n    height: 100%;\n    overflow: hidden;\n    font-size: 0.8em;\n    align-items: center;\n    margin: 0;\n    padding: 0;\n    color: inherit;\n    min-width: $width;\n    min-width: var(--file-tab-width);\n    max-width: $width;\n    max-width: var(--file-tab-width);\n\n    .text {\n      display: inline-block;\n      white-space: nowrap;\n      max-width: $width;\n      max-width: var(--file-tab-width);\n      overflow: hidden;\n      text-overflow: ellipsis;\n      margin: 0;\n      padding: 0;\n      color: inherit;\n    }\n\n    &.notice {\n      &::before {\n        content: \"•\";\n        color: #ffda0c;\n        font-size: 1.5em;\n        margin-left: 2.5px;\n        text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);\n      }\n    }\n\n    &.active {\n      border-top: solid 2px gold;\n      background-color: rgba(0, 0, 0, 0.2);\n    }\n\n    .file,\n    .icon {\n      height: 24px;\n      width: 24px;\n      font-size: 1em;\n      background-size: 22px;\n      background-position: center;\n      color: inherit;\n    }\n  }\n}\n\na.icon {\n  pointer-events: all !important;\n  color: white;\n\n  &:focus,\n  &:active {\n    border: none;\n    outline: none;\n  }\n}\n\n.no-scroll {\n  &::-webkit-scrollbar {\n    width: 0px;\n    height: 0px;\n  }\n}\n\n.list,\n.prompt,\n.scroll {\n  &::-webkit-scrollbar {\n    width: var(--scrollbar-width);\n    height: var(--scrollbar-width);\n  }\n\n  &::-webkit-scrollbar-track {\n    background: transparent;\n  }\n\n  &::-webkit-scrollbar-thumb {\n    background: rgba(0, 0, 0, 0.333);\n    background: var(--scrollbar-color);\n    border-radius: calc(var(--scrollbar-width) / 2);\n  }\n}\n\n.icon {\n  user-select: none;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 50%;\n  text-decoration: none;\n  text-rendering: auto;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  background-position: center;\n  background-size: 24px;\n  background-repeat: no-repeat;\n\n  &.hidden {\n    display: none !important;\n  }\n\n  &.color {\n    display: flex;\n\n    &::before {\n      content: \"\";\n      height: 16px;\n      width: 16px;\n      border: solid 1px #a90000;\n      border: solid 1px var(--active-color);\n      background-color: currentColor;\n      color: inherit !important;\n    }\n\n    &.dark {\n      color: #252525;\n    }\n\n    &.light {\n      color: #ffffff;\n    }\n  }\n\n  &.notice {\n    @include mixins.icon-badge;\n  }\n\n  &.angularjs::before {\n    content: \"\\e92f\";\n    color: #dd0031;\n  }\n\n  &.html::before {\n    content: \"\\e939\";\n    color: #e34f26;\n  }\n\n  &.disabled {\n    opacity: 0.6;\n    pointer-events: none;\n  }\n\n  &.dull {\n    opacity: 0.6;\n  }\n\n  &:focus {\n    border: rgba(0, 0, 0, 0.1);\n  }\n\n  &:not(.floating):active {\n    transition: all 100ms ease;\n    background-color: rgba(0, 0, 0, 0.2) !important;\n    background-color: var(--active-icon-color) !important;\n  }\n\n  &.active {\n    background-color: rgba(0, 0, 0, 0.2) !important;\n    background-color: var(--active-icon-color) !important;\n  }\n\n  &.no-icon {\n    max-width: 5px;\n    margin-right: 5px;\n    border-radius: 0;\n  }\n\n  &.letters::before {\n    content: attr(data-letters);\n    text-transform: uppercase;\n    font-size: 0.6em;\n    font-weight: bolder;\n  }\n}\n\n.mask {\n  position: fixed;\n  left: 0;\n  top: 0;\n  display: block;\n  height: 100vh;\n  width: 100vw;\n  background-color: black;\n  opacity: 0;\n}\n\nfooter {\n\n  &.button-container,\n  .button-container {\n    overflow-x: auto;\n\n    .section {\n      max-width: 100%;\n      min-width: 100%;\n\n      .icon.active {\n        @include mixins.active-icon;\n      }\n    }\n\n    background-color: #9999ff;\n    background-color: var(--primary-color);\n    color: white;\n    color: var(--primary-text-color);\n  }\n}\n\n.section,\n.button-container {\n  display: flex;\n  min-height: 40px;\n  background-color: inherit;\n  color: inherit;\n  user-select: none;\n  width: 100%;\n\n  &.primary {\n    button {\n      color: white !important;\n      color: var(--button-text-color) !important;\n      background-color: #39f !important;\n      background-color: var(--button-background-color) !important;\n      box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);\n      box-shadow: 0 0 4px var(--box-shadow-color);\n      border-radius: 4px;\n\n      &:active {\n        background-color: #2c8ef0 !important;\n        background-color: var(--button-active-color) !important;\n        box-shadow: 0 0 2px rgba(0, 0, 0, 0.4);\n        box-shadow: inset 0 0 2px var(--box-shadow-color);\n      }\n    }\n  }\n\n  &.disabled {\n    pointer-events: none;\n\n    .icon,\n    input,\n    button {\n      opacity: 0.6;\n    }\n  }\n\n  >button {\n    flex: 1;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    border: none;\n    text-transform: uppercase;\n    background-color: inherit;\n    color: inherit;\n\n    * {\n      pointer-events: none;\n    }\n\n    &.disabled {\n      pointer-events: none;\n      opacity: 0.6;\n    }\n\n    &:active {\n      transition: all 100ms ease;\n      box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2);\n      box-shadow: inset 0 0 4px var(--box-shadow-color);\n    }\n\n    &:disabled {\n      opacity: 0.6;\n    }\n  }\n\n  textarea,\n  input {\n    flex: 2;\n    height: auto;\n    color: inherit;\n    border-bottom: 1px solid currentColor;\n    margin: 5px;\n    background-color: inherit;\n\n    &::placeholder {\n      color: rgba(255, 255, 255, 0.6);\n    }\n  }\n\n  .icon {\n    color: inherit;\n    font-size: 1.3em;\n  }\n\n  .search,\n  .save {\n    font-size: 1em;\n  }\n}\n\ninput {\n  height: 40px;\n  outline: none;\n  border: none;\n  background-color: inherit;\n  border-bottom: solid 1px #252525;\n  border-bottom: solid 1px var(--secondary-text-color);\n  padding: 0;\n  box-sizing: border-box;\n  color: #252525;\n  color: var(--secondary-text-color);\n  caret-color: currentColor;\n  text-indent: 10px;\n\n  &:focus {\n    border-bottom-color: #a90000 !important;\n    border-bottom-color: var(--active-color) !important;\n  }\n}\n\ninput,\ntextarea {\n  &::placeholder {\n    color: inherit;\n    opacity: 0.8;\n  }\n}\n\n.search-status {\n  flex: 1;\n  display: flex;\n  color: white;\n  color: var(--primary-text-color);\n  align-items: center;\n  justify-content: center;\n\n  span:not(:nth-child(2)) {\n    margin: 0 5px;\n    color: white;\n    color: var(--primary-text-color);\n  }\n}\n\n.cursor-menu {\n  position: absolute;\n  top: 0;\n  left: 0;\n  height: 40px;\n  background-color: #ffffff;\n  background-color: var(--secondary-color);\n  display: flex;\n  border-radius: 4px;\n  box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 0 8px var(--box-shadow-color);\n  border: none;\n  border: solid 1px var(--popup-border-color);\n  color: #252525;\n  color: var(--secondary-text-color);\n  transform-origin: left center;\n  z-index: 4;\n\n  >span,\n  >div {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    height: 100%;\n    font-size: 0.9em;\n    min-width: 50px;\n    color: inherit;\n    user-select: none;\n    white-space: nowrap;\n\n    &.disabled {\n      opacity: 0.6;\n      pointer-events: none;\n    }\n  }\n}\n\n.file {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background-repeat: no-repeat;\n  background-position: 6px center;\n  background-size: 18px;\n  width: 30px;\n  height: 30px;\n}\n\n.hr {\n  display: flex;\n  align-items: center;\n  margin: auto auto 15px auto;\n\n  &::after,\n  &::before {\n    content: \"\";\n    height: 1px;\n    width: 60px;\n    background-color: #252525;\n    background-color: var(--secondary-text-color);\n    margin: auto 15px;\n    opacity: 0.5;\n  }\n}\n\n.d-none {\n  display: none !important;\n}\n\n.floating.icon {\n  position: fixed;\n  height: 50px;\n  width: 50px;\n  font-size: 1.6rem;\n  border: 1px solid;\n  background-color: #9999ff;\n  background-color: var(--primary-color);\n  top: 10px;\n  right: 10px;\n  opacity: 0.2;\n  box-sizing: border-box;\n  color: white;\n  color: var(--primary-text-color);\n  transition: all 300ms ease;\n  box-shadow: -5px 5px 20px 0px rgba(0, 0, 0, 0.5);\n\n  &:active {\n    transition: all 100ms ease;\n    box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.5);\n  }\n\n  &.hide {\n    opacity: 0 !important;\n  }\n}\n\nbutton {\n  cursor: pointer;\n\n  &.floating.icon {\n    z-index: 1;\n    opacity: 1;\n\n    &:disabled {\n      opacity: 0.2;\n    }\n  }\n}\n\n#social-links {\n  position: relative;\n  height: 60px;\n  font-size: 1.2em;\n  width: 100%;\n  text-align: center;\n\n  &::after {\n    display: block;\n    width: 100%;\n    content: attr(title);\n    text-align: center;\n    font-size: 0.5em;\n    text-transform: none;\n  }\n\n  a {\n    display: inline-flex;\n    min-height: 40px;\n    min-width: 40px;\n    text-shadow: 0 0 1px white;\n\n    &.github {\n      color: black;\n    }\n  }\n}\n\n#header-toggler {\n  display: none;\n  top: 10px;\n  right: 10px;\n  z-index: 1;\n  height: 40px;\n  width: 40px;\n}\n\n#sidebar-toggler {\n  display: none;\n  top: 10px;\n  left: 10px;\n  z-index: 1;\n  height: 40px;\n  width: 40px;\n}\n\n#quicktools-toggler {\n  top: auto;\n  bottom: 10px;\n  right: 10px;\n  z-index: 1;\n}\n\n.sake {\n  animation: sake 3s ease-out infinite;\n}\n\n.flex-center {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.link {\n  text-decoration: underline;\n}\n\n.w-resize {\n  cursor: w-resize;\n}\n\n.note {\n  margin: 20px 0;\n\n  .note-title {\n    background-color: rgba(0, 0, 0, 0.2);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    height: 30px;\n    text-transform: uppercase;\n\n    .icon {\n      margin: 0 10px;\n    }\n  }\n\n  p {\n    padding: 10px;\n    box-sizing: border-box;\n    opacity: 0.8;\n    font-size: 0.9rem;\n  }\n}\n\ninput[type=\"search\"]::-webkit-search-decoration,\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-results-button,\ninput[type=\"search\"]::-webkit-search-results-decoration {\n  -webkit-appearance: none;\n}\n\n.tab-page-container {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  overflow: hidden;\n}\n\n.tab-page-content {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  overflow: auto;\n}\n\n.notification-toast-container {\n  position: absolute;\n  bottom: 20px;\n  left: 10px;\n  right: 10px;\n  display: flex;\n  flex-direction: column;\n  align-items: flex-end;\n  gap: 8px;\n  z-index: 1000;\n  pointer-events: none;\n\n  >* {\n    pointer-events: auto;\n  }\n\n  .notification-toast {\n    padding: 12px;\n    border-radius: 6px;\n    background: var(--secondary-color);\n    min-width: 200px;\n    max-width: min(400px, calc(100vw - 40px));\n    display: flex;\n    gap: 12px;\n    align-items: flex-start;\n    box-shadow: 0 4px 12px var(--box-shadow-color);\n    animation: toastSlideIn 0.3s ease-out;\n    transition: all 0.3s ease;\n    border: 1px solid var(--border-color);\n    word-break: break-word;\n    white-space: normal;\n    box-sizing: border-box;\n\n    &.hiding {\n      transform: translateX(120%);\n      opacity: 0;\n    }\n\n    .close-icon {\n      cursor: pointer;\n      font-size: 14px;\n      color: var(--secondary-text-color);\n      margin-left: auto;\n\n      &:hover {\n        color: var(--button-background-color);\n      }\n    }\n\n    .notification-icon {\n      width: 20px;\n      height: 20px;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      flex-shrink: 0;\n      color: var(--primary-text-color);\n    }\n\n    .notification-content {\n      flex: 1;\n      min-width: 0;\n\n      .notification-title {\n        font-size: 13px;\n        font-weight: 500;\n        margin-bottom: 4px;\n        color: var(--primary-text-color);\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n      }\n\n      .notification-message {\n        font-size: 12px;\n        color: var(--secondary-text-color);\n        line-height: 1.4;\n        overflow-wrap: break-word;\n        hyphens: auto;\n      }\n    }\n\n    &.success {\n      .notification-icon {\n        color: #48c158;\n      }\n    }\n\n    &.warning {\n      .notification-icon {\n        color: var(--danger-text-color);\n      }\n    }\n\n    &.error {\n      .notification-icon {\n        color: var(--error-text-color);\n      }\n    }\n\n    &.info {\n      .notification-icon {\n        color: var(--primary-text-color);\n      }\n    }\n  }\n\n  @media (max-width: 768px) {\n    align-items: stretch;\n\n    .notification-toast {\n      min-width: auto;\n      max-width: 100%;\n    }\n  }\n}\n\n@keyframes toastSlideIn {\n  from {\n    transform: translateX(120%);\n    opacity: 0;\n  }\n\n  to {\n    transform: translateX(0);\n    opacity: 1;\n  }\n}\n"
  },
  {
    "path": "src/pages/about/about.js",
    "content": "import \"./about.scss\";\nimport Logo from \"components/logo\";\nimport Page from \"components/page\";\nimport Reactive from \"html-tag-js/reactive\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport { hideAd } from \"lib/startAd\";\nimport helpers from \"utils/helpers\";\nexport default function AboutInclude() {\n\tconst $page = Page(strings.about.capitalize());\n\tconst webviewVersionName = Reactive(\"N/A\");\n\tconst webviewPackageName = Reactive(\"N/A\");\n\n\t$page.classList.add(\"about-us\");\n\t$page.body = (\n\t\t<main id=\"about-page\" className=\"main scroll\">\n\t\t\t<Logo />\n\n\t\t\t<div className=\"version-info\">\n\t\t\t\t<h1 className=\"version-title\">Acode editor</h1>\n\t\t\t\t<div className=\"version-number\">\n\t\t\t\t\tVersion {BuildInfo.version} ({BuildInfo.versionCode})\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"info-section\">\n\t\t\t\t<a\n\t\t\t\t\thref=\"#\"\n\t\t\t\t\tclassName=\"info-item\"\n\t\t\t\t\tonclick={(e) => {\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\tsystem.openInBrowser(\n\t\t\t\t\t\t\t`https://play.google.com/store/apps/details?id=${webviewPackageName.value}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"info-item-icon\">\n\t\t\t\t\t\t<span className=\"icon googlechrome\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"info-item-text\">\n\t\t\t\t\t\tWebview {webviewVersionName}\n\t\t\t\t\t\t<div className=\"info-item-subtext\">{webviewPackageName}</div>\n\t\t\t\t\t</div>\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.WEBSITE_URL} className=\"info-item\">\n\t\t\t\t\t<div className=\"info-item-icon\">\n\t\t\t\t\t\t<span className=\"icon acode\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"info-item-text\">\n\t\t\t\t\t\tOfficial webpage\n\t\t\t\t\t\t<div className=\"info-item-subtext\">{constants.WEBSITE_URL}</div>\n\t\t\t\t\t</div>\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.FOXBIZ_URL} className=\"info-item\">\n\t\t\t\t\t<div className=\"info-item-icon\">\n\t\t\t\t\t\t<span className=\"icon foxbiz\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"info-item-text\">\n\t\t\t\t\t\tFoxbiz Software Pvt. Ltd.\n\t\t\t\t\t\t<div className=\"info-item-subtext\">{constants.FOXBIZ_URL}</div>\n\t\t\t\t\t</div>\n\t\t\t\t</a>\n\t\t\t</div>\n\n\t\t\t<div className=\"social-links\">\n\t\t\t\t<a href=\"mailto:apps@foxdebug.com\" className=\"social-link\">\n\t\t\t\t\t<div className=\"social-icon\">\n\t\t\t\t\t\t<span className=\"icon gmail\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\tMail\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.TWITTER_URL} className=\"social-link\">\n\t\t\t\t\t<div className=\"social-icon\">\n\t\t\t\t\t\t<span className=\"icon twitter\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\tTwitter\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.INSTAGRAM_URL} className=\"social-link\">\n\t\t\t\t\t<div className=\"social-icon\">\n\t\t\t\t\t\t<span className=\"icon instagram\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\tInstagram\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.GITHUB_URL} className=\"social-link\">\n\t\t\t\t\t<div className=\"social-icon\">\n\t\t\t\t\t\t<span className=\"icon github\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\tGitHub\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.TELEGRAM_URL} className=\"social-link\">\n\t\t\t\t\t<div className=\"social-icon\">\n\t\t\t\t\t\t<span className=\"icon telegram\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\tTelegram\n\t\t\t\t</a>\n\t\t\t\t<a href={constants.DISCORD_URL} className=\"social-link\">\n\t\t\t\t\t<div className=\"social-icon\">\n\t\t\t\t\t\t<span className=\"icon discord\"></span>\n\t\t\t\t\t</div>\n\t\t\t\t\tDiscord\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</main>\n\t);\n\n\tsystem.getWebviewInfo((res) => {\n\t\twebviewPackageName.value = res?.packageName || \"N/A\";\n\t\twebviewVersionName.value = res?.versionName || \"N/A\";\n\t});\n\n\tactionStack.push({\n\t\tid: \"about\",\n\t\taction: $page.hide,\n\t});\n\n\t$page.onhide = function () {\n\t\tactionStack.remove(\"about\");\n\t\thideAd();\n\t};\n\n\tapp.append($page);\n\thelpers.showAd();\n}\n"
  },
  {
    "path": "src/pages/about/about.scss",
    "content": "#about-page {\n  padding: 16px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  gap: 24px;\n  overflow-y: auto;\n\n  .version-info {\n    text-align: center;\n    margin-bottom: 32px;\n\n    .version-title {\n      font-size: 24px;\n      font-weight: 600;\n      margin-bottom: 4px;\n    }\n\n    .version-number {\n      color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n      font-size: 14px;\n    }\n  }\n\n  .info-section {\n    width: 100%;\n    max-width: 400px;\n    min-height: fit-content;\n    height: auto;\n    background: color-mix(in srgb,\n        var(--popup-background-color) 20%,\n        transparent);\n    border-radius: 12px;\n    overflow: visible;\n    margin-bottom: 16px;\n    display: flex;\n    flex-direction: column;\n\n    .info-item {\n      display: flex;\n      align-items: center;\n      padding: 16px;\n      color: var(--secondary-text-color);\n      text-decoration: none;\n      border-bottom: 1px solid var(--border-color);\n      transition: background 0.2s ease;\n\n      &:last-child {\n        border-bottom: none;\n      }\n\n      &:hover {\n        background: color-mix(in srgb,\n            var(--popup-background-color) 40%,\n            transparent);\n      }\n\n      .info-item-icon {\n        width: 24px;\n        height: 24px;\n        margin-right: 12px;\n        display: flex;\n        align-items: center;\n        font-size: 24px;\n      }\n\n      .info-item-text {\n        flex: 1;\n        font-size: 15px;\n\n        .info-item-subtext {\n          color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n          font-size: 13px;\n          margin-top: 2px;\n        }\n      }\n    }\n  }\n\n  .social-links {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    gap: 12px;\n    width: 100%;\n    max-width: 400px;\n\n    .social-link {\n      display: inline-flex;\n      align-items: center;\n      padding: 12px;\n      background: color-mix(in srgb,\n          var(--popup-background-color) 20%,\n          transparent);\n      border-radius: 12px;\n      color: var(--secondary-text-color);\n      text-decoration: none;\n      transition: all 0.2s ease;\n\n      &:hover {\n        background: color-mix(in srgb,\n            var(--popup-background-color) 40%,\n            transparent);\n        transform: translateY(-1px);\n      }\n\n      .social-icon {\n        width: 24px;\n        height: 24px;\n        margin-right: 12px;\n        font-size: 24px;\n        display: flex;\n        align-items: center;\n      }\n    }\n  }\n\n  .icon {\n    height: 100%;\n    width: 100%;\n  }\n\n  .foxbiz {\n    background-image: url(./foxbiz.svg);\n  }\n\n  .discord {\n    background-image: url(./discord.svg);\n  }\n}"
  },
  {
    "path": "src/pages/about/index.js",
    "content": "//jshint ignore:start\n\nfunction About() {\n\timport(/* webpackChunkName: \"about\" */ \"./about\").then((res) => {\n\t\tres.default();\n\t});\n}\nexport default About;\n"
  },
  {
    "path": "src/pages/adRewards/adRewards.scss",
    "content": "#ad-rewards-page {\n\tpadding: 16px;\n\tmax-width: 600px;\n\tmargin: 0 auto;\n\tcolor: var(--primary-text-color);\n\n\t.reward-hero {\n\t\tmargin-bottom: 20px;\n\n\t\t.hero-copy {\n\t\t\tmargin-bottom: 16px;\n\n\t\t\t.eyebrow {\n\t\t\t\tdisplay: inline-flex;\n\t\t\t\talign-items: center;\n\t\t\t\tpadding: 4px 10px;\n\t\t\t\tborder-radius: 6px;\n\t\t\t\tbackground: color-mix(in srgb,\n\t\t\t\t\t\tvar(--active-color) 15%,\n\t\t\t\t\t\ttransparent);\n\t\t\t\tcolor: var(--active-color);\n\t\t\t\tfont-size: 0.72rem;\n\t\t\t\tfont-weight: 600;\n\t\t\t\tletter-spacing: 0.05em;\n\t\t\t\ttext-transform: uppercase;\n\t\t\t}\n\n\t\t\th1 {\n\t\t\t\tmargin: 10px 0 6px;\n\t\t\t\tfont-size: 1.2rem;\n\t\t\t\tfont-weight: 700;\n\t\t\t\tline-height: 1.3;\n\t\t\t}\n\n\t\t\tp {\n\t\t\t\tmargin: 0;\n\t\t\t\tfont-size: 0.85rem;\n\t\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\t\tvar(--primary-text-color) 60%,\n\t\t\t\t\t\ttransparent);\n\t\t\t\tline-height: 1.5;\n\t\t\t}\n\t\t}\n\n\t\t.reward-status {\n\t\t\tpadding: 12px 14px;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: 12px;\n\t\t\tflex-wrap: wrap;\n\t\t\tbackground: color-mix(in srgb,\n\t\t\t\t\tvar(--primary-color) 10%,\n\t\t\t\t\ttransparent);\n\t\t\tborder-radius: 10px;\n\t\t\tborder: 1px solid var(--border-color);\n\t\t\ttransition: all 0.2s ease;\n\n\t\t\t&.is-active {\n\t\t\t\tborder-color: color-mix(in srgb,\n\t\t\t\t\t\tvar(--active-color) 50%,\n\t\t\t\t\t\ttransparent);\n\t\t\t\tbackground: color-mix(in srgb,\n\t\t\t\t\t\tvar(--active-color) 10%,\n\t\t\t\t\t\ttransparent);\n\t\t\t}\n\n\t\t\t.status-label {\n\t\t\t\tfont-size: 0.72rem;\n\t\t\t\tfont-weight: 600;\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tletter-spacing: 0.04em;\n\t\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\t\tvar(--primary-text-color) 55%,\n\t\t\t\t\t\ttransparent);\n\t\t\t}\n\n\t\t\t.status-value {\n\t\t\t\tfont-size: 0.9rem;\n\t\t\t\tfont-weight: 700;\n\t\t\t\tflex: 1;\n\t\t\t\tmin-width: 0;\n\t\t\t}\n\n\t\t\t.status-note {\n\t\t\t\tfont-size: 0.78rem;\n\t\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\t\tvar(--primary-text-color) 55%,\n\t\t\t\t\t\ttransparent);\n\t\t\t\twidth: 100%;\n\t\t\t}\n\n\t\t\t.status-subnote {\n\t\t\t\tfont-size: 0.74rem;\n\t\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\t\tvar(--primary-text-color) 50%,\n\t\t\t\t\t\ttransparent);\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t}\n\t}\n\n\t.reward-grid {\n\t\tdisplay: grid;\n\t\tgrid-template-columns: repeat(2, 1fr);\n\t\tgap: 10px;\n\t\tmargin-bottom: 20px;\n\t}\n\n\t.reward-offer {\n\t\tbackground: color-mix(in srgb,\n\t\t\t\tvar(--popup-background-color) 20%,\n\t\t\t\ttransparent);\n\t\tborder-radius: 12px;\n\t\tborder: 1px solid var(--border-color);\n\t\tpadding: 14px;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 8px;\n\t\ttransition: border-color 0.2s ease;\n\n\t\t&.is-focus {\n\t\t\tborder-color: color-mix(in srgb,\n\t\t\t\t\tvar(--link-text-color) 50%,\n\t\t\t\t\ttransparent);\n\t\t}\n\n\t\t&.is-upgrade {\n\t\t\tborder-color: color-mix(in srgb,\n\t\t\t\t\tvar(--button-background-color) 40%,\n\t\t\t\t\ttransparent);\n\t\t}\n\n\t\t.offer-header {\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: space-between;\n\t\t\talign-items: flex-start;\n\t\t\tgap: 8px;\n\t\t}\n\n\t\t.offer-kicker {\n\t\t\tfont-size: 0.68rem;\n\t\t\tfont-weight: 600;\n\t\t\ttext-transform: uppercase;\n\t\t\tletter-spacing: 0.04em;\n\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\tvar(--primary-text-color) 55%,\n\t\t\t\t\ttransparent);\n\t\t}\n\n\t\th2 {\n\t\t\tmargin: 3px 0 0;\n\t\t\tfont-size: 0.88rem;\n\t\t\tfont-weight: 600;\n\t\t}\n\n\t\t.offer-duration {\n\t\t\tpadding: 3px 8px;\n\t\t\tborder-radius: 6px;\n\t\t\tbackground: var(--primary-color);\n\t\t\tcolor: var(--primary-text-color);\n\t\t\tfont-size: 0.68rem;\n\t\t\tfont-weight: 600;\n\t\t\twhite-space: nowrap;\n\t\t\talign-self: flex-start;\n\t\t}\n\n\t\tp {\n\t\t\tmargin: 0;\n\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\tvar(--primary-text-color) 60%,\n\t\t\t\t\ttransparent);\n\t\t\tline-height: 1.45;\n\t\t\tfont-size: 0.8rem;\n\t\t\tflex: 1;\n\t\t}\n\n\t\t.offer-limit {\n\t\t\tfont-size: 0.74rem;\n\t\t\tline-height: 1.4;\n\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\tvar(--primary-text-color) 55%,\n\t\t\t\t\ttransparent);\n\t\t}\n\t}\n\n\t.offer-action {\n\t\tappearance: none;\n\t\tborder: 0;\n\t\tborder-radius: 8px;\n\t\tpadding: 9px 12px;\n\t\tfont: inherit;\n\t\tfont-size: 0.82rem;\n\t\tfont-weight: 600;\n\t\tbackground: var(--button-background-color);\n\t\tcolor: var(--button-text-color);\n\t\tcursor: pointer;\n\t\ttransition: all 0.2s ease;\n\n\t\t&:active {\n\t\t\ttransform: translateY(1px);\n\t\t\topacity: 0.9;\n\t\t}\n\n\t\t&:disabled {\n\t\t\topacity: 0.45;\n\t\t\tcursor: not-allowed;\n\t\t}\n\n\t\t&.secondary {\n\t\t\tbackground: var(--primary-color);\n\t\t\tcolor: var(--primary-text-color);\n\t\t}\n\t}\n\n\t.reward-notes {\n\t\tdisplay: grid;\n\t\tgap: 10px;\n\n\t\t.note-card {\n\t\t\tpadding: 12px 14px;\n\t\t\tborder-left: 3px solid var(--active-color);\n\t\t\tborder-radius: 0 8px 8px 0;\n\t\t\tbackground: color-mix(in srgb,\n\t\t\t\t\tvar(--primary-color) 8%,\n\t\t\t\t\ttransparent);\n\n\t\t\th3 {\n\t\t\t\tmargin: 0 0 4px;\n\t\t\t\tfont-size: 0.82rem;\n\t\t\t\tfont-weight: 600;\n\t\t\t}\n\n\t\t\tp {\n\t\t\t\tmargin: 0;\n\t\t\t\tline-height: 1.45;\n\t\t\t\tfont-size: 0.78rem;\n\t\t\t\tcolor: color-mix(in srgb,\n\t\t\t\t\t\tvar(--primary-text-color) 60%,\n\t\t\t\t\t\ttransparent);\n\t\t\t}\n\t\t}\n\t}\n}\n\n@media (max-width: 400px) {\n\t#ad-rewards-page .reward-grid {\n\t\tgrid-template-columns: 1fr;\n\t}\n}\n"
  },
  {
    "path": "src/pages/adRewards/index.js",
    "content": "import \"./adRewards.scss\";\n\nimport Page from \"components/page\";\nimport loader from \"dialogs/loader\";\nimport actionStack from \"lib/actionStack\";\nimport adRewards from \"lib/adRewards\";\nimport removeAds from \"lib/removeAds\";\nimport { hideAd } from \"lib/startAd\";\nimport helpers from \"utils/helpers\";\n\nlet $rewardPage = null;\n\nexport default function openAdRewardsPage() {\n\tif ($rewardPage) {\n\t\t$rewardPage.show?.();\n\t\treturn $rewardPage;\n\t}\n\n\tconst $page = Page(\"Ad-free passes\");\n\n\tfunction render() {\n\t\tconst rewardState = adRewards.getState();\n\t\tconst rewardedSupported = adRewards.isRewardedSupported();\n\t\tconst unavailableReason = adRewards.getRewardedUnavailableReason();\n\t\tconst offers = adRewards.getOffers();\n\t\tconst isBusy = adRewards.isWatchingReward();\n\t\tconst redemptionStatus = adRewards.canRedeemNow();\n\t\tconst rewardDisabledReason = !rewardedSupported\n\t\t\t? unavailableReason\n\t\t\t: !redemptionStatus.ok\n\t\t\t\t? redemptionStatus.reason\n\t\t\t\t: \"\";\n\n\t\t$page.body = (\n\t\t\t<main id=\"ad-rewards-page\" className=\"main scroll\">\n\t\t\t\t<section className=\"reward-hero\">\n\t\t\t\t\t<div className=\"hero-copy\">\n\t\t\t\t\t\t<div className=\"eyebrow\">Rewarded ads</div>\n\t\t\t\t\t\t<h1>Trade a short ad break for focused coding time.</h1>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tUnlock temporary ad-free time without leaving the free version.\n\t\t\t\t\t\t\tWhen your pass expires, Acode will show a toast and add a\n\t\t\t\t\t\t\tnotification in-app.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`reward-status ${rewardState.isActive ? \"is-active\" : \"is-idle\"}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"status-label\">\n\t\t\t\t\t\t\t{rewardState.isActive ? \"Ad-free active\" : \"No active pass\"}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"status-value\">\n\t\t\t\t\t\t\t{rewardState.isActive\n\t\t\t\t\t\t\t\t? adRewards.getRemainingLabel()\n\t\t\t\t\t\t\t\t: \"Watch a rewarded ad to start a pass\"}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"status-note\">\n\t\t\t\t\t\t\t{rewardState.isActive\n\t\t\t\t\t\t\t\t? `Expires ${adRewards.getExpiryLabel()}`\n\t\t\t\t\t\t\t\t: \"Passes stack on top of any active rewarded time.\"}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"status-subnote\">\n\t\t\t\t\t\t\t{rewardState.redemptionsToday}/{rewardState.maxRedemptionsPerDay}{\" \"}\n\t\t\t\t\t\t\trewards used today\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</section>\n\n\t\t\t\t<section className=\"reward-grid\">\n\t\t\t\t\t{offers.map((offer) => (\n\t\t\t\t\t\t<article className={`reward-offer ${offer.accentClass}`}>\n\t\t\t\t\t\t\t<div className=\"offer-header\">\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<div className=\"offer-kicker\">\n\t\t\t\t\t\t\t\t\t\t{offer.adsRequired} rewarded ad\n\t\t\t\t\t\t\t\t\t\t{offer.adsRequired > 1 ? \"s\" : \"\"}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<h2>{offer.title}</h2>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"offer-duration\">{offer.durationLabel}</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<p>{offer.description}</p>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclassName=\"offer-action\"\n\t\t\t\t\t\t\t\tdisabled={!rewardedSupported || isBusy || !redemptionStatus.ok}\n\t\t\t\t\t\t\t\tonclick={() => watchOffer(offer.id)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isBusy\n\t\t\t\t\t\t\t\t\t? \"Loading ad...\"\n\t\t\t\t\t\t\t\t\t: `Watch ${offer.adsRequired} ad${offer.adsRequired > 1 ? \"s\" : \"\"}`}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<div className=\"offer-limit\">\n\t\t\t\t\t\t\t\t{rewardDisabledReason ||\n\t\t\t\t\t\t\t\t\t`${rewardState.remainingRedemptions} of ${rewardState.maxRedemptionsPerDay} rewards left today`}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t))}\n\n\t\t\t\t\t<article className=\"reward-offer is-upgrade\">\n\t\t\t\t\t\t<div className=\"offer-header\">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<div className=\"offer-kicker\">Permanent option</div>\n\t\t\t\t\t\t\t\t<h2>Remove ads for good</h2>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"offer-duration\">One purchase</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tIf you use Acode daily, Pro still gives the cleanest experience.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"offer-action secondary\"\n\t\t\t\t\t\t\tonclick={purchaseRemoveAds}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tBuy remove ads\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</article>\n\t\t\t\t</section>\n\n\t\t\t\t<section className=\"reward-notes\">\n\t\t\t\t\t<div className=\"note-card\">\n\t\t\t\t\t\t<h3>How it works</h3>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tRewarded passes hide your banners and interstitials until the\n\t\t\t\t\t\t\ttimer ends. If you already have time left, new rewards extend the\n\t\t\t\t\t\t\texpiry.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"note-card\">\n\t\t\t\t\t\t<h3>Limits</h3>\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\tYou can redeem up to {rewardState.maxRedemptionsPerDay} rewards\n\t\t\t\t\t\t\tper day, and your active ad-free pass is capped at 10 hours.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</section>\n\t\t\t</main>\n\t\t);\n\t}\n\n\tasync function purchaseRemoveAds() {\n\t\ttry {\n\t\t\tloader.showTitleLoader();\n\t\t\tawait removeAds();\n\t\t\t$page.hide();\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t} finally {\n\t\t\tloader.removeTitleLoader();\n\t\t}\n\t}\n\n\tasync function watchOffer(offerId) {\n\t\ttry {\n\t\t\trender();\n\t\t\tawait adRewards.watchOffer(offerId);\n\t\t} catch (error) {\n\t\t\thelpers.error(error);\n\t\t} finally {\n\t\t\trender();\n\t\t}\n\t}\n\n\tconst unsubscribe = adRewards.onChange(() => {\n\t\tif ($page.isConnected) {\n\t\t\trender();\n\t\t}\n\t});\n\n\t$page.onhide = () => {\n\t\tunsubscribe();\n\t\tactionStack.remove(\"ad-rewards\");\n\t\thelpers.showAd();\n\t\t$rewardPage = null;\n\t};\n\n\tactionStack.push({\n\t\tid: \"ad-rewards\",\n\t\taction: $page.hide,\n\t});\n\n\thideAd(true);\n\trender();\n\tapp.append($page);\n\t$rewardPage = $page;\n\n\treturn $page;\n}\n"
  },
  {
    "path": "src/pages/changelog/changelog.js",
    "content": "import \"./style.scss\";\nimport fsOperation from \"fileSystem\";\nimport Contextmenu from \"components/contextmenu\";\nimport Page from \"components/page\";\nimport toast from \"components/toast\";\nimport DOMPurify from \"dompurify\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport { hideAd } from \"lib/startAd\";\nimport markdownIt from \"markdown-it\";\nimport markdownItFootnote from \"markdown-it-footnote\";\nimport markdownItTaskLists from \"markdown-it-task-lists\";\nimport helpers from \"utils/helpers\";\n\nexport default async function Changelog() {\n\tconst GITHUB_API_URL =\n\t\t\"https://api.github.com/repos/Acode-Foundation/Acode/releases\";\n\tconst CHANGELOG_FILE_URL =\n\t\t\"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/CHANGELOG.md\";\n\tconst currentVersion = BuildInfo.version;\n\n\tlet selectedVersion = currentVersion;\n\tlet selectedStatus = \"current\";\n\tconst versionIndicatorRef = Ref();\n\tconst versionTextRef = Ref();\n\tconst body = Ref();\n\n\tconst versionSelector = (\n\t\t<div className=\"changelog-version-selector\" data-action=\"select-version\">\n\t\t\t<span\n\t\t\t\tclassName={\"status-indicator status-\" + selectedStatus}\n\t\t\t\tref={versionIndicatorRef}\n\t\t\t></span>\n\t\t\t<span ref={versionTextRef}>{selectedVersion}</span>\n\t\t</div>\n\t);\n\n\tconst $page = Page(strings[\"changelog\"], {\n\t\ttail: versionSelector,\n\t});\n\n\tconst versionSelectorMenu = Contextmenu({\n\t\ttop: \"36px\",\n\t\tright: \"5px\",\n\t\ttoggler: versionSelector,\n\t\ttransformOrigin: \"top right\",\n\t\tonclick: menuClickHandler,\n\t\tinnerHTML: () => {\n\t\t\treturn `\n        <li action=\"current\">\n          <span class=\"text\">Current Version (${currentVersion})</span>\n        </li>\n        <li action=\"latest\">\n          <span class=\"text\">Latest Release</span>\n        </li>\n        <li action=\"beta\">\n          <span class=\"text\">Beta Version</span>\n        </li>\n        <li action=\"full\">\n          <span class=\"text\">Full Changelog</span>\n        </li>\n      `;\n\t\t},\n\t});\n\n\tconst changelogMd = await import(\"../../../CHANGELOG.md\");\n\n\ttoast(\"Loading changelog...\");\n\tloadVersionChangelog();\n\tbody.onref = () => renderChangelog(changelogMd.default);\n\t$page.body = <div className=\"md\" id=\"changelog\" ref={body} />;\n\tapp.append($page);\n\thelpers.showAd();\n\n\t$page.onhide = function () {\n\t\tactionStack.remove(\"changelog\");\n\t\thideAd();\n\t};\n\n\tactionStack.push({\n\t\tid: \"changelog\",\n\t\taction: $page.hide,\n\t});\n\n\tasync function loadLatestRelease() {\n\t\ttry {\n\t\t\tconst releases = await fsOperation(`${GITHUB_API_URL}/latest`).readFile(\n\t\t\t\t\"json\",\n\t\t\t);\n\t\t\tselectedVersion = releases.tag_name.replace(\"v\", \"\");\n\t\t\tselectedStatus = \"latest\";\n\t\t\tupdateVersionSelector();\n\t\t\treturn renderChangelog(releases.body);\n\t\t} catch (error) {\n\t\t\ttoast(\"Failed to load latest release notes\");\n\t\t\trenderChangelog(changelogMd.default);\n\t\t}\n\t}\n\n\tasync function loadBetaRelease() {\n\t\ttry {\n\t\t\tconst releases = await fsOperation(GITHUB_API_URL).readFile(\"json\");\n\t\t\tconst betaRelease = releases.find((r) => r.prerelease);\n\t\t\tif (!betaRelease) {\n\t\t\t\tbody.content = <div className=\"error\">No beta release found</div>;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tselectedVersion = betaRelease.tag_name.replace(\"v\", \"\");\n\t\t\tselectedStatus = \"prerelease\";\n\t\t\tupdateVersionSelector();\n\t\t\treturn renderChangelog(betaRelease.body);\n\t\t} catch (error) {\n\t\t\ttoast(\"Failed to load beta release notes\");\n\t\t\trenderChangelog(changelogMd.default);\n\t\t}\n\t}\n\n\tasync function loadFullChangelog() {\n\t\ttry {\n\t\t\tconst changeLogText =\n\t\t\t\tawait fsOperation(CHANGELOG_FILE_URL).readFile(\"utf8\");\n\t\t\tconst cleanedText = changeLogText.replace(/^#\\s*Change\\s*Log\\s*\\n*/i, \"\");\n\t\t\tselectedVersion = \"Changelogs.md\";\n\t\t\tselectedStatus = \"current\";\n\t\t\tupdateVersionSelector();\n\t\t\treturn renderChangelog(cleanedText);\n\t\t} catch (error) {\n\t\t\ttoast(\"Failed to load full changelog\");\n\t\t\trenderChangelog(changelogMd.default);\n\t\t}\n\t}\n\n\tasync function loadVersionChangelog() {\n\t\ttry {\n\t\t\tconst releases = await fsOperation(GITHUB_API_URL).readFile(\"json\");\n\t\t\tconst currentRelease = releases.find(\n\t\t\t\t(r) => r.tag_name.replace(\"v\", \"\") === currentVersion,\n\t\t\t);\n\t\t\tselectedVersion = currentVersion;\n\t\t\tselectedStatus = \"current\";\n\t\t\tupdateVersionSelector();\n\t\t\tif (currentRelease) {\n\t\t\t\treturn renderChangelog(currentRelease.body);\n\t\t\t} else {\n\t\t\t\treturn loadLatestRelease();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\ttoast(\"Failed to load version changelog\");\n\t\t\trenderChangelog(changelogMd.default);\n\t\t}\n\t}\n\n\tfunction renderChangelog(text) {\n\t\tconst md = markdownIt({ html: true, linkify: true });\n\t\tconst REPO_URL = \"https://github.com/Acode-Foundation/Acode\";\n\t\tlet processedText = text\n\t\t\t// Convert full PR URLs to #number format with links preserved in markdown\n\t\t\t.replace(\n\t\t\t\t/https:\\/\\/github\\.com\\/Acode-Foundation\\/Acode\\/pull\\/(\\d+)/g,\n\t\t\t\t`[#$1](${REPO_URL}/pull/$1)`,\n\t\t\t)\n\t\t\t// Convert existing #number references to links if they aren't already\n\t\t\t.replace(/(?<!\\[)#(\\d+)(?!\\])/g, `[#$1](${REPO_URL}/pull/$1)`)\n\t\t\t// Convert @username mentions to GitHub profile links\n\t\t\t.replace(/@(\\w+)/g, \"[@$1](https://github.com/$1)\");\n\n\t\tmd.use(markdownItTaskLists);\n\t\tmd.use(markdownItFootnote);\n\t\tconst renderedHtml = md.render(processedText);\n\t\tbody.innerHTML = DOMPurify.sanitize(renderedHtml);\n\t}\n\n\tfunction updateVersionSelector() {\n\t\tversionTextRef.textContent = selectedVersion;\n\t\tversionIndicatorRef.className = \"status-indicator status-\" + selectedStatus;\n\t}\n\n\tasync function menuClickHandler(e) {\n\t\tconst action = e.target.closest(\"li\")?.getAttribute(\"action\");\n\t\tif (!action) return;\n\t\tversionSelectorMenu.hide();\n\n\t\tswitch (action) {\n\t\t\tcase \"current\":\n\t\t\t\tawait loadVersionChangelog();\n\t\t\t\tbreak;\n\t\t\tcase \"latest\":\n\t\t\t\tawait loadLatestRelease();\n\t\t\t\tbreak;\n\t\t\tcase \"beta\":\n\t\t\t\tawait loadBetaRelease();\n\t\t\t\tbreak;\n\t\t\tcase \"full\":\n\t\t\t\tawait loadFullChangelog();\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/pages/changelog/index.js",
    "content": "function plugin({ id, installed }, onInstall, onUninstall) {\n\timport(/* webpackChunkName: \"changelog\" */ \"./changelog\").then((res) => {\n\t\tconst Changelog = res.default;\n\t\tChangelog();\n\t});\n}\n\nexport default plugin;\n"
  },
  {
    "path": "src/pages/changelog/style.scss",
    "content": "#changelog {\n  max-width: 800px;\n  margin: auto;\n  overflow: auto;\n  padding: 0 1rem;\n}\n\n.changelog-version-selector {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  background-color: var(--popup-background-color);\n  border: none;\n  border-radius: 8px;\n  padding: 8px 16px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: all 0.2s ease;\n  margin-right: 6px;\n\n  &:hover {\n    background-color: var(--secondary-color);\n  }\n}\n\n.status-indicator {\n  display: inline-block;\n  width: 8px;\n  height: 8px;\n  border-radius: 50%;\n}\n.status-latest {\n  background-color: #10b981;\n}\n.status-prerelease {\n  background-color: var(--danger-color);\n}\n.status-current {\n  background-color: var(--active-icon-color);\n}\n\n.loading {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  height: 100%;\n  color: var(--primary-text-color);\n  font-size: 1.2em;\n  animation: pulse 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse {\n  0% {\n    opacity: 0.6;\n  }\n  50% {\n    opacity: 1;\n  }\n  100% {\n    opacity: 0.6;\n  }\n}\n"
  },
  {
    "path": "src/pages/customTheme/customTheme.js",
    "content": "import \"./customTheme.scss\";\n\nimport Page from \"components/page\";\nimport color from \"dialogs/color\";\nimport confirm from \"dialogs/confirm\";\nimport select from \"dialogs/select\";\nimport actionStack from \"lib/actionStack\";\nimport settings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport ThemeBuilder from \"theme/builder\";\nimport themes from \"theme/list\";\nimport { isValidColor } from \"utils/color/regex\";\nimport helpers from \"utils/helpers\";\n\nexport default function CustomThemeInclude() {\n\tconst theme = themes.get(\"custom\");\n\tconst $page = Page(`${strings[\"custom\"]} ${strings[\"theme\"]}`.capitalize());\n\t$page.header.append(\n\t\t<span\n\t\t\tattr-action=\"reset-theme\"\n\t\t\tstyle={{ color: \"red\" }}\n\t\t\tclassName=\"icon historyrestore\"\n\t\t></span>,\n\t\t<span attr-action=\"set-theme\" className=\"icon check\"></span>,\n\t);\n\n\trender();\n\tapp.append($page);\n\thelpers.showAd();\n\n\tactionStack.push({\n\t\tid: \"custom-theme\",\n\t\taction: $page.hide,\n\t});\n\n\t$page.onhide = () => {\n\t\tactionStack.remove(\"custom-theme\");\n\t\thideAd();\n\t};\n\n\t$page.addEventListener(\"click\", handleClick);\n\n\t/**\n\t * Handle click event\n\t * @param {MouseEvent | TouchEvent} e\n\t */\n\tasync function handleClick(e) {\n\t\tconst $target = e.target;\n\t\tif ($target instanceof HTMLElement) {\n\t\t\tconst action = $target.getAttribute(\"action\");\n\n\t\t\tif (action === \"set-theme\") {\n\t\t\t\ttry {\n\t\t\t\t\ttheme.type = await select(strings[\"theme type\"], [\n\t\t\t\t\t\t[\"light\", strings[\"light\"]],\n\t\t\t\t\t\t[\"dark\", strings[\"dark\"]],\n\t\t\t\t\t]);\n\t\t\t\t\tapplyTheme();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(\"Unable to update custom theme type.\", error);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (action === \"reset-theme\") {\n\t\t\t\tconst confirmation = await confirm(\n\t\t\t\t\tstrings[\"info\"].toUpperCase(),\n\t\t\t\t\tstrings[\"reset warning\"],\n\t\t\t\t);\n\t\t\t\tif (!confirmation) return;\n\t\t\t\tsettings.reset(\"customTheme\");\n\t\t\t\tthemes.update(ThemeBuilder.fromJSON(settings.value.customTheme));\n\t\t\t\tapplyTheme();\n\t\t\t\trender();\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction applyTheme() {\n\t\tsetTimeout(() => {\n\t\t\tthemes.apply(\"custom\");\n\t\t}, 300);\n\t}\n\n\tfunction render() {\n\t\tconst pascalToNormal = (str) =>\n\t\t\tstr.replace(/([A-Z])/g, \" $1\").toLowerCase();\n\t\tconst customTheme = themes.get(\"custom\");\n\n\t\t$page.body = (\n\t\t\t<div id=\"custom-theme\" className=\"main\">\n\t\t\t\t<div className=\"list scroll\">\n\t\t\t\t\t{Object.keys(customTheme.toJSON())\n\t\t\t\t\t\t.filter((key) => isValidColor(customTheme[key]))\n\t\t\t\t\t\t.map((key) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"list-item\"\n\t\t\t\t\t\t\t\ttabindex={0}\n\t\t\t\t\t\t\t\tonclick={async (e) => {\n\t\t\t\t\t\t\t\t\tconst newColor = await color(customTheme[key]);\n\t\t\t\t\t\t\t\t\tcustomTheme[key] = newColor;\n\t\t\t\t\t\t\t\t\te.target.get(\".icon\").style.color = newColor;\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tstyle={{ color: customTheme[key] }}\n\t\t\t\t\t\t\t\t\tclassName=\"icon color\"\n\t\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t\t\t<div className=\"container\">\n\t\t\t\t\t\t\t\t\t<span className=\"text\">{pascalToNormal(key)}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "src/pages/customTheme/customTheme.scss",
    "content": "#custom-theme {\r\n  overflow: auto;\r\n\r\n  .icon.color::before {\r\n    content: '';\r\n    height: 30px;\r\n    width: 30px;\r\n    border: solid 1px rgb(255, 255, 255);\r\n    border-radius: 50%;\r\n    box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);\r\n    background-color: currentColor;\r\n  }\r\n}"
  },
  {
    "path": "src/pages/customTheme/index.js",
    "content": "export default async function CustomTheme(...args) {\n\tconst customTheme = (\n\t\tawait import(/* webpackChunkName: \"customTheme\" */ \"./customTheme\")\n\t).default;\n\tcustomTheme();\n}\n"
  },
  {
    "path": "src/pages/fileBrowser/add-menu-home.hbs",
    "content": "<li action='add-path'>{{add path}}</li>\r\n<li action='addFtp'>{{add ftp}}</li>\r\n<li action='addSftp'>{{add sftp}}</li>"
  },
  {
    "path": "src/pages/fileBrowser/add-menu.hbs",
    "content": "<li action=\"create\" value=\"file\">{{new file}}</li>\n<li action=\"create\" value=\"folder\">{{new folder}}</li>\n<li action=\"create\" value=\"project\">{{new project}}</li>\n<li action=\"import-project-zip\" value=\"import-project-zip\">{{import project zip}}</li>\n"
  },
  {
    "path": "src/pages/fileBrowser/fileBrowser.hbs",
    "content": "<div tabindex='-1' class='main' id='file-browser' type='{{type}}'>\r\n  <div class='navigation' tabindex='-1'></div>\r\n  <div class='info'>ⓘ {{info}}</div>\r\n</div>"
  },
  {
    "path": "src/pages/fileBrowser/fileBrowser.js",
    "content": "import \"./fileBrowser.scss\";\n\nimport fsOperation from \"fileSystem\";\nimport externalFs from \"fileSystem/externalFs\";\nimport Checkbox from \"components/checkbox\";\nimport Contextmenu from \"components/contextmenu\";\nimport Page from \"components/page\";\nimport searchBar from \"components/searchbar\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport JSZip from \"jszip\";\nimport actionStack from \"lib/actionStack\";\nimport checkFiles from \"lib/checkFiles\";\nimport constants from \"lib/constants\";\nimport openFolder from \"lib/openFolder\";\nimport projects from \"lib/projects\";\nimport recents from \"lib/recents\";\nimport remoteStorage from \"lib/remoteStorage\";\nimport appSettings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport mimeTypes from \"mime-types\";\nimport mustache from \"mustache\";\nimport filesSettings from \"settings/filesSettings\";\nimport URLParse from \"url-parse\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport _addMenu from \"./add-menu.hbs\";\nimport _addMenuHome from \"./add-menu-home.hbs\";\nimport _template from \"./fileBrowser.hbs\";\nimport _list from \"./list.hbs\";\nimport util from \"./util\";\n\n/**\n * @typedef {{url: String, name: String}} Location\n */\n\n/**\n * @typedef Storage\n * @property {String} name\n * @property {String} uuid\n * @property {String} url\n * @property {'dir'} type\n * @property {'permission'|'ftp'|'sftp'|'sd'} storageType\n */\n\n/**\n *\n * @param {import('.').BrowseMode} [mode='file']\n * @param {string} [info]\n * @param {boolean} [doesOpenLast]\n * @returns {Promise<import('.').SelectedFile>}\n */\nfunction FileBrowserInclude(mode, info, doesOpenLast = true) {\n\tmode = mode || \"file\";\n\n\tconst IS_FOLDER_MODE = [\"folder\", \"both\"].includes(mode);\n\tconst IS_FILE_MODE = [\"file\", \"both\"].includes(mode);\n\tconst storedState = helpers.parseJSON(localStorage.fileBrowserState) || [];\n\t/**@type {Array<Location>} */\n\tconst state = [];\n\t/**@type {Array<Storage>} */\n\tconst allStorages = [];\n\tlet storageList = helpers.parseJSON(localStorage.storageList);\n\tif (!Array.isArray(storageList)) storageList = [];\n\n\tlet isSelectionMode = false;\n\tlet selectedItems = new Set();\n\n\tif (!info) {\n\t\tif (mode !== \"both\") {\n\t\t\tinfo = IS_FOLDER_MODE ? strings[\"open folder\"] : strings[\"open file\"];\n\t\t} else {\n\t\t\tinfo = strings[\"file browser\"];\n\t\t}\n\t}\n\n\treturn new Promise((resolve, reject) => {\n\t\t//#region Declaration\n\t\tconst $menuToggler = (\n\t\t\t<span className=\"icon more_vert\" data-action=\"toggle-menu\"></span>\n\t\t);\n\t\tconst $selectionMenuToggler = (\n\t\t\t<span\n\t\t\t\tclassName=\"icon more_vert\"\n\t\t\t\tdata-action=\"toggle-selection-menu\"\n\t\t\t></span>\n\t\t);\n\t\tconst $addMenuToggler = (\n\t\t\t<span className=\"icon add\" data-action=\"toggle-add-menu\"></span>\n\t\t);\n\t\tconst $selectionModeToggler = (\n\t\t\t<span\n\t\t\t\tclassName=\"icon text_format\"\n\t\t\t\tdata-action=\"toggle-selection-mode\"\n\t\t\t></span>\n\t\t);\n\n\t\tconst $search = <span className=\"icon search\" data-action=\"search\"></span>;\n\t\tconst $lead = <span className=\"icon clearclose\" data-action=\"close\"></span>;\n\t\tconst $page = Page(strings[\"file browser\"].capitalize(), {\n\t\t\tlead: $lead,\n\t\t});\n\t\tlet hideSearchBar = () => {};\n\t\tconst $content = helpers.parseHTML(\n\t\t\tmustache.render(_template, {\n\t\t\t\ttype: mode,\n\t\t\t\tinfo,\n\t\t\t}),\n\t\t);\n\t\tconst $navigation = $content.get(\".navigation\");\n\t\tconst menuOption = {\n\t\t\ttop: \"8px\",\n\t\t\tright: \"8px\",\n\t\t\ttoggler: $menuToggler,\n\t\t\ttransformOrigin: \"top right\",\n\t\t};\n\t\tconst $fbMenu = Contextmenu({\n\t\t\tinnerHTML: () => {\n\t\t\t\treturn `\n        <li action=\"settings\">${strings.settings.capitalize(0)}</li>\n        ${currentDir.url === \"/\" ? `<li action=\"refresh\">${strings[\"reset connections\"].capitalize(0)}</li>` : \"\"}\n        <li action=\"reload\">${strings.reload.capitalize(0)}</li>\n        `;\n\t\t\t},\n\t\t\t...menuOption,\n\t\t});\n\t\tconst $selectionMenu = Contextmenu({\n\t\t\tinnerHTML: () => {\n\t\t\t\treturn `\n        <li action=\"compress\">${strings.compress.capitalize(0)}</li>\n        <li action=\"delete\">${strings.delete.capitalize(0)}</li>\n        `;\n\t\t\t},\n\t\t\t...((menuOption.toggler = $selectionMenuToggler) && menuOption),\n\t\t});\n\t\tconst $addMenu = Contextmenu({\n\t\t\tinnerHTML: () => {\n\t\t\t\tif (currentDir.url === \"/\") {\n\t\t\t\t\treturn mustache.render(_addMenuHome, {\n\t\t\t\t\t\t...strings,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\treturn mustache.render(_addMenu, strings);\n\t\t\t\t}\n\t\t\t},\n\t\t\t...((menuOption.toggler = $addMenuToggler) && menuOption),\n\t\t});\n\n\t\t$selectionMenuToggler.style.display = \"none\";\n\t\tconst progress = {};\n\t\tlet cachedDir = {};\n\t\tlet currentDir = {\n\t\t\turl: null,\n\t\t\tname: null,\n\t\t\tlist: [],\n\t\t\tscroll: 0,\n\t\t};\n\t\t/**\n\t\t * @type {HTMLButtonElement}\n\t\t */\n\t\tlet $openFolder;\n\t\t//#endregion\n\n\t\tactionStack.setMark();\n\t\t$lead.onclick = close;\n\t\t$content.addEventListener(\"click\", handleClick);\n\t\t$content.addEventListener(\"contextmenu\", handleContextMenu, true);\n\t\t$page.body = $content;\n\t\t$page.header.append(\n\t\t\t$search,\n\t\t\t$selectionModeToggler,\n\t\t\t$addMenuToggler,\n\t\t\t$menuToggler,\n\t\t\t$selectionMenuToggler,\n\t\t);\n\n\t\tif (IS_FOLDER_MODE) {\n\t\t\t$openFolder = tag(\"button\", {\n\t\t\t\tclassName: \"floating icon check\",\n\t\t\t\tstyle: {\n\t\t\t\t\tbottom: \"10px\",\n\t\t\t\t\ttop: \"auto\",\n\t\t\t\t},\n\t\t\t\tdisabled: true,\n\t\t\t\tonclick() {\n\t\t\t\t\t$page.hide();\n\n\t\t\t\t\tresolve({\n\t\t\t\t\t\ttype: \"folder\",\n\t\t\t\t\t\t...currentDir,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t});\n\n\t\t\t$page.append($openFolder);\n\t\t}\n\n\t\tapp.append($page);\n\t\thelpers.showAd();\n\n\t\tactionStack.push({\n\t\t\tid: \"filebrowser\",\n\t\t\taction: close,\n\t\t});\n\n\t\t$selectionModeToggler.onclick = function () {\n\t\t\tisSelectionMode = !isSelectionMode;\n\t\t\ttoggleSelectionMode(isSelectionMode);\n\t\t};\n\n\t\t$fbMenu.onclick = function (e) {\n\t\t\t$fbMenu.hide();\n\t\t\tconst action = e.target.getAttribute(\"action\");\n\t\t\tif (action === \"settings\") {\n\t\t\t\tfilesSettings().show();\n\t\t\t\tconst onshow = () => {\n\t\t\t\t\t$page.off(\"show\", onshow);\n\t\t\t\t\treload();\n\t\t\t\t};\n\t\t\t\t$page.on(\"show\", onshow);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (action === \"reload\") {\n\t\t\t\tconst { url } = currentDir;\n\t\t\t\tif (url in cachedDir) delete cachedDir[url];\n\t\t\t\treload();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (action === \"refresh\") {\n\t\t\t\tftp.disconnect(\n\t\t\t\t\t() => {},\n\t\t\t\t\t() => {},\n\t\t\t\t);\n\t\t\t\tsftp.close(\n\t\t\t\t\t() => {},\n\t\t\t\t\t() => {},\n\t\t\t\t);\n\t\t\t\ttoast(strings.success);\n\t\t\t\treturn;\n\t\t\t}\n\t\t};\n\n\t\t$addMenu.onclick = async (e) => {\n\t\t\t$addMenu.hide();\n\t\t\tconst $target = e.target;\n\t\t\tconst action = $target.getAttribute(\"action\");\n\t\t\tconst value = $target.getAttribute(\"value\");\n\t\t\tif (!action) return;\n\n\t\t\tswitch (action) {\n\t\t\t\tcase \"create\": {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst newUrl = await create(value);\n\t\t\t\t\t\tif (!newUrl) break;\n\n\t\t\t\t\t\tconst type = value === \"file\" ? \"file\" : \"folder\";\n\t\t\t\t\t\topenFolder.add(newUrl, type);\n\t\t\t\t\t\treload();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\twindow.log(\"error\", error);\n\t\t\t\t\t\thelpers.error(error);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"import-project-zip\": {\n\t\t\t\t\tlet zipFile = await new Promise((resolve, reject) => {\n\t\t\t\t\t\tsdcard.openDocumentFile(\n\t\t\t\t\t\t\t(res) => {\n\t\t\t\t\t\t\t\tresolve(res.uri);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"application/zip\",\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!zipFile) break;\n\n\t\t\t\t\tconst loadingLoader = loader.create(\n\t\t\t\t\t\tstrings[\"loading\"],\n\t\t\t\t\t\t\"Importing zip file...\",\n\t\t\t\t\t\t{ timeout: 10000 },\n\t\t\t\t\t);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst zipContent = await fsOperation(zipFile).readFile();\n\t\t\t\t\t\tconst zip = await JSZip.loadAsync(zipContent);\n\t\t\t\t\t\tconst targetDir = currentDir.url;\n\t\t\t\t\t\tconst targetFs = fsOperation(targetDir);\n\n\t\t\t\t\t\t// Create folder with zip name\n\t\t\t\t\t\tconst zipName = Url.basename(zipFile).replace(/\\.zip$/, \"\");\n\t\t\t\t\t\tconst extractDir = Url.join(targetDir, zipName);\n\t\t\t\t\t\tawait targetFs.createDirectory(zipName);\n\n\t\t\t\t\t\tconst files = Object.keys(zip.files);\n\t\t\t\t\t\tconst total = files.length;\n\t\t\t\t\t\tlet current = 0;\n\n\t\t\t\t\t\tfor (const filePath of files) {\n\t\t\t\t\t\t\tconst file = zip.files[filePath];\n\t\t\t\t\t\t\tcurrent++;\n\n\t\t\t\t\t\t\tloadingLoader.setMessage(\n\t\t\t\t\t\t\t\t`Extracting ${filePath} (${Math.round((current / total) * 100)}%)`,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tif (file.dir) {\n\t\t\t\t\t\t\t\tawait fsOperation(extractDir).createDirectory(filePath);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst content = await file.async(\"arraybuffer\");\n\t\t\t\t\t\t\t\tawait fsOperation(extractDir).createFile(filePath, content);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tloadingLoader.destroy();\n\t\t\t\t\t\ttoast(strings.success);\n\t\t\t\t\t\treload();\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tloadingLoader.destroy();\n\t\t\t\t\t\thelpers.error(err);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"add-path\":\n\t\t\t\t\taddStorage();\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"addFtp\":\n\t\t\t\tcase \"addSftp\": {\n\t\t\t\t\tconst storage = await remoteStorage[action]();\n\t\t\t\t\tupdateStorage(storage);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t};\n\n\t\t$selectionMenu.onclick = async (e) => {\n\t\t\t$selectionMenu.hide();\n\t\t\tconst $target = e.target;\n\t\t\tconst action = $target.getAttribute(\"action\");\n\t\t\tif (!action) return;\n\n\t\t\tswitch (action) {\n\t\t\t\tcase \"compress\":\n\t\t\t\t\tif (currentDir.url === \"/\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst zip = new JSZip();\n\t\t\t\t\tlet loadingLoader = loader.create(\n\t\t\t\t\t\tstrings[\"loading\"],\n\t\t\t\t\t\t\"Compressing files\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttimeout: 3000,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (const url of selectedItems) {\n\t\t\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\t\t\tconst stats = await fs.stat();\n\t\t\t\t\t\t\tconst isDir = stats.isDirectory;\n\n\t\t\t\t\t\t\tif (isDir) {\n\t\t\t\t\t\t\t\tconst addDirToZip = async (dirUrl, zipFolder) => {\n\t\t\t\t\t\t\t\t\tconst entries = await fsOperation(dirUrl).lsDir();\n\t\t\t\t\t\t\t\t\tfor (const entry of entries) {\n\t\t\t\t\t\t\t\t\t\tconst percent = (\n\t\t\t\t\t\t\t\t\t\t\t((entries.length - entries.indexOf(entry)) /\n\t\t\t\t\t\t\t\t\t\t\t\tentries.length) *\n\t\t\t\t\t\t\t\t\t\t\t100\n\t\t\t\t\t\t\t\t\t\t).toFixed(0);\n\t\t\t\t\t\t\t\t\t\tloadingLoader.setMessage(\n\t\t\t\t\t\t\t\t\t\t\t`Compressing ${entry.name.length > 20 ? entry.name.substring(0, 20) + \"...\" : entry.name} (${percent}%)`,\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tif (entry.isDirectory) {\n\t\t\t\t\t\t\t\t\t\t\tconst newZipFolder = zipFolder.folder(entry.name);\n\t\t\t\t\t\t\t\t\t\t\tawait addDirToZip(entry.url, newZipFolder);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tconst content = await fsOperation(entry.url).readFile();\n\t\t\t\t\t\t\t\t\t\t\tzipFolder.file(entry.name, content, { binary: true });\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tawait addDirToZip(url, zip.folder(Url.basename(url)));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst content = await fs.readFile();\n\t\t\t\t\t\t\t\tzip.file(Url.basename(url), content);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst zipContent = await zip.generateAsync({\n\t\t\t\t\t\t\ttype: \"arraybuffer\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst zipName = \"archive_\" + Date.now() + \".zip\";\n\t\t\t\t\t\tconst zipPath = Url.join(currentDir.url, zipName);\n\t\t\t\t\t\tconst shortPath =\n\t\t\t\t\t\t\tcurrentDir.url.length > 40\n\t\t\t\t\t\t\t\t? currentDir.url.substring(0, 37) + \"...\"\n\t\t\t\t\t\t\t\t: currentDir.url;\n\t\t\t\t\t\tloadingLoader.setMessage(`Saving ${zipName} to ${shortPath}`);\n\t\t\t\t\t\tawait fsOperation(currentDir.url).createFile(zipName, zipContent);\n\t\t\t\t\t\tloadingLoader.destroy();\n\t\t\t\t\t\ttoast(strings.success);\n\t\t\t\t\t\tisSelectionMode = !isSelectionMode;\n\t\t\t\t\t\ttoggleSelectionMode(isSelectionMode);\n\t\t\t\t\t\treload();\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tloadingLoader.destroy();\n\t\t\t\t\t\ttoast(strings.error);\n\t\t\t\t\t\tconsole.error(err);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"delete\": {\n\t\t\t\t\tif (currentDir.url === \"/\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Show confirmation dialog\n\t\t\t\t\tconst confirmMessage =\n\t\t\t\t\t\tselectedItems.size === 1\n\t\t\t\t\t\t\t? strings[\"delete entry\"].replace(\n\t\t\t\t\t\t\t\t\t\"{name}\",\n\t\t\t\t\t\t\t\t\tArray.from(selectedItems)[0].split(\"/\").pop(),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t: strings[\"delete entries\"].replace(\n\t\t\t\t\t\t\t\t\t\"{count}\",\n\t\t\t\t\t\t\t\t\tselectedItems.size,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\tconst confirmation = await confirm(strings.warning, confirmMessage);\n\t\t\t\t\tif (!confirmation) break;\n\n\t\t\t\t\tconst loadingDialog = loader.create(\n\t\t\t\t\t\tstrings.loading,\n\t\t\t\t\t\tstrings[\"deleting items\"].replace(\"{count}\", selectedItems.size),\n\t\t\t\t\t\t{ timeout: 3000 },\n\t\t\t\t\t);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (const url of selectedItems) {\n\t\t\t\t\t\t\tif ((await fsOperation(url).stat()).isDirectory) {\n\t\t\t\t\t\t\t\tif (url.startsWith(\"content://com.termux.documents/tree/\")) {\n\t\t\t\t\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\t\t\t\t\tconst entries = await fs.lsDir();\n\t\t\t\t\t\t\t\t\tif (entries.length === 0) {\n\t\t\t\t\t\t\t\t\t\tawait fs.delete();\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconst deleteRecursively = async (currentUrl) => {\n\t\t\t\t\t\t\t\t\t\t\tconst currentFs = fsOperation(currentUrl);\n\t\t\t\t\t\t\t\t\t\t\tconst currentEntries = await currentFs.lsDir();\n\t\t\t\t\t\t\t\t\t\t\tfor (const entry of currentEntries) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (entry.isDirectory) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tawait deleteRecursively(entry.url);\n\t\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t\tawait fsOperation(entry.url).delete();\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tawait currentFs.delete();\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tawait deleteRecursively(url);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tawait fsOperation(url).delete();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\thelpers.updateUriOfAllActiveFiles(url);\n\t\t\t\t\t\t\t\trecents.removeFolder(url);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\t\t\t\tawait fs.delete();\n\t\t\t\t\t\t\t\tconst openedFile = editorManager.getFile(url, \"uri\");\n\t\t\t\t\t\t\t\tif (openedFile) openedFile.uri = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trecents.removeFile(url);\n\t\t\t\t\t\t\topenFolder.removeItem(url);\n\t\t\t\t\t\t\tdelete cachedDir[url];\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttoast(strings.success);\n\t\t\t\t\t\treload();\n\t\t\t\t\t\tisSelectionMode = false;\n\t\t\t\t\t\ttoggleSelectionMode(false);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tloadingDialog.destroy();\n\t\t\t\t\t\thelpers.error(err);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tloadingDialog.destroy();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t};\n\n\t\t$search.onclick = function () {\n\t\t\tconst $list = $content.get(\"#list\");\n\t\t\tif ($list) searchBar($list, (hide) => (hideSearchBar = hide));\n\t\t};\n\n\t\t$page.onhide = function () {\n\t\t\thideSearchBar();\n\t\t\thideAd();\n\t\t\tactionStack.clearFromMark();\n\t\t\tactionStack.remove(\"filebrowser\");\n\t\t\t$content.removeEventListener(\"click\", handleClick);\n\t\t\t$content.removeEventListener(\"contextmenu\", handleContextMenu);\n\t\t\tdocument.removeEventListener(\"resume\", reload);\n\t\t};\n\n\t\tif (doesOpenLast && storedState.length) {\n\t\t\tloadStates(storedState);\n\t\t\treturn;\n\t\t}\n\t\tnavigate(\"/\", \"/\");\n\n\t\tfunction close() {\n\t\t\tconst err = new Error(\"User cancelled\");\n\t\t\tObject.defineProperty(err, \"code\", {\n\t\t\t\tvalue: 0,\n\t\t\t});\n\t\t\treject(err);\n\t\t\t$page.hide();\n\t\t}\n\n\t\tfunction updateSelectionCount($count) {\n\t\t\tif ($count) {\n\t\t\t\t$count.textContent = `${selectedItems.size} items selected`;\n\t\t\t}\n\t\t}\n\n\t\tfunction toggleSelectionMode(active) {\n\t\t\tconst $list = $content.get(\"#list\");\n\t\t\tif (active) {\n\t\t\t\t$list.classList.add(\"selection-mode\");\n\t\t\t\tconst $header = tag(\"div\", {\n\t\t\t\t\tclassName: \"selection-header\",\n\t\t\t\t});\n\n\t\t\t\tconst selectAllCheckbox = Checkbox(\"\", false);\n\t\t\t\tconst $count = tag(\"span\", {\n\t\t\t\t\tclassName: \"text selection-count\",\n\t\t\t\t\ttextContent: \"0 items selected\",\n\t\t\t\t});\n\n\t\t\t\t// Handle select all functionality\n\t\t\t\tselectAllCheckbox.onclick = () => {\n\t\t\t\t\tconst checked = selectAllCheckbox.checked;\n\t\t\t\t\tconst items = $list.querySelectorAll(\".tile:not(.selection-header)\");\n\t\t\t\t\titems.forEach((item) => {\n\t\t\t\t\t\tconst checkbox = item.querySelector(\".input-checkbox\");\n\t\t\t\t\t\tif (checkbox) {\n\t\t\t\t\t\t\tcheckbox.checked = checked;\n\t\t\t\t\t\t\tconst url = item.querySelector(\"data-url\").textContent;\n\t\t\t\t\t\t\tif (checked) {\n\t\t\t\t\t\t\t\tselectedItems.add(url);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tselectedItems.delete(url);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tupdateSelectionCount($count);\n\t\t\t\t};\n\n\t\t\t\t$header.append(selectAllCheckbox, $count);\n\t\t\t\t$list.insertBefore($header, $list.firstChild);\n\n\t\t\t\t// Add checkboxes to list items\n\t\t\t\t$list\n\t\t\t\t\t.querySelectorAll(\".tile:not(.selection-header)\")\n\t\t\t\t\t.forEach((item) => {\n\t\t\t\t\t\tconst checkbox = Checkbox(\"\", false);\n\t\t\t\t\t\tcheckbox.onclick = () => {\n\t\t\t\t\t\t\tconst url = item.querySelector(\"data-url\").textContent;\n\t\t\t\t\t\t\tif (checkbox.checked) {\n\t\t\t\t\t\t\t\tselectedItems.add(url);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tselectedItems.delete(url);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tupdateSelectionCount($count);\n\t\t\t\t\t\t};\n\t\t\t\t\t\titem.prepend(checkbox);\n\t\t\t\t\t});\n\n\t\t\t\t$addMenuToggler.style.display = \"none\";\n\t\t\t\t$menuToggler.style.display = \"none\";\n\t\t\t\t$selectionMenuToggler.style.display = \"\";\n\n\t\t\t\t// Disable floating button in selection mode\n\t\t\t\tif ($openFolder) {\n\t\t\t\t\t$openFolder.disabled = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$list.classList.remove(\"selection-mode\");\n\t\t\t\t$list.querySelector(\".selection-header\")?.remove();\n\t\t\t\t$list.querySelectorAll(\".input-checkbox\").forEach((cb) => cb.remove());\n\t\t\t\tselectedItems.clear();\n\n\t\t\t\t$addMenuToggler.style.display = \"\";\n\t\t\t\t$menuToggler.style.display = \"\";\n\t\t\t\t$selectionMenuToggler.style.display = \"none\";\n\n\t\t\t\t// Re-enable floating button when exiting selection mode\n\t\t\t\tif ($openFolder) {\n\t\t\t\t\t$openFolder.disabled = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Called when any file folder is clicked\n\t\t * @param {MouseEvent} e\n\t\t * @param {\"contextmenu\"} [isContextMenu]\n\t\t */\n\t\tfunction handleClick(e, isContextMenu) {\n\t\t\t/**\n\t\t\t * @type {HTMLElement}\n\t\t\t */\n\t\t\tconst $el = e.target;\n\n\t\t\tif (isSelectionMode) {\n\t\t\t\tconst checkbox = $el.closest(\".tile\")?.querySelector(\".input-checkbox\");\n\t\t\t\tif (checkbox && !$el.closest(\".selection-header\")) {\n\t\t\t\t\tcheckbox.checked = !checkbox.checked;\n\t\t\t\t\tconst url = $el\n\t\t\t\t\t\t.closest(\".tile\")\n\t\t\t\t\t\t.querySelector(\"data-url\").textContent;\n\t\t\t\t\tif (checkbox.checked) {\n\t\t\t\t\t\tselectedItems.add(url);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tselectedItems.delete(url);\n\t\t\t\t\t}\n\t\t\t\t\tconst $count = $content.querySelector(\".selection-count\");\n\t\t\t\t\tupdateSelectionCount($count);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet action = $el.getAttribute(\"action\") || $el.dataset.action;\n\t\t\tif (!action) return;\n\n\t\t\tlet url = $el.dataset.url;\n\t\t\tlet name = $el.dataset.name || $el.getAttribute(\"name\");\n\t\t\tconst idOpenDoc = $el.hasAttribute(\"open-doc\");\n\t\t\tconst uuid = $el.getAttribute(\"uuid\");\n\t\t\tconst type = $el.getAttribute(\"type\");\n\t\t\tconst storageType = $el.getAttribute(\"storageType\");\n\t\t\tconst home = $el.getAttribute(\"home\");\n\t\t\tconst isDir = [\"dir\", \"directory\", \"folder\"].includes(type);\n\n\t\t\tif (!url) {\n\t\t\t\tconst $url = $el.get(\"data-url\");\n\t\t\t\tif ($url) {\n\t\t\t\t\turl = $url.textContent;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (storageType === \"notification\") {\n\t\t\t\tswitch (uuid) {\n\t\t\t\t\tcase \"addstorage\":\n\t\t\t\t\t\taddStorage();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!url && action === \"open\" && isDir && !idOpenDoc && !isContextMenu) {\n\t\t\t\tloader.hide();\n\t\t\t\tutil.addPath(name, uuid).then((res) => {\n\t\t\t\t\tconst storage = allStorages.find((storage) => storage.uuid === uuid);\n\t\t\t\t\tstorage.url = res.uri;\n\t\t\t\t\tstorage.name = res.name;\n\t\t\t\t\tname = res.name;\n\t\t\t\t\tupdateStorage(storage, false);\n\t\t\t\t\turl = res.uri;\n\t\t\t\t\tfolder();\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isContextMenu) action = \"contextmenu\";\n\t\t\telse if (idOpenDoc) action = \"open-doc\";\n\n\t\t\tswitch (action) {\n\t\t\t\tcase \"navigation\":\n\t\t\t\t\tfolder();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"contextmenu\":\n\t\t\t\t\tcontextMenuHandler();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"open\":\n\t\t\t\t\tif (isDir) folder();\n\t\t\t\t\telse if (!$el.hasAttribute(\"disabled\")) file();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"open-doc\":\n\t\t\t\t\topenDoc();\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfunction folder() {\n\t\t\t\tif (home) {\n\t\t\t\t\tnavigateToHome();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tnavigate(url, name);\n\t\t\t}\n\n\t\t\tfunction navigateToHome() {\n\t\t\t\tconst navigationArray = [];\n\t\t\t\tconst dirs = home.split(\"/\");\n\t\t\t\tconst { url: parsedUrl, query } = Url.parse(url);\n\t\t\t\tlet path = \"\";\n\n\t\t\t\tfor (let dir of dirs) {\n\t\t\t\t\tpath = Url.join(path, dir);\n\t\t\t\t\tnavigationArray.push({\n\t\t\t\t\t\turl: `${Url.join(parsedUrl, path, \"\")}${query}`,\n\t\t\t\t\t\tname: dir || name,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tloadStates(navigationArray);\n\t\t\t}\n\n\t\t\tfunction file() {\n\t\t\t\t$page.hide();\n\t\t\t\tresolve({\n\t\t\t\t\ttype: \"file\",\n\t\t\t\t\turl,\n\t\t\t\t\tname,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tasync function getShareableUri(fileUrl) {\n\t\t\t\tif (!fileUrl) return null;\n\t\t\t\ttry {\n\t\t\t\t\tconst fs = fsOperation(fileUrl);\n\t\t\t\t\tif (/^s?ftp:/.test(fileUrl)) {\n\t\t\t\t\t\treturn fs.localName;\n\t\t\t\t\t}\n\t\t\t\t\tconst stat = await fs.stat();\n\t\t\t\t\treturn stat?.url || null;\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tasync function contextMenuHandler() {\n\t\t\t\tif (appSettings.value.vibrateOnTap) {\n\t\t\t\t\tnavigator.vibrate(constants.VIBRATION_TIME);\n\t\t\t\t}\n\t\t\t\tif ($el.getAttribute(\"open-doc\") === \"true\") return;\n\n\t\t\t\tconst deleteText =\n\t\t\t\t\tcurrentDir.url === \"/\" ? strings.remove : strings.delete;\n\t\t\t\tconst options = [\n\t\t\t\t\t[\"delete\", deleteText, \"delete\"],\n\t\t\t\t\t[\"rename\", strings.rename, \"text_format\"],\n\t\t\t\t];\n\n\t\t\t\tif (/s?ftp/.test(storageType)) {\n\t\t\t\t\toptions.push([\"edit\", strings.edit, \"edit\"]);\n\t\t\t\t}\n\n\t\t\t\tif (helpers.isFile(type)) {\n\t\t\t\t\toptions.push([\"info\", strings.info, \"info\"]);\n\t\t\t\t\toptions.push([\"open_with\", strings[\"open with\"], \"open_in_browser\"]);\n\t\t\t\t}\n\n\t\t\t\tif (currentDir.url !== \"/\" && url) {\n\t\t\t\t\toptions.push([\"copyuri\", strings[\"copy uri\"], \"copy\"]);\n\t\t\t\t}\n\n\t\t\t\tconst option = await select(strings[\"select\"], options);\n\t\t\t\tswitch (option) {\n\t\t\t\t\tcase \"delete\": {\n\t\t\t\t\t\tlet deleteFunction = removeFile;\n\t\t\t\t\t\tlet message = strings[\"delete entry\"].replace(\"{name}\", name);\n\t\t\t\t\t\tif (uuid) {\n\t\t\t\t\t\t\tdeleteFunction = removeStorage;\n\t\t\t\t\t\t\tmessage = strings[\"remove entry\"].replace(\"{name}\", name);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst confirmation = await confirm(strings.warning, message);\n\t\t\t\t\t\tif (!confirmation) break;\n\t\t\t\t\t\tdeleteFunction();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"rename\": {\n\t\t\t\t\t\tlet newname = await prompt(strings.rename, name, \"text\", {\n\t\t\t\t\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tnewname = helpers.fixFilename(newname);\n\t\t\t\t\t\tif (!newname || newname === name) break;\n\n\t\t\t\t\t\tif (uuid) renameStorage(newname);\n\t\t\t\t\t\telse renameFile(newname);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"edit\": {\n\t\t\t\t\t\tconst storage = await remoteStorage.edit(\n\t\t\t\t\t\t\tstorageList.find((storage) => storage.uuid === uuid),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (!storage) break;\n\t\t\t\t\t\tstorage.uuid = uuid;\n\t\t\t\t\t\tupdateStorage(storage);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase \"info\":\n\t\t\t\t\t\tacode.exec(\"file-info\", url);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"copyuri\":\n\t\t\t\t\t\tnavigator.clipboard.writeText(url);\n\t\t\t\t\t\talert(strings.success, strings[\"copied to clipboard\"]);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"open_with\":\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst shareableUri = await getShareableUri(url);\n\t\t\t\t\t\t\tif (!shareableUri) {\n\t\t\t\t\t\t\t\ttoast(strings[\"no app found to handle this file\"]);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst mimeType =\n\t\t\t\t\t\t\t\tmimeTypes.lookup(name) ||\n\t\t\t\t\t\t\t\tmimeTypes.lookup(shareableUri) ||\n\t\t\t\t\t\t\t\t\"text/plain\";\n\n\t\t\t\t\t\t\tsystem.fileAction(shareableUri, name, \"VIEW\", mimeType, () => {\n\t\t\t\t\t\t\t\ttoast(strings[\"no app found to handle this file\"]);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(error);\n\t\t\t\t\t\t\ttoast(strings.error);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tasync function renameFile(newname) {\n\t\t\t\tif (url.startsWith(\"content://com.termux.documents/tree/\")) {\n\t\t\t\t\tif (helpers.isDir(type)) {\n\t\t\t\t\t\talert(strings.warning, strings[\"rename not supported\"]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Special handling for Termux content files\n\t\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst content = await fs.readFile();\n\t\t\t\t\t\t\tconst newUrl = Url.join(Url.dirname(url), newname);\n\t\t\t\t\t\t\tawait fsOperation(Url.dirname(url)).createFile(newname, content);\n\t\t\t\t\t\t\tawait fs.delete();\n\n\t\t\t\t\t\t\trecents.removeFile(url);\n\t\t\t\t\t\t\trecents.addFile(newUrl);\n\t\t\t\t\t\t\tconst file = editorManager.getFile(url, \"uri\");\n\t\t\t\t\t\t\tif (file) {\n\t\t\t\t\t\t\t\tfile.uri = newUrl;\n\t\t\t\t\t\t\t\tfile.filename = newname;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\topenFolder.renameItem(url, newUrl, newname);\n\t\t\t\t\t\t\ttoast(strings.success);\n\t\t\t\t\t\t\treload();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\twindow.log(\"error\", err);\n\t\t\t\t\t\t\thelpers.error(err);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\ttry {\n\t\t\t\t\tconst newUrl = await fs.renameTo(newname);\n\t\t\t\t\trecents.removeFile(url);\n\t\t\t\t\trecents.addFile(newUrl);\n\t\t\t\t\tconst file = editorManager.getFile(url, \"uri\");\n\t\t\t\t\tif (file) {\n\t\t\t\t\t\tfile.uri = newUrl;\n\t\t\t\t\t\tfile.filename = newname;\n\t\t\t\t\t}\n\t\t\t\t\topenFolder.renameItem(url, newUrl, newname);\n\t\t\t\t\ttoast(strings.success);\n\t\t\t\t\treload();\n\t\t\t\t} catch (err) {\n\t\t\t\t\twindow.log(\"error\", err);\n\t\t\t\t\thelpers.error(err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tasync function removeFile() {\n\t\t\t\ttry {\n\t\t\t\t\tif (helpers.isDir(type)) {\n\t\t\t\t\t\tif (url.startsWith(\"content://com.termux.documents/tree/\")) {\n\t\t\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\t\t\tconst entries = await fs.lsDir();\n\t\t\t\t\t\t\tif (entries.length === 0) {\n\t\t\t\t\t\t\t\tawait fs.delete();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst deleteRecursively = async (currentUrl) => {\n\t\t\t\t\t\t\t\t\tconst currentFs = fsOperation(currentUrl);\n\t\t\t\t\t\t\t\t\tconst currentEntries = await currentFs.lsDir();\n\t\t\t\t\t\t\t\t\tfor (const entry of currentEntries) {\n\t\t\t\t\t\t\t\t\t\tif (entry.isDirectory) {\n\t\t\t\t\t\t\t\t\t\t\tawait deleteRecursively(entry.url);\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tawait fsOperation(entry.url).delete();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tawait currentFs.delete();\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\tawait deleteRecursively(url);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tawait fsOperation(url).delete();\n\t\t\t\t\t\t}\n\t\t\t\t\t\thelpers.updateUriOfAllActiveFiles(url);\n\t\t\t\t\t\trecents.removeFolder(url);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\t\tawait fs.delete();\n\t\t\t\t\t\tconst openedFile = editorManager.getFile(url, \"uri\");\n\t\t\t\t\t\tif (openedFile) openedFile.uri = null;\n\t\t\t\t\t}\n\t\t\t\t\trecents.removeFile(url);\n\t\t\t\t\topenFolder.removeItem(url);\n\t\t\t\t\ttoast(strings.success);\n\t\t\t\t\tdelete cachedDir[url];\n\t\t\t\t\treload();\n\t\t\t\t} catch (err) {\n\t\t\t\t\twindow.log(\"error\", err);\n\t\t\t\t\thelpers.error(err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction removeStorage() {\n\t\t\t\tif (url) {\n\t\t\t\t\trecents.removeFolder(url);\n\t\t\t\t\trecents.removeFile(url);\n\t\t\t\t}\n\t\t\t\tstorageList = storageList.filter((storage) => {\n\t\t\t\t\tif (storage.uuid !== uuid) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (storage.url) {\n\t\t\t\t\t\tconst parsedUrl = URLParse(storage.url, true);\n\t\t\t\t\t\tconst keyFile = decodeURIComponent(\n\t\t\t\t\t\t\tparsedUrl.query[\"keyFile\"] || \"\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tif (keyFile) {\n\t\t\t\t\t\t\tfsOperation(keyFile).delete();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\t\t\t\tlocalStorage.storageList = JSON.stringify(storageList);\n\t\t\t\treload();\n\t\t\t}\n\n\t\t\tfunction renameStorage(newname) {\n\t\t\t\tstorageList = storageList.map((storage) => {\n\t\t\t\t\tif (storage.uuid === uuid) storage.name = newname;\n\t\t\t\t\treturn storage;\n\t\t\t\t});\n\t\t\t\tlocalStorage.storageList = JSON.stringify(storageList);\n\t\t\t\treload();\n\t\t\t}\n\n\t\t\tfunction openDoc() {\n\t\t\t\tcheckFiles.check = false;\n\t\t\t\tsdcard.openDocumentFile(\n\t\t\t\t\t(res) => {\n\t\t\t\t\t\tres.url = res.uri;\n\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\ttype: \"file\",\n\t\t\t\t\t\t\t...res,\n\t\t\t\t\t\t\tname: res.filename,\n\t\t\t\t\t\t\tmode: \"single\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\t$page.hide();\n\t\t\t\t\t},\n\t\t\t\t\t(err) => {\n\t\t\t\t\t\thelpers.error(err);\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tfunction handleContextMenu(e) {\n\t\t\thandleClick(e, true);\n\t\t}\n\n\t\tasync function listAllStorages() {\n\t\t\tlet hasInternalStorage = true;\n\t\t\tallStorages.length = 0;\n\n\t\t\tif (ANDROID_SDK_INT === 29) {\n\t\t\t\tconst rootDirName = cordova.file.externalRootDirectory;\n\t\t\t\tconst testDirName = \"Acode_Test_file\" + helpers.uuid();\n\t\t\t\tconst testDirFs = fsOperation(Url.join(rootDirName, testDirName));\n\n\t\t\t\ttry {\n\t\t\t\t\tawait fsOperation(rootDirName).createDirectory(testDirName);\n\t\t\t\t\tawait testDirFs.createFile(\"test\" + helpers.uuid());\n\n\t\t\t\t\thasInternalStorage = !!(await testDirFs.lsDir()).length;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(error);\n\t\t\t\t} finally {\n\t\t\t\t\ttestDirFs.delete();\n\t\t\t\t}\n\t\t\t} else if (ANDROID_SDK_INT > 29) {\n\t\t\t\thasInternalStorage = false;\n\t\t\t}\n\n\t\t\tif (hasInternalStorage) {\n\t\t\t\tutil.pushFolder(\n\t\t\t\t\tallStorages,\n\t\t\t\t\t\"Internal storage\",\n\t\t\t\t\tcordova.file.externalRootDirectory,\n\t\t\t\t\t{\n\t\t\t\t\t\tuuid: \"internal-storage\",\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Check for Terminal Home Directory storage\n\t\t\ttry {\n\t\t\t\tconst isTerminalInstalled = await Terminal.isInstalled();\n\t\t\t\tif (typeof Terminal !== \"undefined\" && isTerminalInstalled) {\n\t\t\t\t\tconst isTerminalSupported = await Terminal.isSupported();\n\n\t\t\t\t\tif (isTerminalSupported && isTerminalInstalled) {\n\t\t\t\t\t\tconst terminalHomeUrl = cordova.file.dataDirectory + \"alpine/home\";\n\n\t\t\t\t\t\t// Check if this storage is not already in the list\n\t\t\t\t\t\tconst terminalStorageExists = allStorages.find(\n\t\t\t\t\t\t\t(storage) =>\n\t\t\t\t\t\t\t\tstorage.uuid === \"terminal-home\" ||\n\t\t\t\t\t\t\t\tstorage.url === terminalHomeUrl,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (!terminalStorageExists) {\n\t\t\t\t\t\t\tutil.pushFolder(allStorages, \"Terminal Home\", terminalHomeUrl, {\n\t\t\t\t\t\t\t\tuuid: \"terminal-home\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error checking Terminal installation:\", error);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst res = await externalFs.listStorages();\n\t\t\t\tres.forEach((storage) => {\n\t\t\t\t\tif (storageList.find((s) => s.uuid === storage.uuid)) return;\n\t\t\t\t\tlet path;\n\t\t\t\t\tif (storage.path && isStorageManager) {\n\t\t\t\t\t\tpath = \"file://\" + storage.path;\n\t\t\t\t\t}\n\t\t\t\t\tutil.pushFolder(allStorages, storage.name, path || \"\", {\n\t\t\t\t\t\t...storage,\n\t\t\t\t\t\tstorageType: \"sd\",\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconsole.warn(\"Unable to list external storages.\", err);\n\t\t\t}\n\n\t\t\tstorageList.forEach((storage) => {\n\t\t\t\tlet url = storage.url || /**@deprecated */ storage[\"uri\"];\n\n\t\t\t\tutil.pushFolder(allStorages, storage.name, url, {\n\t\t\t\t\tstorageType: storage.storageType,\n\t\t\t\t\tuuid: storage.uuid,\n\t\t\t\t\thome: storage.home,\n\t\t\t\t});\n\t\t\t});\n\n\t\t\tif (!allStorages.length) {\n\t\t\t\tutil.pushFolder(allStorages, strings[\"add a storage\"], \"\", {\n\t\t\t\t\tstorageType: \"notification\",\n\t\t\t\t\tuuid: \"addstorage\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (IS_FILE_MODE) {\n\t\t\t\tutil.pushFolder(allStorages, \"Select document\", null, {\n\t\t\t\t\t\"open-doc\": true,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn allStorages;\n\t\t}\n\n\t\t/**\n\t\t * Gets directory for given url for rendering\n\t\t * @param {String} url\n\t\t * @param {String} name\n\t\t * @returns {Promise<{name: String, url: String, list: [], scroll: Number}>}\n\t\t */\n\t\tasync function getDir(url, name) {\n\t\t\tconst { fileBrowser } = appSettings.value;\n\t\t\tlet list = [];\n\t\t\tlet error = false;\n\n\t\t\tif (url in cachedDir) {\n\t\t\t\treturn cachedDir[url];\n\t\t\t} else {\n\t\t\t\tif (url === \"/\") {\n\t\t\t\t\tlist = await listAllStorages();\n\t\t\t\t} else {\n\t\t\t\t\tconst id = helpers.uuid();\n\n\t\t\t\t\tprogress[id] = true;\n\t\t\t\t\tconst timeout = setTimeout(() => {\n\t\t\t\t\t\tloader.create(name, strings.loading + \"...\", {\n\t\t\t\t\t\t\ttimeout: 10000,\n\t\t\t\t\t\t\tcallback() {\n\t\t\t\t\t\t\t\tloader.destroy();\n\t\t\t\t\t\t\t\tnavigate(\"/\", \"/\");\n\t\t\t\t\t\t\t\tprogress[id] = false;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}, 100);\n\n\t\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlist = (await fs.lsDir()) ?? [];\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tif (progress[id]) {\n\t\t\t\t\t\t\thelpers.error(err, url);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconsole.error(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\terror = !progress[id];\n\n\t\t\t\t\tdelete progress[id];\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t\tloader.destroy();\n\t\t\t\t}\n\t\t\t\tif (error) return null;\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\tname,\n\t\t\t\t\tscroll: 0,\n\t\t\t\t\tlist: helpers.sortDir(list, fileBrowser, mode),\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Navigates to specific directory\n\t\t * @param {String} url\n\t\t * @param {String} name\n\t\t */\n\t\tasync function navigate(url, name, assignBackButton = true) {\n\t\t\tif (document.getElementById(\"search-bar\")) {\n\t\t\t\thideSearchBar();\n\t\t\t}\n\t\t\tif (!url) {\n\t\t\t\tthrow new Error('navigate(url, name): \"url\" is required.');\n\t\t\t}\n\n\t\t\tif (!name) {\n\t\t\t\tthrow new Error('navigate(url, name): \"name\" is required.');\n\t\t\t}\n\n\t\t\tif (url === \"/\") {\n\t\t\t\tif (IS_FOLDER_MODE) $openFolder.disabled = true;\n\t\t\t} else {\n\t\t\t\tif (IS_FOLDER_MODE) $openFolder.disabled = false;\n\t\t\t}\n\n\t\t\tconst $nav = tag.get(`#${getNavId(url)}`);\n\n\t\t\t//If navigate to previous directories, clear the rest navigation\n\t\t\tif ($nav) {\n\t\t\t\tlet $topNav;\n\t\t\t\twhile (($topNav = $navigation.lastChild) !== $nav) {\n\t\t\t\t\tconst url = $topNav.dataset.url;\n\t\t\t\t\tactionStack.remove(url);\n\t\t\t\t\t$topNav.remove();\n\t\t\t\t}\n\n\t\t\t\twhile (1) {\n\t\t\t\t\tconst location = state.slice(-1)[0];\n\t\t\t\t\tif (!location || location.url === url) break;\n\t\t\t\t\tstate.pop();\n\t\t\t\t}\n\t\t\t\tlocalStorage.fileBrowserState = JSON.stringify(state);\n\n\t\t\t\tconst dir = await getDir(url, name);\n\t\t\t\tif (dir) {\n\t\t\t\t\trender(dir);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst dir = await getDir(url, name);\n\t\t\tif (dir) {\n\t\t\t\tconst { url: curl, name: cname } = currentDir;\n\t\t\t\tlet action;\n\t\t\t\tif (doesOpenLast) pushState({ name, url });\n\t\t\t\tif (curl && cname && assignBackButton) {\n\t\t\t\t\taction = () => {\n\t\t\t\t\t\tnavigate(curl, cname, false);\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tpushToNavbar(name, url, action);\n\t\t\t\trender(dir);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * @param {\"file\"|\"folder\"|\"project\"} arg\n\t\t */\n\t\tasync function create(arg) {\n\t\t\tconst { url } = currentDir;\n\t\t\tconst alreadyCreated = [];\n\t\t\tconst options = [];\n\t\t\tlet ctUrl = \"\";\n\t\t\tlet projectLocation = null;\n\t\t\tlet projectFiles = \"\";\n\t\t\tlet projectName = \"\";\n\t\t\tlet project = \"\";\n\t\t\tlet newUrl;\n\n\t\t\tif (arg === \"file\" || arg === \"folder\") {\n\t\t\t\tlet title = strings[\"enter folder name\"];\n\t\t\t\tif (arg === \"file\") {\n\t\t\t\t\ttitle = strings[\"enter file name\"];\n\t\t\t\t}\n\n\t\t\t\tlet entryName = await prompt(title, \"\", \"filename\", {\n\t\t\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\t\t\trequired: true,\n\t\t\t\t});\n\n\t\t\t\tif (!entryName) return;\n\t\t\t\tentryName = helpers.fixFilename(entryName);\n\n\t\t\t\tif (arg === \"folder\") {\n\t\t\t\t\tnewUrl = await helpers.createFileStructure(url, entryName, false);\n\t\t\t\t}\n\t\t\t\tif (arg === \"file\") {\n\t\t\t\t\tnewUrl = await helpers.createFileStructure(url, entryName);\n\t\t\t\t}\n\t\t\t\tif (!newUrl.created) return;\n\t\t\t\treturn newUrl.uri;\n\t\t\t}\n\n\t\t\tif (arg === \"project\") {\n\t\t\t\tprojects.list().map((project) => {\n\t\t\t\t\tconst { name, icon } = project;\n\t\t\t\t\toptions.push([name, name, icon]);\n\t\t\t\t});\n\n\t\t\t\tproject = await select(strings[\"new project\"], options);\n\t\t\t\tloader.create(project, strings.loading + \"...\");\n\t\t\t\tprojectFiles = await projects.get(project).files();\n\t\t\t\tloader.destroy();\n\t\t\t\tprojectName = await prompt(strings[\"project name\"], project, \"text\", {\n\t\t\t\t\trequired: true,\n\t\t\t\t\tmatch: constants.FILE_NAME_REGEX,\n\t\t\t\t});\n\n\t\t\t\tif (!projectName) return;\n\t\t\t\tloader.create(projectName, strings.loading + \"...\");\n\t\t\t\tconst fs = fsOperation(url);\n\t\t\t\tconst files = Object.keys(projectFiles); // All project files\n\n\t\t\t\tnewUrl = await fs.createDirectory(projectName);\n\t\t\t\tprojectLocation = Url.join(url, projectName, \"/\");\n\t\t\t\tawait createProject(files); // Creating project\n\t\t\t\tloader.destroy();\n\t\t\t\treturn newUrl;\n\t\t\t}\n\n\t\t\tasync function createProject(files) {\n\t\t\t\t// checking if it's the last file\n\t\t\t\tif (!files.length) {\n\t\t\t\t\treload();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tctUrl = \"\";\n\t\t\t\tconst file = files.pop();\n\t\t\t\tawait createFile(file);\n\t\t\t\treturn await createProject(files);\n\t\t\t}\n\n\t\t\tfunction createFile(fileUrl) {\n\t\t\t\tconst paths = fileUrl.split(\"/\");\n\t\t\t\tconst filename = paths.pop();\n\t\t\t\treturn createDir(projectFiles, fileUrl, filename, paths);\n\t\t\t}\n\n\t\t\tasync function createDir(project, fileUrl, filename, paths) {\n\t\t\t\tconst lclUrl = Url.join(projectLocation, ctUrl);\n\t\t\t\tconst fs = fsOperation(lclUrl);\n\n\t\t\t\tif (paths.length === 0) {\n\t\t\t\t\tconst data = project[fileUrl].replace(/<%name%>/g, projectName);\n\t\t\t\t\tawait fs.createFile(filename, data);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst name = paths.splice(0, 1)[0];\n\t\t\t\tconst toCreate = Url.join(lclUrl, name);\n\t\t\t\tif (!alreadyCreated.includes(toCreate)) {\n\t\t\t\t\tawait fs.createDirectory(name);\n\t\t\t\t\talreadyCreated.push(toCreate);\n\t\t\t\t}\n\t\t\t\tctUrl += name + \"/\";\n\t\t\t\treturn await createDir(project, fileUrl, filename, paths);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t *  Pushes a navigation button to navbar\n\t\t * @param {String} id\n\t\t * @param {String} name\n\t\t * @param {String} url\n\t\t */\n\t\tfunction pushToNavbar(name, url, action) {\n\t\t\tif (!url) return;\n\t\t\tconst displayName = name || Url.basename(url) || url;\n\t\t\t$navigation.append(\n\t\t\t\t<span\n\t\t\t\t\tid={getNavId(url)}\n\t\t\t\t\tclassName=\"nav\"\n\t\t\t\t\tdata-url={url}\n\t\t\t\t\tdata-name={displayName}\n\t\t\t\t\tattr-action=\"navigation\"\n\t\t\t\t\tattr-text={displayName}\n\t\t\t\t\ttabIndex={-1}\n\t\t\t\t></span>,\n\t\t\t);\n\t\t\t$navigation.scrollLeft = $navigation.scrollWidth;\n\n\t\t\tif (action && !actionStack.has(url)) {\n\t\t\t\tactionStack.push({\n\t\t\t\t\tid: url,\n\t\t\t\t\taction,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Loads up given states\n\t\t * @param {Array<Location>} states\n\t\t */\n\t\tfunction loadStates(states) {\n\t\t\tif (!Array.isArray(states) || !states.length) return;\n\n\t\t\tconst backNavigation = [];\n\t\t\tconst lastState = states.pop();\n\t\t\tif (!lastState || !lastState.url) return;\n\t\t\tconst { url } = lastState;\n\t\t\tconst name = lastState.name || Url.basename(url) || url;\n\t\t\tlet { url: lastUrl, name: lastName } = currentDir;\n\n\t\t\twhile (states.length) {\n\t\t\t\tconst location = states.splice(0, 1)[0];\n\t\t\t\tif (!location || !location.url) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst { url, name } = location;\n\t\t\t\tlet action;\n\n\t\t\t\tif (doesOpenLast) pushState({ name, url });\n\t\t\t\tif (lastUrl && lastName) {\n\t\t\t\t\tbackNavigation.push([lastUrl, lastName]);\n\t\t\t\t\taction = () => {\n\t\t\t\t\t\tconst [url, name] = backNavigation.pop();\n\t\t\t\t\t\tnavigate(url, name, false);\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tpushToNavbar(name, url, action);\n\t\t\t\tlastUrl = url;\n\t\t\t\tlastName = name;\n\t\t\t}\n\n\t\t\tcurrentDir = { url: lastUrl, name: lastName };\n\t\t\tnavigate(url, name);\n\t\t}\n\n\t\t/**\n\t\t *\n\t\t * @param {String} url\n\t\t */\n\t\tfunction getNavId(url) {\n\t\t\treturn `nav_${url.hashCode()}`;\n\t\t}\n\n\t\t/**\n\t\t *\n\t\t * @param {Storage} storage\n\t\t * @param {Boolean} doesReload\n\t\t */\n\t\tfunction updateStorage(storage, doesReload = true) {\n\t\t\tif (storage.uuid) {\n\t\t\t\tstorageList = storageList.filter((s) => s.uuid !== storage.uuid);\n\t\t\t} else {\n\t\t\t\tstorage.uuid = helpers.uuid();\n\t\t\t}\n\n\t\t\tif (!storage.type) {\n\t\t\t\tstorage.type = \"dir\";\n\t\t\t}\n\n\t\t\tif (!storage.storageType) {\n\t\t\t\tstorage.storageType = storage.type;\n\t\t\t}\n\n\t\t\tstorageList.push(storage);\n\t\t\tlocalStorage.storageList = JSON.stringify(storageList);\n\t\t\tif (doesReload) reload();\n\t\t}\n\n\t\tfunction render(dir) {\n\t\t\tconst { list, scroll } = dir;\n\t\t\tconst $list = helpers.parseHTML(\n\t\t\t\tmustache.render(_list, {\n\t\t\t\t\tmsg: strings[\"empty folder message\"],\n\t\t\t\t\tlist,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (document.getElementById(\"search-bar\")) {\n\t\t\t\thideSearchBar();\n\t\t\t}\n\n\t\t\tconst $oldList = $content.get(\"#list\");\n\t\t\tif ($oldList) {\n\t\t\t\tconst { url } = currentDir;\n\t\t\t\tif (url && cachedDir[url]) {\n\t\t\t\t\tcachedDir[url].scroll = $oldList.scrollTop;\n\t\t\t\t}\n\t\t\t\t$oldList.remove();\n\t\t\t}\n\t\t\t$content.append($list);\n\t\t\t$list.scrollTop = scroll;\n\t\t\t$list.focus();\n\n\t\t\tcurrentDir = dir;\n\t\t\tcachedDir[dir.url] = dir;\n\t\t}\n\n\t\tfunction reload() {\n\t\t\tconst { url, name } = currentDir;\n\t\t\tdelete cachedDir[url];\n\t\t\tnavigate(url, name);\n\t\t}\n\n\t\tfunction pushState({ url, name }) {\n\t\t\tif (!url || !name) return;\n\t\t\tif (state.find((l) => l.url === url)) return;\n\t\t\tstate.push({ url, name });\n\t\t\tlocalStorage.fileBrowserState = JSON.stringify(state);\n\t\t}\n\n\t\t/**\n\t\t * Adds a new storage and refresh location\n\t\t */\n\t\tfunction addStorage() {\n\t\t\tutil\n\t\t\t\t.addPath()\n\t\t\t\t.then((res) => {\n\t\t\t\t\tstorageList.push(res);\n\t\t\t\t\tlocalStorage.storageList = JSON.stringify(storageList);\n\t\t\t\t\treload();\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\thelpers.error(err);\n\t\t\t\t});\n\t\t}\n\t});\n}\n\nexport default FileBrowserInclude;\n"
  },
  {
    "path": "src/pages/fileBrowser/fileBrowser.scss",
    "content": "#file-browser {\n  data-url {\n    display: none;\n  }\n\n  .tile {\n    &[storageType=\"notification\"] {\n      background-color: rgb(153, 153, 255);\n      background-color: var(--primary-color);\n      color: rgb(255, 255, 255);\n      color: var(--primary-text-color);\n      height: 40px;\n      text-align: center;\n\n      .icon {\n        display: none;\n        content: \"\";\n      }\n    }\n\n    .icon {\n      position: relative;\n      color: rgb(65, 85, 133);\n\n      &[storageType]::after {\n        position: absolute;\n        top: 50%;\n        left: 50%;\n        content: attr(storageType);\n        transform: translate(-50%, -50%);\n        color: rgb(255, 255, 255);\n        font-size: 0.6rem;\n        font-weight: 600;\n        text-transform: uppercase;\n      }\n\n      &.clearclose {\n        color: currentColor;\n        font-size: 1.2rem;\n      }\n\n      &.folder {\n        color: rgb(206, 206, 53);\n\n        &.user-added-storage {\n          color: rgb(53, 101, 206);\n        }\n      }\n\n      &.code {\n        color: rgb(79, 155, 79);\n      }\n    }\n    &.symlink {\n      .icon {\n        position: relative;\n        color: var(--link-text-color);\n        &::after {\n          content: \"🔗\";\n          position: absolute;\n          bottom: 13px;\n          right: 10px;\n          font-size: 0.4em;\n          color: inherit;\n        }\n      }\n\n      .text {\n        color: var(--link-text-color);\n        &::after {\n          content: \" (symlink)\";\n          font-size: 0.8em;\n          color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n        }\n      }\n    }\n  }\n\n  .info {\n    height: 30px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 0.9rem;\n    background-color: rgba($color: #000000, $alpha: 0.1);\n  }\n\n  #list {\n    height: calc(100% - 60px);\n    overflow-y: auto;\n\n    .tile {\n      &[disabled] {\n        .text {\n          opacity: 0.5;\n        }\n      }\n\n      &[read-only] {\n        .text::after {\n          content: \"Read only\";\n          font-size: 0.6em;\n          color: rgb(255, 255, 255);\n          background-color: rgb(62, 100, 138);\n          border-radius: 4px;\n          padding: 5px;\n          margin: auto 15px;\n        }\n      }\n    }\n  }\n  .selection-header {\n    display: flex;\n    align-items: center;\n    padding: 5px;\n    background: var(--primary-color);\n    color: var(--primary-text-color);\n    position: sticky;\n    top: 0;\n    z-index: 1;\n    gap: 10px;\n\n    .selection-count {\n      font-size: 0.9em;\n    }\n  }\n}\n"
  },
  {
    "path": "src/pages/fileBrowser/index.js",
    "content": "import alert from \"dialogs/alert\";\nimport openFile from \"lib/openFile\";\nimport openFolder, { addedFolder } from \"lib/openFolder\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\n/**\n * @typedef {\"file\"|\"folder\"|\"both\"} BrowseMode\n * @typedef {{type: 'file' | 'folder', url: String, name: String}} SelectedFile\n */\n\n/**\n *\n * @param {BrowseMode} [mode='file'] Specify file browser mode, value can be 'file', 'folder' or 'both'\n * @param {string} info A small message to show what's file browser is opened for\n * @param {boolean} doesOpenLast Should file browser open lastly visited directory?\n * @param {Array<{name: String, url: String}>} defaultDir Default directory to open.\n * @returns {Promise<SelectedFile>}\n */\nfunction FileBrowser(mode, info, doesOpenLast, ...args) {\n\treturn new Promise((resolve, reject) => {\n\t\timport(/* webpackChunkName: \"fileBrowser\" */ \"./fileBrowser\").then(\n\t\t\t(res) => {\n\t\t\t\tconst FileBrowser = res.default;\n\t\t\t\tFileBrowser(mode, info, doesOpenLast, ...args)\n\t\t\t\t\t.then(resolve)\n\t\t\t\t\t.catch(reject);\n\t\t\t},\n\t\t);\n\t});\n}\n\nFileBrowser.openFile = (res) => {\n\tconst { url, name, mode } = res;\n\tconst createOption = {\n\t\turi: url,\n\t\tname,\n\t\trender: true,\n\t};\n\n\tif (mode) {\n\t\tcreateOption.mode = mode;\n\t}\n\n\topenFile(url, createOption);\n};\n\nFileBrowser.openFileError = (err) => {\n\tconst ERROR = strings.error.toUpperCase();\n\tconst message = `${strings[\"unable to open file\"]}. ${helpers.errorMessage(err.code)}`;\n\tif (err.code) {\n\t\talert(ERROR, message);\n\t} else if (err.code !== 0) {\n\t\talert(ERROR, strings[\"unable to open file\"]);\n\t}\n};\n\nFileBrowser.openFolder = async (res) => {\n\tconst { url, name } = res;\n\tconst protocol = Url.getProtocol(url);\n\n\tif (protocol === \"ftp:\") {\n\t\topenFolder(url, {\n\t\t\tname: name,\n\t\t\tsaveState: false,\n\t\t});\n\t} else {\n\t\topenFolder(url, {\n\t\t\tname: name,\n\t\t});\n\t}\n\n\tconst folder = addedFolder.find((folder) => folder.url === url);\n\tfolder?.$node?.$title?.click();\n};\n\nFileBrowser.openFolderError = (err) => {\n\tconst ERROR = strings.error.toUpperCase();\n\tconst message = `${strings[\"unable to open folder\"]}. ${helpers.errorMessage(err.code)}`;\n\tif (err.code) {\n\t\talert(ERROR, message);\n\t} else if (err.code !== 0) {\n\t\talert(ERROR, strings[\"unable to open folder\"]);\n\t}\n};\n\nFileBrowser.open = (res) => {\n\tif (res.type === \"folder\") {\n\t\tFileBrowser.openFolder(res);\n\t\treturn;\n\t}\n\n\tFileBrowser.openFile(res);\n};\n\nFileBrowser.openError = (err) => {\n\tFileBrowser.openFileError(err);\n};\n\nexport default FileBrowser;\n"
  },
  {
    "path": "src/pages/fileBrowser/list.hbs",
    "content": "<ul class=\"list\" id=\"list\" empty-msg=\"{{msg}}\">{{#list}}\r\n  {{#.}}\r\n  <li\r\n    tabindex=\"1\"\r\n    class=\"tile {{#isLink}}symlink{{/isLink}}\"\r\n    action=\"open\"\r\n    type=\"{{type}}\"\r\n    name=\"{{name}}\"\r\n    {{#home}}home=\"{{.}}\"{{/home}}\r\n    {{#open-doc}}open-doc=\"true\"{{/open-doc}}\r\n    {{#ftp-account}}ftp-account{{/ftp-account}}\r\n    {{#disabled}}disabled{{/disabled}}\r\n    {{#uuid}}uuid=\"{{uuid}}\"{{/uuid}}\r\n    {{#storageType}}storageType=\"{{.}}\"{{/storageType}}\r\n  >\r\n    <span\r\n      class=\"icon {{icon}} {{#uuid}}user-added-storage{{/uuid}}\"\r\n      {{#storageType}}storageType=\"{{.}}\"{{/storageType}}\r\n    ></span>\r\n\r\n    <div class=\"text\">\r\n      <span>{{name}}</span>\r\n    </div>\r\n    <data-url>{{url}}</data-url>\r\n  </li>\r\n  {{/.}}\r\n  {{/list}}\r\n</ul>\r\n"
  },
  {
    "path": "src/pages/fileBrowser/util.js",
    "content": "import multiPrompt from \"dialogs/multiPrompt\";\nimport helpers from \"utils/helpers\";\n\nexport default {\n\t/**\n\t *\n\t * @param {Array} list\n\t * @param {String} name\n\t * @param {String} url\n\t * @param {Object} extra\n\t */\n\tpushFolder(list, name, url, extra = {}) {\n\t\tlist.push({\n\t\t\turl: url,\n\t\t\tname: name,\n\t\t\tisDirectory: true,\n\t\t\tparent: true,\n\t\t\ttype: \"dir\",\n\t\t\t...extra,\n\t\t});\n\t},\n\t/**\n\t * Save a new path using storage access framework\n\t * @param {String} name\n\t * @returns {Promise<{name: String, uri: String, uuid: string}>}\n\t */\n\tasync addPath(name, uuid) {\n\t\tconst res = await multiPrompt(\n\t\t\tstrings[\"add path\"],\n\t\t\t[\n\t\t\t\t{\n\t\t\t\t\tid: \"uri\",\n\t\t\t\t\tplaceholder: strings[\"select folder\"],\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\treadOnly: true,\n\t\t\t\t\tonclick() {\n\t\t\t\t\t\tsdcard.getStorageAccessPermission(\n\t\t\t\t\t\t\tuuid,\n\t\t\t\t\t\t\t(res) => {\n\t\t\t\t\t\t\t\tconst $name = tag.get(\"#name\");\n\t\t\t\t\t\t\t\tif (!$name.value && res) {\n\t\t\t\t\t\t\t\t\tconst name = window\n\t\t\t\t\t\t\t\t\t\t.decodeURIComponent(res)\n\t\t\t\t\t\t\t\t\t\t?.split(\":\")\n\t\t\t\t\t\t\t\t\t\t.pop()\n\t\t\t\t\t\t\t\t\t\t?.split(\"/\")\n\t\t\t\t\t\t\t\t\t\t.pop();\n\t\t\t\t\t\t\t\t\t$name.value = name ?? \"\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthis.value = res;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\t\thelpers.error(err);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: \"name\",\n\t\t\t\t\tplaceholder: strings[\"folder name\"],\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\trequired: true,\n\t\t\t\t\tvalue: name ?? \"\",\n\t\t\t\t},\n\t\t\t],\n\t\t\t\"https://acode.app/faqs/224761680\",\n\t\t);\n\n\t\tif (!res) return;\n\n\t\treturn {\n\t\t\tname: res.name,\n\t\t\turi: res.uri,\n\t\t\tuuid: helpers.uuid(),\n\t\t};\n\t},\n};\n"
  },
  {
    "path": "src/pages/fontManager/fontManager.js",
    "content": "import \"./style.scss\";\nimport fsOperation from \"fileSystem\";\nimport Page from \"components/page\";\nimport searchBar from \"components/searchbar\";\nimport { DEFAULT_TERMINAL_SETTINGS } from \"components/terminal\";\nimport toast from \"components/toast\";\nimport box from \"dialogs/box\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport fonts from \"lib/fonts\";\nimport appSettings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport { updateActiveTerminals } from \"settings/terminalSettings\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nexport default function fontManager() {\n\tconst defaultEditorFont = \"Roboto Mono\";\n\tconst defaultTerminalFont = DEFAULT_TERMINAL_SETTINGS.fontFamily;\n\tconst defaultAppFontLabel = strings.default || \"Default\";\n\tconst targetLabels = {\n\t\tapp: \"App\",\n\t\teditor: \"Editor\",\n\t\tterminal: \"Terminal\",\n\t\tall: \"All\",\n\t};\n\tconst $page = Page(strings.fonts?.capitalize());\n\tconst $search = <span attr-action=\"search\" className=\"icon search\"></span>;\n\tconst $addFont = <span attr-action=\"add-font\" className=\"icon add\"></span>;\n\tconst list = Ref();\n\t$page.classList.add(\"font-manager-page\");\n\n\tactionStack.push({\n\t\tid: \"fontManager\",\n\t\taction: () => {\n\t\t\t$page.hide();\n\t\t\t$page.removeEventListener(\"click\", clickHandler);\n\t\t},\n\t});\n\n\t$page.onhide = () => {\n\t\thideAd();\n\t\tactionStack.remove(\"fontManager\");\n\t};\n\n\t$page.body = <div ref={list} className=\"main list font-manager-list\"></div>;\n\n\t$page.querySelector(\"header\").append($search, $addFont);\n\n\tapp.append($page);\n\trenderFonts();\n\thelpers.showAd();\n\n\t$page.addEventListener(\"click\", clickHandler);\n\n\tfunction renderFonts() {\n\t\tconst fontNames = fonts.getNames();\n\t\tlet $currentItem;\n\t\tconst content = [];\n\t\tconst defaultAppliedTargets = getAppliedTargets(\"\");\n\n\t\tconst $defaultItem = (\n\t\t\t<FontItem\n\t\t\t\tname={defaultAppFontLabel}\n\t\t\t\tappliedTargets={defaultAppliedTargets}\n\t\t\t\tsubtitle=\"System default app font\"\n\t\t\t\tdeletable={false}\n\t\t\t\tonSelect={() => chooseApplyTarget(\"\")}\n\t\t\t/>\n\t\t);\n\t\tif (defaultAppliedTargets.length) $currentItem = $defaultItem;\n\t\tcontent.push($defaultItem);\n\n\t\tfontNames.forEach((fontName) => {\n\t\t\tconst appliedTargets = getAppliedTargets(fontName);\n\t\t\tconst $item = (\n\t\t\t\t<FontItem\n\t\t\t\t\tname={fontName}\n\t\t\t\t\tappliedTargets={appliedTargets}\n\t\t\t\t\tdeletable={fonts.isCustom(fontName)}\n\t\t\t\t\tonSelect={() => chooseApplyTarget(fontName)}\n\t\t\t\t\tonDelete={() => deleteFont(fontName)}\n\t\t\t\t/>\n\t\t\t);\n\t\t\tif (!$currentItem && appliedTargets.length) $currentItem = $item;\n\t\t\tcontent.push($item);\n\t\t});\n\n\t\tlist.el.content = content;\n\t\t$currentItem?.scrollIntoView();\n\t}\n\n\tasync function clickHandler(e) {\n\t\tconst $target = e.target;\n\t\tif (!($target instanceof HTMLElement)) return;\n\t\tconst action = $target.getAttribute(\"action\") || $target.dataset.action;\n\t\tif (!action) return;\n\n\t\tswitch (action) {\n\t\t\tcase \"search\":\n\t\t\t\tsearchBar(list.el);\n\t\t\t\tbreak;\n\t\t\tcase \"add-font\":\n\t\t\t\tawait addNewFont();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tasync function addNewFont() {\n\t\ttry {\n\t\t\tconst { url, name } = await FileBrowser(\n\t\t\t\t\"file\",\n\t\t\t\t\"Select font file (.ttf, .otf, .woff)\",\n\t\t\t\tfalse,\n\t\t\t);\n\n\t\t\t// Check if file is a font file\n\t\t\tconst ext = name.toLowerCase().split(\".\").pop();\n\t\t\tif (![\"ttf\", \"otf\", \"woff\", \"woff2\"].includes(ext)) {\n\t\t\t\ttoast(\"Please select a valid font file (.ttf, .otf, .woff)\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst fontName = await prompt(\n\t\t\t\t\"Font Name\",\n\t\t\t\tname.replace(/\\.(ttf|otf|woff|woff2)$/i, \"\"),\n\t\t\t);\n\t\t\tif (!fontName) return;\n\n\t\t\t// Check if font already exists\n\t\t\tif (fonts.get(fontName)) {\n\t\t\t\ttoast(\"Font with this name already exists\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait addFontFromFile(fontName, url);\n\t\t} catch (error) {\n\t\t\tif (error.message !== \"User cancelled\") {\n\t\t\t\ttoast(\"Failed to add font: \" + error.message);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function addFontFromFile(fontName, fontUrl) {\n\t\ttry {\n\t\t\t// Download the font to local storage first\n\t\t\tloader.showTitleLoader();\n\t\t\tconst FONT_DIR = Url.join(DATA_STORAGE, \"fonts\");\n\t\t\tconst fontFileName = `${fontName.replace(/[^a-zA-Z0-9]/g, \"_\")}.ttf`;\n\t\t\tconst FONT_FILE = Url.join(FONT_DIR, fontFileName);\n\n\t\t\t// Create fonts directory if it doesn't exist\n\t\t\tif (!(await fsOperation(FONT_DIR).exists())) {\n\t\t\t\tawait fsOperation(DATA_STORAGE).createDirectory(\"fonts\");\n\t\t\t}\n\n\t\t\t// Read and save the font file\n\t\t\tconst fontData = await fsOperation(fontUrl).readFile();\n\t\t\tawait fsOperation(FONT_DIR).createFile(fontFileName, fontData);\n\n\t\t\t// Get internal URI for the saved font\n\t\t\tconst internalUrl = await helpers.toInternalUri(FONT_FILE);\n\n\t\t\t// Generate CSS for the font\n\t\t\tlet css = `@font-face {\n  font-family: '${fontName}';\n  src: url(${internalUrl}) format('truetype');\n  font-weight: normal;\n  font-style: normal;\n}`;\n\n\t\t\tloader.removeTitleLoader();\n\n\t\t\t// Show CSS preview/edit dialog\n\t\t\tconst editedCSS = await showCSSEditor(css, fontName);\n\t\t\tif (editedCSS === null) return; // User cancelled\n\n\t\t\t// Add the font\n\t\t\tfonts.addCustom(fontName, editedCSS);\n\t\t\trenderFonts();\n\t\t\ttoast(`Font \"${fontName}\" added successfully`);\n\t\t} catch (error) {\n\t\t\tloader.removeTitleLoader();\n\t\t\ttoast(\"Failed to add font: \" + error.message);\n\t\t}\n\t}\n\n\tasync function showCSSEditor(css, fontName) {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst htmlContent = `\n\t\t\t\t<div style=\"margin-bottom: 10px; font-size: 0.9em; opacity: 0.8;\">\n\t\t\t\t\tEdit the CSS @font-face rule below:\n\t\t\t\t</div>\n\t\t\t\t<textarea \n\t\t\t\t\tclass=\"input font-css-editor\" \n\t\t\t\t\tplaceholder=\"Enter CSS @font-face rule...\"\n\t\t\t\t\trows=\"8\"\n\t\t\t\t\tstyle=\"font-family: ${appSettings.value.editorFont}, monospace; font-size: 0.85em; line-height: 1.4; resize: vertical;\"\n\t\t\t\t>${css}</textarea>\n\t\t\t`;\n\n\t\t\tconst dialog = box(\n\t\t\t\t`Edit CSS - ${fontName}`,\n\t\t\t\thtmlContent,\n\t\t\t\t\"Save\",\n\t\t\t\t\"Cancel\",\n\t\t\t)\n\t\t\t\t.then((children) => {\n\t\t\t\t\tconst textarea = children[0].querySelector(\".font-css-editor\");\n\t\t\t\t\tif (textarea) {\n\t\t\t\t\t\ttextarea.focus();\n\t\t\t\t\t\ttextarea.select();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.ok(() => {\n\t\t\t\t\tconst textarea = document.querySelector(\".font-css-editor\");\n\t\t\t\t\tconst value = textarea ? textarea.value : css;\n\t\t\t\t\tresolve(value);\n\t\t\t\t\tdialog.hide();\n\t\t\t\t})\n\t\t\t\t.cancel(() => {\n\t\t\t\t\tresolve(null);\n\t\t\t\t\tdialog.hide();\n\t\t\t\t});\n\t\t});\n\t}\n\n\tfunction getAppliedTargets(fontName) {\n\t\tconst appFont = appSettings.value.appFont || \"\";\n\t\tconst editorFont = appSettings.value.editorFont || defaultEditorFont;\n\t\tconst terminalFont =\n\t\t\tappSettings.value.terminalSettings?.fontFamily || defaultTerminalFont;\n\t\tconst appliedTargets = [];\n\n\t\tif (fontName) {\n\t\t\tif (appFont === fontName) appliedTargets.push(\"app\");\n\t\t\tif (editorFont === fontName) appliedTargets.push(\"editor\");\n\t\t\tif (terminalFont === fontName) appliedTargets.push(\"terminal\");\n\t\t\treturn appliedTargets;\n\t\t}\n\n\t\tif (!appFont) appliedTargets.push(\"app\");\n\t\treturn appliedTargets;\n\t}\n\n\tfunction getTargetOptionText(fontName, target) {\n\t\tif (fontName) {\n\t\t\treturn `Apply to ${targetLabels[target]}`;\n\t\t}\n\n\t\tswitch (target) {\n\t\t\tcase \"app\":\n\t\t\t\treturn \"Reset App font\";\n\t\t\tcase \"editor\":\n\t\t\t\treturn \"Reset Editor font\";\n\t\t\tcase \"terminal\":\n\t\t\t\treturn \"Reset Terminal font\";\n\t\t\tcase \"all\":\n\t\t\t\treturn \"Reset all fonts\";\n\t\t\tdefault:\n\t\t\t\treturn \"Reset font\";\n\t\t}\n\t}\n\n\tasync function chooseApplyTarget(fontName) {\n\t\tconst title = fontName\n\t\t\t? `Apply \"${fontName}\"`\n\t\t\t: `${defaultAppFontLabel} font`;\n\n\t\tconst target = await select(\n\t\t\ttitle,\n\t\t\t[\n\t\t\t\t[\"app\", getTargetOptionText(fontName, \"app\")],\n\t\t\t\t[\"editor\", getTargetOptionText(fontName, \"editor\")],\n\t\t\t\t[\"terminal\", getTargetOptionText(fontName, \"terminal\")],\n\t\t\t\t[\"all\", getTargetOptionText(fontName, \"all\")],\n\t\t\t],\n\t\t\ttrue,\n\t\t).catch(() => null);\n\n\t\tif (!target) return;\n\n\t\tawait applyFontToTarget(fontName, target);\n\t}\n\n\tasync function applyFontToTarget(fontName, target) {\n\t\ttry {\n\t\t\tconst nextEditorFont = fontName || defaultEditorFont;\n\t\t\tconst nextTerminalFont = fontName || defaultTerminalFont;\n\t\t\tconst nextTerminalSettings = {\n\t\t\t\t...(appSettings.value.terminalSettings || DEFAULT_TERMINAL_SETTINGS),\n\t\t\t};\n\t\t\tconst nextSettings = {};\n\n\t\t\tswitch (target) {\n\t\t\t\tcase \"app\":\n\t\t\t\t\tawait fonts.setAppFont(fontName);\n\t\t\t\t\tnextSettings.appFont = fontName;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"editor\":\n\t\t\t\t\tawait fonts.setEditorFont(nextEditorFont);\n\t\t\t\t\tnextSettings.editorFont = nextEditorFont;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"terminal\":\n\t\t\t\t\tnextTerminalSettings.fontFamily = nextTerminalFont;\n\t\t\t\t\tnextSettings.terminalSettings = nextTerminalSettings;\n\t\t\t\t\tawait updateActiveTerminals(\"fontFamily\", nextTerminalFont);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase \"all\":\n\t\t\t\t\tawait fonts.setAppFont(fontName);\n\t\t\t\t\tawait fonts.setEditorFont(nextEditorFont);\n\t\t\t\t\tnextTerminalSettings.fontFamily = nextTerminalFont;\n\t\t\t\t\tawait updateActiveTerminals(\"fontFamily\", nextTerminalFont);\n\t\t\t\t\tnextSettings.appFont = fontName;\n\t\t\t\t\tnextSettings.editorFont = nextEditorFont;\n\t\t\t\t\tnextSettings.terminalSettings = nextTerminalSettings;\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait appSettings.update(nextSettings, false);\n\t\t\ttoast(getApplyToast(fontName, target));\n\t\t\trenderFonts();\n\t\t} catch (error) {\n\t\t\ttoast(\"Failed to apply font: \" + error.message);\n\t\t}\n\t}\n\n\tfunction getApplyToast(fontName, target) {\n\t\tconst label = fontName ? `\"${fontName}\"` : \"default font\";\n\t\tswitch (target) {\n\t\t\tcase \"app\":\n\t\t\t\treturn `${label} applied to app`;\n\t\t\tcase \"editor\":\n\t\t\t\treturn `${label} applied to editor`;\n\t\t\tcase \"terminal\":\n\t\t\t\treturn `${label} applied to terminal`;\n\t\t\tcase \"all\":\n\t\t\t\treturn `${label} applied to app, editor, and terminal`;\n\t\t\tdefault:\n\t\t\t\treturn \"Font applied\";\n\t\t}\n\t}\n\n\tasync function deleteFont(fontName) {\n\t\t// Don't allow deleting default fonts\n\t\tif (!fonts.isCustom(fontName)) {\n\t\t\ttoast(\"Cannot delete default fonts\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldDelete = await confirm(\n\t\t\t\"Delete Font\",\n\t\t\t`Are you sure you want to delete \"${fontName}\"?`,\n\t\t);\n\n\t\tif (shouldDelete) {\n\t\t\ttry {\n\t\t\t\tconst currentEditorFont =\n\t\t\t\t\tappSettings.value.editorFont || defaultEditorFont;\n\t\t\t\tconst currentAppFont = appSettings.value.appFont || \"\";\n\t\t\t\tconst currentTerminalFont =\n\t\t\t\t\tappSettings.value.terminalSettings?.fontFamily || defaultTerminalFont;\n\t\t\t\tconst isCurrentEditorFont = fontName === currentEditorFont;\n\t\t\t\tconst isCurrentAppFont = fontName === currentAppFont;\n\t\t\t\tconst isCurrentTerminalFont = fontName === currentTerminalFont;\n\n\t\t\t\t// Remove from fonts collection\n\t\t\t\tfonts.remove(fontName);\n\n\t\t\t\t// Try to delete the font file from storage\n\t\t\t\tconst FONT_DIR = Url.join(DATA_STORAGE, \"fonts\");\n\t\t\t\tconst fontFileName = `${fontName.replace(/[^a-zA-Z0-9]/g, \"_\")}.ttf`;\n\t\t\t\tconst FONT_FILE = Url.join(FONT_DIR, fontFileName);\n\n\t\t\t\tconst fs = fsOperation(FONT_FILE);\n\t\t\t\tif (await fs.exists()) {\n\t\t\t\t\tawait fs.delete();\n\t\t\t\t}\n\n\t\t\t\tif (isCurrentAppFont) {\n\t\t\t\t\tawait fonts.setAppFont(\"\");\n\t\t\t\t}\n\n\t\t\t\tif (isCurrentEditorFont) {\n\t\t\t\t\tawait fonts.setEditorFont(defaultEditorFont);\n\t\t\t\t}\n\n\t\t\t\tif (isCurrentTerminalFont) {\n\t\t\t\t\tawait updateActiveTerminals(\"fontFamily\", defaultTerminalFont);\n\t\t\t\t}\n\n\t\t\t\tif (isCurrentAppFont || isCurrentEditorFont || isCurrentTerminalFont) {\n\t\t\t\t\tawait appSettings.update(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...(isCurrentAppFont ? { appFont: \"\" } : {}),\n\t\t\t\t\t\t\t...(isCurrentEditorFont ? { editorFont: defaultEditorFont } : {}),\n\t\t\t\t\t\t\t...(isCurrentTerminalFont\n\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\tterminalSettings: {\n\t\t\t\t\t\t\t\t\t\t\t...(appSettings.value.terminalSettings ||\n\t\t\t\t\t\t\t\t\t\t\t\tDEFAULT_TERMINAL_SETTINGS),\n\t\t\t\t\t\t\t\t\t\t\tfontFamily: defaultTerminalFont,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (isCurrentAppFont || isCurrentEditorFont || isCurrentTerminalFont) {\n\t\t\t\t\tconst restoredTargets = [\n\t\t\t\t\t\tisCurrentAppFont ? \"app\" : null,\n\t\t\t\t\t\tisCurrentEditorFont ? \"editor\" : null,\n\t\t\t\t\t\tisCurrentTerminalFont ? \"terminal\" : null,\n\t\t\t\t\t].filter(Boolean);\n\t\t\t\t\ttoast(\n\t\t\t\t\t\t`Font \"${fontName}\" deleted, restored ${restoredTargets.join(\", \")} font defaults`,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\ttoast(`Font \"${fontName}\" deleted`);\n\t\t\t\t}\n\n\t\t\t\trenderFonts();\n\t\t\t} catch (error) {\n\t\t\t\t// Font removed from collection even if file deletion fails\n\t\t\t\tconst currentEditorFont =\n\t\t\t\t\tappSettings.value.editorFont || defaultEditorFont;\n\t\t\t\tconst currentAppFont = appSettings.value.appFont || \"\";\n\t\t\t\tconst currentTerminalFont =\n\t\t\t\t\tappSettings.value.terminalSettings?.fontFamily || defaultTerminalFont;\n\t\t\t\tconst isCurrentEditorFont = fontName === currentEditorFont;\n\t\t\t\tconst isCurrentAppFont = fontName === currentAppFont;\n\t\t\t\tconst isCurrentTerminalFont = fontName === currentTerminalFont;\n\n\t\t\t\tif (isCurrentAppFont || isCurrentEditorFont || isCurrentTerminalFont) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (isCurrentAppFont) {\n\t\t\t\t\t\t\tawait fonts.setAppFont(\"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (isCurrentEditorFont) {\n\t\t\t\t\t\t\tawait fonts.setEditorFont(defaultEditorFont);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (isCurrentTerminalFont) {\n\t\t\t\t\t\t\tawait updateActiveTerminals(\"fontFamily\", defaultTerminalFont);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait appSettings.update(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t...(isCurrentAppFont ? { appFont: \"\" } : {}),\n\t\t\t\t\t\t\t\t...(isCurrentEditorFont\n\t\t\t\t\t\t\t\t\t? { editorFont: defaultEditorFont }\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t...(isCurrentTerminalFont\n\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\tterminalSettings: {\n\t\t\t\t\t\t\t\t\t\t\t\t...(appSettings.value.terminalSettings ||\n\t\t\t\t\t\t\t\t\t\t\t\t\tDEFAULT_TERMINAL_SETTINGS),\n\t\t\t\t\t\t\t\t\t\t\t\tfontFamily: defaultTerminalFont,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttoast(`Font \"${fontName}\" deleted (file cleanup may have failed)`);\n\t\t\t\t\t} catch (setFontError) {\n\t\t\t\t\t\ttoast(\n\t\t\t\t\t\t\t`Font \"${fontName}\" deleted, but failed to restore a fallback font`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttoast(`Font \"${fontName}\" deleted (file cleanup may have failed)`);\n\t\t\t\t}\n\n\t\t\t\trenderFonts();\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction FontItem({\n\t\tname,\n\t\tappliedTargets,\n\t\tsubtitle,\n\t\tdeletable = true,\n\t\tonSelect,\n\t\tonDelete,\n\t}) {\n\t\tconst isBuiltIn = name !== defaultAppFontLabel && !fonts.isCustom(name);\n\t\tconst isApplied = appliedTargets.length > 0;\n\t\tconst resolvedSubtitle =\n\t\t\tsubtitle ||\n\t\t\t(isApplied\n\t\t\t\t? \"Applied font\"\n\t\t\t\t: isBuiltIn\n\t\t\t\t\t? \"Built-in font\"\n\t\t\t\t\t: \"Custom font\");\n\n\t\tconst $item = (\n\t\t\t<div\n\t\t\t\ttabIndex={1}\n\t\t\t\tclassName={`list-item has-subtitle ${isApplied ? \"current-font\" : \"\"}`}\n\t\t\t\tdata-key={name}\n\t\t\t\tdata-action=\"select-font\"\n\t\t\t>\n\t\t\t\t<span className=\"icon text_format\"></span>\n\t\t\t\t<div className=\"container\">\n\t\t\t\t\t<div className=\"text\">{name}</div>\n\t\t\t\t\t<small className=\"value\">{resolvedSubtitle}</small>\n\t\t\t\t</div>\n\t\t\t\t{appliedTargets.length || deletable ? (\n\t\t\t\t\t<div className=\"setting-tail\">\n\t\t\t\t\t\t{appliedTargets.map((target) => (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={`${name}-${target}`}\n\t\t\t\t\t\t\t\tclassName={`font-manager-badge font-manager-badge-${target}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{targetLabels[target]}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{deletable ? (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName=\"icon delete font-manager-action\"\n\t\t\t\t\t\t\t\tdata-action=\"delete\"\n\t\t\t\t\t\t\t\ttitle=\"Delete font\"\n\t\t\t\t\t\t\t></span>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t);\n\n\t\t$item.onclick = (e) => {\n\t\t\tconst $target = e.target;\n\t\t\tconst action = $target.dataset.action;\n\t\t\tif (action === \"delete\" && deletable) {\n\t\t\t\te.stopPropagation();\n\t\t\t\tonDelete();\n\t\t\t} else if (\n\t\t\t\t!$target.classList.contains(\"font-manager-action\") ||\n\t\t\t\taction === \"select-font\"\n\t\t\t) {\n\t\t\t\tonSelect();\n\t\t\t}\n\t\t};\n\n\t\treturn $item;\n\t}\n}\n"
  },
  {
    "path": "src/pages/fontManager/index.js",
    "content": "export default function fontManager(...args) {\n\timport(/* webpackChunkName: \"fontManager\" */ \"./fontManager\").then(\n\t\t(module) => {\n\t\t\tmodule.default(...args);\n\t\t},\n\t);\n}\n"
  },
  {
    "path": "src/pages/fontManager/style.scss",
    "content": "wc-page.font-manager-page {\n\tbackground: var(--secondary-color);\n\n\t.font-manager-list {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\twidth: 100%;\n\t\tmax-width: 48rem;\n\t\tmargin: 0 auto;\n\t\tpadding: 0.5rem 0 5.5rem;\n\t\tbox-sizing: border-box;\n\t\tbackground: var(--secondary-color);\n\t}\n\n\t.font-manager-list > .list-item {\n\t\tdisplay: flex;\n\t\twidth: 100%;\n\t\tmin-height: 4.1rem;\n\t\tmargin: 0;\n\t\tpadding: 0.75rem 1rem;\n\t\tbox-sizing: border-box;\n\t\talign-items: center;\n\t\tgap: 0.85rem;\n\t\tbackground: transparent;\n\t\tcursor: pointer;\n\t\ttransition: background-color 140ms ease;\n\t\ttext-decoration: none;\n\n\t\t&:not(:last-of-type) {\n\t\t\tborder-bottom: 1px solid var(--border-color);\n\t\t\tborder-bottom: 1px solid color-mix(in srgb, var(--border-color), transparent 20%);\n\t\t}\n\n\t\t&:focus,\n\t\t&:active {\n\t\t\tbackground: color-mix(\n\t\t\t\tin srgb,\n\t\t\t\tvar(--secondary-color),\n\t\t\t\tvar(--popup-text-color) 4%\n\t\t\t);\n\t\t}\n\n\t\t> .icon:first-child {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\twidth: 1.4rem;\n\t\t\tmin-width: 1.4rem;\n\t\t\theight: 1.4rem;\n\t\t\tfont-size: 1.15rem;\n\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 18%);\n\t\t}\n\n\t\t> .container {\n\t\t\tflex: 1;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tmin-width: 0;\n\t\t\toverflow: visible;\n\t\t\tgap: 0.24rem;\n\t\t\tpadding-right: 0.6rem;\n\n\t\t\t> .text {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmin-width: 0;\n\t\t\t\tfont-size: 1rem;\n\t\t\t\tline-height: 1.2;\n\t\t\t\tfont-weight: 600;\n\t\t\t\tcolor: var(--popup-text-color);\n\t\t\t}\n\n\t\t\t> .value {\n\t\t\t\tdisplay: block;\n\t\t\t\tfont-size: 0.82rem;\n\t\t\t\tline-height: 1.35;\n\t\t\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 30%);\n\t\t\t\ttext-transform: none;\n\t\t\t\twhite-space: normal;\n\t\t\t\toverflow: visible;\n\t\t\t\toverflow-wrap: anywhere;\n\t\t\t\topacity: 1;\n\t\t\t}\n\t\t}\n\n\t\t> .setting-tail {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tjustify-content: flex-end;\n\t\t\tflex-shrink: 0;\n\t\t\tmin-height: 1.65rem;\n\t\t\tgap: 0.65rem;\n\t\t\tmargin-left: 0.9rem;\n\t\t\talign-self: center;\n\t\t}\n\t}\n\n\t.font-manager-list > .list-item.current-font {\n\t\tbackground: color-mix(in srgb, var(--secondary-color), var(--active-color) 8%);\n\t}\n\n\t.font-manager-list > .list-item.current-font:focus,\n\t.font-manager-list > .list-item.current-font:active {\n\t\tbackground: color-mix(in srgb, var(--secondary-color), var(--active-color) 12%);\n\t}\n\n\t.font-manager-list > .list-item.current-font > .icon:first-child {\n\t\tcolor: color-mix(in srgb, var(--active-color), transparent 10%);\n\t}\n\n\t.font-manager-list > .list-item.current-font > .container > .text {\n\t\tfont-weight: 700;\n\t}\n\n\t@media screen and (min-width: 768px) {\n\t\t.font-manager-list {\n\t\t\tpadding-left: 0.5rem;\n\t\t\tpadding-right: 0.5rem;\n\t\t}\n\t}\n\n\t.font-manager-badge {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tpadding: 0.16rem 0.48rem;\n\t\tborder-radius: 999px;\n\t\tfont-size: 0.82rem;\n\t\tfont-weight: 600;\n\t\tline-height: 1.2;\n\t\tbackground: color-mix(in srgb, var(--secondary-color), var(--active-color) 10%);\n\t\tcolor: color-mix(in srgb, var(--active-color), transparent 12%);\n\t}\n\n\t.font-manager-badge-editor {\n\t\tbackground: color-mix(in srgb, var(--secondary-color), #4ca3ff 14%);\n\t\tcolor: color-mix(in srgb, #4ca3ff, white 4%);\n\t}\n\n\t.font-manager-badge-terminal {\n\t\tbackground: color-mix(in srgb, var(--secondary-color), #21b36b 14%);\n\t\tcolor: color-mix(in srgb, #21b36b, white 4%);\n\t}\n\n\t.font-manager-badge-app {\n\t\tbackground: color-mix(in srgb, var(--secondary-color), var(--active-color) 12%);\n\t\tcolor: color-mix(in srgb, var(--active-color), transparent 14%);\n\t}\n\n\t.font-manager-action {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 1.2rem;\n\t\tmin-width: 1.2rem;\n\t\theight: 1.2rem;\n\t\tfont-size: 1.1rem;\n\t\tline-height: 1;\n\t\tcolor: color-mix(in srgb, var(--secondary-text-color), transparent 28%);\n\t\tcursor: pointer;\n\t\ttransition: color 140ms ease;\n\n\t\t&:hover,\n\t\t&:active {\n\t\t\tcolor: var(--error-text-color);\n\t\t}\n\t}\n}\n\n.prompt.box {\n\t.font-css-editor {\n\t\t&.input {\n\t\t\tborder-bottom: 1px solid var(--border-color);\n\t\t\tmin-height: 120px;\n\t\t\tmargin-top: 5px;\n\n\t\t\t&:focus {\n\t\t\t\tborder-bottom-color: var(--active-color);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/pages/markdownPreview/index.js",
    "content": "import \"./style.scss\";\n\nimport fsOperation from \"fileSystem\";\nimport Page from \"components/page\";\nimport DOMPurify from \"dompurify\";\nimport actionStack from \"lib/actionStack\";\nimport openFile from \"lib/openFile\";\nimport { highlightCodeBlock, initHighlighting } from \"utils/codeHighlight\";\nimport {\n\tgetMarkdownBaseUri,\n\thasMathContent,\n\tisExternalLink,\n\tisMarkdownPath,\n\trenderMarkdown,\n\tresolveMarkdownTarget,\n} from \"./renderer\";\n\nlet previewController = null;\nlet mermaidModulePromise = null;\nlet mermaidThemeSignature = \"\";\nlet mathStylesPromise = null;\n\nfunction getThemeColor(name, fallback) {\n\tconst value = getComputedStyle(document.documentElement)\n\t\t.getPropertyValue(name)\n\t\t.trim();\n\treturn value || fallback;\n}\n\nfunction isDarkColor(color) {\n\tconst normalized = color.replace(/\\s+/g, \"\");\n\tconst match = normalized.match(/^#([0-9a-f]{6})$/i);\n\tif (!match) return true;\n\n\tconst value = match[1];\n\tconst r = Number.parseInt(value.slice(0, 2), 16);\n\tconst g = Number.parseInt(value.slice(2, 4), 16);\n\tconst b = Number.parseInt(value.slice(4, 6), 16);\n\tconst luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n\treturn luminance < 0.5;\n}\n\nfunction escapeHtml(text) {\n\treturn String(text ?? \"\")\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\");\n}\n\nfunction getTargetElement(container, targetId) {\n\tconst decodedId = decodeURIComponent(targetId || \"\");\n\tif (!decodedId) return null;\n\n\tconst elements = container.querySelectorAll(\"[id], [name]\");\n\treturn (\n\t\tArray.from(elements).find(\n\t\t\t(element) =>\n\t\t\t\telement.getAttribute(\"id\") === decodedId ||\n\t\t\t\telement.getAttribute(\"name\") === decodedId,\n\t\t) || null\n\t);\n}\n\nfunction getOffsetTopWithinContainer(target, container) {\n\tlet top = 0;\n\tlet element = target;\n\n\twhile (element && element !== container) {\n\t\ttop += element.offsetTop || 0;\n\t\telement = element.offsetParent;\n\t}\n\n\treturn top;\n}\n\nasync function getMermaid() {\n\tif (!mermaidModulePromise) {\n\t\tmermaidModulePromise = import(\"mermaid\")\n\t\t\t.then(({ default: mermaid }) => mermaid)\n\t\t\t.catch((error) => {\n\t\t\t\tmermaidModulePromise = null;\n\t\t\t\tthrow error;\n\t\t\t});\n\t}\n\n\treturn mermaidModulePromise;\n}\n\nasync function ensureMathStyles() {\n\tif (!mathStylesPromise) {\n\t\tmathStylesPromise = Promise.all([\n\t\t\timport(\"katex/dist/katex.min.css\"),\n\t\t\timport(\"markdown-it-texmath/css/texmath.css\"),\n\t\t]).catch((error) => {\n\t\t\tmathStylesPromise = null;\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\treturn mathStylesPromise;\n}\n\nfunction getMermaidThemeConfig() {\n\tconst backgroundColor = getThemeColor(\"--background-color\", \"#1e1e1e\");\n\tconst panelColor = getThemeColor(\"--popup-background-color\", \"#2a2f3a\");\n\tconst borderColor = getThemeColor(\"--border-color\", \"#4a4f5a\");\n\tconst primaryTextColor = getThemeColor(\"--primary-text-color\", \"#f5f5f5\");\n\tconst accentColor = getThemeColor(\"--link-text-color\", \"#4ba3ff\");\n\tconst activeColor = getThemeColor(\"--active-color\", accentColor);\n\n\treturn {\n\t\tstartOnLoad: false,\n\t\tsecurityLevel: \"strict\",\n\t\thtmlLabels: false,\n\t\ttheme: \"base\",\n\t\tflowchart: {\n\t\t\thtmlLabels: false,\n\t\t},\n\t\tthemeVariables: {\n\t\t\tdarkMode: isDarkColor(backgroundColor),\n\t\t\tbackground: backgroundColor,\n\t\t\tmainBkg: panelColor,\n\t\t\tprimaryColor: panelColor,\n\t\t\tmainContrastColor: primaryTextColor,\n\t\t\ttextColor: primaryTextColor,\n\t\t\tprimaryTextColor,\n\t\t\tprimaryBorderColor: borderColor,\n\t\t\tlineColor: primaryTextColor,\n\t\t\tsecondaryColor: accentColor,\n\t\t\tsecondaryBorderColor: borderColor,\n\t\t\tsecondaryTextColor: primaryTextColor,\n\t\t\ttertiaryColor: backgroundColor,\n\t\t\ttertiaryBorderColor: borderColor,\n\t\t\ttertiaryTextColor: primaryTextColor,\n\t\t\tclusterBkg: panelColor,\n\t\t\tclusterBorder: borderColor,\n\t\t\tnodeBorder: borderColor,\n\t\t\tnodeTextColor: primaryTextColor,\n\t\t\ttitleColor: primaryTextColor,\n\t\t\tdefaultLinkColor: activeColor,\n\t\t\tactorTextColor: primaryTextColor,\n\t\t\tlabelTextColor: primaryTextColor,\n\t\t\tloopTextColor: primaryTextColor,\n\t\t\tnoteTextColor: primaryTextColor,\n\t\t\tsectionBkgColor: panelColor,\n\t\t\tsectionBkgColor2: backgroundColor,\n\t\t\tsectionTitleColor: primaryTextColor,\n\t\t\tsequenceNumberColor: primaryTextColor,\n\t\t\tsignalTextColor: primaryTextColor,\n\t\t\ttaskTextColor: primaryTextColor,\n\t\t\ttaskTextDarkColor: primaryTextColor,\n\t\t\ttaskTextOutsideColor: primaryTextColor,\n\t\t\tedgeLabelBackground: backgroundColor,\n\t\t\tpieTitleTextColor: primaryTextColor,\n\t\t\tpieLegendTextColor: primaryTextColor,\n\t\t\tpieSectionTextColor: primaryTextColor,\n\t\t\tgit0: panelColor,\n\t\t\tgit1: backgroundColor,\n\t\t\tgit2: accentColor,\n\t\t\tgit3: activeColor,\n\t\t},\n\t};\n}\n\nfunction initializeMermaid(mermaid) {\n\tconst config = getMermaidThemeConfig();\n\tconst signature = JSON.stringify(config);\n\tif (signature === mermaidThemeSignature) return;\n\tmermaid.initialize(config);\n\tmermaidThemeSignature = signature;\n}\n\nasync function copyText(text) {\n\tif (cordova?.plugins?.clipboard) {\n\t\tcordova.plugins.clipboard.copy(text);\n\t\treturn;\n\t}\n\n\tif (navigator.clipboard?.writeText) {\n\t\tawait navigator.clipboard.writeText(text);\n\t\treturn;\n\t}\n\n\tthrow new Error(\"Clipboard API unavailable\");\n}\n\nasync function fileToObjectUrl(file) {\n\tconst fs = fsOperation(file);\n\tconst fileInfo = await fs.stat();\n\tconst binData = await fs.readFile();\n\treturn URL.createObjectURL(\n\t\tnew Blob([binData], { type: fileInfo.mime || \"application/octet-stream\" }),\n\t);\n}\n\nfunction revokeObjectUrls(urls) {\n\turls.forEach((url) => {\n\t\ttry {\n\t\t\tURL.revokeObjectURL(url);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to revoke object URL\", error);\n\t\t}\n\t});\n}\n\nasync function resolveRenderedImages(container, file) {\n\tconst baseUri = getMarkdownBaseUri(file);\n\tconst objectUrls = [];\n\tconst images = Array.from(container.querySelectorAll(\"img[src]\"));\n\n\timages.forEach((image) => {\n\t\tconst src = image.getAttribute(\"src\");\n\t\tif (!src || src.startsWith(\"data:\") || src.startsWith(\"blob:\")) return;\n\t\tif (src.startsWith(\"#\") || isExternalLink(src)) return;\n\t\tif (!image.hasAttribute(\"data-markdown-local-src\")) {\n\t\t\timage.setAttribute(\n\t\t\t\t\"data-markdown-local-src\",\n\t\t\t\tresolveMarkdownTarget(src, baseUri),\n\t\t\t);\n\t\t}\n\t});\n\n\tawait Promise.all(\n\t\timages.map(async (image) => {\n\t\t\tconst resolvedPath = image.getAttribute(\"data-markdown-local-src\");\n\t\t\tif (!resolvedPath) return;\n\n\t\t\ttry {\n\t\t\t\tconst objectUrl = await fileToObjectUrl(resolvedPath);\n\n\t\t\t\timage.setAttribute(\"src\", objectUrl);\n\t\t\t\timage.setAttribute(\"data-source-uri\", resolvedPath);\n\t\t\t\timage.setAttribute(\"loading\", \"lazy\");\n\t\t\t\timage.setAttribute(\"decoding\", \"async\");\n\t\t\t\timage.classList.add(\"markdown-image\");\n\t\t\t\tobjectUrls.push(objectUrl);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\"Failed to resolve markdown image:\", resolvedPath, error);\n\t\t\t}\n\t\t}),\n\t);\n\n\treturn objectUrls;\n}\n\nfunction createMarkdownPreview(file) {\n\tconst $page = Page(file.filename);\n\tconst $content = <div className=\"main markdown-preview md\"></div>;\n\t$page.body = $content;\n\tapp.append($page);\n\n\tconst previewState = {\n\t\tpage: $page,\n\t\tfile,\n\t\tcontent: $content,\n\t\trenderVersion: 0,\n\t\tobjectUrls: [],\n\t\tpendingHash: \"\",\n\t\tdisposed: false,\n\t};\n\n\tconst removeAction = () => actionStack.remove(\"markdown-preview\");\n\n\tactionStack.push({\n\t\tid: \"markdown-preview\",\n\t\taction: () => $page.hide(),\n\t});\n\n\t$page.onhide = () => {\n\t\tremoveAction();\n\t\tdispose();\n\t};\n\n\tconst onFileChanged = (changedFile) => {\n\t\tif (changedFile?.id !== previewState.file?.id) return;\n\t\tvoid render();\n\t};\n\n\tconst onFileRenamed = (renamedFile) => {\n\t\tif (renamedFile?.id !== previewState.file?.id) return;\n\t\tpreviewState.file = renamedFile;\n\t\t$page.settitle(renamedFile.filename);\n\t\tvoid render();\n\t};\n\n\tconst onFileRemoved = (removedFile) => {\n\t\tif (removedFile?.id !== previewState.file?.id) return;\n\t\tif ($page.isConnected) {\n\t\t\t$page.hide();\n\t\t} else {\n\t\t\tdispose();\n\t\t}\n\t};\n\n\tpreviewState.content.addEventListener(\"click\", onContentClick, true);\n\teditorManager.on(\"file-content-changed\", onFileChanged);\n\teditorManager.on(\"rename-file\", onFileRenamed);\n\teditorManager.on(\"remove-file\", onFileRemoved);\n\tinitHighlighting();\n\n\tasync function onContentClick(event) {\n\t\tconst link = event.target.closest(\"a[href]\");\n\t\tif (!link) return;\n\n\t\tconst originalHref = link.getAttribute(\"href\") || \"\";\n\t\tconst resolvedHref =\n\t\t\tlink.getAttribute(\"data-resolved-href\") ||\n\t\t\tresolveMarkdownTarget(\n\t\t\t\toriginalHref,\n\t\t\t\tgetMarkdownBaseUri(previewState.file),\n\t\t\t);\n\t\tevent.preventDefault();\n\t\tevent.stopPropagation();\n\n\t\tif (originalHref.startsWith(\"#\")) {\n\t\t\tscrollToHash(originalHref.slice(1));\n\t\t\treturn;\n\t\t}\n\n\t\tif (isExternalLink(originalHref)) {\n\t\t\tsystem.openInBrowser(originalHref);\n\t\t\treturn;\n\t\t}\n\n\t\tconst hashIndex = resolvedHref.indexOf(\"#\");\n\t\tconst targetPath =\n\t\t\thashIndex === -1 ? resolvedHref : resolvedHref.slice(0, hashIndex);\n\t\tconst targetHash =\n\t\t\thashIndex === -1 ? \"\" : resolvedHref.slice(hashIndex + 1);\n\n\t\tif (!targetPath && targetHash) {\n\t\t\tscrollToHash(targetHash);\n\t\t\treturn;\n\t\t}\n\n\t\tif (isMarkdownPath(resolvedHref)) {\n\t\t\tawait openFile(targetPath, { render: true });\n\t\t\tconst nextFile =\n\t\t\t\teditorManager.getFile(targetPath, \"uri\") || editorManager.activeFile;\n\t\t\tif (nextFile) {\n\t\t\t\tawait bind(nextFile, targetHash);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t$page.hide();\n\t\tawait openFile(targetPath, { render: true });\n\t}\n\n\tfunction scrollToHash(targetId) {\n\t\tconst target = getTargetElement(previewState.content, targetId);\n\t\tif (!target) return;\n\n\t\tconst topOffset = 12;\n\t\tconst top =\n\t\t\tgetOffsetTopWithinContainer(target, previewState.content) - topOffset;\n\n\t\tpreviewState.content.scrollTo({\n\t\t\ttop: Math.max(0, top),\n\t\t\tbehavior: \"smooth\",\n\t\t});\n\t}\n\n\tasync function enhanceCodeBlocks(version) {\n\t\tconst codeBlocks = Array.from(previewState.content.querySelectorAll(\"pre\"));\n\n\t\tawait Promise.all(\n\t\t\tcodeBlocks.map(async (pre) => {\n\t\t\t\tconst codeElement = pre.querySelector(\"code\");\n\t\t\t\tif (!codeElement || codeElement.closest(\".mermaid-error\")) return;\n\n\t\t\t\tconst language =\n\t\t\t\t\tcodeElement.dataset.language ||\n\t\t\t\t\tcodeElement.className.match(/language-(\\S+)/)?.[1];\n\t\t\t\tif (!language) return;\n\n\t\t\t\tconst originalCode = codeElement.textContent || \"\";\n\t\t\t\tcodeElement.classList.add(\"cm-highlighted\");\n\n\t\t\t\tconst highlighted = await highlightCodeBlock(originalCode, language);\n\t\t\t\tif (\n\t\t\t\t\tpreviewState.disposed ||\n\t\t\t\t\tversion !== previewState.renderVersion ||\n\t\t\t\t\t!codeElement.isConnected\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (highlighted && highlighted !== originalCode) {\n\t\t\t\t\tcodeElement.innerHTML = DOMPurify.sanitize(highlighted, {\n\t\t\t\t\t\tALLOWED_TAGS: [\"span\"],\n\t\t\t\t\t\tALLOWED_ATTR: [\"class\"],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tif (previewState.disposed || version !== previewState.renderVersion) return;\n\n\t\tcodeBlocks.forEach((pre) => {\n\t\t\tif (pre.querySelector(\".copy-button\")) return;\n\n\t\t\tpre.style.position = \"relative\";\n\n\t\t\tconst copyButton = document.createElement(\"button\");\n\t\t\tcopyButton.className = \"copy-button\";\n\t\t\tcopyButton.textContent = \"Copy\";\n\t\t\tcopyButton.addEventListener(\"click\", async (event) => {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tevent.stopPropagation();\n\n\t\t\t\ttry {\n\t\t\t\t\tconst code = pre.querySelector(\"code\")?.textContent || \"\";\n\t\t\t\t\tawait copyText(code);\n\t\t\t\t\tcopyButton.textContent = \"Copied!\";\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tif (copyButton.isConnected) copyButton.textContent = \"Copy\";\n\t\t\t\t\t}, 2000);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.warn(\"Failed to copy markdown code block\", error);\n\t\t\t\t\tcopyButton.textContent = \"Failed to copy\";\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tif (copyButton.isConnected) copyButton.textContent = \"Copy\";\n\t\t\t\t\t}, 2000);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tpre.append(copyButton);\n\t\t});\n\t}\n\n\tasync function renderMermaidBlocks(version) {\n\t\tconst mermaidBlocks = Array.from(\n\t\t\tpreviewState.content.querySelectorAll(\".mermaid\"),\n\t\t);\n\t\tif (!mermaidBlocks.length) return;\n\n\t\tconst mermaid = await getMermaid();\n\t\tif (previewState.disposed || version !== previewState.renderVersion) return;\n\t\tinitializeMermaid(mermaid);\n\t\tlet index = 0;\n\t\tawait Promise.all(\n\t\t\tmermaidBlocks.map(async (block) => {\n\t\t\t\tconst source = block.textContent || \"\";\n\t\t\t\tconst id = `acode-markdown-mermaid-${Date.now()}-${version}-${index++}`;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst { svg, bindFunctions } = await mermaid.render(id, source);\n\t\t\t\t\tif (\n\t\t\t\t\t\tpreviewState.disposed ||\n\t\t\t\t\t\tversion !== previewState.renderVersion ||\n\t\t\t\t\t\t!block.isConnected\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst sanitizedSvg = DOMPurify.sanitize(svg, {\n\t\t\t\t\t\tUSE_PROFILES: { svg: true, svgFilters: true },\n\t\t\t\t\t\tADD_TAGS: [\"style\"],\n\t\t\t\t\t\tADD_ATTR: [\"data-et\", \"data-id\", \"data-node\", \"data-zoom\", \"class\"],\n\t\t\t\t\t});\n\t\t\t\t\tblock.innerHTML = sanitizedSvg;\n\t\t\t\t\tbindFunctions?.(block);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!block.isConnected) return;\n\t\t\t\t\tblock.classList.add(\"mermaid-error\");\n\t\t\t\t\tblock.innerHTML = `\n\t\t\t\t\t\t<pre><code>${escapeHtml(source)}</code></pre>\n\t\t\t\t\t\t<div class=\"mermaid-error-message\">${escapeHtml(error?.message || \"Failed to render Mermaid diagram.\")}</div>\n\t\t\t\t\t`;\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t}\n\n\tasync function render() {\n\t\tconst version = ++previewState.renderVersion;\n\t\tpreviewState.page.settitle(previewState.file.filename);\n\t\trevokeObjectUrls(previewState.objectUrls);\n\t\tpreviewState.objectUrls = [];\n\n\t\tconst markdownText = previewState.file.session?.doc?.toString?.() || \"\";\n\t\tconst pendingRenderTasks = [\n\t\t\trenderMarkdown(markdownText, previewState.file),\n\t\t];\n\t\tif (hasMathContent(markdownText)) {\n\t\t\tpendingRenderTasks.push(ensureMathStyles());\n\t\t}\n\t\tconst [{ html }] = await Promise.all(pendingRenderTasks);\n\n\t\tif (previewState.disposed || version !== previewState.renderVersion) {\n\t\t\treturn;\n\t\t}\n\n\t\tpreviewState.content.innerHTML = DOMPurify.sanitize(html, {\n\t\t\tFORBID_TAGS: [\"style\"],\n\t\t\tADD_TAGS: [\"eq\", \"eqn\"],\n\t\t});\n\n\t\tconst objectUrls = await resolveRenderedImages(\n\t\t\tpreviewState.content,\n\t\t\tpreviewState.file,\n\t\t);\n\n\t\tif (previewState.disposed || version !== previewState.renderVersion) {\n\t\t\trevokeObjectUrls(objectUrls);\n\t\t\treturn;\n\t\t}\n\t\tpreviewState.objectUrls = objectUrls;\n\t\tawait enhanceCodeBlocks(version);\n\t\tawait renderMermaidBlocks(version);\n\n\t\tif (\n\t\t\tpreviewState.pendingHash &&\n\t\t\t!previewState.disposed &&\n\t\t\tversion === previewState.renderVersion\n\t\t) {\n\t\t\tscrollToHash(previewState.pendingHash);\n\t\t\tpreviewState.pendingHash = \"\";\n\t\t}\n\t}\n\n\tasync function bind(nextFile, hash = \"\") {\n\t\tpreviewState.file = nextFile;\n\t\tpreviewState.pendingHash = hash;\n\t\tif (!previewState.page.isConnected) {\n\t\t\tapp.append(previewState.page);\n\t\t}\n\t\tawait render();\n\t}\n\n\tfunction dispose() {\n\t\tif (previewState.disposed) return;\n\t\tpreviewState.disposed = true;\n\t\tpreviewState.content.removeEventListener(\"click\", onContentClick, true);\n\t\teditorManager.off(\"file-content-changed\", onFileChanged);\n\t\teditorManager.off(\"rename-file\", onFileRenamed);\n\t\teditorManager.off(\"remove-file\", onFileRemoved);\n\t\trevokeObjectUrls(previewState.objectUrls);\n\t\tif (previewController === controller) {\n\t\t\tpreviewController = null;\n\t\t}\n\t}\n\n\tconst controller = {\n\t\tbind,\n\t\trender,\n\t\tpage: $page,\n\t};\n\n\treturn controller;\n}\n\nexport default async function openMarkdownPreview(file, hash = \"\") {\n\tif (!file) return null;\n\n\tif (!previewController || previewController.page?.isConnected === false) {\n\t\tpreviewController = createMarkdownPreview(file);\n\t}\n\n\tawait previewController.bind(file, hash);\n\treturn previewController.page;\n}\n"
  },
  {
    "path": "src/pages/markdownPreview/renderer.js",
    "content": "import markdownIt from \"markdown-it\";\nimport anchor from \"markdown-it-anchor\";\nimport { full as markdownItEmoji } from \"markdown-it-emoji\";\nimport markdownItFootnote from \"markdown-it-footnote\";\nimport MarkdownItGitHubAlerts from \"markdown-it-github-alerts\";\nimport markdownItTaskLists from \"markdown-it-task-lists\";\nimport Url from \"utils/Url\";\n\nconst EXTERNAL_LINK_PATTERN = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i;\nconst IMAGE_PLACEHOLDER =\n\t\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\";\nconst BLOCK_MATH_PATTERN = /(^|[^\\\\])\\$\\$[\\s\\S]+?\\$\\$/m;\nconst INLINE_MATH_PATTERN =\n\t/(^|[^\\\\])\\$(?!\\s)(?:\\\\.|[^$\\\\\\n])*(?:\\\\[{^_(]|[{^_])(?:\\\\.|[^$\\\\\\n])*\\$(?!\\w)/m;\nconst BEGIN_END_MATH_PATTERN =\n\t/\\\\begin\\{(?:equation|align|gather|multline|eqnarray)\\*?\\}[\\s\\S]*?\\\\end\\{(?:equation|align|gather|multline|eqnarray)\\*?\\}/m;\n\nlet mathModulesPromise = null;\nlet mathMarkdownItPromise = null;\n\nfunction slugify(text) {\n\treturn text\n\t\t.trim()\n\t\t.toLowerCase()\n\t\t.normalize(\"NFD\")\n\t\t.replace(/[\\u0300-\\u036f]/g, \"\")\n\t\t.replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n}\n\nfunction escapeAttribute(value = \"\") {\n\treturn String(value)\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\");\n}\n\nfunction splitLinkTarget(target = \"\") {\n\tconst hashIndex = target.indexOf(\"#\");\n\tif (hashIndex === -1) {\n\t\treturn { path: target, hash: \"\" };\n\t}\n\n\treturn {\n\t\tpath: target.slice(0, hashIndex),\n\t\thash: target.slice(hashIndex),\n\t};\n}\n\nexport function isExternalLink(target = \"\") {\n\treturn EXTERNAL_LINK_PATTERN.test(String(target).trim());\n}\n\nexport function isMarkdownPath(target = \"\") {\n\treturn /\\.md(?:[#?].*)?$/i.test(String(target).trim());\n}\n\nexport function getMarkdownBaseUri(file) {\n\tif (!file) return \"\";\n\tif (file.uri) return file.uri;\n\tif (file.location && file.filename) {\n\t\treturn Url.join(file.location, file.filename);\n\t}\n\treturn file.location || \"\";\n}\n\nexport function resolveMarkdownTarget(target = \"\", baseUri = \"\") {\n\tif (!target || target.startsWith(\"#\") || isExternalLink(target)) {\n\t\treturn target;\n\t}\n\n\tconst { path, hash } = splitLinkTarget(target);\n\tif (!path) return target;\n\n\tlet resolvedPath = path;\n\tif (!path.startsWith(\"/\")) {\n\t\tconst baseDir = baseUri ? Url.dirname(baseUri) : \"\";\n\t\tif (baseDir) {\n\t\t\tresolvedPath = Url.join(baseDir, path);\n\t\t}\n\t}\n\n\treturn `${resolvedPath}${hash}`;\n}\n\nfunction resolveImageTarget(target = \"\", baseUri = \"\") {\n\tif (\n\t\t!target ||\n\t\ttarget.startsWith(\"#\") ||\n\t\ttarget.startsWith(\"data:\") ||\n\t\ttarget.startsWith(\"blob:\") ||\n\t\tisExternalLink(target)\n\t) {\n\t\treturn null;\n\t}\n\n\tconst { path } = splitLinkTarget(target);\n\tif (!path) return null;\n\n\tlet resolvedPath = path;\n\tif (!path.startsWith(\"/\")) {\n\t\tconst baseDir = baseUri ? Url.dirname(baseUri) : \"\";\n\t\tif (baseDir) {\n\t\t\tresolvedPath = Url.join(baseDir, path);\n\t\t}\n\t}\n\n\treturn resolvedPath;\n}\n\nfunction collectTokens(tokens, callback) {\n\tfor (const token of tokens) {\n\t\tcallback(token);\n\t\tif (Array.isArray(token.children) && token.children.length) {\n\t\t\tcollectTokens(token.children, callback);\n\t\t}\n\t}\n}\n\nexport function hasMathContent(text = \"\") {\n\treturn (\n\t\tBLOCK_MATH_PATTERN.test(text) ||\n\t\tINLINE_MATH_PATTERN.test(text) ||\n\t\tBEGIN_END_MATH_PATTERN.test(text)\n\t);\n}\n\nasync function getKatexAndTexmathModules() {\n\tif (!mathModulesPromise) {\n\t\tmathModulesPromise = Promise.all([\n\t\t\timport(\"katex\").then(({ default: katex }) => katex),\n\t\t\timport(\"markdown-it-texmath\").then(\n\t\t\t\t({ default: markdownItTexmath }) => markdownItTexmath,\n\t\t\t),\n\t\t]).then(([katex, markdownItTexmath]) => ({\n\t\t\tkatex,\n\t\t\tmarkdownItTexmath,\n\t\t}));\n\t\tmathModulesPromise = mathModulesPromise.catch((error) => {\n\t\t\tmathModulesPromise = null;\n\t\t\tthrow error;\n\t\t});\n\t}\n\n\treturn mathModulesPromise;\n}\n\nfunction createMarkdownIt({ katex = null, markdownItTexmath = null } = {}) {\n\tconst md = markdownIt({\n\t\thtml: true,\n\t\tlinkify: true,\n\t});\n\n\tmd.use(MarkdownItGitHubAlerts)\n\t\t.use(anchor, { slugify })\n\t\t.use(markdownItTaskLists)\n\t\t.use(markdownItFootnote);\n\n\tif (katex && markdownItTexmath) {\n\t\tmd.use(markdownItTexmath, {\n\t\t\tengine: katex,\n\t\t\tdelimiters: [\"dollars\", \"beg_end\"],\n\t\t\tkatexOptions: {\n\t\t\t\tthrowOnError: false,\n\t\t\t\tstrict: \"ignore\",\n\t\t\t},\n\t\t});\n\t}\n\n\tmd.use(markdownItEmoji);\n\n\tmd.renderer.rules.image = (tokens, idx, options, env, self) => {\n\t\tconst token = tokens[idx];\n\t\ttoken.attrSet(\"loading\", \"lazy\");\n\t\ttoken.attrSet(\"decoding\", \"async\");\n\n\t\tconst src = token.attrGet(\"src\");\n\t\tif (src && !src.startsWith(\"data:\") && !isExternalLink(src)) {\n\t\t\tconst resolvedPath = resolveImageTarget(src, env.markdownBaseUri);\n\t\t\tif (resolvedPath) {\n\t\t\t\ttoken.attrSet(\"data-markdown-local-src\", resolvedPath);\n\t\t\t\ttoken.attrSet(\"src\", IMAGE_PLACEHOLDER);\n\t\t\t}\n\t\t}\n\n\t\treturn self.renderToken(tokens, idx, options);\n\t};\n\n\tmd.renderer.rules.fence = (tokens, idx) => {\n\t\tconst token = tokens[idx];\n\t\tconst info = (token.info || \"\").trim();\n\t\tconst language = info.split(/\\s+/)[0].toLowerCase();\n\t\tconst escapedCode = md.utils.escapeHtml(token.content || \"\");\n\n\t\tif (language === \"mermaid\") {\n\t\t\treturn `<div class=\"mermaid\">${escapedCode}</div>`;\n\t\t}\n\n\t\tconst className = language\n\t\t\t? ` class=\"language-${escapeAttribute(language)}\"`\n\t\t\t: \"\";\n\t\tconst dataLanguage = ` data-language=\"${escapeAttribute(language)}\"`;\n\n\t\treturn `<pre><code${className}${dataLanguage}>${escapedCode}</code></pre>`;\n\t};\n\n\treturn md;\n}\n\nconst baseMarkdownIt = createMarkdownIt();\n\nasync function getMarkdownIt(text = \"\") {\n\tif (!hasMathContent(text)) {\n\t\treturn baseMarkdownIt;\n\t}\n\n\tif (!mathMarkdownItPromise) {\n\t\tmathMarkdownItPromise = getKatexAndTexmathModules()\n\t\t\t.then(({ katex, markdownItTexmath }) =>\n\t\t\t\tcreateMarkdownIt({ katex, markdownItTexmath }),\n\t\t\t)\n\t\t\t.catch((error) => {\n\t\t\t\tmathMarkdownItPromise = null;\n\t\t\t\tthrow error;\n\t\t\t});\n\t}\n\n\treturn mathMarkdownItPromise;\n}\n\nexport async function renderMarkdown(text, file) {\n\tconst markdownText = text || \"\";\n\tconst md = await getMarkdownIt(markdownText);\n\tconst env = {};\n\tenv.markdownBaseUri = getMarkdownBaseUri(file);\n\tconst tokens = md.parse(markdownText, env);\n\n\tcollectTokens(tokens, (token) => {\n\t\tif (token.type === \"link_open\") {\n\t\t\tconst href = token.attrGet(\"href\");\n\t\t\tif (!href || href.startsWith(\"#\") || isExternalLink(href)) return;\n\n\t\t\ttoken.attrSet(\n\t\t\t\t\"data-resolved-href\",\n\t\t\t\tresolveMarkdownTarget(href, env.markdownBaseUri),\n\t\t\t);\n\t\t}\n\t});\n\n\treturn {\n\t\thtml: md.renderer.render(tokens, md.options, env),\n\t};\n}\n"
  },
  {
    "path": "src/pages/markdownPreview/style.scss",
    "content": ".markdown-preview {\n  overflow: auto;\n  padding: 16px;\n  max-width: 960px;\n  margin: 0 auto;\n  user-select: text;\n\n  [id] {\n    scroll-margin-top: 12px;\n  }\n\n  pre {\n    margin: 16px 0;\n    border-radius: 8px;\n    overflow-x: auto;\n    max-width: 100%;\n  }\n\n  pre code {\n    display: block;\n    padding: 16px;\n    margin: 0;\n    background: transparent !important;\n    white-space: pre;\n    word-wrap: normal;\n    tab-size: 2;\n    user-select: text;\n    -webkit-overflow-scrolling: touch;\n  }\n\n  code[class*=\"language-\"] {\n    font-size: 13px;\n    line-height: 1.5;\n  }\n\n  eq {\n    display: inline-block;\n    max-width: 100%;\n  }\n\n  eqn {\n    display: block;\n    width: 100%;\n    overflow-x: auto;\n    overflow-y: hidden;\n    padding: 8px 0;\n  }\n\n  section.eqno {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    overflow-x: auto;\n  }\n\n  section.eqno > eqn {\n    margin-left: 0;\n  }\n\n  section.eqno > span {\n    flex: 0 0 auto;\n    opacity: 0.7;\n  }\n\n  .katex-display {\n    margin: 1rem 0;\n    overflow-x: auto;\n    overflow-y: hidden;\n    padding-bottom: 4px;\n  }\n\n  .markdown-image {\n    display: block;\n    margin: 1rem auto;\n    max-width: 100%;\n    height: auto;\n    border-radius: 12px;\n    background: color-mix(in srgb, var(--primary-color) 8%, transparent);\n    box-shadow: 0 0 0 1px\n      color-mix(in srgb, var(--primary-color) 12%, transparent);\n  }\n\n  .mermaid {\n    display: flex;\n    justify-content: center;\n    overflow-x: auto;\n    padding: 12px 0;\n  }\n\n  .mermaid svg {\n    max-width: 100%;\n    height: auto;\n  }\n\n  .mermaid svg text,\n  .mermaid svg tspan,\n  .mermaid .label,\n  .mermaid .nodeLabel,\n  .mermaid .edgeLabel,\n  .mermaid .cluster-label,\n  .mermaid .flowchart-label {\n    fill: var(--primary-text-color) !important;\n    color: var(--primary-text-color) !important;\n  }\n\n  .mermaid svg foreignObject,\n  .mermaid svg foreignObject * {\n    color: var(--primary-text-color) !important;\n  }\n\n  .mermaid-error {\n    border: 1px solid var(--danger-color);\n    border-radius: 8px;\n    padding: 12px;\n    background: color-mix(in srgb, var(--danger-color) 10%, transparent);\n  }\n\n  .mermaid-error-message {\n    margin-top: 8px;\n    color: var(--danger-text-color);\n  }\n}\n"
  },
  {
    "path": "src/pages/plugin/index.js",
    "content": "function plugin({ id, installed, install }, onInstall, onUninstall) {\n\timport(/* webpackChunkName: \"plugins\" */ \"./plugin\").then((res) => {\n\t\tconst Plugin = res.default;\n\t\tPlugin(id, installed, onInstall, onUninstall, install);\n\t});\n}\n\nexport default plugin;\n"
  },
  {
    "path": "src/pages/plugin/plugin.js",
    "content": "import \"./plugin.scss\";\nimport fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport Page from \"components/page\";\nimport alert from \"dialogs/alert\";\nimport loader from \"dialogs/loader\";\nimport purchaseListener from \"handlers/purchase\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport installPlugin from \"lib/installPlugin\";\nimport InstallState from \"lib/installState\";\nimport settings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport markdownIt from \"markdown-it\";\nimport anchor from \"markdown-it-anchor\";\nimport markdownItFootnote from \"markdown-it-footnote\";\nimport MarkdownItGitHubAlerts from \"markdown-it-github-alerts\";\nimport markdownItTaskLists from \"markdown-it-task-lists\";\nimport { highlightCodeBlock, initHighlighting } from \"utils/codeHighlight\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\nimport view from \"./plugin.view.js\";\n\nlet $lastPluginPage;\n\n/**\n * Plugin page\n * @param {string} id\n * @param {boolean} installed\n * @param {() => void} [onInstall]\n * @param {() => void} [onUninstall]\n * @param {boolean} [installOnRender]\n */\nexport default async function PluginInclude(\n\tid,\n\tinstalled,\n\tonInstall,\n\tonUninstall,\n\tinstallOnRender,\n) {\n\tif ($lastPluginPage) {\n\t\t$lastPluginPage.hide();\n\t}\n\n\tinstalled = typeof installed !== \"boolean\" ? installed === \"true\" : installed;\n\tconst $page = Page(strings[\"plugin\"]);\n\tlet plugin = {};\n\tlet currentVersion = \"\";\n\tlet purchased = false;\n\tlet cancelled = false;\n\tlet update = false;\n\tlet isPaid = false;\n\tlet price;\n\tlet product;\n\tlet purchaseToken;\n\tlet $settingsIcon;\n\tlet minVersionCode = -1;\n\n\tactionStack.push({\n\t\tid: \"plugin\",\n\t\taction: $page.hide,\n\t});\n\n\t$page.onhide = function () {\n\t\thideAd();\n\t\tactionStack.remove(\"plugin\");\n\t\tloader.removeTitleLoader();\n\t\tcancelled = true;\n\t\t$lastPluginPage = null;\n\t};\n\n\t$lastPluginPage = $page;\n\tapp.append($page);\n\thelpers.showAd();\n\n\ttry {\n\t\tif (installed) {\n\t\t\tconst manifest = Url.join(PLUGIN_DIR, id, \"plugin.json\");\n\t\t\tconst installedPlugin = await fsOperation(manifest)\n\t\t\t\t.readFile(\"json\")\n\t\t\t\t.catch((err) => {\n\t\t\t\t\talert(`Failed to load plugin metadata: ${manifest}`);\n\t\t\t\t\tconsole.error(err);\n\t\t\t\t});\n\t\t\tconst { author } = installedPlugin;\n\t\t\tconst readme = Url.join(\n\t\t\t\tPLUGIN_DIR,\n\t\t\t\tid,\n\t\t\t\tinstalledPlugin.readme || \"readme.md\",\n\t\t\t);\n\t\t\tconst description = await fsOperation(readme)\n\t\t\t\t.readFile(\"utf8\")\n\t\t\t\t.catch((err) => {\n\t\t\t\t\talert(`Failed to load plugin readme: ${readme}`);\n\t\t\t\t\tconsole.error(err);\n\t\t\t\t});\n\t\t\tlet changelogs = \"\";\n\t\t\tif (installedPlugin.changelogs) {\n\t\t\t\tconst changelogPath = Url.join(\n\t\t\t\t\tPLUGIN_DIR,\n\t\t\t\t\tid,\n\t\t\t\t\tinstalledPlugin.changelogs,\n\t\t\t\t);\n\t\t\t\tconst changelogExists = await fsOperation(changelogPath).exists();\n\t\t\t\tif (changelogExists) {\n\t\t\t\t\tchangelogs = await fsOperation(changelogPath).readFile(\"utf8\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst iconUrl = await helpers.toInternalUri(\n\t\t\t\tUrl.join(PLUGIN_DIR, id, installedPlugin.icon),\n\t\t\t);\n\t\t\tconst iconData = await fsOperation(iconUrl).readFile();\n\t\t\tconst icon = URL.createObjectURL(\n\t\t\t\tnew Blob([iconData], { type: \"image/png\" }),\n\t\t\t);\n\t\t\tplugin = {\n\t\t\t\tid,\n\t\t\t\ticon,\n\t\t\t\tname: installedPlugin.name,\n\t\t\t\tversion: installedPlugin.version,\n\t\t\t\tauthor: author.name,\n\t\t\t\tauthor_github: author.github,\n\t\t\t\tsource: installedPlugin.source,\n\t\t\t\tlicense: installedPlugin.license,\n\t\t\t\tkeywords: installedPlugin.keywords,\n\t\t\t\tcontributors: installedPlugin.contributors,\n\t\t\t\trepository: installedPlugin.repository,\n\t\t\t\tdescription,\n\t\t\t\tchangelogs,\n\t\t\t};\n\n\t\t\tisPaid = installedPlugin.price > 0;\n\t\t\t$page.settitle(plugin.name);\n\t\t\trender();\n\t\t}\n\n\t\tawait (async () => {\n\t\t\ttry {\n\t\t\t\tloader.showTitleLoader();\n\t\t\t\tif ((await helpers.checkAPIStatus()) && isValidSource(plugin.source)) {\n\t\t\t\t\tconst remotePlugin = await fsOperation(\n\t\t\t\t\t\tconstants.API_BASE,\n\t\t\t\t\t\t`plugin/${id}`,\n\t\t\t\t\t)\n\t\t\t\t\t\t.readFile(\"json\")\n\t\t\t\t\t\t.catch(() => null);\n\n\t\t\t\t\tif (cancelled || !remotePlugin) return;\n\n\t\t\t\t\tif (installed && remotePlugin?.version !== plugin.version) {\n\t\t\t\t\t\tcurrentVersion = plugin.version;\n\t\t\t\t\t\tupdate = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (remotePlugin.min_version_code) {\n\t\t\t\t\t\tminVersionCode = remotePlugin.min_version_code;\n\t\t\t\t\t}\n\n\t\t\t\t\tplugin = Object.assign({}, remotePlugin);\n\n\t\t\t\t\tif (!Number.parseFloat(remotePlugin.price)) return;\n\n\t\t\t\t\tisPaid = remotePlugin.price > 0;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t[product] = await helpers.promisify(iap.getProducts, [\n\t\t\t\t\t\t\tremotePlugin.sku,\n\t\t\t\t\t\t]);\n\t\t\t\t\t\tif (product) {\n\t\t\t\t\t\t\tconst purchase = await getPurchase(product.productId);\n\t\t\t\t\t\t\tpurchased = !!purchase;\n\t\t\t\t\t\t\tprice = product.price;\n\t\t\t\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\thelpers.error(error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(error);\n\t\t\t} finally {\n\t\t\t\tloader.removeTitleLoader();\n\t\t\t}\n\t\t})();\n\n\t\t$page.settitle(plugin.name);\n\t\trender();\n\n\t\tif (installOnRender && !installed) {\n\t\t\tconst $button = $page.get('[data-type=\"install\"], [data-type=\"buy\"]');\n\t\t\t$button?.click();\n\t\t}\n\t} catch (err) {\n\t\tconsole.error(err);\n\t\thelpers.error(err);\n\t} finally {\n\t\tloader.removeTitleLoader();\n\t}\n\n\tasync function install() {\n\t\ttry {\n\t\t\tawait Promise.all([\n\t\t\t\tloadAd(this),\n\t\t\t\tinstallPlugin(plugin.source || id, plugin.name, purchaseToken),\n\t\t\t]);\n\t\t\tif (onInstall) onInstall(plugin);\n\t\t\tinstalled = true;\n\t\t\tupdate = false;\n\t\t\tif (!plugin.price) {\n\t\t\t\tawait helpers.showInterstitialIfReady();\n\t\t\t}\n\t\t\trender();\n\t\t} catch (err) {\n\t\t\twindow.log(\"error\", err);\n\t\t\thelpers.error(err);\n\t\t}\n\t}\n\n\tasync function uninstall() {\n\t\ttry {\n\t\t\tconst pluginDir = Url.join(PLUGIN_DIR, plugin.id);\n\t\t\tconst state = await InstallState.new(plugin.id);\n\t\t\tawait Promise.all([\n\t\t\t\tloadAd(this),\n\t\t\t\tfsOperation(pluginDir).delete(),\n\t\t\t\tstate.delete(state.storeUrl),\n\t\t\t]);\n\t\t\tacode.unmountPlugin(plugin.id);\n\t\t\tif (onUninstall) onUninstall(plugin.id);\n\t\t\tinstalled = false;\n\t\t\tupdate = false;\n\t\t\tif (!plugin.price) {\n\t\t\t\tawait helpers.showInterstitialIfReady();\n\t\t\t}\n\t\t\trender();\n\t\t} catch (err) {\n\t\t\twindow.log(\"error\", err);\n\t\t\thelpers.error(err);\n\t\t}\n\t}\n\n\tasync function buy(e) {\n\t\tconst $button = e.target;\n\t\tconst oldText = $button.textContent;\n\n\t\ttry {\n\t\t\tif (!product) throw new Error(\"Product not found\");\n\t\t\tconst apiStatus = await helpers.checkAPIStatus();\n\n\t\t\tif (!apiStatus) {\n\t\t\t\talert(strings.error, strings.api_error);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tiap.setPurchaseUpdatedListener(...purchaseListener(onpurchase, onerror));\n\t\t\t$button.textContent = strings[\"loading...\"];\n\t\t\tawait helpers.promisify(iap.purchase, product.productId);\n\n\t\t\tasync function onpurchase(e) {\n\t\t\t\tconst purchase = await getPurchase(product.productId);\n\t\t\t\tawait ajax.post(Url.join(constants.API_BASE, \"plugin/order\"), {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tid: plugin.id,\n\t\t\t\t\t\ttoken: purchase?.purchaseToken,\n\t\t\t\t\t\tpackage: BuildInfo.packageName,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t\t\tpurchased = !!purchase;\n\t\t\t\t$button.textContent = oldText;\n\t\t\t\tinstall();\n\t\t\t}\n\n\t\t\tasync function onerror(error) {\n\t\t\t\thelpers.error(error);\n\t\t\t\t$button.textContent = oldText;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", \"Failed to buy:\");\n\t\t\twindow.log(\"error\", error);\n\t\t\thelpers.error(error);\n\t\t\t$button.textContent = oldText;\n\t\t}\n\t}\n\n\tasync function refund(e) {\n\t\tconst $button = e.target;\n\t\tconst oldText = $button.textContent;\n\t\ttry {\n\t\t\tif (!product) throw new Error(\"Product not found\");\n\t\t\t$button.textContent = strings[\"loading...\"];\n\t\t\tconst { refer, refunded, error } = await ajax.post(\n\t\t\t\tUrl.join(constants.API_BASE, \"plugin/refund\"),\n\t\t\t\t{\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tid: plugin.id,\n\t\t\t\t\t\tpackage: BuildInfo.packageName,\n\t\t\t\t\t\ttoken: purchaseToken,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\tif (refer) {\n\t\t\t\tsystem.openInBrowser(refer);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (refunded) {\n\t\t\t\ttoast(strings.success);\n\t\t\t\tif (installed) uninstall();\n\t\t\t\telse render();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttoast(error || strings.error);\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", error);\n\t\t\thelpers.error(error);\n\t\t} finally {\n\t\t\t$button.textContent = oldText;\n\t\t}\n\t}\n\n\tasync function render() {\n\t\tconst pluginSettings = settings.uiSettings[`plugin-${plugin.id}`];\n\t\t$page.body = view({\n\t\t\t...plugin,\n\t\t\tbody: markdownIt({\n\t\t\t\thtml: true,\n\t\t\t\txhtmlOut: true,\n\t\t\t})\n\t\t\t\t.use(MarkdownItGitHubAlerts)\n\t\t\t\t.use(anchor, {\n\t\t\t\t\tslugify: (s) =>\n\t\t\t\t\t\ts\n\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t.replace(/[^a-z0-9]+/g, \"-\"),\n\t\t\t\t})\n\t\t\t\t.use(markdownItTaskLists)\n\t\t\t\t.use(markdownItFootnote)\n\t\t\t\t.render(plugin.description),\n\t\t\tchangelogs: plugin.changelogs\n\t\t\t\t? markdownIt({ html: true, xhtmlOut: true })\n\t\t\t\t\t\t.use(MarkdownItGitHubAlerts)\n\t\t\t\t\t\t.use(anchor, {\n\t\t\t\t\t\t\tslugify: (s) =>\n\t\t\t\t\t\t\t\ts\n\t\t\t\t\t\t\t\t\t.trim()\n\t\t\t\t\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t\t\t\t\t.replace(/[^a-z0-9]+/g, \"-\"),\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.use(markdownItTaskLists)\n\t\t\t\t\t\t.use(markdownItFootnote)\n\t\t\t\t\t\t.render(plugin.changelogs)\n\t\t\t\t: null,\n\t\t\tpurchased,\n\t\t\tinstalled,\n\t\t\tupdate,\n\t\t\tisPaid,\n\t\t\tprice,\n\t\t\tbuy,\n\t\t\trefund,\n\t\t\tinstall,\n\t\t\tuninstall,\n\t\t\tcurrentVersion,\n\t\t\tminVersionCode,\n\t\t});\n\n\t\t// Handle anchor links\n\t\t$page.body.querySelectorAll(\"a[href^='#']\").forEach((link) => {\n\t\t\tconst originalHref = link.getAttribute(\"href\");\n\t\t\tlink.setAttribute(\"data-href\", originalHref);\n\t\t\tlink.style.cursor = \"pointer\";\n\t\t\t// Remove default click behavior\n\t\t\tlink.removeAttribute(\"href\");\n\n\t\t\t// Add custom click handler\n\t\t\tlink.addEventListener(\n\t\t\t\t\"click\",\n\t\t\t\t(e) => {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\te.stopPropagation();\n\n\t\t\t\t\tconst hash = link.getAttribute(\"data-href\") || link.textContent;\n\t\t\t\t\tconst targetId = hash.startsWith(\"#\") ? hash.slice(1) : hash;\n\n\t\t\t\t\t// Look for either the anchor link or a heading with matching id\n\t\t\t\t\tconst targetElement =\n\t\t\t\t\t\t$page.body.querySelector(`[name=\"${targetId}\"]`) ||\n\t\t\t\t\t\t$page.body.querySelector(`#${targetId}`);\n\n\t\t\t\t\tif (targetElement) {\n\t\t\t\t\t\tconst headerOffset =\n\t\t\t\t\t\t\tdocument.querySelector(\"header\")?.offsetHeight || 0;\n\t\t\t\t\t\tconst elementPosition = targetElement.getBoundingClientRect().top;\n\t\t\t\t\t\tconst offsetPosition = elementPosition - headerOffset;\n\n\t\t\t\t\t\t$page.body.scrollBy({\n\t\t\t\t\t\t\ttop: offsetPosition,\n\t\t\t\t\t\t\tbehavior: \"smooth\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t\t{ capture: true },\n\t\t\t);\n\t\t});\n\n\t\t// Initialize theme-aware highlight styles\n\t\tinitHighlighting();\n\n\t\t// Add copy button and syntax highlighting to code blocks\n\t\tconst codeBlocks = $page.body.querySelectorAll(\"pre\");\n\t\tcodeBlocks.forEach((pre) => {\n\t\t\tpre.style.position = \"relative\";\n\t\t\tconst copyButton = document.createElement(\"button\");\n\t\t\tcopyButton.className = \"copy-button\";\n\t\t\tcopyButton.textContent = \"Copy\";\n\n\t\t\tconst codeElement = pre.querySelector(\"code\");\n\t\t\tif (codeElement) {\n\t\t\t\tconst langMatch = codeElement.className.match(/language-(\\w+)/);\n\t\t\t\tif (langMatch) {\n\t\t\t\t\tconst lang = langMatch[1];\n\t\t\t\t\tconst originalCode = codeElement.textContent || \"\";\n\t\t\t\t\tcodeElement.classList.add(\"cm-highlighted\");\n\n\t\t\t\t\thighlightCodeBlock(originalCode, lang).then((highlighted) => {\n\t\t\t\t\t\tif (highlighted && highlighted !== originalCode) {\n\t\t\t\t\t\t\tcodeElement.innerHTML = highlighted;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcopyButton.addEventListener(\"click\", async () => {\n\t\t\t\tconst code = pre.querySelector(\"code\")?.textContent || pre.textContent;\n\t\t\t\ttry {\n\t\t\t\t\tcordova.plugins.clipboard.copy(code);\n\t\t\t\t\tcopyButton.textContent = \"Copied!\";\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tcopyButton.textContent = \"Copy\";\n\t\t\t\t\t}, 2000);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcopyButton.textContent = \"Failed to copy\";\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tcopyButton.textContent = \"Copy\";\n\t\t\t\t\t}, 2000);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tpre.appendChild(copyButton);\n\t\t});\n\n\t\tif ($settingsIcon) {\n\t\t\t$settingsIcon.remove();\n\t\t\t$settingsIcon = null;\n\t\t}\n\n\t\tif (pluginSettings) {\n\t\t\tpluginSettings.setTitle(plugin.name);\n\t\t\t$settingsIcon = (\n\t\t\t\t<span\n\t\t\t\t\tattr-action=\"settings\"\n\t\t\t\t\tclassName=\"icon settings\"\n\t\t\t\t\tonclick={() => pluginSettings.show()}\n\t\t\t\t></span>\n\t\t\t);\n\t\t\tif (!$page.header.contains($settingsIcon)) {\n\t\t\t\t$page.header.append($settingsIcon);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function loadAd(el) {\n\t\tif (!helpers.canShowAds()) return;\n\t\ttry {\n\t\t\tif (!(await window.iad?.isLoaded())) {\n\t\t\t\tconst oldText = el.textContent;\n\t\t\t\tel.textContent = strings[\"loading...\"];\n\t\t\t\tawait window.iad.load();\n\t\t\t\tel.textContent = oldText;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Failed to load plugin page ad.\", error);\n\t\t}\n\t}\n\n\tasync function getPurchase(sku) {\n\t\tconst purchases = await helpers.promisify(iap.getPurchases);\n\t\tconst purchase = purchases.find((p) => p.productIds.includes(sku));\n\t\treturn purchase;\n\t}\n}\n\nfunction isValidSource(source) {\n\treturn source\n\t\t? source.startsWith(Url.join(constants.API_BASE, \"plugin\"))\n\t\t: true;\n}\n"
  },
  {
    "path": "src/pages/plugin/plugin.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n#plugin {\n  overflow: auto;\n  max-width: 800px;\n  padding: 20px;\n  margin: 0 auto;\n\n  .plugin-header {\n    display: grid;\n    grid-template-columns: auto 1fr auto;\n    gap: 20px;\n    align-items: start;\n    margin-bottom: 24px;\n\n    .plugin-icon {\n      width: 80px;\n      height: 80px;\n      border-radius: 16px;\n      background-position: center;\n      background-repeat: no-repeat;\n      background-size: contain;\n    }\n\n    .plugin-info {\n      .title-wrapper {\n        display: flex;\n        align-items: center;\n        flex-wrap: wrap;\n        gap: 16px;\n        margin-bottom: 8px;\n\n        .plugin-name {\n          font-size: 24px;\n          margin-bottom: 0;\n        }\n\n        .source-indicator {\n          display: flex;\n          align-items: center;\n          gap: 6px;\n          background: rgba(0, 0, 0, 0.3);\n          backdrop-filter: blur(10px);\n          border-radius: 20px;\n          padding: 6px 10px;\n          font-size: 11px;\n          color: var(--primary-text-color);\n          cursor: pointer;\n          transition: all 0.3s ease;\n          border: 1px solid rgba(255, 255, 255, 0.1);\n          text-decoration: none;\n\n          &:hover {\n            background-color: rgba(0, 0, 0, 0.5);\n            transform: translateY(-1px);\n          }\n\n          .icon {\n            color: var(--primary-text-color);\n            font-size: 14px;\n          }\n        }\n      }\n\n      .plugin-meta {\n        display: flex;\n        gap: 16px;\n        flex-wrap: wrap;\n        color: var(--secondary-text-color);\n        color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n        font-size: 14px;\n        margin-bottom: 12px;\n\n        .meta-item {\n          display: inline-flex;\n          align-items: center;\n          gap: 4px;\n          vertical-align: middle;\n\n          .icon {\n            flex-shrink: 0;\n          }\n        }\n\n        .author-name {\n          a {\n            text-decoration: none;\n            color: inherit;\n          }\n        }\n\n        .verified-tick {\n          color: #3b82f6;\n          font-size: 16px;\n        }\n\n        .version-updated {\n          opacity: 0.6;\n          font-size: 0.8em;\n          margin-left: 4px;\n        }\n      }\n\n      .metrics-row {\n        display: flex;\n        flex-wrap: wrap;\n        gap: 16px;\n        color: var(--secondary-text-color);\n        color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n        font-size: 14px;\n\n        .metric {\n          display: flex;\n          align-items: center;\n          gap: 4px;\n\n          .metric-value {\n            color: var(--primary-text-color);\n            font-weight: 500;\n          }\n\n          .rating-value {\n            padding: 2px 8px;\n            border-radius: 12px;\n            font-weight: 600;\n          }\n\n          .rating-high {\n            background: var(--link-text-color);\n            color: #0a3600;\n          }\n\n          .rating-medium {\n            background: #f0a500;\n            color: #3d2800;\n          }\n\n          .rating-low {\n            background: var(--error-text-color);\n            color: #fff;\n          }\n        }\n      }\n\n      .keywords {\n        display: flex;\n        gap: 6px;\n        flex-wrap: wrap;\n        margin-top: 16px;\n        position: relative;\n\n        .keyword {\n          background: var(--popup-background-color);\n          background: color-mix(in srgb,\n              var(--link-text-color) 10%,\n              transparent);\n          color: var(--link-text-color);\n          padding: 6px 10px;\n          border-radius: 12px;\n          font-size: 13px;\n          transition: all 0.2s;\n          border: 1px solid var(--link-text-color);\n          border: 1px solid color-mix(in srgb, var(--link-text-color) 25%, transparent);\n        }\n      }\n    }\n\n    .action-buttons {\n      display: flex;\n      gap: 8px;\n\n      .error {\n        display: flex;\n        color: rgb(255, 185, 92);\n        color: var(--error-text-color);\n        align-items: center;\n\n        a {\n          color: inherit;\n          text-decoration: none;\n        }\n      }\n\n      .btn {\n        padding: 8px 16px;\n        border-radius: 6px;\n        border: none;\n        font-size: 14px;\n        font-weight: 500;\n        cursor: pointer;\n        transition: all 0.2s;\n        display: inline-flex;\n        align-items: center;\n        gap: 6px;\n\n        &:hover {\n          transform: translateY(-1px);\n        }\n      }\n\n      .btn-install {\n        background: var(--button-background-color);\n        color: white;\n      }\n\n      .btn-update {\n        background: var(--button-background-color);\n        color: white;\n      }\n\n      .btn-uninstall {\n        background-color: var(--danger-color) !important;\n        color: white;\n      }\n\n      .btn-secondary {\n        background: var(--primary-color);\n        color: var(--primary-text-color);\n        box-shadow: 0 0 10px var(--box-shadow-color);\n      }\n    }\n\n    .more-info-small {\n      text-align: center;\n      font-style: italic;\n      font-size: 0.8rem;\n      opacity: 0.8;\n    }\n  }\n\n  #plugin-tab {\n    .options {\n      display: flex;\n      gap: 0;\n      margin: 20px -20px;\n      padding: 0 20px;\n      border-bottom: 1px solid var(--border-color);\n\n      .tab {\n        color: var(--secondary-text-color);\n        color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n      }\n\n      .tab.active {\n        color: var(--primary-text-color);\n      }\n    }\n\n    .tab-content {\n      padding: 0 0 24px;\n\n      #overview {\n        pre {\n          margin: 16px 0;\n          border-radius: 8px;\n          overflow: hidden;\n        }\n\n        code[class*=\"language-\"] {\n          display: block;\n          font-size: 13px;\n          line-height: 1.5;\n          padding: 16px;\n          margin: 0;\n          overflow-x: auto;\n          white-space: pre;\n          word-wrap: normal;\n          tab-size: 2;\n          user-select: text;\n          -webkit-overflow-scrolling: touch;\n        }\n      }\n    }\n\n    .content-section {\n      display: none;\n    }\n\n    .content-section.active {\n      display: block;\n    }\n\n    .content-section.md {\n      overflow-x: hidden;\n      overflow-wrap: break-word;\n\n      img {\n        max-width: 100%;\n        height: auto;\n      }\n\n      pre {\n        overflow-x: auto;\n        max-width: 100%;\n      }\n\n      * {\n        max-width: 100%;\n        box-sizing: border-box;\n      }\n    }\n\n    .contributor {\n      display: flex;\n      align-items: center;\n      gap: 12px;\n      padding: 12px 0;\n      border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n      text-decoration: none;\n\n      &:last-child {\n        border-bottom: none;\n      }\n\n      img {\n        width: 32px;\n        height: 32px;\n        border-radius: 50%;\n      }\n\n      .contributor-info {\n        flex-grow: 1;\n\n        .contributor-name {\n          font-weight: 500;\n          margin-bottom: 2px;\n          color: var(--primary-text-color);\n        }\n\n        .contributor-role {\n          font-size: 13px;\n          color: var(--secondary-text-color);\n          color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n        }\n      }\n    }\n\n    #changelog {\n      .no-changelog {\n        text-align: center;\n        padding: 2rem;\n        color: var(--secondary-text-color);\n        color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n\n        i {\n          font-size: 3rem;\n          margin-bottom: 1rem;\n          opacity: 0.7;\n          display: block;\n        }\n\n        p {\n          margin: 0.5rem 0;\n        }\n      }\n    }\n  }\n\n  @media (max-width: 768px) {\n    .plugin-header {\n      grid-template-columns: 1fr;\n      justify-items: center;\n      text-align: center;\n    }\n\n    .source-indicator {\n      position: absolute;\n      top: 20px;\n      right: 20px;\n    }\n\n    .title-wrapper,\n    .plugin-meta,\n    .metrics-row,\n    .keywords {\n      justify-content: center;\n    }\n\n    .action-buttons {\n      flex-direction: column;\n      width: 100%;\n    }\n\n    .btn {\n      width: 100%;\n      justify-content: center;\n    }\n\n    .tabs {\n      overflow-x: auto;\n      margin: 24px -24px;\n      padding: 0 24px;\n    }\n  }\n}\n\n.reviews-container {\n  position: fixed;\n  display: flex;\n  flex-direction: column;\n  width: 100vw;\n  height: 70vh;\n  top: auto;\n  bottom: 0;\n  background-color: rgb(255, 255, 255);\n  background-color: var(--secondary-color);\n  z-index: 999;\n  box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);\n  box-shadow: 0 0 20px var(--box-shadow-color);\n  border-radius: 40px 40px 0 0;\n  overflow: hidden;\n  animation: slide-up 0.3s ease-in-out;\n\n  &.hide {\n    transition: all 0.3s ease-in-out !important;\n    transform: translateY(100%) !important;\n  }\n\n  .reviews-header {\n    height: 40px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n\n    &::after {\n      content: \"\";\n      width: 40px;\n      height: 5px;\n      border-radius: 2.5px;\n      background-color: rgba(0, 0, 0, 0.2);\n    }\n  }\n\n  .write-review {\n    padding: 0 10px;\n    display: flex;\n    align-content: center;\n    justify-content: center;\n    width: fit-content;\n    margin: auto;\n\n    span {\n      height: fit-content;\n      margin: auto;\n    }\n\n    .icon {\n      height: 30px;\n      margin-right: 10px;\n    }\n  }\n\n  .reviews-body {\n    flex: 1;\n    overflow: auto;\n\n    &.loading {\n      @include mixins.loader(30px);\n    }\n\n    .review {\n      padding: 10px;\n      display: flex;\n      flex-direction: column;\n      border-bottom: solid 1px rgba(255, 255, 255, 0.1);\n\n      .review-author {\n        display: flex;\n        height: 30px;\n        align-items: center;\n        justify-content: flex-start;\n        margin-bottom: 5px;\n\n        .vote {\n          height: 40px;\n          width: 40px;\n          background-position: center;\n          background-repeat: no-repeat;\n          background-size: 20px;\n        }\n\n        .user-profile {\n          height: 30px;\n          width: 30px;\n          border-radius: 50%;\n          background-position: center;\n          background-repeat: no-repeat;\n          background-size: cover;\n          margin-right: 10px;\n          font-weight: 400;\n        }\n      }\n\n      .review-body {\n        display: flex;\n        align-items: center;\n        font-weight: 300;\n        overflow: auto;\n      }\n\n      .author-reply {\n        &::before {\n          display: block;\n          content: attr(data-author);\n          font-weight: 400;\n          margin-bottom: 10px;\n        }\n\n        padding: 10px;\n        background-color: rgba(0, 0, 0, 0.1);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/pages/plugin/plugin.view.js",
    "content": "import fsOperation from \"fileSystem\";\nimport TabView from \"components/tabView\";\nimport toast from \"components/toast\";\nimport dayjs from \"dayjs/esm\";\nimport dayjsRelativeTime from \"dayjs/esm/plugin/relativeTime\";\nimport dayjsUpdateLocale from \"dayjs/esm/plugin/updateLocale\";\nimport dayjsUtc from \"dayjs/esm/plugin/utc\";\nimport alert from \"dialogs/alert\";\nimport DOMPurify from \"dompurify\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\ndayjs.extend(dayjsRelativeTime);\ndayjs.extend(dayjsUtc);\ndayjs.extend(dayjsUpdateLocale);\n\n// Configure dayjs for shorter relative time format\ndayjs.updateLocale(\"en\", {\n\trelativeTime: {\n\t\tfuture: \"in %s\",\n\t\tpast: (value, withoutSuffix) => {\n\t\t\tif (value === \"now\") {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t\treturn withoutSuffix ? value : `${value} ago`;\n\t\t},\n\t\ts: \"now\",\n\t\tss: \"now\",\n\t\tm: \"1m\",\n\t\tmm: \"%dm\",\n\t\th: \"1h\",\n\t\thh: \"%dh\",\n\t\td: \"1d\",\n\t\tdd: \"%dd\",\n\t\tM: \"1mo\",\n\t\tMM: \"%dmo\",\n\t\ty: \"1y\",\n\t\tyy: \"%dy\",\n\t},\n});\n\nexport default (props) => {\n\tconst {\n\t\tid,\n\t\tname,\n\t\tbody,\n\t\ticon,\n\t\tauthor,\n\t\tdownloads,\n\t\tlicense,\n\t\tchangelogs,\n\t\trepository,\n\t\tkeywords: keywordsRaw,\n\t\tcontributors: contributorsRaw,\n\t\tvotes_up: votesUp,\n\t\tvotes_down: votesDown,\n\t\tauthor_verified: authorVerified,\n\t\tauthor_github: authorGithub,\n\t\tcomment_count: commentCount,\n\t\tpackage_updated_at: packageUpdatedAt,\n\t} = props;\n\n\tlet rating = \"unrated\";\n\n\tconst keywords =\n\t\ttypeof keywordsRaw === \"string\" ? JSON.parse(keywordsRaw) : keywordsRaw;\n\tconst contributors =\n\t\ttypeof contributorsRaw === \"string\"\n\t\t\t? JSON.parse(contributorsRaw)\n\t\t\t: contributorsRaw;\n\n\tif (votesUp || votesDown) {\n\t\trating = `${Math.round((votesUp / (votesUp + votesDown)) * 100)}%`;\n\t}\n\n\tconst formatUpdatedDate = (dateString) => {\n\t\tif (!dateString) return null;\n\n\t\ttry {\n\t\t\tconst updateTime = dayjs.utc(dateString);\n\t\t\tif (!updateTime.isValid()) return null;\n\n\t\t\treturn updateTime.fromNow();\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"Error parsing date with dayjs:\", dateString, error);\n\t\t\treturn null;\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"main\" id=\"plugin\">\n\t\t\t<div className=\"plugin-header\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"plugin-icon\"\n\t\t\t\t\tstyle={{ backgroundImage: `url(${icon})` }}\n\t\t\t\t></div>\n\t\t\t\t<div className=\"plugin-info\">\n\t\t\t\t\t<div className=\"title-wrapper\">\n\t\t\t\t\t\t<h1 className=\"plugin-name\">{name}</h1>\n\t\t\t\t\t\t{repository ? (\n\t\t\t\t\t\t\t<a href={repository} className=\"source-indicator\">\n\t\t\t\t\t\t\t\t<i className=\"icon github\"></i>\n\t\t\t\t\t\t\t\t<span>{strings.open_source}</span>\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"plugin-meta\">\n\t\t\t\t\t\t<span className=\"meta-item\">\n\t\t\t\t\t\t\t<i className=\"icon tag\" style={{ fontSize: \"12px\" }}></i>\n\t\t\t\t\t\t\t<Version\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tpackageUpdatedAt={packageUpdatedAt}\n\t\t\t\t\t\t\t\tformatUpdatedDate={formatUpdatedDate}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"meta-item author-name\">\n\t\t\t\t\t\t\t<i className=\"icon person\"></i>\n\t\t\t\t\t\t\t<a href={`https://github.com/${authorGithub}`} className=\"\">\n\t\t\t\t\t\t\t\t{author}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t{authorVerified ? (\n\t\t\t\t\t\t\t\t<i\n\t\t\t\t\t\t\t\t\ton:click={() => {\n\t\t\t\t\t\t\t\t\t\ttoast(strings[\"verified publisher\"]);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tclassName=\"icon verified verified-tick\"\n\t\t\t\t\t\t\t\t></i>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\"\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"meta-item\">\n\t\t\t\t\t\t\t<span className=\"icon scale\" style={{ fontSize: \"12px\" }}></span>\n\t\t\t\t\t\t\t{license || \"Unknown\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{votesUp !== undefined ? (\n\t\t\t\t\t\t<div className=\"metrics-row\">\n\t\t\t\t\t\t\t<div className=\"metric\">\n\t\t\t\t\t\t\t\t<span className=\"icon save_alt\"></span>\n\t\t\t\t\t\t\t\t<span className=\"metric-value\">\n\t\t\t\t\t\t\t\t\t{helpers.formatDownloadCount(\n\t\t\t\t\t\t\t\t\t\ttypeof downloads === \"string\"\n\t\t\t\t\t\t\t\t\t\t\t? Number.parseInt(downloads)\n\t\t\t\t\t\t\t\t\t\t\t: downloads,\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span>{strings.downloads}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"metric\">\n\t\t\t\t\t\t\t\t<i className=\"icon favorite\"></i>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName={`rating-value ${rating === \"unrated\" ? \"\" : rating.replace(\"%\", \"\") >= 80 ? \"rating-high\" : rating.replace(\"%\", \"\") >= 50 ? \"rating-medium\" : \"rating-low\"}`}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{rating}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"metric\"\n\t\t\t\t\t\t\t\tonclick={showReviews.bind(null, id, author)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<i className=\"icon chat_bubble\"></i>\n\t\t\t\t\t\t\t\t<span className=\"metric-value\">{commentCount}</span>\n\t\t\t\t\t\t\t\t<span>{strings.reviews}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null}\n\t\t\t\t\t{Array.isArray(keywords) && keywords.length ? (\n\t\t\t\t\t\t<div className=\"keywords\">\n\t\t\t\t\t\t\t{keywords.map((keyword) => (\n\t\t\t\t\t\t\t\t<span className=\"keyword\">{keyword}</span>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"action-buttons\">\n\t\t\t\t\t<Buttons {...props} />\n\t\t\t\t</div>\n\t\t\t\t<MoreInfo {...props} />\n\t\t\t</div>\n\t\t\t<TabView id=\"plugin-tab\" disableSwipe={true}>\n\t\t\t\t<div className=\"options\" onclick={handleTabClick}>\n\t\t\t\t\t<span className=\"tab active\" data-tab=\"overview\" tabindex=\"0\">\n\t\t\t\t\t\t{strings.overview}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"tab\" data-tab=\"contributors\" tabindex=\"0\">\n\t\t\t\t\t\t{strings.contributors}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"tab\" data-tab=\"changelog\" tabindex=\"0\">\n\t\t\t\t\t\t{strings.changelog}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"tab-content\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tid=\"overview\"\n\t\t\t\t\t\tclassName=\"content-section active md\"\n\t\t\t\t\t\tinnerHTML={DOMPurify.sanitize(body, { FORBID_TAGS: [\"style\"] })}\n\t\t\t\t\t></div>\n\t\t\t\t\t<div id=\"contributors\" className=\"content-section\">\n\t\t\t\t\t\t{(() => {\n\t\t\t\t\t\t\tlet contributorsList = contributors?.length\n\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t{ name: author, role: \"Developer\", github: authorGithub },\n\t\t\t\t\t\t\t\t\t\t...contributors,\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t: [{ name: author, role: \"Developer\", github: authorGithub }];\n\n\t\t\t\t\t\t\treturn contributorsList.map(({ name, role, github }) => {\n\t\t\t\t\t\t\t\tlet dp = Url.join(constants.API_BASE, `../user.png`);\n\t\t\t\t\t\t\t\tif (github) {\n\t\t\t\t\t\t\t\t\tdp = `https://avatars.githubusercontent.com/${github}`;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\tclassName=\"contributor\"\n\t\t\t\t\t\t\t\t\t\thref={`https://github.com/${github}`}\n\t\t\t\t\t\t\t\t\t\tstyle={{ textDecoration: \"none\" }}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<img src={dp} alt={name} />\n\t\t\t\t\t\t\t\t\t\t<div className=\"contributor-info\">\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"contributor-name\">{name}</div>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"contributor-role\">{role}</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t})()}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div\n\t\t\t\t\t\tid=\"changelog\"\n\t\t\t\t\t\tclassName=\"content-section md\"\n\t\t\t\t\t\tinnerHTML={\n\t\t\t\t\t\t\tDOMPurify.sanitize(changelogs, { FORBID_TAGS: [\"style\"] }) ||\n\t\t\t\t\t\t\t`\n\t\t\t\t\t\t\t<div class=\"no-changelog\">\n\t\t\t\t\t\t\t\t<i class=\"icon historyrestore\"></i>\n\t\t\t\t\t\t\t\t<p style=\"font-size: 1.1rem;\">\n\t\t\t\t\t\t\t\t\tNo changelog is available for this plugin yet.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<p style=\"font-size: 0.9rem; font-style: italic;\">\n\t\t\t\t\t\t\t\t\tCheck back later for updates!\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t`\n\t\t\t\t\t\t}\n\t\t\t\t\t></div>\n\t\t\t\t</div>\n\t\t\t</TabView>\n\t\t</div>\n\t);\n};\n\nfunction handleTabClick(e) {\n\tconst $target = e.target;\n\tif (!$target.classList.contains(\"tab\")) return;\n\n\tconst tabs = document.querySelectorAll(\".tab\");\n\tconst contents = document.querySelectorAll(\".content-section\");\n\n\ttabs.forEach((tab) => tab.classList.remove(\"active\"));\n\tcontents.forEach((content) => content.classList.remove(\"active\"));\n\n\t$target.classList.add(\"active\");\n\tconst tabId = $target.dataset.tab;\n\tdocument.getElementById(tabId).classList.add(\"active\");\n}\n\nfunction Buttons({\n\tname,\n\tisPaid,\n\tinstalled,\n\tupdate,\n\tinstall,\n\tuninstall,\n\tpurchased,\n\tprice,\n\tbuy,\n\tminVersionCode,\n}) {\n\tif (\n\t\ttypeof minVersionCode === \"number\" &&\n\t\tminVersionCode > BuildInfo.versionCode\n\t) {\n\t\treturn (\n\t\t\t<div className=\"error\">\n\t\t\t\t<span className=\"icon info\"></span>\n\t\t\t\t<a href={constants.PLAY_STORE_URL} className=\"text\">\n\t\t\t\t\t{strings[\"plugin min version\"]\n\t\t\t\t\t\t.replace(\"{name}\", name)\n\t\t\t\t\t\t.replace(\"{v-code}\", minVersionCode)}\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (installed && update) {\n\t\treturn (\n\t\t\t<>\n\t\t\t\t<button\n\t\t\t\t\tdata-type=\"uninstall\"\n\t\t\t\t\tonclick={uninstall}\n\t\t\t\t\tclassName=\"btn btn-uninstall\"\n\t\t\t\t>\n\t\t\t\t\t<i className=\"icon delete_outline\"></i>\n\t\t\t\t\t{strings.uninstall}\n\t\t\t\t</button>\n\t\t\t\t<button data-type=\"update\" className=\"btn btn-update\" onclick={install}>\n\t\t\t\t\t<i className=\"icon update\"></i>\n\t\t\t\t\t{strings.update}\n\t\t\t\t</button>\n\t\t\t</>\n\t\t);\n\t}\n\n\tif (installed) {\n\t\treturn (\n\t\t\t<button\n\t\t\t\tdata-type=\"uninstall\"\n\t\t\t\tclassName=\"btn btn-uninstall\"\n\t\t\t\tonclick={uninstall}\n\t\t\t>\n\t\t\t\t<i className=\"icon delete_outline\"></i>\n\t\t\t\t{strings.uninstall}\n\t\t\t</button>\n\t\t);\n\t}\n\n\tif (isPaid && !purchased && price) {\n\t\treturn (\n\t\t\t<button data-type=\"buy\" className=\"btn btn-install\" onclick={buy}>\n\t\t\t\t<i className=\"icon cart\"></i>\n\t\t\t\t{price}\n\t\t\t</button>\n\t\t);\n\t}\n\n\tif (isPaid && !purchased && !price) {\n\t\treturn (\n\t\t\t<div style={{ margin: \"auto\" }} className=\"flex-center\">\n\t\t\t\t<span\n\t\t\t\t\tonclick={() => alert(strings.info, strings[\"no-product-info\"])}\n\t\t\t\t\tclassName=\"icon info\"\n\t\t\t\t></span>\n\t\t\t\t<span>{strings[\"product not available\"]}</span>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<button data-type=\"install\" className=\"btn btn-install\" onclick={install}>\n\t\t\t<i className=\"icon save_alt\"></i>\n\t\t\t{strings.install}\n\t\t</button>\n\t);\n}\n\nfunction Version({\n\tcurrentVersion,\n\tversion,\n\tpackageUpdatedAt,\n\tformatUpdatedDate,\n}) {\n\tconst updatedText =\n\t\tformatUpdatedDate && packageUpdatedAt\n\t\t\t? formatUpdatedDate(packageUpdatedAt)\n\t\t\t: null;\n\n\tif (!currentVersion) {\n\t\treturn (\n\t\t\t<span>\n\t\t\t\tv{version}\n\t\t\t\t{updatedText && (\n\t\t\t\t\t<span className=\"version-updated\">({updatedText})</span>\n\t\t\t\t)}\n\t\t\t</span>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span>\n\t\t\tv{currentVersion}&nbsp;&#8594;&nbsp;v{version}\n\t\t\t{updatedText && <span className=\"version-updated\">({updatedText})</span>}\n\t\t</span>\n\t);\n}\n\nasync function showReviews(pluginId, author) {\n\tconst mask = Ref();\n\tconst body = Ref();\n\tconst container = Ref();\n\n\tactionStack.push({\n\t\tid: \"reviews\",\n\t\taction: closeReviews,\n\t});\n\n\tapp.append(\n\t\t<span\n\t\t\tstyle={{ zIndex: 998 }}\n\t\t\tref={mask}\n\t\t\tonclick={closeReviews}\n\t\t\tclassName=\"mask\"\n\t\t></span>,\n\t);\n\tapp.append(\n\t\t<div ref={container} className=\"reviews-container\">\n\t\t\t<div className=\"reviews-header\" ontouchstart={ontouchstart}></div>\n\t\t\t<div className=\"write-review\">\n\t\t\t\t<a\n\t\t\t\t\tstyle={{ textDecoration: \"none\", display: \"flex\" }}\n\t\t\t\t\thref={Url.join(constants.API_BASE, `../plugin/${pluginId}/comments`)}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"icon edit\"></span>\n\t\t\t\t\t<span className=\"title\">Review</span>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t\t<div ref={body} className=\"reviews-body loading\"></div>\n\t\t</div>,\n\t);\n\n\ttry {\n\t\tconst reviews = await fsOperation(\n\t\t\tconstants.API_BASE,\n\t\t\t`/comments/${pluginId}`,\n\t\t).readFile(\"json\");\n\t\tif (!reviews.length) {\n\t\t\tbody.style.textAlign = \"center\";\n\t\t\tbody.textContent = \"No reviews yet\";\n\t\t\treturn;\n\t\t}\n\n\t\treviews.forEach((review) => {\n\t\t\tif (!review.comment) return;\n\t\t\treview.author = author;\n\t\t\tbody.append(<Review {...review} />);\n\t\t});\n\t} catch (error) {\n\t\tbody.textContent = error.message;\n\t} finally {\n\t\tbody.classList.remove(\"loading\");\n\t}\n\n\tfunction closeReviews() {\n\t\tactionStack.remove(\"reviews\");\n\t\tcontainer.classList.add(\"hide\");\n\n\t\tsetTimeout(() => {\n\t\t\tmask.el.remove();\n\t\t\tcontainer.el.remove();\n\t\t}, 300);\n\t}\n\n\t/**\n\t * @param {TouchEvent} e\n\t */\n\tfunction ontouchstart(e) {\n\t\tconst { clientY } = e.touches[0];\n\t\tconst { top } = container.el.getBoundingClientRect();\n\t\tconst y = clientY - top;\n\t\tlet dy = 0;\n\n\t\tcontainer.style.transition = \"none\";\n\t\tdocument.addEventListener(\"touchmove\", ontouchmove);\n\t\tdocument.addEventListener(\"touchend\", ontouchend);\n\t\tdocument.addEventListener(\"touchcancel\", ontouchend);\n\n\t\tfunction ontouchmove(e) {\n\t\t\tconst { clientY } = e.touches[0];\n\t\t\tdy = clientY - top - y;\n\n\t\t\tif (dy < 0) dy = 0;\n\n\t\t\tcontainer.style.transform = `translateY(${dy}px)`;\n\t\t}\n\n\t\tfunction ontouchend() {\n\t\t\tdocument.removeEventListener(\"touchmove\", ontouchmove);\n\t\t\tdocument.removeEventListener(\"touchend\", ontouchend);\n\t\t\tdocument.removeEventListener(\"touchcancel\", ontouchcancel);\n\t\t\tif (dy < 100) {\n\t\t\t\tcontainer.style.transition = \"transform 0.3s ease-in-out\";\n\t\t\t\tcontainer.style.transform = \"translateY(0)\";\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcloseReviews();\n\t\t}\n\t}\n}\n\nfunction Review({\n\tname,\n\tgithub,\n\tvote,\n\tcomment,\n\tauthor,\n\tauthor_reply: authorReply,\n}) {\n\tlet dp = Url.join(constants.API_BASE, `../user.png`);\n\tlet voteImage = Ref();\n\tlet review = Ref();\n\n\tif (github) {\n\t\tdp = `https://avatars.githubusercontent.com/${github}`;\n\t}\n\n\tif (vote === 1) {\n\t\tvoteImage.style.backgroundImage = `url(${Url.join(constants.API_BASE, `../thumbs-up.gif`)})`;\n\t} else if (vote === -1) {\n\t\tvoteImage.style.backgroundImage = `url(${Url.join(constants.API_BASE, `../thumbs-down.gif`)})`;\n\t}\n\n\tif (authorReply) {\n\t\tsetTimeout(() => {\n\t\t\treview.append(\n\t\t\t\t<p className=\"author-reply\" data-author={author}>\n\t\t\t\t\t{authorReply}\n\t\t\t\t</p>,\n\t\t\t);\n\t\t}, 0);\n\t}\n\n\treturn (\n\t\t<div ref={review} className=\"review\">\n\t\t\t<div title={name} className=\"review-author\">\n\t\t\t\t<span\n\t\t\t\t\tstyle={{ backgroundImage: `url(${dp})` }}\n\t\t\t\t\tclassName=\"user-profile\"\n\t\t\t\t></span>\n\t\t\t\t<span className=\"user-name\">{name}</span>\n\t\t\t\t<span ref={voteImage} className=\"vote\"></span>\n\t\t\t</div>\n\t\t\t<p className=\"review-body\">{comment}</p>\n\t\t</div>\n\t);\n}\n\nfunction MoreInfo({ purchased, price, refund }) {\n\tif (!purchased) return \"\";\n\n\treturn (\n\t\t<small className=\"more-info-small\">\n\t\t\t<span>{strings.owned}</span> • <span>{price}</span> •{\" \"}\n\t\t\t<span className=\"link\" onclick={refund}>\n\t\t\t\t{strings.refund}\n\t\t\t</span>\n\t\t</small>\n\t);\n}\n"
  },
  {
    "path": "src/pages/plugins/index.js",
    "content": "function plugins(updates) {\n  import(/* webpackChunkName: \"plugins\" */ './plugins').then(\n    (res) => {\n      const Plugins = res.default;\n      Plugins(updates);\n    },\n  );\n}\n\nexport default plugins;\n"
  },
  {
    "path": "src/pages/plugins/item.js",
    "content": "import helpers from \"utils/helpers\";\nimport pluginIcon from './plugin-icon.png';\n\n/**\n * Creates a plugin list item\n * @param {object} param0\n * @param {string} [param0.id]\n * @param {string} [param0.name]\n * @param {string} [param0.icon]\n * @param {string} [param0.version]\n * @param {number} [param0.downloads]\n * @param {boolean} [param0.installed]\n * @param {boolean} [param0.enabled]\n * @param {function} [param0.onToggleEnabled]\n * @returns\n */\nexport default function Item({\n  id,\n  name,\n  icon,\n  version,\n  license,\n  author,\n  price,\n  author_verified,\n  downloads,\n  installed,\n  enabled,\n  onToggleEnabled,\n  updates,\n}) {\n  const authorName = (() => {\n    const displayName =\n      typeof author === \"object\" ? author.name : author || \"Unknown\";\n    // Check if it's likely an email or too long\n    if (displayName.includes(\"@\") || displayName.length > 20) {\n      return displayName.substring(0, 20) + \"...\";\n    }\n    return displayName;\n  })();\n\n  return (\n    <div\n      data-id={id}\n      data-plugin-enabled={enabled !== false}\n      className=\"list-item\"\n      data-action=\"open\"\n      data-installed={(!!installed).toString()}\n      style={enabled === false ? { opacity: 0.6 } : {}}\n    >\n      <div className=\"plugin-header\">\n        <div className=\"plugin-icon\">\n          <img src={icon || pluginIcon} alt={name + \" icon\"} />\n        </div>\n        <div className=\"plugin-info\">\n          <div className=\"plugin-main\">\n            <div className=\"plugin-title\">\n              <span className=\"plugin-name\" title={name}>\n                {name}\n              </span>\n              <span className=\"plugin-version\">v{version}</span>\n            </div>\n            <div className=\"plugin-meta\">\n              <div\n                className=\"plugin-stats plugin-author\"\n                title={\n                  typeof author === \"object\" ? author.name : author || \"Unknown\"\n                }\n              >\n                <span className=\"icon person\"></span>\n                {authorName}\n                {author_verified ? (\n                  <i\n                    className=\"icon verified\"\n                    style={{ color: \"#3b82f6\" }}\n                  ></i>\n                ) : (\n                  \"\"\n                )}\n              </div>\n              <span className=\"plugin-meta-dot\"></span>\n              <div className=\"plugin-stats\">\n                <span\n                  className=\"icon scale\"\n                  style={{ fontSize: \"12px\" }}\n                ></span>\n                {license || \"Unknown\"}\n              </div>\n              {downloads && (\n                <>\n                  <span className=\"plugin-meta-dot\"></span>\n                  <div className=\"plugin-stats\">\n                    <span className=\"icon save_alt\"></span>\n                    {helpers.formatDownloadCount(downloads)}\n                  </div>\n                </>\n              )}\n            </div>\n          </div>\n          {price !== null && price !== undefined && price !== 0 ? (\n            <span className=\"plugin-price\">₹{price}</span>\n          ) : null}\n          {installed && !updates ? (\n            <span\n              className=\"plugin-toggle-switch\"\n              data-enabled={enabled}\n              onclick={e => {\n                e.stopPropagation();\n                onToggleEnabled?.(id, enabled);\n              }}\n            >\n              <span\n                className=\"plugin-toggle-track\"\n                data-enabled={enabled}\n              >\n                <span className=\"plugin-toggle-thumb\" />\n              </span>\n            </span>\n          ) : null}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "src/pages/plugins/plugins.js",
    "content": "import \"./plugins.scss\";\n\nimport Item from \"./item\";\nimport Url from \"utils/Url\";\nimport Plugin from \"pages/plugin\";\nimport Page from \"components/page\";\nimport helpers from \"utils/helpers\";\nimport fsOperation from \"fileSystem\";\nimport constants from \"lib/constants\";\nimport TabView from \"components/tabView\";\nimport searchBar from \"components/searchbar\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport installPlugin from \"lib/installPlugin\";\nimport prompt from \"dialogs/prompt\";\nimport actionStack from \"lib/actionStack\";\nimport Contextmenu from \"components/contextmenu\";\nimport settings from \"lib/settings\";\nimport loadPlugin from \"lib/loadPlugin\";\nimport { hideAd } from \"lib/startAd\";\n\n/**\n *\n * @param {Array<object>} updates\n */\nexport default function PluginsInclude(updates) {\n  const $page = Page(strings[\"plugins\"]);\n  const $search = <span className=\"icon search\" data-action=\"search\"></span>;\n  const $add = <span className=\"icon add\" data-action=\"add-source\"></span>;\n  const $filter = <span className=\"icon tune\" data-action=\"filter\"></span>;\n  const List = () => (\n    <div\n      id=\"plugin-list\"\n      className=\"list scroll\"\n      empty-msg={strings[\"loading...\"]}\n    ></div>\n  );\n  const $list = {\n    all: <List />,\n    installed: <List />,\n    owned: <List />,\n  };\n  const plugins = {\n    all: [],\n    installed: [],\n    owned: [],\n  };\n  let $currList = $list.installed;\n  let currSection = \"installed\";\n  let hideSearchBar = () => {};\n  let currentPage = 1;\n  let isLoading = false;\n  let hasMore = true;\n  let isSearching = false;\n  let currentFilter = null;\n  const LIMIT = 50;\n  const SUPPORTED_EDITOR = \"cm\";\n\n  const withSupportedEditor = (url) => {\n    const separator = url.includes(\"?\") ? \"&\" : \"?\";\n    return `${url}${separator}supported_editor=${SUPPORTED_EDITOR}`;\n  };\n\n  Contextmenu({\n    toggler: $add,\n    top: \"8px\",\n    right: \"8px\",\n    items: [\n      [strings.remote, \"remote\"],\n      [strings.local, \"local\"],\n    ],\n    onselect(item) {\n      addSource(item);\n    },\n  });\n\n  const verifiedLabel = strings[\"verified publisher\"];\n  const authorLabel = strings.author || strings.name;\n  const keywordsLabel = strings.keywords;\n\n  const filterOptions = {\n    \"orderBy:top_rated\": { type: \"orderBy\", value: \"top_rated\", baseLabel: strings.top_rated },\n    \"orderBy:newest\": { type: \"orderBy\", value: \"newest\", baseLabel: strings.newly_added },\n    \"orderBy:downloads\": { type: \"orderBy\", value: \"downloads\", baseLabel: strings.most_downloaded },\n    \"attribute:verified\": { type: \"verified\", value: true, baseLabel: verifiedLabel },\n    \"attribute:author\": { type: \"author\", baseLabel: authorLabel },\n    \"attribute:keywords\": { type: \"keywords\", baseLabel: keywordsLabel },\n  };\n\n  async function applyFilter(filterState) {\n    if (!filterState) return;\n\n    const normalizedFilter = {\n      ...filterState,\n      displayLabel: filterState.displayLabel || filterState.baseLabel,\n      nextPage: 1,\n      buffer: [],\n      hasMoreSource: true,\n    };\n\n    currentFilter = normalizedFilter;\n    currentPage = 1;\n    hasMore = true;\n    isLoading = false;\n    plugins.all = [];\n\n    if (currSection !== \"all\") {\n      render(\"all\");\n    } else {\n      $list.all.replaceChildren();\n    }\n\n    const filterMessage = (\n      <div className=\"filter-message\">\n        {strings[\"filtered by\"]} <strong>{normalizedFilter.displayLabel}</strong>\n        <span className=\"icon clearclose\" data-action=\"clear-filter\" onclick={clearFilter}></span>\n      </div>\n    );\n\n    $list.all.append(filterMessage);\n    $list.all.setAttribute(\"empty-msg\", strings[\"loading...\"]);\n    await getFilteredPlugins(currentFilter, true);\n  }\n\n  function clearFilter() {\n    currentFilter = null;\n    currentPage = 1;\n    hasMore = true;\n    isLoading = false;\n    plugins.all = [];\n    $list.all.replaceChildren();\n    $list.all.setAttribute(\"empty-msg\", strings[\"loading...\"]);\n    getAllPlugins();\n  }\n\n  Contextmenu({\n    toggler: $filter,\n    top: \"8px\",\n    right: \"16px\",\n    items: [\n      [strings.top_rated, \"orderBy:top_rated\"],\n      [strings.newly_added, \"orderBy:newest\"],\n      [strings.most_downloaded, \"orderBy:downloads\"],\n      [verifiedLabel, \"attribute:verified\"],\n      [authorLabel, \"attribute:author\"],\n      [keywordsLabel, \"attribute:keywords\"],\n    ],\n    async onselect(action) {\n      const option = filterOptions[action];\n      if (!option) return;\n\n      const filterState = {\n        type: option.type,\n        value: option.value,\n        baseLabel: option.baseLabel,\n        displayLabel: option.baseLabel,\n      };\n\n      if (option.type === \"author\") {\n        const authorName = (await prompt(\"Enter author name\", \"\", \"text\"))?.trim();\n        if (!authorName) return;\n        filterState.value = authorName.toLowerCase();\n        filterState.originalValue = authorName;\n        filterState.displayLabel = `${option.baseLabel}: ${authorName}`;\n      } else if (option.type === \"keywords\") {\n        const rawKeywords = (await prompt(\"Enter keywords\", \"\", \"text\"))?.trim();\n        if (!rawKeywords) return;\n      const keywordList = rawKeywords\n        .split(\",\")\n        .map((item) => item.trim())\n        .filter(Boolean);\n      if (!keywordList.length) return;\n      filterState.value = keywordList.map((item) => item.toLowerCase());\n      filterState.originalValue = keywordList.join(\", \");\n      filterState.displayLabel = `${option.baseLabel}: ${filterState.originalValue}`;\n      }\n\n      await applyFilter(filterState);\n    },\n  });\n\n  $page.body = (\n    <TabView id=\"plugins\">\n      <div className=\"options\">\n        <span\n          id=\"installed_plugins\"\n          onclick={renderInstalled}\n          tabindex=\"0\"\n          className=\"active\"\n        >\n          {strings.installed}\n        </span>\n        <span id=\"all_plugins\" onclick={renderAll} tabindex=\"0\">\n          {strings.all}\n        </span>\n        <span id=\"owned_plugins\" onclick={renderOwned} tabindex=\"0\">\n          {strings.owned}\n        </span>\n      </div>\n      {$list.installed}\n    </TabView>\n  );\n  $page.header.append($search, $filter, $add);\n\n  actionStack.push({\n    id: \"plugins\",\n    action: $page.hide,\n  });\n\n  $page.onhide = function () {\n    hideAd();\n    actionStack.remove(\"plugins\");\n  };\n\n  $page.onconnect = () => {\n    $currList.scrollTop = $currList._scroll || 0;\n  };\n\n  $page.onwilldisconnect = () => {\n    $currList._scroll = $currList.scrollTop;\n  };\n\n  $page.ondisconnect = () => hideSearchBar();\n\n  $page.onclick = handleClick;\n\n  $list.all.addEventListener('scroll', async (e) => {\n    if (isLoading || !hasMore || isSearching) return;\n\n    const { scrollTop, scrollHeight, clientHeight } = e.target;\n    if (scrollTop + clientHeight >= scrollHeight - 50) {\n        if (currentFilter) {\n          await getFilteredPlugins(currentFilter);\n        } else {\n          await getAllPlugins();\n        }\n    }\n  })\n\n  app.append($page);\n  helpers.showAd();\n\n  if (updates) {\n    $page.get(\".options\").style.display = \"none\";\n    $add.style.display = \"none\";\n    $filter.style.display = \"none\";\n    $page.settitle(strings.update);\n    getInstalledPlugins(updates).then(() => {\n      render(\"installed\");\n    });\n    return;\n  }\n\n  if (navigator.onLine) {\n    getAllPlugins();\n    getOwned();\n  }\n\n  getInstalledPlugins().then(() => {\n    if (plugins.installed.length) {\n      return;\n    }\n\n    render(\"all\");\n  });\n\n  function handleClick(event) {\n    const $target = event.target;\n    const { action } = $target.dataset;\n    if (action === \"search\") {\n      if (currSection === \"all\") {\n        isSearching = true;\n        searchBar(\n          $currList,\n          (hide) => {\n            hideSearchBar = hide;\n            isSearching = false;\n          },\n          undefined,\n          searchRemotely,\n        );\n        return;\n      } else {\n        isSearching = true;\n        searchBar($currList, (hide) => {\n          hideSearchBar = hide;\n          isSearching = false;\n        });\n        return;\n      }\n    }\n    if (action === \"open\") {\n      Plugin($target.dataset, onInstall, onUninstall);\n      return;\n    }\n  }\n\n  function render(section) {\n    if (currSection === section) return;\n\n    if (!section) {\n      section = currSection;\n    }\n\n    if (document.getElementById(\"search-bar\")) {\n      hideSearchBar();\n    }\n\n    const $section = $list[section];\n    $currList._scroll = $currList.scrollTop;\n    $currList.replaceWith($section);\n    $section.scrollTop = $section._scroll || 0;\n    $currList = $section;\n    currSection = section;\n    if (section === \"all\") {\n      currentPage = 1;\n      hasMore = true;\n      isLoading = false;\n      plugins.all = []; // Reset the all plugins array\n      $list.all.replaceChildren();\n      if (!currentFilter) {\n        getAllPlugins();\n      }\n    }\n    $page.get(\".options .active\").classList.remove(\"active\");\n    $page.get(`#${section}_plugins`).classList.add(\"active\");\n  }\n\n  function renderAll() {\n    render(\"all\");\n    if (currentFilter) {\n      applyFilter(currentFilter);\n    }\n  }\n\n  function renderInstalled() {\n    render(\"installed\");\n  }\n\n  function renderOwned() {\n    render(\"owned\");\n  }\n\n  async function searchRemotely(query) {\n    if (!query) return [];\n    try {\n      const response = await fetch(\n        withSupportedEditor(`${constants.API_BASE}/plugins?name=${query}`),\n      );\n      const plugins = await response.json();\n      // Map the plugins to Item elements and return\n      return plugins.map((plugin) => <Item {...plugin} />);\n    } catch (error) {\n      $list.all.setAttribute(\"empty-msg\", strings[\"error\"]);\n      window.log(\"error\", \"Failed to search remotely:\");\n      window.log(\"error\", error);\n      return [];\n    }\n  }\n\n  async function getFilteredPlugins(filterState, isInitial = false) {\n    if (!filterState) return;\n    if (isLoading || !hasMore) return;\n\n    try {\n      isLoading = true;\n      $list.all.setAttribute(\"empty-msg\", strings[\"loading...\"]);\n\n      const { items, hasMore: hasMoreResults } = await retrieveFilteredPlugins(filterState);\n\n      if (currentFilter !== filterState) {\n        return;\n      }\n\n      const installed = await fsOperation(PLUGIN_DIR).lsDir();\n      const disabledMap = settings.value.pluginsDisabled || {};\n\n      installed.forEach(({ url }) => {\n        const plugin = items.find(({ id }) => id === Url.basename(url));\n        if (plugin) {\n          plugin.installed = true;\n          plugin.enabled = disabledMap[plugin.id] !== true;\n          plugin.onToggleEnabled = onToggleEnabled;\n          plugin.localPlugin = getLocalRes(plugin.id, \"plugin.json\");\n        }\n      });\n\n      if (isInitial) {\n        $list.all.querySelectorAll(\".filter-empty\").forEach((el) => el.remove());\n      }\n\n      plugins.all.push(...items);\n\n      const fragment = document.createDocumentFragment();\n      items.forEach((plugin) => {\n        fragment.append(<Item {...plugin} updates={updates} />);\n      });\n\n      if (fragment.childNodes.length) {\n        $list.all.append(fragment);\n      } else if (isInitial) {\n        $list.all.append(\n          <div className=\"filter-empty\">{strings[\"no plugins found\"] || \"No plugins found\"}</div>,\n        );\n      }\n\n      hasMore = hasMoreResults;\n      if (!hasMore) {\n        $list.all.setAttribute(\"empty-msg\", strings[\"no plugins found\"]);\n      }\n    } catch (error) {\n      $list.all.setAttribute(\"empty-msg\", strings[\"error\"]);\n      console.error(\"Failed to filter plugins:\", error);\n      hasMore = false;\n    } finally {\n      isLoading = false;\n    }\n  }\n\n  async function retrieveFilteredPlugins(filterState) {\n    if (!filterState) return { items: [], hasMore: false };\n\n    if (filterState.type === \"orderBy\") {\n      const page = filterState.nextPage || 1;\n      try {\n        let response;\n        if (filterState.value === \"top_rated\") {\n          response = await fetch(\n            withSupportedEditor(\n              `${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,\n            ),\n          );\n        } else {\n          response = await fetch(\n            withSupportedEditor(\n              `${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,\n            ),\n          );\n        }\n        const items = await response.json();\n        if (!Array.isArray(items)) {\n          return { items: [], hasMore: false };\n        }\n        filterState.nextPage = page + 1;\n        const hasMoreResults = items.length === LIMIT;\n        return { items, hasMore: hasMoreResults };\n      } catch (error) {\n        console.error(\"Failed to fetch ordered plugins:\", error);\n        return { items: [], hasMore: false };\n      }\n    }\n\n    if (!Array.isArray(filterState.buffer)) {\n      filterState.buffer = [];\n    }\n    if (filterState.hasMoreSource === undefined) {\n      filterState.hasMoreSource = true;\n    }\n    if (!filterState.nextPage) {\n      filterState.nextPage = 1;\n    }\n\n    const items = [];\n\n    while (items.length < LIMIT) {\n      if (filterState.buffer.length) {\n        items.push(filterState.buffer.shift());\n        continue;\n      }\n\n      if (filterState.hasMoreSource === false) break;\n\n      try {\n        const page = filterState.nextPage;\n        const response = await fetch(\n          withSupportedEditor(`${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`),\n        );\n        const data = await response.json();\n        filterState.nextPage = page + 1;\n\n        if (!Array.isArray(data) || !data.length) {\n          filterState.hasMoreSource = false;\n          break;\n        }\n\n        if (data.length < LIMIT) {\n          filterState.hasMoreSource = false;\n        }\n\n        const matched = data.filter((plugin) => matchesFilter(plugin, filterState));\n        filterState.buffer.push(...matched);\n      } catch (error) {\n        console.error(\"Failed to fetch filtered plugins:\", error);\n        filterState.hasMoreSource = false;\n        break;\n      }\n    }\n\n    while (items.length < LIMIT && filterState.buffer.length) {\n      items.push(filterState.buffer.shift());\n    }\n\n    const hasMoreResults =\n      (filterState.hasMoreSource !== false && filterState.nextPage) ||\n      filterState.buffer.length > 0;\n\n    return { items, hasMore: Boolean(hasMoreResults) };\n  }\n\n  function matchesFilter(plugin, filterState) {\n    if (!plugin) return false;\n\n    switch (filterState.type) {\n      case \"verified\":\n        return Boolean(plugin.author_verified);\n      case \"author\": {\n        const authorName = normalizePluginAuthor(plugin);\n        if (!authorName) return false;\n        return authorName.toLowerCase().includes(filterState.value);\n      }\n      case \"keywords\": {\n        const pluginKeywords = normalizePluginKeywords(plugin)\n          .map((keyword) => keyword.toLowerCase())\n          .filter(Boolean);\n        if (!pluginKeywords.length) return false;\n        return filterState.value.some((keyword) =>\n          pluginKeywords.some((pluginKeyword) => pluginKeyword.includes(keyword)),\n        );\n      }\n      default:\n        return true;\n    }\n  }\n\n  function normalizePluginAuthor(plugin) {\n    const { author } = plugin || {};\n    if (!author) return \"\";\n    if (typeof author === \"string\") return author;\n    if (typeof author === \"object\") {\n      return author.name || author.username || author.github || \"\";\n    }\n    return \"\";\n  }\n\n  function normalizePluginKeywords(plugin) {\n    const { keywords } = plugin || {};\n    if (!keywords) return [];\n    if (Array.isArray(keywords)) return keywords;\n    if (typeof keywords === \"string\") {\n      try {\n        const parsed = JSON.parse(keywords);\n        if (Array.isArray(parsed)) return parsed;\n      } catch (error) {\n        return keywords\n          .split(\",\")\n          .map((item) => item.trim())\n          .filter(Boolean);\n      }\n    }\n    return [];\n  }\n\n  async function getAllPlugins() {\n    if (currentFilter) return;\n    if (isLoading || !hasMore) return;\n\n    try {\n      isLoading = true;\n\n      $list.all.setAttribute(\"empty-msg\", strings[\"loading...\"]);\n\n      const response = await fetch(\n        withSupportedEditor(`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`),\n      );\n      const newPlugins = await response.json();\n\n      if (newPlugins.length < LIMIT) {\n        hasMore = false;\n      }\n\n      const installed = await fsOperation(PLUGIN_DIR).lsDir();\n      const disabledMap = settings.value.pluginsDisabled || {};\n\n      installed.forEach(({ url }) => {\n        const plugin = newPlugins.find(({ id }) => id === Url.basename(url));\n        if (plugin) {\n          plugin.installed = true;\n          plugin.enabled = disabledMap[plugin.id] !== true;\n          plugin.onToggleEnabled = onToggleEnabled;\n          plugin.localPlugin = getLocalRes(plugin.id, \"plugin.json\");\n        }\n      });\n\n      // Add plugins to the all plugins array\n      plugins.all.push(...newPlugins);\n\n      const fragment = document.createDocumentFragment();\n      newPlugins.forEach((plugin) => {\n        fragment.append(<Item {...plugin} updates={updates} />);\n      });\n      $list.all.append(fragment);\n\n      currentPage++;\n      $list.all.setAttribute(\"empty-msg\", strings[\"no plugins found\"]);\n    } catch (error) {\n      window.log(\"error\", error);\n    } finally {\n      isLoading = false;\n    }\n  }\n\n  async function getInstalledPlugins(updates) {\n    $list.installed.setAttribute(\"empty-msg\", strings[\"loading...\"]);\n    plugins.installed = [];\n    const disabledMap = settings.value.pluginsDisabled || {};\n    const installed = await fsOperation(PLUGIN_DIR).lsDir();\n    await Promise.all(\n      installed.map(async (item) => {\n        const id = Url.basename(item.url);\n        if (!((updates && updates.includes(id)) || !updates)) return;\n        const url = Url.join(item.url, \"plugin.json\");\n        const plugin = await fsOperation(url).readFile(\"json\");\n        const iconUrl = getLocalRes(id, plugin.icon);\n        plugin.icon = await helpers.toInternalUri(iconUrl);\n        plugin.installed = true;\n        plugin.enabled = disabledMap[id] !== true; // default to true\n        plugin.onToggleEnabled = onToggleEnabled;\n        plugins.installed.push(plugin);\n        if ($list.installed.get(`[data-id=\\\"${id}\\\"]`)) return;\n        $list.installed.append(<Item {...plugin} updates={updates} />);\n      }),\n    );\n    $list.installed.setAttribute(\"empty-msg\", strings[\"no plugins found\"]);\n  }\n\n  async function getOwned() {\n    $list.owned.setAttribute(\"empty-msg\", strings[\"loading...\"]);\n    const purchases = await helpers.promisify(iap.getPurchases);\n    const disabledMap = settings.value.pluginsDisabled || {};\n\n    purchases.forEach(async ({ productIds }) => {\n      const [sku] = productIds;\n      const url = Url.join(constants.API_BASE, \"plugin/owned\", sku);\n      const plugin = await fsOperation(url).readFile(\"json\");\n      const isInstalled = plugins.installed.find(({ id }) => id === plugin.id);\n      plugin.installed = !!isInstalled;\n\n      if (plugin.installed) {\n        plugin.enabled = disabledMap[plugin.id] !== true;\n        plugin.onToggleEnabled = onToggleEnabled;\n      }\n\n      plugins.owned.push(plugin);\n      $list.owned.append(<Item {...plugin} updates={updates} />);\n    });\n    $list.owned.setAttribute(\"empty-msg\", strings[\"no plugins found\"]);\n  }\n\n  function onInstall(plugin) {\n    if (updates) return;\n\n    if (!plugin || !plugin.id) {\n      console.error(\"Invalid plugin object passed to onInstall\");\n      return;\n    }\n    plugin.installed = true;\n\n    const existingIndex = plugins.installed.findIndex(p => p.id === plugin.id);\n    if (existingIndex === -1) {\n      plugins.installed.push(plugin);\n    } else {\n      // Update existing plugin\n      plugins.installed[existingIndex] = plugin;\n    }\n\n    const allPluginIndex = plugins.all.findIndex(p => p.id === plugin.id);\n    if (allPluginIndex !== -1) {\n      plugins.all[allPluginIndex] = plugin;\n    }\n\n    const existingItem = $list.installed.get(`[data-id=\"${plugin.id}\"]`);\n    if (!existingItem) {\n      $list.installed.append(<Item {...plugin} updates={updates} />);\n    }\n\n  }\n\n  function onUninstall(pluginId) {\n    if (!updates) {\n      plugins.installed = plugins.installed.filter(\n        (plugin) => plugin.id !== pluginId,\n      );\n\n      const plugin = plugins.all.find((plugin) => plugin.id === pluginId);\n      if (plugin) {\n        plugin.installed = false;\n        plugin.localPlugin = null;\n      }\n    }\n\n    // Remove from DOM\n    const existingItem = $list.installed.get(`[data-id=\"${pluginId}\"]`);\n    if (existingItem) {\n      existingItem.remove();\n    }\n  }\n\n  function getLocalRes(id, name) {\n    return Url.join(PLUGIN_DIR, id, name);\n  }\n\n  async function addSource(sourceType, value = \"https://\") {\n    let source;\n    if (sourceType === \"remote\") {\n      source = await prompt(\"Enter plugin source\", value, \"url\");\n    } else {\n      source = (await FileBrowser(\"file\", \"Select plugin source\")).url;\n    }\n\n    if (!source) return;\n\n    try {\n      await installPlugin(source);\n      await getInstalledPlugins();\n    } catch (error) {\n      console.error(error);\n      window.toast(helpers.errorMessage(error));\n      addSource(sourceType, source);\n    }\n  }\n\n  async function onToggleEnabled(id, enabled) {\n    const disabledMap = settings.value.pluginsDisabled || {};\n\n    if (enabled) {\n      disabledMap[id] = true;\n      settings.update({ pluginsDisabled: disabledMap }, false);\n      window.acode.unmountPlugin(id);\n      window.toast(strings[\"plugin_disabled\"] || \"Plugin Disabled\");\n    } else {\n      delete disabledMap[id];\n      settings.update({ pluginsDisabled: disabledMap }, false);\n      await loadPlugin(id);\n      window.toast(strings[\"plugin_enabled\"] || \"Plugin enabled\");\n    }\n\n    // Update the plugin object's state in all plugin arrays\n     const installedPlugin = plugins.installed.find(p => p.id === id);\n     if (installedPlugin) {\n       installedPlugin.enabled = !enabled;\n     }\n\n     const allPlugin = plugins.all.find(p => p.id === id);\n     if (allPlugin) {\n       allPlugin.enabled = !enabled;\n     }\n\n     const ownedPlugin = plugins.owned.find(p => p.id === id);\n     if (ownedPlugin) {\n       ownedPlugin.enabled = !enabled;\n     }\n\n     // Re-render the specific item in all tabs\n     const $installedItem = $list.installed.get(`[data-id=\"${id}\"]`);\n     if ($installedItem && installedPlugin) {\n       const $newItem = <Item {...installedPlugin} updates={updates} />;\n       $installedItem.replaceWith($newItem);\n     }\n\n     const $allItem = $list.all.get(`[data-id=\"${id}\"]`);\n     if ($allItem && allPlugin) {\n       const $newItem = <Item {...allPlugin} updates={updates} />;\n       $allItem.replaceWith($newItem);\n     }\n\n     const $ownedItem = $list.owned.get(`[data-id=\"${id}\"]`);\n     if ($ownedItem && ownedPlugin) {\n       const $newItem = <Item {...ownedPlugin} updates={updates} />;\n       $ownedItem.replaceWith($newItem);\n     }\n  }\n}\n"
  },
  {
    "path": "src/pages/plugins/plugins.scss",
    "content": "#plugins {\n  display: flex;\n  flex-direction: column;\n\n  .filter-message {\n    font-size: 0.9rem;\n    font-weight: 500;\n    color: var(--popup-text-color);\n    padding: 1rem 1.25rem;\n    border-bottom: 1px solid var(--border-color);\n    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    background: var(--secondary-color);\n    border-radius: 8px 8px 0 0;\n    margin-bottom: 2px;\n\n    strong {\n      color: var(--link-text-color);\n      margin-left: 0.25rem;\n    }\n\n    span {\n      margin-left: auto;\n      padding: 0.5rem;\n      border-radius: 6px;\n      transition: all 0.2s ease;\n\n      &:hover {\n        cursor: pointer;\n        background-color: var(--error-text-color);\n        transform: scale(1.05);\n      }\n    }\n  }\n\n  .list {\n    overflow-y: auto;\n    padding: 0.5rem 0;\n\n    &:empty::after {\n      content: attr(empty-msg);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      height: 200px;\n      color: var(--secondary-text-color);\n      font-size: 0.9rem;\n      font-style: italic;\n    }\n  }\n\n  .list-item {\n    margin: 0 0.5rem 0.5rem 0.5rem;\n    background: var(--secondary-color);\n    padding: 0.875rem;\n    border-radius: 12px;\n    transition: all 0.2s ease;\n    display: flex;\n    align-items: center;\n    overflow: hidden;\n    border: 1px solid var(--border-color);\n    cursor: pointer;\n    position: relative;\n\n    &:hover {\n      background: var(--primary-color);\n      background: color-mix(in srgb, var(--primary-color) 12%, transparent);\n      transform: translateY(-1px);\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n      border-color: var(--active-color);\n    }\n\n    &:active {\n      transform: translateY(0);\n    }\n\n    .plugin-header {\n      display: flex;\n      align-items: center;\n      gap: 0.75rem;\n      width: 100%;\n      max-width: 100%;\n      height: fit-content;\n      min-height: 40px;\n\n      .plugin-icon {\n        width: 40px;\n        height: 40px;\n        border-radius: 10px;\n        flex-shrink: 0;\n        overflow: hidden;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        background: var(--primary-color);\n        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n\n        img {\n          width: 100%;\n          height: 100%;\n          object-fit: cover;\n          object-position: center;\n        }\n      }\n\n      .plugin-info {\n        flex: 1;\n        min-width: 0;\n        display: flex;\n        align-items: center;\n        justify-content: space-between;\n        gap: 0.75rem;\n        margin: 0;\n        height: fit-content;\n\n        .plugin-main {\n          min-width: 0;\n          flex: 1;\n\n          .plugin-title {\n            display: flex;\n            align-items: center;\n            gap: 0.75rem;\n            min-width: 0;\n            margin-bottom: 0.375rem;\n\n            .plugin-name {\n              font-weight: 600;\n              font-size: 0.95rem;\n              overflow: hidden;\n              text-overflow: ellipsis;\n              white-space: nowrap;\n              min-width: 0;\n              max-width: calc(100% - 60px);\n              color: var(--primary-text-color);\n            }\n\n            .plugin-version {\n              color: var(--secondary-text-color);\n              font-size: 0.75rem;\n              font-weight: 500;\n              padding: 0.25rem 0.5rem;\n              background: var(--primary-color);\n              border-radius: 6px;\n              white-space: nowrap;\n              flex-shrink: 0;\n            }\n          }\n\n          .plugin-meta {\n            font-size: 0.875rem;\n            color: var(--secondary-text-color);\n            display: flex;\n            flex-wrap: wrap;\n            gap: 0.5rem;\n            align-items: center;\n\n            .plugin-meta-dot {\n              width: 4px;\n              height: 4px;\n              background: var(--secondary-text-color);\n              border-radius: 50%;\n              display: inline-block;\n              opacity: 0.6;\n            }\n\n            .plugin-stats {\n              display: flex;\n              align-items: center;\n              gap: 0.375rem;\n              font-size: 0.875rem;\n\n              &.plugin-author {\n                max-width: 150px;\n                white-space: nowrap;\n                overflow: hidden;\n                text-overflow: ellipsis;\n              }\n\n              .icon {\n                display: inline-flex;\n                align-items: center;\n                justify-content: center;\n                width: 14px;\n                height: 14px;\n                font-size: 14px;\n                line-height: 1;\n                flex-shrink: 0;\n                opacity: 0.8;\n              }\n            }\n          }\n        }\n\n        .plugin-price {\n          background: var(--active-color);\n          color: var(--button-text-color);\n          padding: 0.375rem 0.75rem;\n          border-radius: 8px;\n          font-size: 0.875rem;\n          font-weight: 600;\n          display: flex;\n          align-items: center;\n          gap: 0.25rem;\n          flex-shrink: 0;\n          height: fit-content;\n          box-shadow: 0 2px 4px var(--box-shadow-color);\n        }\n      }\n    }\n\n    .plugin-toggle-switch {\n      display: flex !important;\n      align-items: center;\n      gap: 0;\n      cursor: pointer;\n      z-index: 100;\n      min-width: auto;\n      pointer-events: auto !important;\n      position: relative;\n      margin-left: auto;\n      justify-content: flex-end;\n      padding: 0;\n      border-radius: 999px;\n      transition: background-color 0.2s ease;\n\n      &:hover {\n        background-color: transparent;\n      }\n    }\n\n    .plugin-toggle-track {\n      width: 2.8rem;\n      height: 1.65rem;\n      border-radius: 999px;\n      border: 1px solid var(--border-color);\n      border: 1px solid color-mix(in srgb, var(--border-color), transparent 6%);\n      background: var(--secondary-color);\n      background: color-mix(\n        in srgb,\n        var(--secondary-color),\n        var(--popup-background-color) 30%\n      );\n      position: relative;\n      transition:\n        background-color 160ms ease,\n        border-color 160ms ease,\n        box-shadow 180ms ease;\n      display: inline-block;\n      margin-right: 0;\n      box-sizing: border-box;\n      box-shadow: none;\n    }\n\n    .plugin-toggle-switch[data-enabled=\"true\"] .plugin-toggle-track,\n    .plugin-toggle-track[data-enabled=\"true\"] {\n      background: var(--button-background-color);\n      border-color: var(--button-background-color);\n      border-color: color-mix(in srgb, var(--button-background-color), transparent 10%);\n      box-shadow: inset 0 0 0 1px var(--button-background-color);\n      box-shadow: inset 0 0 0 1px\n        color-mix(in srgb, var(--button-background-color), transparent 12%);\n    }\n\n    .plugin-toggle-thumb {\n      position: absolute;\n      left: 0;\n      top: 0;\n      width: 1.25rem;\n      height: 1.25rem;\n      margin: 0.14rem;\n      border-radius: 999px;\n      background: var(--popup-text-color);\n      background: color-mix(\n        in srgb,\n        var(--popup-text-color),\n        var(--popup-background-color) 18%\n      );\n      border: 1px solid var(--border-color);\n      border: 1px solid color-mix(in srgb, var(--border-color), transparent 15%);\n      box-sizing: border-box;\n      box-shadow:\n        0 0 0 1px var(--border-color),\n        0 1px 3px rgba(0, 0, 0, 0.22);\n      box-shadow:\n        0 0 0 1px color-mix(in srgb, var(--border-color), transparent 34%),\n        0 1px 3px rgba(0, 0, 0, 0.22);\n      transition:\n        transform 180ms cubic-bezier(0.2, 0.9, 0.3, 1),\n        background-color 160ms ease,\n        box-shadow 180ms ease;\n    }\n\n    .plugin-toggle-switch[data-enabled=\"true\"] .plugin-toggle-thumb,\n    .plugin-toggle-track[data-enabled=\"true\"] .plugin-toggle-thumb {\n      transform: translateX(1.12rem);\n      background: var(--button-text-color);\n      box-shadow: 0 2px 8px var(--button-background-color);\n      box-shadow: 0 2px 8px color-mix(in srgb, var(--button-background-color), transparent 55%);\n    }\n  }\n}\n"
  },
  {
    "path": "src/pages/problems/index.js",
    "content": "function plugin({ id, installed }, onInstall, onUninstall) {\n\timport(/* webpackChunkName: \"problems\" */ \"./problems\").then((res) => {\n\t\tconst Problems = res.default;\n\t\tProblems();\n\t});\n}\n\nexport default plugin;\n"
  },
  {
    "path": "src/pages/problems/problems.js",
    "content": "import \"./style.scss\";\nimport { getLspDiagnostics } from \"cm/lsp/diagnostics\";\nimport Page from \"components/page\";\nimport actionStack from \"lib/actionStack\";\nimport EditorFile from \"lib/editorFile\";\nimport { hideAd } from \"lib/startAd\";\nimport helpers from \"utils/helpers\";\n\nexport default function Problems() {\n\tconst $page = Page(strings[\"problems\"]);\n\t/**@type {EditorFile[]} */\n\tconst files = editorManager.files;\n\tconst $content = <div id=\"problems\"></div>;\n\n\tfiles.forEach((file) => {\n\t\tif (file.type !== \"editor\") return;\n\t\tconst annotations = collectAnnotations(file);\n\t\tif (!annotations.length) return;\n\n\t\tconst title = `${file.name} (${annotations.length})`;\n\t\t$content.append(\n\t\t\t<details open=\"true\" className=\"single-file\">\n\t\t\t\t<summary>{title}</summary>\n\t\t\t\t<div className=\"problems\">\n\t\t\t\t\t{annotations.map((annotation) => {\n\t\t\t\t\t\tconst { type, text, row, column } = annotation;\n\t\t\t\t\t\tconst icon = getIconForType(type);\n\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"problem\"\n\t\t\t\t\t\t\t\tdata-action=\"goto\"\n\t\t\t\t\t\t\t\tdata-file-id={file.id}\n\t\t\t\t\t\t\t\tannotation={annotation}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className={`icon ${icon}`}></span>\n\t\t\t\t\t\t\t\t<span data-type={type} className=\"problem-message\">\n\t\t\t\t\t\t\t\t\t{text}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"problem-line\">\n\t\t\t\t\t\t\t\t\t{row + 1}:{column + 1}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</details>,\n\t\t);\n\t});\n\n\t$content.addEventListener(\"click\", clickHandler);\n\t$page.body = $content;\n\tapp.append($page);\n\thelpers.showAd();\n\n\t$page.onhide = function () {\n\t\thideAd();\n\t\tactionStack.remove(\"problems\");\n\t};\n\n\tactionStack.push({\n\t\tid: \"problems\",\n\t\taction: $page.hide,\n\t});\n\n\t/**\n\t * Click handler for problems page\n\t * @param {MouseEvent} e\n\t */\n\tfunction clickHandler(e) {\n\t\tconst $target = e.target.closest(\"[data-action='goto']\");\n\t\tif (!$target) return;\n\t\tconst { action } = $target.dataset;\n\n\t\tif (action === \"goto\") {\n\t\t\tconst { fileId } = $target.dataset;\n\t\t\tconst annotation = $target.annotation;\n\t\t\tif (!annotation) return;\n\t\t\tconst row = normalizeIndex(annotation.row);\n\t\t\tconst column = normalizeIndex(annotation.column);\n\n\t\t\teditorManager.switchFile(fileId);\n\t\t\teditorManager.editor.gotoLine(row + 1, column);\n\t\t\t$page.hide();\n\n\t\t\tsetTimeout(() => {\n\t\t\t\teditorManager.editor.focus();\n\t\t\t}, 100);\n\t\t}\n\t}\n\n\tfunction collectAnnotations(file) {\n\t\tconst annotations = [];\n\t\tconst { session } = file;\n\t\tconst isActiveFile = editorManager.activeFile?.id === file.id;\n\t\tconst state =\n\t\t\tisActiveFile && editorManager.editor\n\t\t\t\t? editorManager.editor.state\n\t\t\t\t: session;\n\n\t\tif (session && typeof session.getAnnotations === \"function\") {\n\t\t\tconst aceAnnotations = session.getAnnotations() || [];\n\t\t\tfor (const item of aceAnnotations) {\n\t\t\t\tif (!item) continue;\n\t\t\t\tconst row = normalizeIndex(item.row);\n\t\t\t\tconst column = normalizeIndex(item.column);\n\t\t\t\tannotations.push({\n\t\t\t\t\trow,\n\t\t\t\t\tcolumn,\n\t\t\t\t\ttext: item.text || \"\",\n\t\t\t\t\ttype: normalizeSeverity(item.type),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif (state && typeof state.field === \"function\") {\n\t\t\tannotations.push(...readLspAnnotations(state));\n\t\t}\n\n\t\treturn annotations;\n\t}\n\n\tfunction readLspAnnotations(state) {\n\t\tconst diagnostics = getLspDiagnostics(state);\n\t\tif (!diagnostics.length) return [];\n\n\t\tconst doc = state.doc;\n\t\tif (!doc || typeof doc.lineAt !== \"function\") return [];\n\n\t\treturn diagnostics\n\t\t\t.map((diagnostic) => {\n\t\t\t\tconst start = clampPosition(diagnostic.from, doc.length);\n\t\t\t\tconst line = doc.lineAt(start);\n\t\t\t\tconst row = Math.max(0, line.number - 1);\n\t\t\t\tconst column = Math.max(0, start - line.from);\n\n\t\t\t\tlet message = diagnostic.message || \"\";\n\t\t\t\tif (diagnostic.source) {\n\t\t\t\t\tmessage = message\n\t\t\t\t\t\t? `${message} (${diagnostic.source})`\n\t\t\t\t\t\t: diagnostic.source;\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\trow: normalizeIndex(row),\n\t\t\t\t\tcolumn: normalizeIndex(column),\n\t\t\t\t\ttext: message,\n\t\t\t\t\ttype: normalizeSeverity(diagnostic.severity),\n\t\t\t\t};\n\t\t\t})\n\t\t\t.filter((annotation) => annotation.text);\n\t}\n\n\tfunction clampPosition(pos, length) {\n\t\tif (typeof pos !== \"number\" || Number.isNaN(pos)) return 0;\n\t\treturn Math.max(0, Math.min(pos, Math.max(0, length)));\n\t}\n\n\tfunction normalizeIndex(value) {\n\t\tif (typeof value === \"number\" && Number.isFinite(value)) {\n\t\t\treturn Math.max(0, value);\n\t\t}\n\t\tconst parsed = Number(value);\n\t\tif (Number.isFinite(parsed)) {\n\t\t\treturn Math.max(0, parsed);\n\t\t}\n\t\treturn 0;\n\t}\n\n\tfunction normalizeSeverity(severity) {\n\t\tswitch (severity) {\n\t\t\tcase \"error\":\n\t\t\tcase \"fatal\":\n\t\t\t\treturn \"error\";\n\t\t\tcase \"warn\":\n\t\t\tcase \"warning\":\n\t\t\t\treturn \"warning\";\n\t\t\tdefault:\n\t\t\t\treturn \"info\";\n\t\t}\n\t}\n\n\tfunction getIconForType(type) {\n\t\tswitch (type) {\n\t\t\tcase \"error\":\n\t\t\t\treturn \"cancel\";\n\t\t\tcase \"warning\":\n\t\t\t\treturn \"warningreport_problem\";\n\t\t\tdefault:\n\t\t\t\treturn \"info\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/pages/problems/style.scss",
    "content": "#problems {\n  height: 100%;\n  width: 100%;\n\n  .single-file {\n    padding: 10px;\n    box-sizing: border-box;\n\n    summary {\n      height: 40px;\n      width: 100%;\n      font-weight: bold;\n      line-height: 40px;\n    }\n\n    .problem {\n      display: flex;\n      padding: 5px;\n      border-bottom: solid 1px var(--border-color);\n\n      * {\n        pointer-events: none;\n      }\n\n      .icon {\n        margin: 0 5px;\n        font-size: 0.9rem;\n        color: var(--primary-text-color) !important;\n      }\n\n      .problem-line {\n        display: flex;\n        align-items: center;\n        margin-left: 10px;\n        font-size: 0.9rem;\n      }\n\n      .problem-message {\n        flex: 1;\n\n        &[data-type='error'] {\n          color: var(--danger-color);\n        }\n\n        &[data-type='warning'] {\n          color: var(--error-text-color);\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/pages/quickTools/index.js",
    "content": "export default async function QuickToolsSettings() {\n\tconst { default: Settings } = await import(\"./quickTools.js\");\n\tSettings();\n}\n"
  },
  {
    "path": "src/pages/quickTools/quickTools.js",
    "content": "import \"./style.scss\";\nimport Page from \"components/page\";\nimport items, { description } from \"components/quickTools/items\";\nimport actionStack from \"lib/actionStack\";\nimport settings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport helpers from \"utils/helpers\";\n\nexport default function QuickTools() {\n\tconst $page = Page(strings[\"shortcut buttons\"]);\n\t$page.id = \"quicktools-settings-page\";\n\t$page.style.overflow = \"hidden\";\n\t$page.style.display = \"flex\";\n\t$page.style.flexDirection = \"column\";\n\n\tconst manager = new QuickToolsManager();\n\t$page.body = manager.getContainer();\n\n\tconst onShow = $page.onshow;\n\t$page.onshow = function () {\n\t\tif (onShow) onShow.call(this);\n\t\tconst scrollContainer = $page.get(\".scroll-container\") || $page;\n\t\tscrollContainer.style.overflow = \"hidden\";\n\t\tmanager.getContainer().style.height = \"100%\";\n\t};\n\n\tactionStack.push({\n\t\tid: \"quicktools-settings\",\n\t\taction: $page.hide,\n\t});\n\n\t$page.onhide = () => {\n\t\tactionStack.remove(\"quicktools-settings\");\n\t\thideAd();\n\t\t// Cleanup manager\n\t\tmanager.destroy();\n\t};\n\n\tapp.append($page);\n\thelpers.showAd();\n}\n\nclass QuickToolsManager {\n\tconstructor() {\n\t\tthis.container = <div id=\"quicktools-settings\"></div>;\n\t\tthis.render();\n\t\tthis.bindEvents();\n\n\t\tthis.longPressTimer = null;\n\t\tthis.dragState = null;\n\t}\n\n\tgetContainer() {\n\t\treturn this.container;\n\t}\n\n\trender() {\n\t\tthis.destroy(); // Cleanup potential drag states before re-rendering\n\t\tthis.container.textContent = \"\";\n\n\t\t// --- Active Tools Section ---\n\t\tconst activeSection = <div className=\"section active-tools\"></div>;\n\t\tactiveSection.appendChild(\n\t\t\t<div className=\"section-title\">{strings[\"active tools\"]}</div>,\n\t\t);\n\n\t\tconst activeGrid = <div className=\"quicktools-grid active-grid\"></div>;\n\n\t\tconst totalSlots =\n\t\t\tsettings.QUICKTOOLS_ROWS *\n\t\t\tsettings.QUICKTOOLS_GROUPS *\n\t\t\tsettings.QUICKTOOLS_GROUP_CAPACITY;\n\n\t\tfor (let i = 0; i < totalSlots; i++) {\n\t\t\tconst itemIndex = settings.value.quicktoolsItems[i];\n\t\t\tconst itemDef = items[itemIndex];\n\t\t\tconst el = this.createItemElement(itemDef, i, \"active\");\n\t\t\tactiveGrid.appendChild(el);\n\t\t}\n\n\t\tactiveSection.appendChild(activeGrid);\n\t\tthis.container.appendChild(activeSection);\n\n\t\t// --- Available Tools Section ---\n\t\tconst availableSection = <div className=\"section available-tools\"></div>;\n\t\tavailableSection.appendChild(\n\t\t\t<div className=\"section-title\">{strings[\"available tools\"]}</div>,\n\t\t);\n\n\t\t// Group items\n\t\tconst categories = {\n\t\t\tModifiers: [\"ctrl\", \"shift\", \"alt\", \"meta\"],\n\t\t\tCommands: [\"command\", \"undo\", \"redo\", \"save\", \"search\"],\n\t\t\tNavigation: [\"key\"],\n\t\t\tSymbols: [\"insert\"],\n\t\t\tOther: [],\n\t\t};\n\n\t\tconst groupedItems = {};\n\t\titems.forEach((item, index) => {\n\t\t\tlet category = \"Other\";\n\t\t\tfor (const [cat, actions] of Object.entries(categories)) {\n\t\t\t\tif (actions.includes(item.action)) {\n\t\t\t\t\tcategory = cat;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!groupedItems[category]) groupedItems[category] = [];\n\t\t\tgroupedItems[category].push({ item, index });\n\t\t});\n\n\t\tObject.entries(groupedItems).forEach(([category, list]) => {\n\t\t\tconst catHeader = <div className=\"category-header\">{category}</div>;\n\t\t\tconst catGrid = <div className=\"quicktools-grid source-grid\"></div>;\n\n\t\t\tlist.forEach(({ item, index }) => {\n\t\t\t\tconst el = this.createItemElement(item, index, \"source\");\n\t\t\t\tcatGrid.appendChild(el);\n\t\t\t});\n\n\t\t\tavailableSection.appendChild(catHeader);\n\t\t\tavailableSection.appendChild(catGrid);\n\t\t});\n\n\t\tthis.container.appendChild(availableSection);\n\t}\n\n\tcreateItemElement(itemDef, index, type) {\n\t\tif (!itemDef)\n\t\t\treturn (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"tool-item empty\"\n\t\t\t\t\tdata-index={index}\n\t\t\t\t\tdata-type={type}\n\t\t\t\t></div>\n\t\t\t);\n\n\t\tconst hasIcon = itemDef.icon && itemDef.icon !== \"letters\";\n\t\t// If it's not an icon, we assume it relies on 'letters'\n\t\t// Some items might have both, but 'letters' mode implies text rendering\n\n\t\tconst el = (\n\t\t\t<div\n\t\t\t\tclassName={`tool-item ${hasIcon ? \"has-icon\" : \"has-letters\"}`}\n\t\t\t\tdata-index={index} // active: slot index, source: item index\n\t\t\t\tdata-type={type}\n\t\t\t\tdata-letters={itemDef.letters || \"\"}\n\t\t\t>\n\t\t\t\t{hasIcon ? <span className={`icon ${itemDef.icon}`}></span> : null}\n\t\t\t</div>\n\t\t);\n\t\treturn el;\n\t}\n\n\tbindEvents() {\n\t\tconst c = this.container;\n\t\tc.addEventListener(\"touchstart\", this.handleTouchStart.bind(this), {\n\t\t\tpassive: false,\n\t\t});\n\t\tc.addEventListener(\"touchmove\", this.handleTouchMove.bind(this), {\n\t\t\tpassive: false,\n\t\t});\n\t\tc.addEventListener(\"touchend\", this.handleTouchEnd.bind(this));\n\t\tc.addEventListener(\"contextmenu\", (e) => e.preventDefault());\n\n\t\tc.addEventListener(\"mousedown\", this.handleMouseDown.bind(this));\n\t}\n\n\t// --- Touch Handlers ---\n\n\thandleTouchStart(e) {\n\t\t// If already dragging or pending, ignore new touches (prevent multi-touch mess)\n\t\tif (this.dragState || this.longPressTimer) return;\n\n\t\tconst target = e.target.closest(\".tool-item\");\n\t\tif (!target) return;\n\n\t\tthis.longPressTimer = setTimeout(() => {\n\t\t\tthis.startDrag(target, e.touches[0]);\n\t\t}, 300);\n\n\t\tthis.touchStartX = e.touches[0].clientX;\n\t\tthis.touchStartY = e.touches[0].clientY;\n\t\tthis.potentialTarget = target;\n\t}\n\n\thandleTouchMove(e) {\n\t\tconst touch = e.touches[0];\n\t\tif (this.dragState) {\n\t\t\te.preventDefault();\n\t\t\tthis.updateDrag(touch);\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tMath.hypot(\n\t\t\t\ttouch.clientX - this.touchStartX,\n\t\t\t\ttouch.clientY - this.touchStartY,\n\t\t\t) > 10\n\t\t) {\n\t\t\tclearTimeout(this.longPressTimer);\n\t\t\tthis.longPressTimer = null;\n\t\t\tthis.potentialTarget = null;\n\t\t}\n\t}\n\n\thandleTouchEnd(e) {\n\t\tclearTimeout(this.longPressTimer);\n\n\t\tif (this.dragState) {\n\t\t\tthis.endDrag();\n\t\t} else if (this.potentialTarget) {\n\t\t\t// It was a tap\n\t\t\tif (e.cancelable) e.preventDefault();\n\t\t\tthis.handleClick(this.potentialTarget);\n\t\t}\n\n\t\tthis.potentialTarget = null;\n\t}\n\n\t// --- Mouse Handlers ---\n\n\thandleMouseDown(e) {\n\t\tconst target = e.target.closest(\".tool-item\");\n\t\tif (!target) return;\n\n\t\tthis.mouseDownInfo = {\n\t\t\ttarget,\n\t\t\tx: e.clientX,\n\t\t\ty: e.clientY,\n\t\t\tisDrag: false,\n\t\t};\n\n\t\tconst moveHandler = (ev) => {\n\t\t\tif (!this.mouseDownInfo.isDrag) {\n\t\t\t\tif (\n\t\t\t\t\tMath.hypot(\n\t\t\t\t\t\tev.clientX - this.mouseDownInfo.x,\n\t\t\t\t\t\tev.clientY - this.mouseDownInfo.y,\n\t\t\t\t\t) > 5\n\t\t\t\t) {\n\t\t\t\t\tthis.mouseDownInfo.isDrag = true;\n\t\t\t\t\tthis.startDrag(target, this.mouseDownInfo);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.dragState) {\n\t\t\t\tthis.updateDrag(ev);\n\t\t\t}\n\t\t};\n\n\t\tconst upHandler = () => {\n\t\t\tdocument.removeEventListener(\"mousemove\", moveHandler);\n\t\t\tdocument.removeEventListener(\"mouseup\", upHandler);\n\n\t\t\tif (this.dragState) {\n\t\t\t\tthis.endDrag();\n\t\t\t} else {\n\t\t\t\tthis.handleClick(target);\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"mousemove\", moveHandler);\n\t\tdocument.addEventListener(\"mouseup\", upHandler);\n\t}\n\n\t// --- Core Drag Logic ---\n\n\tstartDrag(el, pointer) {\n\t\t// Double check state\n\t\tif (this.dragState) {\n\t\t\tthis.destroy();\n\t\t\treturn;\n\t\t}\n\n\t\tif (navigator.vibrate) navigator.vibrate(30);\n\n\t\tconst rect = el.getBoundingClientRect();\n\t\tconst ghost = el.cloneNode(true);\n\t\tghost.classList.add(\"tool-ghost\");\n\t\tghost.style.width = rect.width + \"px\";\n\t\tghost.style.height = rect.height + \"px\";\n\n\t\tdocument.body.appendChild(ghost);\n\t\tel.classList.add(\"dragging\");\n\n\t\tconst type = el.dataset.type;\n\t\tconst index = Number.parseInt(el.dataset.index, 10);\n\n\t\tthis.dragState = {\n\t\t\tel,\n\t\t\ttype, // 'active' or 'source'\n\t\t\tindex, // slot index (active) or item ID (source)\n\t\t\tghost,\n\t\t\toffsetX: pointer.clientX - rect.left - rect.width / 2,\n\t\t\toffsetY: pointer.clientY - rect.top - rect.height / 2,\n\t\t};\n\n\t\tthis.updateDrag(pointer);\n\t}\n\n\tupdateDrag(pointer) {\n\t\tconst { ghost } = this.dragState;\n\t\tghost.style.left = pointer.clientX + \"px\";\n\t\tghost.style.top = pointer.clientY + \"px\";\n\n\t\tconst elementBelow = document.elementFromPoint(\n\t\t\tpointer.clientX,\n\t\t\tpointer.clientY,\n\t\t);\n\n\t\tthis.cleanupHighlight();\n\n\t\tconst targetItem = elementBelow?.closest(\".tool-item\");\n\t\tif (targetItem && targetItem.dataset.type === \"active\") {\n\t\t\ttargetItem.classList.add(\"highlight-target\");\n\t\t\tthis.dragState.dropTarget = targetItem;\n\t\t} else {\n\t\t\tthis.dragState.dropTarget = null;\n\t\t}\n\t}\n\n\tcleanupHighlight() {\n\t\tconst highlighted = this.container.querySelectorAll(\".highlight-target\");\n\t\thighlighted.forEach((el) => el.classList.remove(\"highlight-target\"));\n\t}\n\n\tendDrag() {\n\t\tconst { el, ghost, dropTarget, type, index } = this.dragState;\n\n\t\tthis.cleanupHighlight();\n\t\tel.classList.remove(\"dragging\");\n\t\tghost.remove();\n\t\tthis.dragState = null;\n\n\t\tif (dropTarget) {\n\t\t\tconst targetIndex = Number.parseInt(dropTarget.dataset.index, 10);\n\n\t\t\tif (type === \"active\") {\n\t\t\t\t// Swap within active\n\t\t\t\tif (targetIndex !== index) {\n\t\t\t\t\tthis.swapItems(index, targetIndex);\n\t\t\t\t}\n\t\t\t} else if (type === \"source\") {\n\t\t\t\t// Replace active slot with source item\n\t\t\t\tthis.replaceItem(targetIndex, index);\n\t\t\t}\n\t\t}\n\t}\n\n\tswapItems(srcIndex, destIndex) {\n\t\tconst temp = settings.value.quicktoolsItems[srcIndex];\n\t\tsettings.value.quicktoolsItems[srcIndex] =\n\t\t\tsettings.value.quicktoolsItems[destIndex];\n\t\tsettings.value.quicktoolsItems[destIndex] = temp;\n\n\t\tsettings.update();\n\t\tthis.render(); // Re-render to reflect changes\n\t}\n\n\treplaceItem(slotIndex, newItemId) {\n\t\tsettings.value.quicktoolsItems[slotIndex] = newItemId;\n\t\tsettings.update();\n\t\tthis.render();\n\t}\n\n\tasync handleClick(el) {\n\t\tconst type = el.dataset.type;\n\t\tconst index = Number.parseInt(el.dataset.index, 10);\n\n\t\tlet itemDef;\n\t\tif (type === \"active\") {\n\t\t\tconst itemIndex = settings.value.quicktoolsItems[index];\n\t\t\titemDef = items[itemIndex];\n\t\t} else {\n\t\t\titemDef = items[index];\n\t\t}\n\n\t\tif (itemDef) {\n\t\t\tconst desc = description(itemDef.id);\n\t\t\twindow.toast(desc, 2000);\n\t\t}\n\t}\n\n\tdestroy() {\n\t\tif (this.longPressTimer) clearTimeout(this.longPressTimer);\n\t\tthis.longPressTimer = null;\n\n\t\tif (this.dragState) {\n\t\t\tif (this.dragState.ghost) {\n\t\t\t\tthis.dragState.ghost.remove();\n\t\t\t}\n\t\t\tif (this.dragState.el) {\n\t\t\t\tthis.dragState.el.classList.remove(\"dragging\");\n\t\t\t}\n\t\t}\n\n\t\tthis.cleanupHighlight();\n\t\tthis.dragState = null;\n\t\tthis.potentialTarget = null;\n\t}\n}\n"
  },
  {
    "path": "src/pages/quickTools/style.scss",
    "content": "#quicktools-settings {\n  display: flex;\n  flex-direction: column;\n  height: 100%;\n  width: 100%;\n  overflow-y: hidden;\n  box-sizing: border-box;\n  background-color: var(--primary-color);\n\n  .section-title {\n    font-size: 1rem;\n    font-weight: bold;\n    padding: 10px;\n    color: var(--text-color);\n    opacity: 0.8;\n    background-color: var(--primary-color);\n    z-index: 10;\n    width: 100%;\n    display: block;\n    box-sizing: border-box;\n  }\n\n  .section {\n    display: flex;\n    flex-direction: column;\n    width: 100%;\n    box-sizing: border-box;\n  }\n\n  .section.active-tools {\n    flex: 0 0 auto;\n    border-bottom: 3px dashed var(--border-color);\n    padding-bottom: 10px;\n    margin-bottom: 15px;\n    padding-left: 10px;\n    padding-right: 10px;\n  }\n\n  .section.available-tools {\n    flex: 1 1 auto;\n    overflow-y: auto;\n    padding: 0 10px 10px 10px;\n\n    .category-header {\n      font-size: 0.8rem;\n      text-transform: uppercase;\n      opacity: 0.6;\n      margin-top: 15px;\n      margin-bottom: 5px;\n      margin-left: 5px;\n      font-weight: bold;\n      display: block;\n      width: 100%;\n    }\n  }\n\n  .quicktools-grid {\n    display: grid;\n    grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));\n    gap: 8px;\n    width: 100%;\n    padding-bottom: 5px;\n    padding-top: 5px;\n\n    &.active-grid {\n      grid-template-columns: repeat(8, 1fr);\n      min-height: auto;\n      margin-bottom: 5px;\n    }\n  }\n\n  .tool-item {\n    background-color: var(--secondary-color);\n    color: var(--text-color);\n    border-radius: 8px;\n    aspect-ratio: 1;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 1rem;\n    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);\n    transition: transform 0.2s ease, background-color 0.2s;\n    user-select: none;\n    -webkit-user-select: none;\n    cursor: grab;\n    position: relative;\n    border: 1px solid transparent;\n    border-bottom: 2px dashed var(--border-color);\n\n    &:active {\n      transform: scale(0.95);\n      cursor: grabbing;\n    }\n\n    &.dragging {\n      opacity: 0.3;\n      transform: scale(0.9);\n      border-color: var(--active-color);\n    }\n\n    &.highlight-target {\n      border-color: var(--active-color);\n      background-color: rgba(0, 0, 0, 0.1);\n      transform: scale(1.05);\n    }\n\n    &.empty {\n      background-color: transparent;\n      border: 2px dashed rgba(0, 0, 0, 0.1);\n      box-shadow: none;\n    }\n\n    .icon {\n      font-size: 1.2rem;\n      pointer-events: none;\n    }\n\n    &.has-letters::before {\n      content: attr(data-letters);\n      font-size: 0.9rem;\n      font-weight: bold;\n      text-transform: uppercase;\n    }\n\n    &.has-icon::before {\n      display: none;\n    }\n  }\n}\n\n.tool-ghost {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 60px;\n  height: 60px;\n  background-color: var(--active-color);\n  color: #fff;\n  border-radius: 12px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  z-index: 9999;\n  pointer-events: none;\n  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);\n  transform: translate(-50%, -50%) scale(1.1);\n\n  .icon {\n    font-size: 1.5rem;\n  }\n\n  &.has-letters::before {\n    content: attr(data-letters);\n    font-size: 1rem;\n    font-weight: bold;\n    text-transform: uppercase;\n  }\n}"
  },
  {
    "path": "src/pages/sponsor/index.js",
    "content": "/**\n * Sponsor page\n * @param {() => void} onclose\n */\nexport default function Sponsor(onclose) {\n\timport(\"./sponsor\").then((res) => res.default(onclose));\n}\n"
  },
  {
    "path": "src/pages/sponsor/sponsor.js",
    "content": "import \"./style.scss\";\nimport fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport Logo from \"components/logo\";\nimport Page from \"components/page\";\nimport alert from \"dialogs/alert\";\nimport box from \"dialogs/box\";\nimport loader from \"dialogs/loader\";\nimport multiPrompt from \"dialogs/multiPrompt\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport helpers from \"utils/helpers\";\n\n//TODO: fix (-1 means, user is not logged in to any google account)\n\n/**\n * Sponsor page\n * @param {() => void} onclose\n */\nexport default function Sponsor(onclose) {\n\tconst BASE_URL = \"https://acode.app/res/\";\n\tconst $page = Page(strings.sponsor);\n\tlet cancel = false;\n\n\tactionStack.push({\n\t\tid: \"sponsor_page\",\n\t\taction: $page.hide,\n\t});\n\n\t$page.onhide = function () {\n\t\tonclose?.();\n\t\tcancel = true;\n\t\tactionStack.remove(\"sponsor_page\");\n\t};\n\n\tapp.append($page);\n\n\tiap.setPurchaseUpdatedListener(\n\t\t(purchases) => {\n\t\t\tif (Array.isArray(purchases)) {\n\t\t\t\t(async function () {\n\t\t\t\t\tconst promises = [];\n\t\t\t\t\tfor (let purchase of purchases) {\n\t\t\t\t\t\tpromises.push(\n\t\t\t\t\t\t\tnew Promise((resolve, reject) => {\n\t\t\t\t\t\t\t\tiap.consume(\n\t\t\t\t\t\t\t\t\tpurchase.purchaseToken,\n\t\t\t\t\t\t\t\t\t(resCode) => {\n\t\t\t\t\t\t\t\t\t\tpurchase.consumed = resCode === iap.OK ? true : false;\n\t\t\t\t\t\t\t\t\t\tpurchase.consumeCode = resCode;\n\t\t\t\t\t\t\t\t\t\tresolve(purchase);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t(err) => {\n\t\t\t\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst settledPromises = await Promise.allSettled(promises);\n\t\t\t\t\tconst rejectedPromise = settledPromises.find(\n\t\t\t\t\t\t(promise) => promise.status === \"rejected\",\n\t\t\t\t\t);\n\t\t\t\t\tlet msg = \"\";\n\t\t\t\t\tif (rejectedPromise) {\n\t\t\t\t\t\tmsg = \"Something went wrong.\\n\";\n\t\t\t\t\t\tmsg += `Error: ${rejectedPromise.reason}\\n`;\n\t\t\t\t\t\tmsg += `Code: ${rejectedPromise.value.resCode}`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst blob = await ajax({\n\t\t\t\t\t\t\turl: BASE_URL + \"6.jpeg\",\n\t\t\t\t\t\t\tresponseType: \"blob\",\n\t\t\t\t\t\t}).catch((err) => {\n\t\t\t\t\t\t\thelpers.error(err);\n\t\t\t\t\t\t});\n\t\t\t\t\t\tconst url = URL.createObjectURL(blob);\n\t\t\t\t\t\tmsg = `<img src=\"${url}\" class=\"donate-image\" />`;\n\t\t\t\t\t\tmsg += \"<br><p>Thank you for supporting Acode!</p>\";\n\t\t\t\t\t}\n\n\t\t\t\t\tconst order = settledPromises[0].value;\n\t\t\t\t\tconst [productId] = order.productIds;\n\t\t\t\t\tconst sponsorDetails = JSON.parse(\n\t\t\t\t\t\tlocalStorage.getItem(`sponsor_${productId}`),\n\t\t\t\t\t);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst res = await ajax.post(`${constants.API_BASE}/sponsor`, {\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t...sponsorDetails,\n\t\t\t\t\t\t\t\ttier: productId,\n\t\t\t\t\t\t\t\tpackageName: BuildInfo.packageName,\n\t\t\t\t\t\t\t\tpurchaseToken: order.purchaseToken,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (res.error) {\n\t\t\t\t\t\t\thelpers.error(res.error);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbox(strings.info.toUpperCase(), msg);\n\t\t\t\t\t\t\tlocalStorage.removeItem(`sponsor_${productId}`);\n\t\t\t\t\t\t\t$page.hide();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\thelpers.error(error);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tloader.removeTitleLoader();\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}\n\t\t},\n\t\t(err) => {\n\t\t\tif (err !== iap.USER_CANCELED) {\n\t\t\t\talert(strings.error.toUpperCase(), err);\n\t\t\t}\n\t\t\tloader.removeTitleLoader();\n\t\t},\n\t);\n\n\tloader.showTitleLoader();\n\trender()\n\t\t.catch((error) => {\n\t\t\tactionStack.pop();\n\t\t\thelpers.error(error);\n\t\t})\n\t\t.finally(() => {\n\t\t\tloader.removeTitleLoader();\n\t\t});\n\n\tasync function render() {\n\t\tlet products = await new Promise((resolve, reject) => {\n\t\t\tiap.getProducts(\n\t\t\t\tconstants.SKU_LIST,\n\t\t\t\t(products) => {\n\t\t\t\t\tresolve(products);\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\n\t\tif (cancel) return;\n\n\t\tproducts = products.sort((a, b) => {\n\t\t\tconst aPrice = Number.parseFloat(a.price.replace(/[^0-9.]/g, \"\"));\n\t\t\tconst bPrice = Number.parseFloat(b.price.replace(/[^0-9.]/g, \"\"));\n\t\t\treturn bPrice - aPrice;\n\t\t});\n\n\t\t$page.body = (\n\t\t\t<div id=\"sponsor-page\" className=\"main\">\n\t\t\t\t<div className=\"header\">\n\t\t\t\t\t<Logo />\n\t\t\t\t\t<h1>Sponsor Acode</h1>\n\t\t\t\t\t<p className=\"subtitle\">Support the future of mobile coding</p>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"tiers\">\n\t\t\t\t\t{products.map((product) => (\n\t\t\t\t\t\t<div className={`tier ${product.productId}`}>\n\t\t\t\t\t\t\t<div className=\"tier-header\">\n\t\t\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t\t\t<span className={`tier-icon ${product.productId}`}></span>\n\t\t\t\t\t\t\t\t\t{onlyTitle(product.title)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"tier-price\">{product.price}/Month</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName=\"tier-description\"\n\t\t\t\t\t\t\t\tinnerText={product.description}\n\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tclassName=\"purchase-btn\"\n\t\t\t\t\t\t\t\tonclick={() => handlePurchase(product.productId, product.title)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{strings.select}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n}\n\nasync function handlePurchase(productId, title) {\n\tlet image;\n\tlet result;\n\tconst extraFields = [];\n\n\tif ([\"silver\", \"gold\", \"platinum\", \"titanium\"].includes(productId)) {\n\t\textraFields.push({\n\t\t\tplaceholder: \"Website\",\n\t\t\trequired: false,\n\t\t\tid: \"website\",\n\t\t\ttype: \"url\",\n\t\t});\n\t}\n\n\tif (productId === \"titanium\") {\n\t\textraFields.push({\n\t\t\tplaceholder: \"Tagline\",\n\t\t\trequired: false,\n\t\t\tid: \"tagline\",\n\t\t\ttype: \"text\",\n\t\t});\n\t}\n\n\tif ([\"gold\", \"platinum\", \"titanium\"].includes(productId)) {\n\t\textraFields.push({\n\t\t\tplaceholder: \"Logo/Image (500KB max)\",\n\t\t\trequired: false,\n\t\t\ttype: \"text\",\n\t\t\tid: \"image\",\n\t\t\tonclick() {\n\t\t\t\tsdcard.openDocumentFile(async (res) => {\n\t\t\t\t\tif (res.length > 500000) {\n\t\t\t\t\t\tthis.setError(\"File size exceeds 500KB\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.setError(\"\");\n\t\t\t\t\tthis.value = res.filename;\n\t\t\t\t\tconst arraybuffer = await fsOperation(res.uri).readFile();\n\t\t\t\t\tconst blob = new Blob([arraybuffer], { type: res.type });\n\t\t\t\t\tconst reader = new FileReader();\n\t\t\t\t\treader.onload = () => {\n\t\t\t\t\t\timage = reader.result;\n\n\t\t\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\t\t\tresult.image = image;\n\t\t\t\t\t\t\tlocalStorage.setItem(\n\t\t\t\t\t\t\t\t`sponsor_${productId}`,\n\t\t\t\t\t\t\t\tJSON.stringify(result),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\treader.readAsDataURL(blob);\n\t\t\t\t}, \"image/*\");\n\t\t\t},\n\t\t});\n\t}\n\n\tresult = await multiPrompt(onlyTitle(title), [\n\t\t{\n\t\t\tplaceholder: \"Name\",\n\t\t\trequired: true,\n\t\t\tid: \"name\",\n\t\t},\n\t\t{\n\t\t\tplaceholder: \"Email\",\n\t\t\trequired: false,\n\t\t\tid: \"email\",\n\t\t\ttype: \"email\",\n\t\t\tmatch:\n\t\t\t\t/^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|.(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/,\n\t\t},\n\t\t...extraFields,\n\t\t{\n\t\t\tplaceholder: \"Show in sponsors list\",\n\t\t\trequired: false,\n\t\t\tid: \"public\",\n\t\t\ttype: \"checkbox\",\n\t\t\tvalue: true,\n\t\t},\n\t]);\n\n\tif (!result) {\n\t\treturn;\n\t}\n\n\tif (image) {\n\t\tresult.image = image;\n\t}\n\n\tlocalStorage.setItem(`sponsor_${productId}`, JSON.stringify(result));\n\tloader.showTitleLoader();\n\tiap.purchase(productId);\n}\n\nfunction onlyTitle(title) {\n\treturn title.replace(\" (Acode - code editor | FOSS)\", \"\");\n}\n"
  },
  {
    "path": "src/pages/sponsor/style.scss",
    "content": "#sponsor-page {\n  overflow: auto;\n\n  .tier-icon {\n    display: inline-block;\n    width: 24px;\n    height: 24px;\n    margin-right: 8px;\n    border-radius: 50%;\n\n    &.crystal {\n      background: linear-gradient(45deg, #95a5a6, #bdc3c7);\n    }\n\n    &.bronze {\n      background: linear-gradient(45deg, #cd7f32, #ff8c42);\n    }\n\n    &.silver {\n      background: linear-gradient(45deg, #c0c0c0, #e6e6fa);\n    }\n\n    &.gold {\n      background: linear-gradient(45deg, #ffd700, #ffed4e);\n    }\n\n    &.platinum {\n      background: linear-gradient(45deg, #e5e4e2, #ffffff);\n    }\n\n    &.titanium {\n      background: linear-gradient(45deg, #878681, #c4c4b0);\n    }\n  }\n\n  .header {\n    text-align: center;\n    margin-bottom: 10px;\n    animation: fadeInUp 0.8s ease-out;\n  }\n\n  h1 {\n    font-size: 28px;\n    font-weight: 700;\n  }\n\n  .subtitle {\n    font-size: 16px;\n    opacity: 0.9;\n    margin-bottom: 10px;\n  }\n\n  .tier {\n    width: 90%;\n    max-width: 400px;\n    margin: 0 auto;\n    border-radius: 16px;\n    padding: 20px;\n    margin-bottom: 15px;\n    background-color: var(--popup-background-color);\n    color: var(--popup-text-color);\n    border: 1px solid var(--border-color);\n    transition: all 0.3s ease;\n    animation: fadeInUp 0.8s ease-out calc(0.6s + var(--delay, 0s)) both;\n    box-sizing: border-box;\n  }\n\n  .tier-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    margin-bottom: 10px;\n  }\n\n  .tier-name {\n    font-size: 18px;\n    font-weight: 600;\n    display: flex;\n    align-items: center;\n  }\n\n  .tier-icon {\n    width: 24px;\n    height: 24px;\n    border-radius: 50%;\n    margin-right: 10px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    font-size: 14px;\n  }\n\n  .tier-price {\n    font-size: 16px;\n    font-weight: 600;\n    opacity: 0.9;\n  }\n\n  .tier-description {\n    font-size: 14px;\n    opacity: 0.8;\n    line-height: 1.4;\n  }\n\n  .purchase-btn {\n    background: var(--button-background-color);\n    color: var(--button-text-color);\n    border: none;\n    padding: 10px 20px;\n    border-radius: 20px;\n    font-size: 14px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: all 0.3s ease;\n    width: 100%;\n    margin-top: 10px;\n  }\n\n  .purchase-btn:active {\n    background: linear-gradient(45deg, #44a08d, #4ecdc4);\n  }\n\n  .purchase-btn:disabled {\n    opacity: 0.5;\n    cursor: not-allowed;\n    transform: none;\n  }\n}"
  },
  {
    "path": "src/pages/sponsors/index.js",
    "content": "export default function Sponsors() {\n\timport(\"./sponsors\").then((res) => res.default());\n}\n"
  },
  {
    "path": "src/pages/sponsors/sponsors.js",
    "content": "import ajax from \"@deadlyjack/ajax\";\nimport \"./style.scss\";\nimport Page from \"components/page\";\nimport toast from \"components/toast\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport Sponsor from \"pages/sponsor\";\nimport helpers from \"utils/helpers\";\n\nexport default function Sponsors() {\n\tconst page = Page(\"Sponsors\");\n\tconst titaniumSponsors = Ref();\n\tconst platinumSponsors = Ref();\n\tconst goldSponsors = Ref();\n\tconst silverSponsors = Ref();\n\tconst bronzeSponsors = Ref();\n\tconst crystalSponsors = Ref();\n\tlet cancel = false;\n\n\tactionStack.push({\n\t\tid: \"sponsors_page\",\n\t\taction: page.hide,\n\t});\n\n\tpage.onhide = () => {\n\t\tactionStack.remove(\"sponsors_page\");\n\t\tcancel = true;\n\t};\n\n\tpage.body = (\n\t\t<div id=\"sponsors-page\">\n\t\t\t<div className=\"cta-section\">\n\t\t\t\t<p class=\"cta-text\">\n\t\t\t\t\tJoin our community of supporters and help shape the future of mobile\n\t\t\t\t\tdevelopment\n\t\t\t\t</p>\n\t\t\t\t<button class=\"cta-button\" onclick={() => Sponsor(render)}>\n\t\t\t\t\tBecome a Sponsor <span className=\"icon favorite\"></span>\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div className=\"sponsors-container\">\n\t\t\t\t<h2>Acode's Sponsors</h2>\n\t\t\t\t<div className=\"sponsors-list\" onclick={handleLinkClick}>\n\t\t\t\t\t<div className=\"tier\">\n\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t<span className=\"tier-icon titanium\"></span>Titanium\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"sponsors\"\n\t\t\t\t\t\t\tdata-empty-message=\"Be the first Titanium Sponsor!\"\n\t\t\t\t\t\t\tref={titaniumSponsors}\n\t\t\t\t\t\t></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"tier\">\n\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t<span className=\"tier-icon platinum\"></span>Platinum\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"sponsors\"\n\t\t\t\t\t\t\tdata-empty-message=\"Be the first Platinum Sponsor!\"\n\t\t\t\t\t\t\tref={platinumSponsors}\n\t\t\t\t\t\t></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"tier\">\n\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t<span className=\"tier-icon gold\"></span>Gold\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"sponsors\"\n\t\t\t\t\t\t\tdata-empty-message=\"Be the first Gold Sponsor!\"\n\t\t\t\t\t\t\tref={goldSponsors}\n\t\t\t\t\t\t></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"tier\">\n\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t<span className=\"tier-icon silver\"></span>Silver\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"sponsors\"\n\t\t\t\t\t\t\tdata-empty-message=\"Be the first Silver Sponsor!\"\n\t\t\t\t\t\t\tref={silverSponsors}\n\t\t\t\t\t\t></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"tier\">\n\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t<span className=\"tier-icon bronze\"></span>Bronze\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"sponsors\"\n\t\t\t\t\t\t\tdata-empty-message=\"Be the first Bronze Sponsor!\"\n\t\t\t\t\t\t\tref={bronzeSponsors}\n\t\t\t\t\t\t></div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"tier\">\n\t\t\t\t\t\t<div className=\"tier-name\">\n\t\t\t\t\t\t\t<span className=\"tier-icon crystal\"></span>Crystal\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"sponsors\"\n\t\t\t\t\t\t\tdata-empty-message=\"Be the first Crystal Sponsor!\"\n\t\t\t\t\t\t\tref={crystalSponsors}\n\t\t\t\t\t\t></div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n\n\trender();\n\tapp.append(page);\n\n\tasync function render() {\n\t\tlet sponsors = [];\n\t\ttry {\n\t\t\tconst res = await ajax.get(`${constants.API_BASE}/sponsors`);\n\t\t\tif (res.error) {\n\t\t\t\ttoast(\"Unable to load sponsors...\");\n\t\t\t\tconsole.error(\"Error loading sponsors:\", res.error);\n\t\t\t} else {\n\t\t\t\tsponsors = res;\n\t\t\t\tlocalStorage.setItem(\"cached_sponsors\", JSON.stringify(sponsors));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\ttoast(\"Unable to load sponsors...\");\n\t\t\tconsole.error(\"Error loading sponsors:\", error);\n\t\t}\n\n\t\tif (!sponsors.length && \"cached_sponsors\" in localStorage) {\n\t\t\ttry {\n\t\t\t\tconst cachedSponsors = helpers.parseJSON(\n\t\t\t\t\tlocalStorage.getItem(\"cached_sponsors\"),\n\t\t\t\t);\n\t\t\t\tsponsors = Array.isArray(cachedSponsors) ? cachedSponsors : [];\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Failed to parse cached sponsors\", error);\n\t\t\t}\n\t\t}\n\n\t\ttitaniumSponsors.content = \"\";\n\t\tplatinumSponsors.content = \"\";\n\t\tgoldSponsors.content = \"\";\n\t\tsilverSponsors.content = \"\";\n\t\tbronzeSponsors.content = \"\";\n\t\tcrystalSponsors.content = \"\";\n\n\t\tfor (const sponsor of sponsors) {\n\t\t\t// Append each sponsor to the corresponding tier\n\t\t\tswitch (sponsor.tier) {\n\t\t\t\tcase \"titanium\":\n\t\t\t\t\ttitaniumSponsors.append(<SponsorCard {...sponsor} />);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"platinum\":\n\t\t\t\t\tplatinumSponsors.append(<SponsorCard {...sponsor} />);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"gold\":\n\t\t\t\t\tgoldSponsors.append(<SponsorCard {...sponsor} />);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"silver\":\n\t\t\t\t\tsilverSponsors.append(<SponsorCard {...sponsor} />);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"bronze\":\n\t\t\t\t\tbronzeSponsors.append(<SponsorCard {...sponsor} />);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"crystal\":\n\t\t\t\t\tcrystalSponsors.append(<SponsorCard {...sponsor} />);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Sponsor Card Component\n * @param {object} props\n * @param {string} props.name - The name of the sponsor\n * @param {string} props.image - The image URL of the sponsor\n * @param {string} props.website - The website URL of the sponsor\n * @param {string} props.tier - The tier of the sponsor\n * @param {string} props.tagline - The tagline of the sponsor\n * @returns {JSX.Element}\n */\nfunction SponsorCard({ name, image, website, tier, tagline }) {\n\t// for crystal tier only text, for bronze slightly bigger text, for silver bigger clickable text,\n\t// for gold text with image, for platinum and titanium text with big image\n\n\treturn (\n\t\t<div\n\t\t\tattr-role=\"button\"\n\t\t\tdata-website={website}\n\t\t\tclassName={`sponsor-card ${tier}`}\n\t\t>\n\t\t\t{image && (\n\t\t\t\t<div className=\"sponsor-avatar\">\n\t\t\t\t\t<img src={`https://acode.app/sponsor/image/${image}`} />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"sponsor-name\">{name}</div>\n\t\t\t{tagline && <div className=\"sponsor-tagline\">{tagline}</div>}\n\t\t\t{website && <small className=\"sponsor-website\">{website}</small>}\n\t\t</div>\n\t);\n}\n\n/**\n * Handle link click\n * @param {MouseEvent} e\n * @returns\n */\nfunction handleLinkClick(e) {\n\tconst target = e.target.closest(\".sponsor-card\");\n\tif (!target) return;\n\tconst { website } = target.dataset;\n\tif (!website) return;\n\tif (!website.startsWith(\"http\")) {\n\t\twebsite = \"http://\" + website;\n\t}\n\tsystem.openInBrowser(website);\n}\n"
  },
  {
    "path": "src/pages/sponsors/style.scss",
    "content": "#sponsors-page {\n  .tier-icon {\n    display: inline-block;\n    width: 24px;\n    height: 24px;\n    border-radius: 50%;\n    margin-right: 8px;\n\n    &.crystal {\n      background: linear-gradient(45deg, #95a5a6, #bdc3c7);\n    }\n\n    &.bronze {\n      background: linear-gradient(45deg, #cd7f32, #ff8c42);\n    }\n\n    &.silver {\n      background: linear-gradient(45deg, #c0c0c0, #e6e6fa);\n    }\n\n    &.gold {\n      background: linear-gradient(45deg, #ffd700, #ffed4e);\n    }\n\n    &.platinum {\n      background: linear-gradient(45deg, #e5e4e2, #ffffff);\n    }\n\n    &.titanium {\n      background: linear-gradient(45deg, #878681, #c4c4b0);\n    }\n  }\n\n  .cta-section {\n    text-align: center;\n  }\n\n  .cta-button {\n    background: linear-gradient(45deg, #ff6b6b, #4ecdc4);\n    color: white;\n    border: none;\n    padding: 18px 40px;\n    border-radius: 30px;\n    font-size: 18px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: all 0.3s ease;\n    margin-bottom: 15px;\n    width: 90%;\n    max-width: 400px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin: auto;\n\n    .icon {\n      margin-left: 10px;\n    }\n  }\n\n  .cta-text {\n    font-size: 14px;\n    opacity: 0.8;\n    line-height: 1.4;\n    padding: 1rem;\n  }\n\n  .sponsors-container {\n    padding: 1rem;\n\n    h2 {\n      text-align: center;\n      font-weight: 700;\n    }\n\n    .tier-name {\n      font-size: 18px;\n      font-weight: 600;\n      display: flex;\n      align-items: center;\n      margin: 1rem 0 0.5rem 0;\n    }\n\n    .sponsors {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 1rem;\n\n      &:empty::after {\n        content: attr(data-empty-message);\n        display: block;\n        width: 100%;\n        padding: 2rem;\n        font-style: italic;\n      }\n    }\n\n    .sponsor-card {\n      flex-grow: 1;\n      min-width: 150px;\n      background: var(--popup-background-color);\n      color: var(--popup-text-color);\n      border-radius: 12px;\n      padding: 15px;\n      text-align: center;\n      backdrop-filter: blur(10px);\n      border: 1px solid var(--border-color);\n      transition: all 0.3s ease;\n\n      .sponsor-avatar {\n        width: 50px;\n        height: 50px;\n        border-radius: 50%;\n        display: flex;\n        margin: 0 auto 10px;\n        align-items: center;\n        justify-content: center;\n        font-weight: bold;\n        font-size: 18px;\n        overflow: hidden;\n        background-color: currentColor;\n\n        img {\n          max-width: 100%;\n          max-height: 100%;\n          object-fit: cover;\n        }\n      }\n\n      .sponsor-name {\n        font-size: 14px;\n        font-weight: 500;\n        margin-bottom: 5px;\n      }\n\n      .sponsor-website {\n        opacity: 0.6;\n        font-size: 12px;\n      }\n\n      &.crystal,\n      &.bronze,\n      &.silver {\n        .sponsor-avatar {\n          display: none;\n        }\n      }\n\n      &.bronze,\n      &.crystal {\n        pointer-events: none;\n      }\n\n      &.silver,\n      &.bronze,\n      &.gold,\n      &.platinum,\n      &.titanium {\n        .sponsor-name {\n          font-size: 16px;\n        }\n      }\n\n      &.platinum,\n      &.titanium {\n        .sponsor-avatar {\n          width: 100px;\n          height: 100px;\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/pages/themeSetting/index.js",
    "content": "export default function themeSetting(...args) {\n\timport(/* webpackChunkName: \"themeSetting\" */ \"./themeSetting\").then(\n\t\t(module) => {\n\t\t\tmodule.default(...args);\n\t\t},\n\t);\n}\n"
  },
  {
    "path": "src/pages/themeSetting/themeSetting.js",
    "content": "import \"./themeSetting.scss\";\nimport { javascript } from \"@codemirror/lang-javascript\";\n// For CodeMirror preview\nimport { EditorState } from \"@codemirror/state\";\nimport { oneDark } from \"@codemirror/theme-one-dark\";\nimport { getThemeConfig, getThemeExtensions, getThemes } from \"cm/themes\";\nimport { basicSetup, EditorView } from \"codemirror\";\nimport Page from \"components/page\";\nimport searchBar from \"components/searchbar\";\nimport TabView from \"components/tabView\";\nimport alert from \"dialogs/alert\";\nimport Ref from \"html-tag-js/ref\";\nimport actionStack from \"lib/actionStack\";\nimport removeAds from \"lib/removeAds\";\nimport appSettings from \"lib/settings\";\nimport { hideAd } from \"lib/startAd\";\nimport CustomTheme from \"pages/customTheme\";\nimport ThemeBuilder from \"theme/builder\";\nimport themes from \"theme/list\";\nimport helpers from \"utils/helpers\";\n\nexport default function () {\n\tconst $page = Page(strings.theme.capitalize());\n\tconst $search = <span attr-action=\"search\" className=\"icon search\"></span>;\n\tconst $themePreview = (\n\t\t<div\n\t\t\tid=\"theme-preview\"\n\t\t\tstyle=\"min-height:120px;height:30vh;display:flex;\"\n\t\t></div>\n\t);\n\tconst list = new Ref();\n\tlet cmPreview = null;\n\tconst previewDoc = `// Acode is awesome!\\nconst message = \"Welcome to Acode\";\\nconsole.log(message);`;\n\n\tfunction destroyPreview(context) {\n\t\tif (!cmPreview) return;\n\t\ttry {\n\t\t\tcmPreview.destroy();\n\t\t} catch (error) {\n\t\t\tconsole.warn(`Failed to destroy theme preview (${context}).`, error);\n\t\t} finally {\n\t\t\tcmPreview = null;\n\t\t}\n\t}\n\n\tfunction createPreview(themeId) {\n\t\tdestroyPreview(\"create\");\n\t\tconst theme = getThemeExtensions(themeId, [oneDark]);\n\t\tconst fixedHeightTheme = EditorView.theme({\n\t\t\t\"&\": { height: \"100%\", flex: \"1 1 auto\" },\n\t\t\t\".cm-scroller\": { height: \"100%\", overflow: \"auto\" },\n\t\t});\n\t\tconst state = EditorState.create({\n\t\t\tdoc: previewDoc,\n\t\t\textensions: [basicSetup, javascript(), fixedHeightTheme, ...theme],\n\t\t});\n\t\tcmPreview = new EditorView({ state, parent: $themePreview });\n\t\tcmPreview.contentDOM.setAttribute(\"aria-readonly\", \"true\");\n\t}\n\n\tactionStack.push({\n\t\tid: \"appTheme\",\n\t\taction: () => {\n\t\t\tdestroyPreview(\"close\");\n\t\t\t$page.hide();\n\t\t\t$page.removeEventListener(\"click\", clickHandler);\n\t\t},\n\t});\n\n\t$page.onhide = () => {\n\t\thideAd();\n\t\tactionStack.remove(\"appTheme\");\n\t};\n\n\t$page.body = (\n\t\t<TabView id=\"theme-setting\">\n\t\t\t<div className=\"options\">\n\t\t\t\t<span className=\"active\" onclick={renderAppThemes} tabindex={0}>\n\t\t\t\t\tApp\n\t\t\t\t</span>\n\t\t\t\t<span onclick={renderEditorThemes} tabindex={0}>\n\t\t\t\t\tEditor\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div ref={list} id=\"theme-list\" className=\"list scroll\"></div>\n\t\t</TabView>\n\t);\n\t$page.querySelector(\"header\").append($search);\n\n\tapp.append($page);\n\trenderAppThemes();\n\thelpers.showAd();\n\n\t$page.addEventListener(\"click\", clickHandler);\n\n\tfunction renderAppThemes() {\n\t\t// Remove and destroy CodeMirror preview when showing app themes\n\t\tdestroyPreview(\"switch-tab\");\n\t\t$themePreview.remove();\n\t\tconst content = [];\n\n\t\tif (!DOES_SUPPORT_THEME) {\n\t\t\tcontent.push(\n\t\t\t\t<div className=\"list-item\">\n\t\t\t\t\t<span className=\"icon warningreport_problem\"></span>\n\t\t\t\t\t<div className=\"container\">\n\t\t\t\t\t\t<span className=\"text\">{strings[\"unsupported device\"]}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>,\n\t\t\t);\n\t\t}\n\n\t\tconst currentTheme = appSettings.value.appTheme;\n\t\tlet $currentItem;\n\t\tthemes.list().forEach((themeSummary) => {\n\t\t\tconst theme = themes.get(themeSummary.id);\n\t\t\tconst isCurrentTheme = theme.id === currentTheme;\n\t\t\tconst isPremium = theme.version === \"paid\" && IS_FREE_VERSION;\n\t\t\tconst $item = (\n\t\t\t\t<Item\n\t\t\t\t\tname={themeSummary.name}\n\t\t\t\t\tisPremium={isPremium}\n\t\t\t\t\tisCurrent={isCurrentTheme}\n\t\t\t\t\tswatches={getAppThemeSwatches(theme)}\n\t\t\t\t\tonclick={() => setAppTheme(theme, isPremium)}\n\t\t\t\t/>\n\t\t\t);\n\t\t\tcontent.push($item);\n\t\t\tif (isCurrentTheme) $currentItem = $item;\n\t\t});\n\n\t\tlist.el.content = content;\n\t\t$currentItem?.scrollIntoView();\n\t}\n\n\tfunction renderEditorThemes() {\n\t\tconst currentTheme = (\n\t\t\tappSettings.value.editorTheme || \"one_dark\"\n\t\t).toLowerCase();\n\t\tif (innerHeight * 0.3 >= 120) {\n\t\t\t$page.body.append($themePreview);\n\t\t\tcreatePreview(currentTheme);\n\t\t} else {\n\t\t\t$themePreview.remove();\n\t\t}\n\n\t\tconst themeList = getThemes();\n\t\tlet $currentItem;\n\t\tlist.el.content = themeList.map((t) => {\n\t\t\tconst isCurrent = t.id === currentTheme;\n\t\t\tconst $item = (\n\t\t\t\t<Item\n\t\t\t\t\tname={t.caption}\n\t\t\t\t\tisCurrent={isCurrent}\n\t\t\t\t\tswatches={getEditorThemeSwatches(t.id)}\n\t\t\t\t\tonclick={() => setEditorTheme({ caption: t.caption, theme: t.id })}\n\t\t\t\t/>\n\t\t\t);\n\t\t\tif (isCurrent) $currentItem = $item;\n\t\t\treturn $item;\n\t\t});\n\t\t$currentItem?.scrollIntoView();\n\t}\n\n\t/**\n\t *\n\t * @param {MouseEvent} e\n\t */\n\tfunction clickHandler(e) {\n\t\tconst $target = e.target;\n\t\tif (!($target instanceof HTMLElement)) return;\n\t\tconst action = $target.getAttribute(\"action\");\n\t\tif (!action) return;\n\n\t\tswitch (action) {\n\t\t\tcase \"search\":\n\t\t\t\tsearchBar(list.el);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Sets the selected theme\n\t * @param {ThemeBuilder} theme\n\t */\n\tasync function setAppTheme(theme, buy) {\n\t\tif (!DOES_SUPPORT_THEME) return;\n\n\t\tif (buy) {\n\t\t\ttry {\n\t\t\t\tawait removeAds();\n\t\t\t\trenderAppThemes();\n\t\t\t} catch (e) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (theme.id === \"custom\") {\n\t\t\tCustomTheme();\n\t\t\treturn;\n\t\t}\n\n\t\tthemes.apply(theme.id, true);\n\t\tupdateCheckedItem(theme.name);\n\t}\n\n\t/**\n\t * Sets the selected editor theme\n\t * @param {object} param0\n\t * @param {string} param0.theme\n\t */\n\tfunction setEditorTheme({ caption, theme }) {\n\t\tif (appSettings.value.appTheme.toLowerCase() === \"system\") {\n\t\t\talert(\n\t\t\t\t\"Info\",\n\t\t\t\t\"App theme is set to 'System'. Changing the editor theme will not affect the editor appearance.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst ok = editorManager.editor.setTheme(theme);\n\t\tif (!ok) {\n\t\t\talert(\n\t\t\t\t\"Invalid theme\",\n\t\t\t\t\"This editor theme is not compatible with Acode's CodeMirror runtime.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (cmPreview) createPreview(theme);\n\t\tappSettings.update(\n\t\t\t{\n\t\t\t\teditorTheme: theme,\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t\tupdateCheckedItem(caption);\n\t}\n\n\t/**\n\t * Updates the checked item\n\t * @param {string} theme\n\t */\n\tfunction updateCheckedItem(theme) {\n\t\tlist.get('[checked=\"true\"]')?.uncheck();\n\t\tlist.get(`[theme=\"${theme}\"]`)?.check();\n\t}\n\n\tfunction Item({ name, swatches, onclick, isCurrent, isPremium }) {\n\t\tconst check = <span className=\"icon check\"></span>;\n\t\tconst star = <span className=\"icon stars\"></span>;\n\n\t\tconst $el = (\n\t\t\t<div\n\t\t\t\tattr-checked={isCurrent}\n\t\t\t\tattr-theme={name}\n\t\t\t\tclassName=\"list-item\"\n\t\t\t\tonclick={onclick}\n\t\t\t>\n\t\t\t\t{createSwatchPreview(swatches)}\n\t\t\t\t<div className=\"container\">\n\t\t\t\t\t<span className=\"text\">{name}</span>\n\t\t\t\t</div>\n\t\t\t\t{isCurrent && check}\n\t\t\t\t{isPremium && star}\n\t\t\t</div>\n\t\t);\n\n\t\t$el.uncheck = () => {\n\t\t\tcheck.remove();\n\t\t\t$el.removeAttribute(\"checked\");\n\t\t};\n\t\t$el.check = () => {\n\t\t\t$el.append(check);\n\t\t\t$el.setAttribute(\"checked\", true);\n\t\t};\n\t\treturn $el;\n\t}\n\n\tfunction createSwatchPreview(swatches) {\n\t\tconst colors = [...new Set((swatches || []).filter(Boolean))].slice(0, 3);\n\t\twhile (colors.length < 3) {\n\t\t\tcolors.push(colors[colors.length - 1] || \"var(--border-color)\");\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"theme-swatch-slot\" aria-hidden=\"true\">\n\t\t\t\t<div className=\"theme-swatch-preview\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"theme-swatch theme-swatch-main\"\n\t\t\t\t\t\tstyle={{ backgroundColor: colors[0] }}\n\t\t\t\t\t></span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"theme-swatch\"\n\t\t\t\t\t\tstyle={{ backgroundColor: colors[1] }}\n\t\t\t\t\t></span>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName=\"theme-swatch\"\n\t\t\t\t\t\tstyle={{ backgroundColor: colors[2] }}\n\t\t\t\t\t></span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tfunction getAppThemeSwatches(theme) {\n\t\tif (!theme) {\n\t\t\treturn [\n\t\t\t\t\"var(--primary-color)\",\n\t\t\t\t\"var(--secondary-color)\",\n\t\t\t\t\"var(--active-color)\",\n\t\t\t];\n\t\t}\n\n\t\treturn [theme.primaryColor, theme.secondaryColor, theme.activeColor];\n\t}\n\n\tfunction getEditorThemeSwatches(themeId) {\n\t\tconst config = getThemeConfig(themeId);\n\t\treturn [\n\t\t\tconfig.background,\n\t\t\tconfig.keyword || config.function || config.foreground,\n\t\t\tconfig.string || config.variable || config.foreground,\n\t\t];\n\t}\n}\n"
  },
  {
    "path": "src/pages/themeSetting/themeSetting.scss",
    "content": "#theme-setting {\n  display: flex;\n  flex-direction: column;\n\n  #theme-preview:not(:empty) {\n    height: 120px;\n    box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 0 4px var(--box-shadow-color);\n    pointer-events: none;\n  }\n\n  #theme-preview .cm-editor {\n    width: 100%;\n  }\n\n  #theme-list {\n    flex: 1;\n  }\n\n  .theme-swatch-slot {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    width: 60px;\n    min-width: 60px;\n    height: 60px;\n    flex-shrink: 0;\n  }\n\n  .theme-swatch-preview {\n    display: grid;\n    grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr);\n    grid-template-rows: repeat(2, minmax(0, 1fr));\n    width: 1.5rem;\n    min-width: 1.5rem;\n    height: 1.5rem;\n    padding: 0.08rem;\n    box-sizing: border-box;\n    border: 1px solid var(--border-color);\n    border: 1px solid color-mix(in srgb, var(--border-color), transparent 18%);\n    border-radius: 0.45rem;\n    background: var(--secondary-color);\n    overflow: hidden;\n  }\n\n  .theme-swatch {\n    display: block;\n    min-width: 0;\n    min-height: 0;\n  }\n\n  .theme-swatch-main {\n    grid-row: 1 / 3;\n  }\n}\n"
  },
  {
    "path": "src/pages/welcome/index.js",
    "content": "export { default } from \"./welcome\";\n"
  },
  {
    "path": "src/pages/welcome/welcome.js",
    "content": "import \"./welcome.scss\";\nimport Logo from \"components/logo\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport EditorFile from \"lib/editorFile\";\n\n/**\n * Opens the Welcome tab as an EditorFile page\n */\nexport default function openWelcomeTab() {\n\t// Check if welcome tab is already open\n\tconst existingFile = editorManager.files.find((f) => f.id === \"welcome-tab\");\n\tif (existingFile) {\n\t\texistingFile.makeActive();\n\t\treturn;\n\t}\n\n\tconst welcomeContent = createWelcomeContent();\n\n\tconst welcomeFile = new EditorFile(\"Welcome\", {\n\t\tid: \"welcome-tab\",\n\t\trender: true,\n\t\ttype: \"page\",\n\t\tcontent: welcomeContent,\n\t\ttabIcon: \"icon acode\",\n\t\thideQuickTools: true,\n\t});\n\n\t// Set custom subtitle for the header\n\twelcomeFile.setCustomTitle(() => \"Get Started\");\n\n\tactionStack.push({\n\t\tid: \"welcome-tab\",\n\t\taction: () => welcomeFile.remove(),\n\t});\n}\n\n/**\n * Creates the welcome tab content\n * @returns {HTMLElement}\n */\nfunction createWelcomeContent() {\n\treturn (\n\t\t<div id=\"welcome-tab\" className=\"welcome-page scroll\">\n\t\t\t{/* Hero Section */}\n\t\t\t<header className=\"welcome-header\">\n\t\t\t\t<Logo />\n\t\t\t\t<div className=\"welcome-header-text\">\n\t\t\t\t\t<h1>Welcome to Acode</h1>\n\t\t\t\t\t<p className=\"tagline\">Powerful code editor for Android</p>\n\t\t\t\t</div>\n\t\t\t</header>\n\n\t\t\t{/* Get Started Section */}\n\t\t\t<section className=\"welcome-section\">\n\t\t\t\t<h2 className=\"section-label\">GET STARTED</h2>\n\t\t\t\t<div className=\"action-list\">\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"add\"\n\t\t\t\t\t\tlabel={strings[\"new file\"]}\n\t\t\t\t\t\tshortcut=\"Ctrl+N\"\n\t\t\t\t\t\tonClick={() => acode.exec(\"new-file\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"folder_open\"\n\t\t\t\t\t\tlabel={strings[\"open folder\"]}\n\t\t\t\t\t\tshortcut=\"Ctrl+O\"\n\t\t\t\t\t\tonClick={() => acode.exec(\"open-folder\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"historyrestore\"\n\t\t\t\t\t\tlabel={strings.recent}\n\t\t\t\t\t\tonClick={() => acode.exec(\"recent\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"tune\"\n\t\t\t\t\t\tlabel={strings[\"command palette\"]}\n\t\t\t\t\t\tshortcut=\"Ctrl+Shift+P\"\n\t\t\t\t\t\tonClick={() => acode.exec(\"command-palette\")}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t{/* Configure Section */}\n\t\t\t<section className=\"welcome-section\">\n\t\t\t\t<h2 className=\"section-label\">CONFIGURE</h2>\n\t\t\t\t<div className=\"action-list\">\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"settings\"\n\t\t\t\t\t\tlabel={strings.settings}\n\t\t\t\t\t\tonClick={() => acode.exec(\"open\", \"settings\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"color_lenspalette\"\n\t\t\t\t\t\tlabel={strings[\"change theme\"]}\n\t\t\t\t\t\tonClick={() => acode.exec(\"change-app-theme\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"extension\"\n\t\t\t\t\t\tlabel={strings.explore + \" \" + strings.plugins}\n\t\t\t\t\t\tonClick={() => acode.exec(\"open\", \"plugins\")}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t{/* Learn Section */}\n\t\t\t<section className=\"welcome-section\">\n\t\t\t\t<h2 className=\"section-label\">LEARN</h2>\n\t\t\t\t<div className=\"action-list\">\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"help\"\n\t\t\t\t\t\tlabel={strings.help}\n\t\t\t\t\t\tonClick={() => acode.exec(\"open\", \"help\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<ActionRow\n\t\t\t\t\t\ticon=\"info_outline\"\n\t\t\t\t\t\tlabel={strings.about}\n\t\t\t\t\t\tonClick={() => acode.exec(\"open\", \"about\")}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t{/* Links Section */}\n\t\t\t<section className=\"welcome-section welcome-links\">\n\t\t\t\t<h2 className=\"section-label\">CONNECT</h2>\n\t\t\t\t<div className=\"link-row\">\n\t\t\t\t\t<LinkItem icon=\"acode\" label=\"Website\" url={constants.WEBSITE_URL} />\n\t\t\t\t\t<LinkItem icon=\"github\" label=\"GitHub\" url={constants.GITHUB_URL} />\n\t\t\t\t\t<LinkItem\n\t\t\t\t\t\ticon=\"telegram\"\n\t\t\t\t\t\tlabel=\"Telegram\"\n\t\t\t\t\t\turl={constants.TELEGRAM_URL}\n\t\t\t\t\t/>\n\t\t\t\t\t<LinkItem\n\t\t\t\t\t\ticon=\"discord\"\n\t\t\t\t\t\tlabel=\"Discord\"\n\t\t\t\t\t\turl={constants.DISCORD_URL}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t);\n}\n\n/**\n * Action row component\n */\nfunction ActionRow({ icon, label, shortcut, onClick }) {\n\treturn (\n\t\t<div className=\"action-row\" onclick={onClick}>\n\t\t\t<span className={`icon ${icon}`}></span>\n\t\t\t<span className=\"action-label\">{label}</span>\n\t\t\t{shortcut && <span className=\"action-shortcut\">{shortcut}</span>}\n\t\t</div>\n\t);\n}\n\n/**\n * Link item component - opens URL in external browser\n */\nfunction LinkItem({ icon, label, url }) {\n\tconst handleClick = (e) => {\n\t\te.preventDefault();\n\t\tsystem.openInBrowser(url);\n\t};\n\n\treturn (\n\t\t<a href={url} className=\"link-item\" onclick={handleClick}>\n\t\t\t<span className={`icon ${icon}`}></span>\n\t\t\t<span>{label}</span>\n\t\t</a>\n\t);\n}\n"
  },
  {
    "path": "src/pages/welcome/welcome.scss",
    "content": "#welcome-tab {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: flex-start;\n    min-height: 100%;\n    height: auto;\n    padding: 32px 24px;\n    overflow-y: auto;\n    overflow-x: hidden;\n    background-color: var(--secondary-color);\n\n    // Hero Header\n    .welcome-header {\n        display: flex;\n        align-items: center;\n        gap: 16px;\n        margin-bottom: 48px;\n\n        .logo {\n            width: 64px;\n            height: 64px;\n            flex-shrink: 0;\n\n            &::after {\n                background-size: 48px;\n            }\n\n            &::before {\n                background: radial-gradient(circle,\n                        color-mix(in srgb, var(--button-background-color) 30%, transparent) 0%,\n                        transparent 70%);\n            }\n        }\n\n        .welcome-header-text {\n            h1 {\n                font-size: 20px;\n                font-weight: 600;\n                color: var(--primary-text-color);\n                margin: 0 0 4px 0;\n                letter-spacing: -0.3px;\n            }\n\n            .tagline {\n                font-size: 13px;\n                color: color-mix(in srgb, var(--secondary-text-color) 60%, transparent);\n                margin: 0;\n            }\n        }\n    }\n\n    // Section Styles\n    .welcome-section {\n        width: 100%;\n        max-width: 400px;\n        margin-bottom: 32px;\n\n        .section-label {\n            font-size: 11px;\n            font-weight: 600;\n            letter-spacing: 1px;\n            color: color-mix(in srgb, var(--secondary-text-color) 50%, transparent);\n            margin: 0 0 12px 0;\n            padding-left: 4px;\n        }\n    }\n\n    // Action List\n    .action-list {\n        display: flex;\n        flex-direction: column;\n        gap: 2px;\n    }\n\n    .action-row {\n        display: flex;\n        align-items: center;\n        gap: 12px;\n        padding: 10px 12px;\n        border-radius: 6px;\n        cursor: pointer;\n        transition: background-color 0.15s ease;\n\n        &:hover,\n        &:active {\n            background-color: color-mix(in srgb, var(--popup-background-color) 40%, transparent);\n        }\n\n        .icon {\n            font-size: 16px;\n            color: color-mix(in srgb, var(--secondary-text-color) 70%, transparent);\n            width: 20px;\n            text-align: center;\n        }\n\n        .action-label {\n            flex: 1;\n            font-size: 14px;\n            color: var(--secondary-text-color);\n        }\n\n        .action-shortcut {\n            font-size: 12px;\n            font-family: 'Roboto Mono', monospace;\n            color: color-mix(in srgb, var(--secondary-text-color) 40%, transparent);\n            letter-spacing: 0.5px;\n        }\n    }\n\n    // Links Section\n    .welcome-links {\n        margin-top: 16px;\n\n        .link-row {\n            display: flex;\n            flex-wrap: wrap;\n            gap: 8px;\n            justify-content: flex-start;\n        }\n\n        .link-item {\n            display: inline-flex;\n            align-items: center;\n            gap: 8px;\n            padding: 10px 16px;\n            border-radius: 8px;\n            text-decoration: none;\n            color: var(--secondary-text-color);\n            font-size: 13px;\n            font-weight: 500;\n            transition: all 0.15s ease;\n            background-color: color-mix(in srgb, var(--popup-background-color) 25%, transparent);\n            border: 1px solid color-mix(in srgb, var(--border-color) 20%, transparent);\n\n            &:hover,\n            &:active {\n                background-color: color-mix(in srgb, var(--popup-background-color) 50%, transparent);\n                border-color: color-mix(in srgb, var(--border-color) 40%, transparent);\n                transform: translateY(-1px);\n            }\n\n            .icon {\n                font-size: 16px;\n                color: color-mix(in srgb, var(--secondary-text-color) 80%, transparent);\n            }\n        }\n    }\n}\n\n// Responsive adjustments for smaller screens\n@media (max-width: 360px) {\n    #welcome-tab {\n        padding: 24px 16px;\n\n        .welcome-header {\n            flex-direction: column;\n            text-align: center;\n            margin-bottom: 36px;\n\n            .logo {\n                width: 56px;\n                height: 56px;\n\n                &::after {\n                    background-size: 40px;\n                }\n            }\n\n            .welcome-header-text h1 {\n                font-size: 18px;\n            }\n        }\n\n        .welcome-section {\n            margin-bottom: 24px;\n        }\n\n        .action-row {\n            padding: 8px 10px;\n\n            .action-label {\n                font-size: 13px;\n            }\n\n            .action-shortcut {\n                font-size: 11px;\n            }\n        }\n\n        .welcome-links .link-row {\n            justify-content: center;\n        }\n    }\n}\n\n// Larger screens - center content better\n@media (min-width: 600px) {\n    #welcome-tab {\n        .welcome-section {\n            max-width: 480px;\n        }\n\n        .action-row {\n            padding: 12px 16px;\n        }\n    }\n}\n\n// Discord icon\n.icon.discord {\n    position: relative;\n\n    &::before {\n        content: '';\n        display: block;\n        width: 16px;\n        height: 16px;\n        background-image: url(../../pages/about/discord.svg);\n        background-size: contain;\n        background-repeat: no-repeat;\n        background-position: center;\n    }\n}"
  },
  {
    "path": "src/palettes/changeEditorTheme/index.js",
    "content": "import { getThemes } from \"cm/themes\";\nimport palette from \"components/palette\";\nimport appSettings from \"lib/settings\";\n\nexport default function changeEditorTheme() {\n\tpalette(generateHints, onselect, strings[\"editor theme\"]);\n}\n\nfunction generateHints() {\n\tconst themes = getThemes();\n\tconst current = String(\n\t\tappSettings.value.editorTheme || \"one_dark\",\n\t).toLowerCase();\n\treturn themes.map((t) => {\n\t\tconst isCurrent = current === t.id;\n\t\treturn {\n\t\t\tvalue: t.id,\n\t\t\ttext: `<div class=\"theme-item\"><span>${t.caption}</span>${isCurrent ? '<span class=\"current\">current</span>' : \"\"}</div>`,\n\t\t};\n\t});\n}\n\nfunction onselect(themeId) {\n\tif (!themeId) return;\n\tconst ok = editorManager.editor.setTheme(themeId);\n\tif (!ok) return;\n\tappSettings.update({ editorTheme: themeId }, false);\n}\n"
  },
  {
    "path": "src/palettes/changeEncoding/index.js",
    "content": "import fsOperation from \"fileSystem\";\nimport palette from \"components/palette\";\nimport confirm from \"dialogs/confirm\";\nimport encodings from \"utils/encodings\";\n\nexport default function changeEncoding() {\n\tpalette(generateHints, reopenWithNewEncoding, strings.encoding);\n}\n\nfunction generateHints() {\n\treturn Object.keys(encodings).map((id) => {\n\t\tconst encoding = encodings[id];\n\t\tconst aliases = encoding.aliases.join(\", \");\n\t\treturn {\n\t\t\tvalue: id,\n\t\t\ttext: `<div class=\"palette-content-encoding\">\n      <span>${encoding.label}</span>\n      <small>${aliases}</small>\n    <div>`,\n\t\t};\n\t});\n}\n\nexport async function reopenWithNewEncoding(encoding) {\n\tconst file = editorManager.activeFile;\n\tconst editor = editorManager.editor;\n\tconst message = strings[\"change encoding\"]\n\t\t.replace(\"{file}\", file.filename)\n\t\t.replace(\"{encoding}\", encoding);\n\tconst confirmation = await confirm(strings.warning, message);\n\n\tif (!confirmation) return;\n\n\tconst text = await fsOperation(file.uri).readFile(encoding);\n\tconst cursorPosition = editor.getCursorPosition();\n\n\tfile.encoding = encoding;\n\tfile.session.setValue(text);\n\tfile.isUnsaved = false;\n\tfile.markChanged = false;\n\teditor.moveCursorToPosition(cursorPosition);\n\n\teditorManager.onupdate(\"encoding\");\n\teditorManager.emit(\"update\", \"encoding\");\n}\n"
  },
  {
    "path": "src/palettes/changeMode/index.js",
    "content": "import { getModes } from \"cm/modelist\";\nimport palette from \"components/palette\";\nimport helpers from \"utils/helpers\";\nimport Path from \"utils/Path\";\n\nexport default function changeMode() {\n\tpalette(generateHints, onselect, strings[\"syntax highlighting\"]);\n}\n\nfunction generateHints() {\n\tconst modes = [...getModes()].sort((a, b) =>\n\t\ta.caption.localeCompare(b.caption),\n\t);\n\tconst activeMode = editorManager.activeFile?.currentMode || \"\";\n\tconst activeIndex = modes.findIndex(({ mode }) => mode === activeMode);\n\n\tif (activeIndex > 0) {\n\t\tconst [activeEntry] = modes.splice(activeIndex, 1);\n\t\tmodes.unshift(activeEntry);\n\t}\n\n\treturn modes.map(({ aliases = [], caption, extensions, mode }) => {\n\t\tconst searchTerms = [caption, mode, extensions, ...aliases]\n\t\t\t.filter(Boolean)\n\t\t\t.join(\" \");\n\t\tconst title =\n\t\t\tcaption.toLowerCase() === mode ? caption : `${caption} (${mode})`;\n\n\t\treturn {\n\t\t\tactive: mode === activeMode,\n\t\t\tvalue: mode,\n\t\t\ttext: `<div style=\"display: flex; flex-direction: column;\">\n      <strong style=\"font-size: 1rem;\">${title}</strong>\n      <span hidden>${searchTerms}</span>\n    </div>`,\n\t\t};\n\t});\n}\n\nfunction onselect(mode) {\n\tconst activeFile = editorManager.activeFile;\n\n\tlet modeAssociated;\n\ttry {\n\t\tmodeAssociated = helpers.parseJSON(localStorage.modeassoc) || {};\n\t} catch (error) {\n\t\tmodeAssociated = {};\n\t}\n\n\tmodeAssociated[Path.extname(activeFile.filename)] = mode;\n\tlocalStorage.modeassoc = JSON.stringify(modeAssociated);\n\n\tactiveFile.setMode(mode);\n}\n"
  },
  {
    "path": "src/palettes/changeTheme/index.js",
    "content": "import \"./style.scss\";\nimport palette from \"components/palette\";\nimport appSettings from \"lib/settings\";\nimport { isDeviceDarkTheme } from \"lib/systemConfiguration\";\nimport themes from \"theme/list\";\nimport { updateSystemTheme } from \"theme/preInstalled\";\nimport changeEditorTheme from \"../changeEditorTheme\";\n\nexport default function changeTheme(type = \"editor\") {\n\tif (type === \"editor\") return changeEditorTheme();\n\tpalette(\n\t\t() => generateHints(type),\n\t\t(value) => onselect(value),\n\t\tstrings[\"app theme\"],\n\t);\n}\n\nfunction generateHints(type) {\n\t// Editor handled by changeEditorTheme\n\n\t// App themes\n\tconst currentTheme = appSettings.value.appTheme;\n\tconst availableThemes = themes\n\t\t.list()\n\t\t.filter((theme) => !(theme.version === \"paid\" && IS_FREE_VERSION));\n\n\treturn availableThemes.map((theme) => {\n\t\tconst isCurrent = theme.id === currentTheme;\n\n\t\treturn {\n\t\t\tvalue: JSON.stringify({\n\t\t\t\ttype: \"app\",\n\t\t\t\ttheme: theme.id,\n\t\t\t}),\n\t\t\ttext: `<div class=\"theme-item\">\n\t\t\t\t\t\t\t\t<span>${theme.name}</span>\n\t\t\t\t\t\t\t\t${isCurrent ? '<span class=\"current\">current</span>' : \"\"}\n\t\t\t\t\t\t</div>`,\n\t\t};\n\t});\n}\n\nlet previousDark = isDeviceDarkTheme();\nconst updateTimeMs = 2000;\n\nlet intervalId = null;\n\nfunction syncSystemTheme() {\n\tif (appSettings.value.appTheme.toLowerCase() === \"system\") {\n\t\tconst isDark = isDeviceDarkTheme();\n\t\tif (isDark !== previousDark) {\n\t\t\tpreviousDark = isDark;\n\t\t\tupdateSystemTheme(isDark);\n\t\t}\n\t}\n}\n\nfunction startSystemThemeWatcher() {\n\tif (intervalId) return;\n\tintervalId = setInterval(syncSystemTheme, updateTimeMs);\n}\n\nfunction stopSystemThemeWatcher() {\n\tif (!intervalId) return;\n\tclearInterval(intervalId);\n\tintervalId = null;\n}\n\nfunction updateSystemThemeWatcher(theme) {\n\tif (String(theme).toLowerCase() === \"system\") {\n\t\tstartSystemThemeWatcher();\n\t\tsyncSystemTheme();\n\t\treturn;\n\t}\n\tstopSystemThemeWatcher();\n}\n\nupdateSystemThemeWatcher(appSettings.value.appTheme);\nappSettings.on(\"update:appTheme\", updateSystemThemeWatcher);\n\nfunction onselect(value) {\n\tif (!value) return;\n\n\tconst selection = JSON.parse(value);\n\n\tupdateSystemThemeWatcher(selection.theme);\n\n\tif (selection.type === \"editor\") {\n\t\teditorManager.editor.setTheme(selection.theme);\n\t\tappSettings.update(\n\t\t\t{\n\t\t\t\teditorTheme: selection.theme,\n\t\t\t},\n\t\t\tfalse,\n\t\t);\n\t} else {\n\t\tif (selection.theme === \"custom\") {\n\t\t\tCustomTheme();\n\t\t\treturn;\n\t\t}\n\t\tthemes.apply(selection.theme, true);\n\t}\n}\n"
  },
  {
    "path": "src/palettes/changeTheme/style.scss",
    "content": ".theme-item {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 4px;\n  width: 100%;\n\n  span {\n    font-size: 1rem;\n  }\n\n  .current {\n    color: var(--error-text-color);\n    background-color: var(--primary-color);\n    border-radius: 5px;\n    padding: 2px 6px;\n    font-size: 0.8rem;\n    margin-left: 8px;\n  }\n}\n"
  },
  {
    "path": "src/palettes/commandPalette/index.js",
    "content": "import { executeCommand, getRegisteredCommands } from \"cm/commandRegistry\";\nimport palette from \"components/palette\";\nimport helpers from \"utils/helpers\";\n\nexport default async function commandPalette() {\n\tconst recentCommands = RecentlyUsedCommands();\n\tconst { editor } = editorManager;\n\tconst wasFocused = editor?.hasFocus ?? false;\n\n\tpalette(generateHints, onselect, strings[\"type command\"], () => {\n\t\tif (wasFocused) editor?.focus();\n\t});\n\n\tfunction generateHints() {\n\t\tconst registeredCommands = getRegisteredCommands();\n\t\tconst hints = [];\n\n\t\tregisteredCommands.forEach(({ name, description, key }) => {\n\t\t\tconst keyLabel = key ? key.split(\"|\")[0] : \"\";\n\t\t\tconst item = (recentlyUsed) => ({\n\t\t\t\tvalue: name,\n\t\t\t\ttext: `<span ${recentlyUsed ? `data-str='${strings[\"recently used\"]}'` : \"\"}>${description ?? name}</span><small>${keyLabel}</small>`,\n\t\t\t});\n\t\t\tif (recentCommands.commands.includes(name)) {\n\t\t\t\thints.unshift(item(true));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thints.push(item(false));\n\t\t});\n\n\t\treturn hints;\n\t}\n\n\tfunction onselect(value) {\n\t\tconst executed = executeCommand(value, editorManager.editor);\n\t\tif (executed) recentCommands.push(value);\n\t}\n}\n\nfunction RecentlyUsedCommands() {\n\treturn {\n\t\t/**\n\t\t * @returns {string[]}\n\t\t */\n\t\tget commands() {\n\t\t\treturn (\n\t\t\t\thelpers.parseJSON(localStorage.getItem(\"recentlyUsedCommands\")) || []\n\t\t\t);\n\t\t},\n\t\t/**\n\t\t * Saves command to recently used commands\n\t\t * @param {string} command Command name\n\t\t * @returns {void}\n\t\t */\n\t\tpush(command) {\n\t\t\tconst { commands } = this;\n\t\t\tif (commands.length > 10) {\n\t\t\t\tcommands.pop();\n\t\t\t}\n\t\t\tif (commands.includes(command)) {\n\t\t\t\tcommands.splice(commands.indexOf(command), 1);\n\t\t\t}\n\t\t\tcommands.unshift(command);\n\t\t\tlocalStorage.setItem(\"recentlyUsedCommands\", JSON.stringify(commands));\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "src/palettes/findFile/index.js",
    "content": "import palette from \"components/palette\";\nimport files from \"lib/fileList\";\nimport openFile from \"lib/openFile\";\nimport recents from \"lib/recents\";\nimport helpers from \"utils/helpers\";\n\n/**\n * @typedef {import('components/inputhints').HintModification} HintModification\n */\n\n/**@type {HintModification} */\nlet hintsModification;\n\nexport default async function findFile() {\n\tpalette(generateHints, onselect, strings[\"type filename\"], () => {\n\t\tfiles.off(\"add-file\", onAddFile);\n\t\tfiles.off(\"remove-file\", onRemoveFile);\n\t});\n\n\tfiles.on(\"add-file\", onAddFile);\n\tfiles.on(\"remove-file\", onRemoveFile);\n\n\t/**\n\t * Generates hint for inputhints\n\t * @param {HintModification} hints Hint modification object\n\t */\n\tasync function generateHints(hints) {\n\t\thintsModification = hints;\n\t\tconst list = [];\n\n\t\teditorManager.files.forEach((file) => {\n\t\t\tconst { uri, name } = file;\n\t\t\tlet { location = \"\" } = file;\n\n\t\t\tif (location) {\n\t\t\t\tlocation = helpers.getVirtualPath(location);\n\t\t\t}\n\n\t\t\tlist.push(hintItem(name, location, uri));\n\t\t});\n\n\t\tlist.push(...files(hintItem));\n\t\treturn list;\n\t}\n\n\tfunction onselect(value) {\n\t\tif (!value) return;\n\t\topenFile(value);\n\t}\n}\n\n/**\n * Generates hint item for inputhints\n * @param {string|{name: string, path: string, url: string}} name Hint text\n * @param {string} path Hint subtext\n * @param {string} url Hint value\n * @returns {{text: string, value: string}}\n */\nfunction hintItem(name, path, url) {\n\tif (typeof name === \"object\") {\n\t\t({ name, path, url } = name);\n\t}\n\tconst recent = recents.files.find((file) => file === url);\n\tlet subText = (path || url) ?? strings[\"new file\"];\n\tif (subText.length > 50) {\n\t\tsubText = `...${subText.slice(-50)}`;\n\t}\n\treturn {\n\t\ttext: `<div style=\"display: flex; flex-direction: column;\">\n        <strong ${recent ? `data-str='${strings[\"recently used\"]}'` : \"\"} style=\"font-size: 1rem;\">${name}</strong>\n        <span style=\"font-size: 0.8rem; opacity: 0.8;\">${subText}</span>\n      <div>`,\n\t\tvalue: url,\n\t};\n}\n\nfunction onAddFile({ name, url, path: visiblePath }) {\n\thintsModification?.add(hintItem(name, visiblePath, url));\n}\n\nfunction onRemoveFile({ name, url, path: visiblePath }) {\n\thintsModification?.remove(hintItem(name, visiblePath, url));\n}\n"
  },
  {
    "path": "src/plugins/auth/package.json",
    "content": "{\n  \"name\": \"com.foxdebug.acode.rk.auth\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Authentication plugin\",\n  \"cordova\": {\n    \"id\": \"com.foxdebug.acode.rk.auth\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"ecosystem:cordova\",\n    \"cordova-android\"\n  ],\n  \"author\": \"@RohitKushvaha01\",\n  \"license\": \"MIT\"\n}"
  },
  {
    "path": "src/plugins/auth/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\" xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"com.foxdebug.acode.rk.auth\" version=\"1.0.0\">\n    <name>Authentication</name>\n\n\n    <platform name=\"android\">\n       <config-file parent=\"/*\" target=\"res/xml/config.xml\">\n            <feature name=\"Authenticator\">\n                <param name=\"android-package\" value=\"com.foxdebug.acode.rk.auth.Authenticator\" />\n            </feature>\n        </config-file>\n\n        <framework src=\"androidx.security:security-crypto:1.1.0\" />\n\n        <source-file src=\"src/android/Authenticator.java\" target-dir=\"src/com/foxdebug/acode/rk/auth\" />\n        <source-file src=\"src/android/EncryptedPreferenceManager.java\" target-dir=\"src/com/foxdebug/acode/rk/auth\" />\n        \n           \n    </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/auth/src/android/Authenticator.java",
    "content": "package com.foxdebug.acode.rk.auth;\n\nimport android.util.Log; // Required for logging\nimport com.foxdebug.acode.rk.auth.EncryptedPreferenceManager;\nimport org.apache.cordova.*;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.Scanner;\n\npublic class Authenticator extends CordovaPlugin {\n    // Standard practice: use a TAG for easy filtering in Logcat\n    private static final String TAG = \"AcodeAuth\"; \n    private static final String PREFS_FILENAME = \"acode_auth_secure\";\n    private static final String KEY_TOKEN = \"auth_token\";\n    private EncryptedPreferenceManager prefManager;\n\n    @Override\n    protected void pluginInitialize() {\n        Log.d(TAG, \"Initializing Authenticator Plugin...\");\n        this.prefManager = new EncryptedPreferenceManager(this.cordova.getContext(), PREFS_FILENAME);\n    }\n\n    @Override\n    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {\n        Log.i(TAG, \"Native Action Called: \" + action);\n        \n        switch (action) {\n            case \"logout\":\n                this.logout(callbackContext);\n                return true;\n            case \"isLoggedIn\":\n                this.isLoggedIn(callbackContext);\n                return true;\n            case \"getUserInfo\":\n                this.getUserInfo(callbackContext);\n                return true;\n            case \"saveToken\":\n                String token = args.getString(0);\n                Log.d(TAG, \"Saving new token...\");\n                prefManager.setString(KEY_TOKEN, token);\n                callbackContext.success();\n                return true;\n            default:\n                Log.w(TAG, \"Attempted to call unknown action: \" + action);\n                return false;\n        }\n    }\n\n    private void logout(CallbackContext callbackContext) {\n        Log.d(TAG, \"Logging out, removing token.\");\n        prefManager.remove(KEY_TOKEN);\n        if (callbackContext != null) callbackContext.success();\n    }\n\n    private void isLoggedIn(CallbackContext callbackContext) {\n        String token = prefManager.getString(KEY_TOKEN, null);\n        if (token == null) {\n            Log.d(TAG, \"isLoggedIn check: No token found in preferences.\");\n            callbackContext.error(0);\n            return;\n        }\n\n        Log.d(TAG, \"isLoggedIn check: Token found, validating with server...\");\n        final String tokenToValidate = token; // Make effectively final for lambda\n        \n        cordova.getThreadPool().execute(() -> {\n            try {\n                String result = validateToken(tokenToValidate); \n                if (result != null) {\n                    Log.i(TAG, \"Token validation successful.\");\n                    callbackContext.success(); \n                } else {\n                    Log.w(TAG, \"Token validation failed (result was null).\");\n                    callbackContext.error(401);\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"CRITICAL error in isLoggedIn thread: \" + e.getMessage(), e);\n                callbackContext.error(\"Internal Plugin Error: \" + e.getMessage());\n            }\n        });\n    }\n\n    private void getUserInfo(CallbackContext callbackContext) {\n        Log.d(TAG, \"getUserInfo: Fetching token...\");\n        String token = prefManager.getString(KEY_TOKEN, null);\n        \n        if (token == null) {\n            Log.e(TAG, \"getUserInfo: No token found.\");\n            callbackContext.error(0);\n            return;\n        }\n        \n        final String tokenToValidate = token;\n        cordova.getThreadPool().execute(() -> {\n            try {\n                String response = validateToken(tokenToValidate);\n                if (response != null) {\n                    Log.d(TAG, \"getUserInfo: Successfully fetched user info.\");\n                    callbackContext.success(response);\n                } else {\n                    Log.e(TAG, \"getUserInfo: Validation failed or unauthorized.\");\n                    callbackContext.error(\"Unauthorized\");\n                }\n            } catch (Exception e) {\n                Log.e(TAG, \"Error in getUserInfo: \" + e.getMessage(), e);\n                callbackContext.error(\"Error: \" + e.getMessage());\n            }\n        });\n    }\n\n    private String validateToken(String token) {\n        HttpURLConnection conn = null;\n        try {\n            Log.d(TAG, \"Network Request: Connecting to https://acode.app/api/login\");\n            URL url = new URL(\"https://acode.app/api/login\");  // Changed from /api to /api/login\n            conn = (HttpURLConnection) url.openConnection();\n            conn.setRequestProperty(\"x-auth-token\", token);\n            conn.setRequestMethod(\"GET\");\n            conn.setConnectTimeout(5000);\n            conn.setReadTimeout(5000);  // Add read timeout too\n            \n            int code = conn.getResponseCode();\n            Log.d(TAG, \"Server responded with status code: \" + code);\n\n            if (code == 200) {\n                Scanner s = new Scanner(conn.getInputStream(), \"UTF-8\").useDelimiter(\"\\\\A\");\n                String response = s.hasNext() ? s.next() : \"\";\n                Log.d(TAG, \"Response received: \" + response);  // Debug log\n                return response;\n            } else if (code == 401) {\n                Log.w(TAG, \"401 Unauthorized: Logging user out native-side.\");\n                logout(null);\n            } else {\n                Log.w(TAG, \"Unexpected status code: \" + code);\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"Network Exception in validateToken: \" + e.getMessage(), e);\n            e.printStackTrace();  // Print full stack trace for debugging\n        } finally {\n            if (conn != null) conn.disconnect();\n        }\n        return null;\n    }\n\n    \n}\n"
  },
  {
    "path": "src/plugins/auth/src/android/EncryptedPreferenceManager.java",
    "content": "package com.foxdebug.acode.rk.auth;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport androidx.security.crypto.EncryptedSharedPreferences;\nimport androidx.security.crypto.MasterKeys;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\n\npublic class EncryptedPreferenceManager {\n    private SharedPreferences sharedPreferences;\n\n    /**\n     * @param context  The Android Context\n     * @param prefName The custom name for your preference file (e.g., \"user_session\")\n     */\n    public EncryptedPreferenceManager(Context context, String prefName) {\n        try {\n            String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);\n\n            // EncryptedSharedPreferences handles encryption of both Keys and Values\n            sharedPreferences = EncryptedSharedPreferences.create(\n                    prefName,\n                    masterKeyAlias,\n                    context,\n                    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,\n                    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM\n            );\n        } catch (GeneralSecurityException | IOException e) {\n            // Fallback to standard private preferences if hardware-backed encryption fails\n            sharedPreferences = context.getSharedPreferences(prefName, Context.MODE_PRIVATE);\n        }\n    }\n\n    // --- Reusable Methods ---\n\n    public void setString(String key, String value) {\n        sharedPreferences.edit().putString(key, value).apply();\n    }\n\n    public String getString(String key, String defaultValue) {\n        return sharedPreferences.getString(key, defaultValue);\n    }\n\n    public void setInt(String key, int value) {\n        sharedPreferences.edit().putInt(key, value).apply();\n    }\n\n    public int getInt(String key, int defaultValue) {\n        return sharedPreferences.getInt(key, defaultValue);\n    }\n\n    public void remove(String key) {\n        sharedPreferences.edit().remove(key).apply();\n    }\n\n    public boolean exists(String key) {\n        return sharedPreferences.contains(key);\n    }\n\n    public void clear() {\n        sharedPreferences.edit().clear().apply();\n    }\n}"
  },
  {
    "path": "src/plugins/browser/android/com/foxdebug/browser/Browser.java",
    "content": "package com.foxdebug.browser;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\nimport android.graphics.drawable.GradientDrawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.text.InputType;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\nimport android.view.inputmethod.EditorInfo;\nimport android.view.inputmethod.InputMethodManager;\nimport android.webkit.ConsoleMessage;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebSettings;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\nimport android.widget.EditText;\nimport android.widget.FrameLayout;\nimport android.widget.ImageButton;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport com.foxdebug.browser.Emulator;\nimport com.foxdebug.browser.Menu;\nimport com.foxdebug.system.Ui;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport android.app.AlertDialog;\nimport android.app.DownloadManager;\nimport android.content.Context;\nimport android.net.Uri;\nimport android.os.Environment;\nimport android.webkit.DownloadListener;\nimport android.webkit.URLUtil;\nimport android.webkit.WebView;\nimport android.widget.Toast;\nimport android.os.Handler;\nimport android.os.Looper;\n\n\n\n\npublic class Browser extends LinearLayout {\n\n  public int FILE_SELECT_CODE = 1;\n\n  public Menu menu;\n  public WebView webView;\n  private Ui.Theme theme;\n  public Context context;\n  private TextView urlText;\n  private LinearLayout main;\n  private ImageView favicon;\n  private TextView titleText;\n  private ProgressBar loading;\n  private Emulator deviceEmulator;\n\n  public boolean emulator = false;\n  public boolean console = false;\n  public boolean desktopMode = false;\n\n  private String url = \"\";\n  private String title = \"Browser\";\n\n  private int padding;\n  private int fontSize;\n  private int imageSize;\n  private int titleHeight;\n  private int titleTextHeight;\n  private boolean onlyConsole;\n\n  ValueCallback<Uri[]> filePathCallback;\n  final int REQUEST_SELECT_FILE = 1;\n\n  public Browser(Context context, Ui.Theme theme, Boolean onlyConsole) {\n    super(context);\n    this.theme = theme;\n    this.context = context;\n    this.onlyConsole = onlyConsole;\n    this.padding = Ui.dpToPixels(context, 5);\n    this.fontSize = Ui.dpToPixels(context, 5);\n    this.imageSize = Ui.dpToPixels(context, 35);\n    this.titleHeight = Ui.dpToPixels(context, 45);\n    this.titleTextHeight = Ui.dpToPixels(context, 35);\n\n    this.init();\n  }\n\n  public void init() {\n    WebSettings settings;\n    ImageButton menuIcon;\n    ImageButton refreshIcon;\n    LinearLayout titleLayout;\n    FrameLayout faviconFrame;\n    LinearLayout webViewContainer;\n    LinearLayout.LayoutParams faviconFrameParams;\n\n    favicon = createIcon(Ui.Icons.LOGO);\n    menuIcon = createIconButton(Ui.Icons.MORE_VERT);\n    menuIcon.setOnClickListener(\n      new View.OnClickListener() {\n        @Override\n        public void onClick(View view) {\n          menu.show(view);\n        }\n      }\n    );\n\n    refreshIcon = createIconButton(Ui.Icons.REFRESH);\n    refreshIcon.setOnClickListener(\n      new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n          webView.reload();\n        }\n      }\n    );\n\n    loading = new ProgressBar(context, null, android.R.attr.progressBarStyle);\n    loading.setLayoutParams(\n      new LinearLayout.LayoutParams(this.imageSize, this.imageSize, 0)\n    );\n\n    faviconFrame = new FrameLayout(context);\n    faviconFrameParams = new LinearLayout.LayoutParams(\n      ViewGroup.LayoutParams.WRAP_CONTENT,\n      ViewGroup.LayoutParams.WRAP_CONTENT\n    );\n    faviconFrameParams.gravity = Gravity.CENTER_VERTICAL;\n    faviconFrame.setLayoutParams(faviconFrameParams);\n    faviconFrame.addView(favicon);\n    faviconFrame.addView(loading);\n\n    titleLayout = createTile();\n    titleLayout.addView(faviconFrame);\n    titleText = onlyConsole ? createTextView(title) : createEditText(title);\n    titleLayout.addView(titleText);\n    if (!onlyConsole) {\n      titleLayout.addView(refreshIcon);\n      titleLayout.addView(menuIcon);\n    }\n\n    webView = new WebView(context);\n    webView.setFocusable(true);\n    webView.setFocusableInTouchMode(true);\n    webView.setBackgroundColor(0xFFFFFFFF);\n\n\n    webView.setDownloadListener(new DownloadListener() {\n        @Override\n        public void onDownloadStart(String url, String userAgent,\n                                    String contentDisposition, String mimeType,\n                                    long contentLength) {\n\n            String fileName = URLUtil.guessFileName(url, contentDisposition, mimeType);\n\n            new Handler(Looper.getMainLooper()).post(() -> {\n\n              new AlertDialog.Builder(getContext())\n                .setTitle(\"Download file\")\n                .setMessage(\"Do you want to download \\\"\" + fileName + \"\\\"?\")\n                .setPositiveButton(\"Yes\", (dialog, which) -> {\n                    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));\n                    request.setMimeType(mimeType);\n                    request.addRequestHeader(\"User-Agent\", userAgent);\n                    request.setDescription(\"Downloading file...\");\n                    request.setTitle(fileName);\n                    request.allowScanningByMediaScanner();\n                    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\n                    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);\n\n                    DownloadManager dm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);\n                    dm.enqueue(request);\n\n                    Toast.makeText(getContext(), \"Download started...\", Toast.LENGTH_SHORT).show();\n                })\n                .setNegativeButton(\"Cancel\", null)\n                .show();\n\n\n            });\n            \n        }\n    });\n\n\n    fitWebViewTo(0, 0, 1);\n\n    webView.setWebChromeClient(new BrowserChromeClient(this));\n    webView.setWebViewClient(new BrowserWebViewClient(this));\n\n    settings = webView.getSettings();\n    settings.setJavaScriptEnabled(true);\n    settings.setDomStorageEnabled(true);\n    settings.setAllowContentAccess(true);\n    settings.setDisplayZoomControls(false);\n    settings.setDomStorageEnabled(true);\n\n    webViewContainer = new LinearLayout(context);\n    webViewContainer.setGravity(Gravity.CENTER);\n    webViewContainer.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        1\n      )\n    );\n    webViewContainer.setBackgroundColor(theme.get(\"primaryColor\"));\n    webViewContainer.addView(webView);\n\n    setOrientation(LinearLayout.VERTICAL);\n    setFocusableInTouchMode(true);\n    setFocusable(true);\n    createMenu();\n    addView(titleLayout);\n    addView(webViewContainer);\n  }\n\n  private void createMenu() {\n    Browser browser = this;\n    menu = new Menu(context, theme);\n\n    menu.addItem(Ui.Icons.DEVICES, \"Devices\", false);\n    menu.addItem(Ui.Icons.NO_CACHE, \"Disable Cache\", false);\n    menu.addItem(Ui.Icons.TERMINAL, \"Console\", false);\n    menu.addItem(Ui.Icons.OPEN_IN_BROWSER, \"Open in Browser\");\n    menu.addItem(Ui.Icons.EXIT, \"Exit\");\n\n    menu.setCallback(\n      new Menu.Callback() {\n        @Override\n        public void onSelect(String action, Boolean checked) {\n          switch (action) {\n            case \"Devices\":\n              if (deviceEmulator == null) {\n                createDeviceEmulatorLayout();\n              }\n\n              emulator = checked;\n              if (checked) {\n                setDesktopMode(true);\n                setConsoleVisible(false);\n                menu.setChecked(\"Console\", false);\n                menu.setVisible(\"Console\", false);\n                addView(deviceEmulator);\n                fitWebViewTo(\n                  deviceEmulator.getWidthProgress(),\n                  deviceEmulator.getHeightProgress(),\n                  deviceEmulator.getScaleProgress()\n                );\n              } else {\n                menu.setVisible(\"Console\", true);\n                removeView(deviceEmulator);\n                fitWebViewTo(0, 0, 1);\n                webView\n                  .getViewTreeObserver()\n                  .addOnGlobalLayoutListener(\n                    new ViewTreeObserver.OnGlobalLayoutListener() {\n                      @Override\n                      public void onGlobalLayout() {\n                        webView\n                          .getViewTreeObserver()\n                          .removeOnGlobalLayoutListener(this);\n                        setDesktopMode(false);\n                      }\n                    }\n                  );\n              }\n\n              break;\n            case \"Console\":\n              setConsoleVisible(checked);\n              break;\n            case \"Disable Cache\":\n              webView\n                .getSettings()\n                .setCacheMode(\n                  checked ? WebSettings.LOAD_NO_CACHE : WebSettings.LOAD_DEFAULT\n                );\n              break;\n            case \"Open in Browser\":\n              Intent browserIntent = new Intent(\n                Intent.ACTION_VIEW,\n                Uri.parse(url)\n              );\n              context.startActivity(browserIntent);\n              exit();\n              break;\n            case \"Exit\":\n              exit();\n              break;\n          }\n        }\n      }\n    );\n  }\n\n  private void createDeviceEmulatorLayout() {\n    Browser browser = this;\n    deviceEmulator = new Emulator(context, theme);\n    deviceEmulator.setReference(webView);\n    deviceEmulator.setChangeListener(\n      new Emulator.Callback() {\n        @Override\n        public void onChange(int width, int height, float scale) {\n          fitWebViewTo(width, height, scale);\n        }\n      }\n    );\n  }\n\n  private void setTextViewProperties(TextView textView, int height) {\n    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(\n      ViewGroup.LayoutParams.FILL_PARENT,\n      height,\n      1\n    );\n    params.gravity = Gravity.CENTER_VERTICAL;\n    textView.setMaxLines(1);\n    textView.setEllipsize(TextUtils.TruncateAt.END);\n    textView.setSingleLine(true);\n    textView.setHorizontallyScrolling(true);\n    textView.setTextColor(theme.get(\"primaryTextColor\"));\n    textView.setLayoutParams(params);\n    textView.setGravity(Gravity.CENTER_VERTICAL);\n  }\n\n  private void setDesktopMode(boolean enabled) {\n    int width = 0;\n    int height = 0;\n    WebSettings webSettings = webView.getSettings();\n\n    desktopMode = enabled;\n    webSettings.setUserAgentString(\n      enabled\n        ? \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36\"\n        : null\n    );\n\n    if (enabled) {\n      width = webView.getMeasuredWidth();\n      height = webView.getMeasuredHeight();\n    }\n\n    webSettings.setLoadWithOverviewMode(enabled);\n    webSettings.setUseWideViewPort(enabled);\n    webSettings.setLoadWithOverviewMode(enabled);\n    webSettings.setSupportZoom(enabled);\n    webSettings.setBuiltInZoomControls(enabled);\n    webView.reload();\n  }\n\n  public void setDesktopMode() {\n    if (desktopMode) {\n      int width = webView.getMeasuredWidth();\n      int height = webView.getMeasuredHeight();\n      updateViewportDimension(width, height);\n    }\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n    setTitle(url);\n    setProgressBarVisible(true);\n    webView.loadUrl(url);\n  }\n\n  public void setTitle(String title) {\n    this.title = title;\n    titleText.setText(title);\n  }\n\n  public void setFavicon(Bitmap icon) {\n    favicon.setImageBitmap(icon);\n  }\n\n  public void setConsoleVisible(boolean visible) {\n    console = visible;\n    String javascript = \"document.dispatchEvent(new CustomEvent('%sconsole'))\";\n    javascript = String.format(javascript, visible ? \"show\" : \"hide\");\n    webView.evaluateJavascript(javascript, null);\n  }\n\n  public void setProgressBarVisible(boolean visible) {\n    loading.setVisibility(visible ? View.VISIBLE : View.GONE);\n  }\n\n  private void updateViewportDimension(int width, int height) {\n    String script =\n      \"!function(){var e=document.head;if(e){e.querySelectorAll(\\\"meta[name=viewport]\\\").forEach(function(e){e.remove()});var t=document.createElement(\\\"meta\\\");t.name=\\\"viewport\\\",t.content=\\\"width=%s, height=%s, initial-scale=%s\\\",e.append(t)}}();\";\n    String w = \"device-width\";\n    String h = \"device-height\";\n    String r = \"1\";\n    if (width > 0) {\n      w = String.valueOf(width);\n      r = \"0.1\";\n      h = \"\";\n    }\n\n    if (height > 0) {\n      h = String.valueOf(height);\n    }\n\n    webView.evaluateJavascript(String.format(script, w, h, r), null);\n  }\n\n  private void fitWebViewTo(int width, int height, float scale) {\n    webView.setScaleX(scale);\n    webView.setScaleY(scale);\n    webView.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        width == 0 ? ViewGroup.LayoutParams.MATCH_PARENT : width,\n        height == 0 ? ViewGroup.LayoutParams.MATCH_PARENT : height\n      )\n    );\n    if (width > 0 && height > 0) {\n      updateViewportDimension(width, height);\n    }\n  }\n\n  private void styleIcon(ImageView view) {\n    int padding = Ui.dpToPixels(context, 7);\n    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(\n      imageSize,\n      imageSize\n    );\n\n    params.gravity = Gravity.CENTER_VERTICAL;\n    view.setBackgroundDrawable(null);\n    view.setLayoutParams(params);\n    view.setScaleType(ImageView.ScaleType.FIT_CENTER);\n    view.setAdjustViewBounds(true);\n    view.setPadding(padding, padding, padding, padding);\n  }\n\n  private void keyboardVisible(boolean visible) {\n    if (visible) {\n      InputMethodManager imm = (InputMethodManager) context.getSystemService(\n        Context.INPUT_METHOD_SERVICE\n      );\n      imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);\n    } else {\n      InputMethodManager imm = (InputMethodManager) context.getSystemService(\n        Context.INPUT_METHOD_SERVICE\n      );\n      imm.hideSoftInputFromWindow(webView.getWindowToken(), 0);\n    }\n  }\n\n  public boolean goBack() {\n    if (console) {\n      menu.setChecked(\"Console\", false);\n      setConsoleVisible(false);\n      return true;\n    }\n\n    if (webView.canGoBack()) {\n      webView.goBack();\n      url = webView.getOriginalUrl();\n      return true;\n    }\n\n    return false;\n  }\n\n  private TextView createTextView(String text) {\n    TextView textView = new TextView(context);\n    setTextViewProperties(textView, this.titleHeight);\n    textView.setText(text);\n    return textView;\n  }\n\n  private EditText createEditText(String text) {\n    EditText editText = new EditText(context);\n    GradientDrawable background = new GradientDrawable();\n    String themeType = theme.getType();\n\n    int radius = this.titleTextHeight / 2;\n    background.setCornerRadius(radius);\n    background.setColor(themeType.equals(\"light\") ? 0x11000000 : 0x11ffffff);\n    editText.setBackground(background);\n\n    setTextViewProperties(editText, this.titleTextHeight);\n    editText.setText(text);\n    editText.setPadding(radius, 0, radius, 0);\n    editText.setTextSize(this.fontSize < 10 ? 10 : this.fontSize);\n    editText.setInputType(InputType.TYPE_TEXT_VARIATION_URI);\n    editText.setImeOptions(EditorInfo.IME_ACTION_GO);\n\n    editText.setOnFocusChangeListener(\n      new View.OnFocusChangeListener() {\n        @Override\n        public void onFocusChange(View v, boolean hasFocus) {\n          if (hasFocus) {\n            titleText.setText(url);\n            keyboardVisible(true);\n          } else {\n            titleText.setText(title);\n            keyboardVisible(false);\n          }\n        }\n      }\n    );\n\n    editText.setOnEditorActionListener(\n      new EditText.OnEditorActionListener() {\n        @Override\n        public boolean onEditorAction(\n          TextView v,\n          int actionId,\n          KeyEvent event\n        ) {\n          if (actionId == EditorInfo.IME_ACTION_GO) {\n            String url = v.getText().toString();\n            if (!url.startsWith(\"http://\") && !url.startsWith(\"https://\")) {\n              url = \"http://\" + url;\n            }\n\n            title = url;\n            setUrl(url);\n            editText.clearFocus();\n            keyboardVisible(false);\n            return true;\n          }\n          return false;\n        }\n      }\n    );\n\n    return editText;\n  }\n\n  private LinearLayout createTile() {\n    return createTile(titleHeight);\n  }\n\n  private LinearLayout createTile(int height) {\n    LinearLayout tile = new LinearLayout(context);\n    tile.setOrientation(LinearLayout.HORIZONTAL);\n    tile.setBackgroundColor(theme.get(\"primaryColor\"));\n    tile.setLayoutParams(\n      new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, height)\n    );\n    tile.setHorizontalGravity(Gravity.LEFT);\n    tile.setVerticalGravity(Gravity.TOP);\n    return tile;\n  }\n\n  private ImageButton createIconButton(String icon) {\n    Bitmap bitmap = Ui.Icons.get(context, icon, theme.get(\"primaryTextColor\"));\n    ImageButton button = createIconButton(bitmap);\n    return button;\n  }\n\n  private ImageButton createIconButton(Bitmap icon) {\n    ImageButton button = new ImageButton(context);\n    button.setImageBitmap(icon);\n    styleIcon(button);\n    return button;\n  }\n\n  private ImageView createIcon(String code) {\n    Bitmap bitmap = Ui.Icons.get(context, code, theme.get(\"primaryTextColor\"));\n    ImageView icon = new ImageView(context);\n    icon.setImageBitmap(bitmap);\n    styleIcon(icon);\n    return icon;\n  }\n\n  public void exit() {\n    if (console) {\n      setConsoleVisible(false);\n      return;\n    }\n\n    if (webView.canGoBack()) {\n      webView.goBack();\n      return;\n    }\n\n    webView.destroy();\n    ((Activity) context).finish();\n  }\n}\n\nclass BrowserChromeClient extends WebChromeClient {\n\n  Browser browser;\n\n  public BrowserChromeClient(Browser browser) {\n    super();\n    this.browser = browser;\n  }\n\n  @Override\n  public void onReceivedTitle(WebView view, String title) {\n    super.onReceivedTitle(view, title);\n    browser.setTitle(title);\n  }\n\n  @Override\n  public void onReceivedIcon(WebView view, Bitmap icon) {\n    super.onReceivedIcon(view, icon);\n    browser.setFavicon(icon);\n  }\n\n  public boolean onShowFileChooser(\n    WebView webView,\n    ValueCallback<Uri[]> filePathCallback,\n    WebChromeClient.FileChooserParams fileChooserParams\n  ) {\n    if (browser.filePathCallback != null) {\n      browser.filePathCallback.onReceiveValue(null);\n    }\n    browser.filePathCallback = filePathCallback;\n\n    String[] acceptTypes = fileChooserParams.getAcceptTypes();\n    String mimeType = \"*/*\"; // default if empty or null\n    if (acceptTypes != null && acceptTypes.length > 0) {\n      String firstType = acceptTypes[0];\n      if (firstType != null && !firstType.trim().isEmpty()) {\n        mimeType = firstType;\n      }\n    }\n\n    Intent selectDocument = new Intent(Intent.ACTION_GET_CONTENT);\n    selectDocument.addCategory(Intent.CATEGORY_OPENABLE);\n    selectDocument.setType(mimeType);\n\n    boolean isMultiple =\n      (fileChooserParams.getMode() ==\n        WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE);\n    if (isMultiple) {\n      selectDocument.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);\n    }\n\n    ((Activity) webView.getContext()).startActivityForResult(\n        Intent.createChooser(selectDocument, \"Select File\"),\n        browser.FILE_SELECT_CODE\n      );\n\n    return true;\n  }\n}\n\nclass BrowserWebViewClient extends WebViewClient {\n\n  private Browser browser;\n\n  public BrowserWebViewClient(Browser browser) {\n    super();\n    this.browser = browser;\n  }\n\n  @Override\n  public boolean shouldOverrideUrlLoading(WebView view, String url) {\n    browser.setUrl(url);\n    browser.setProgressBarVisible(true);\n    return false;\n  }\n\n  @Override\n  public void onPageStarted(WebView view, String url, Bitmap icon) {\n    super.onPageStarted(view, url, icon);\n    browser.setProgressBarVisible(true);\n  }\n\n  @Override\n  public void onPageFinished(WebView view, String url) {\n    super.onPageFinished(view, url);\n    browser.setProgressBarVisible(false);\n\n    // Inject console for external sites\n    // this is not a good solution but for now its good, later we'll improve this\n    if (!url.startsWith(\"http://localhost\")) {\n      try {\n        File eruaFile = new File(browser.context.getFilesDir(), \"eruda.js\");\n        StringBuilder scriptContent = new StringBuilder();\n        BufferedReader reader = new BufferedReader(new FileReader(eruaFile));\n        String line;\n        while ((line = reader.readLine()) != null) {\n          scriptContent.append(line);\n          scriptContent.append(\"\\n\");\n        }\n        reader.close();\n\n        // Inject the script content directly\n        String script =\n          \"if(!window.eruda){\" +\n          \"  var script = document.createElement('script');\" +\n          \"  script.textContent = `\" +\n          scriptContent.toString() +\n          \"`;\" +\n          \"  document.head.appendChild(script);\" +\n          \"  eruda.init({\" +\n          \"    theme: 'dark'\" +\n          \"  });\" +\n          \"  eruda._shadowRoot.querySelector('.eruda-entry-btn').style.display = 'none';\" +\n          \"  sessionStorage.setItem('__console_available', true);\" +\n          \"  document.addEventListener('showconsole', function() { eruda.show(); });\" +\n          \"  document.addEventListener('hideconsole', function() { eruda.hide(); });\" +\n          \"}\";\n\n        browser.webView.evaluateJavascript(script, null);\n      } catch (IOException e) {\n        e.printStackTrace();\n        // Fallback to CDN if local file fails\n        String fallbackScript =\n          \"if(!window.eruda){\" +\n          \"  var script = document.createElement('script');\" +\n          \"  script.src = 'https://cdn.jsdelivr.net/npm/eruda';\" +\n          \"  script.crossOrigin = 'anonymous';\" +\n          \"  script.onload = function() {\" +\n          \"    eruda.init({\" +\n          \"      theme: 'dark'\" +\n          \"    });\" +\n          \"    eruda._shadowRoot.querySelector('.eruda-entry-btn').style.display = 'none';\" +\n          \"    sessionStorage.setItem('__console_available', true);\" +\n          \"    document.addEventListener('showconsole', function() { eruda.show(); });\" +\n          \"    document.addEventListener('hideconsole', function() { eruda.hide(); });\" +\n          \"  };\" +\n          \"  document.head.appendChild(script);\" +\n          \"}\";\n        browser.webView.evaluateJavascript(fallbackScript, null);\n      }\n      browser.menu.setChecked(\"Console\", false);\n    } else {\n      browser.webView.evaluateJavascript(\n        \"sessionStorage.getItem('__console_available')\",\n        new ValueCallback<String>() {\n          @Override\n          public void onReceiveValue(String value) {\n            boolean show = !value.equals(\"null\");\n            browser.menu.setVisible(\"Console\", show && !browser.emulator);\n\n            // If user had toggled \"Console\" on before, re-show it\n            if (browser.console && show) {\n              browser.setConsoleVisible(true);\n              browser.menu.setChecked(\"Console\", true);\n            }\n          }\n        }\n      );\n    }\n  }\n\n  @Override\n  public void onLoadResource(WebView view, String url) {\n    browser.setDesktopMode();\n  }\n}\n"
  },
  {
    "path": "src/plugins/browser/android/com/foxdebug/browser/BrowserActivity.java",
    "content": "package com.foxdebug.browser;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowInsetsController;\nimport android.webkit.WebChromeClient;\nimport com.foxdebug.system.Ui;\nimport org.json.JSONObject;\n\npublic class BrowserActivity extends Activity {\n\n  private Browser browser;\n  private Ui.Theme theme;\n\n  @Override\n  public void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n\n    Intent intent = getIntent();\n    String url = intent.getStringExtra(\"url\");\n    String themeString = intent.getStringExtra(\"theme\");\n    boolean onlyConsole = intent.getBooleanExtra(\"onlyConsole\", false);\n\n    try {\n      JSONObject obj = new JSONObject(themeString);\n      theme = new Ui.Theme(obj);\n    } catch (Exception e) {\n      theme = new Ui.Theme(new JSONObject());\n    }\n\n    browser = new Browser(this, theme, onlyConsole);\n    browser.setUrl(url);\n    setContentView(browser);\n    setSystemTheme(theme.get(\"primaryColor\"));\n  }\n\n  @Override\n  public void onBackPressed() {\n    boolean didGoBack = browser.goBack();\n\n    if (!didGoBack) {\n      browser.exit();\n    }\n  }\n\n  private void setSystemTheme(int systemBarColor) {\n    try {\n      Ui.Icons.setSize(Ui.dpToPixels(this, 18));\n      final Window window = getWindow();\n      // Method and constants not available on all SDKs but we want to be able to compile this code with any SDK\n      window.clearFlags(0x04000000); // SDK 19: WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n      window.addFlags(0x80000000); // SDK 21: WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n      try {\n        // Using reflection makes sure any 5.0+ device will work without having to compile with SDK level 21\n\n        window\n          .getClass()\n          .getMethod(\"setNavigationBarColor\", int.class)\n          .invoke(window, systemBarColor);\n\n        window\n          .getClass()\n          .getMethod(\"setStatusBarColor\", int.class)\n          .invoke(window, systemBarColor);\n\n        if (Build.VERSION.SDK_INT < 30) {\n          setStatusBarStyle(window);\n          setNavigationBarStyle(window);\n        } else {\n          String themeType = theme.getType();\n          WindowInsetsController controller = window.getInsetsController();\n          int appearance =\n            WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS |\n            WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;\n\n          if (themeType.equals(\"light\")) {\n            controller.setSystemBarsAppearance(appearance, appearance);\n          } else {\n            controller.setSystemBarsAppearance(0, appearance);\n          }\n        }\n      } catch (IllegalArgumentException error) {\n        Log.w(\"BrowserActivity\", \"Invalid system bar color or appearance input.\", error);\n      } catch (Exception error) {\n        Log.w(\"BrowserActivity\", \"Failed applying system bar theme values.\", error);\n      }\n    } catch (Exception e) {\n      Log.e(\"BrowserActivity\", \"Failed to apply system theme.\", e);\n    }\n  }\n\n  private void setStatusBarStyle(final Window window) {\n    View decorView = window.getDecorView();\n    int uiOptions = decorView.getSystemUiVisibility();\n    String themeType = theme.getType();\n\n    if (themeType.equals(\"light\")) {\n      decorView.setSystemUiVisibility(\n        uiOptions | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR\n      );\n      return;\n    }\n  }\n\n  private void setNavigationBarStyle(final Window window) {\n    View decorView = window.getDecorView();\n    int uiOptions = decorView.getSystemUiVisibility();\n    String themeType = theme.getType();\n\n    if (themeType.equals(\"light\")) {\n      decorView.setSystemUiVisibility(uiOptions | 0x80000000 | 0x00000010);\n      return;\n    }\n  }\n\n  @Override\n  protected void onActivityResult(\n    int requestCode,\n    int resultCode,\n    Intent data\n  ) {\n    super.onActivityResult(requestCode, resultCode, data);\n\n    if (requestCode == browser.FILE_SELECT_CODE) {\n      if (browser.filePathCallback == null) {\n        return;\n      }\n\n      browser.filePathCallback.onReceiveValue(\n        WebChromeClient.FileChooserParams.parseResult(resultCode, data)\n      );\n\n      browser.filePathCallback = null;\n    }\n  }\n}\n"
  },
  {
    "path": "src/plugins/browser/android/com/foxdebug/browser/Emulator.java",
    "content": "package com.foxdebug.browser;\n\nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.ScrollView;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport com.foxdebug.system.Ui;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\n\npublic class Emulator extends LinearLayout {\n\n  private Ui.Theme theme;\n  private View reference;\n  private Context context;\n  private Callback listener;\n  private Device customDevice;\n  private Device selectedDevice;\n  private boolean initialized = false;\n  private LinearLayout seekBarsLayout;\n  private DeviceListView deviceListView;\n  private HashMap<String, SeekBar> seekBars = new HashMap<String, SeekBar>();\n\n  public abstract static class Callback {\n\n    public abstract void onChange(int width, int height, float scale);\n  }\n\n  public Emulator(Context context, Ui.Theme theme) {\n    super(context);\n    View border;\n    LinearLayout main;\n\n    this.context = context;\n    this.theme = theme;\n\n    customDevice = new Device(\"Custom\", 0, 0, Ui.Icons.TUNE, false);\n    deviceListView = new DeviceListView(context, theme);\n    deviceListView.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        Ui.dpToPixels(context, 100),\n        LinearLayout.LayoutParams.MATCH_PARENT\n      )\n    );\n\n    deviceListView.setOnSelect(\n      new DeviceListView.Callback() {\n        @Override\n        public void onSelect(Device device) {\n          selectDevice(device);\n        }\n      }\n    );\n\n    deviceListView.add(\n      customDevice,\n      new Device(\"iPhone SE\", 320, 568, Ui.Icons.PHONE_APPLE),\n      new Device(\"iPhone 8\", 375, 667, Ui.Icons.PHONE_APPLE),\n      new Device(\"iPhone 8+\", 414, 736, Ui.Icons.PHONE_APPLE),\n      new Device(\"iPhone X\", 375, 812, Ui.Icons.PHONE_APPLE),\n      new Device(\"iPad\", 768, 1024, Ui.Icons.TABLET_APPLE),\n      new Device(\"iPad Pro\", 1024, 1366, Ui.Icons.TABLET_APPLE),\n      new Device(\"Galaxy S5\", 360, 640, Ui.Icons.PHONE_ANDROID),\n      new Device(\"Pixel 2\", 411, 731, Ui.Icons.PHONE_ANDROID),\n      new Device(\"Pixel 2 XL\", 411, 823, Ui.Icons.PHONE_ANDROID),\n      new Device(\"Nexus 5X\", 411, 731, Ui.Icons.PHONE_ANDROID),\n      new Device(\"Nexus 6P\", 411, 731, Ui.Icons.PHONE_ANDROID),\n      new Device(\"Nexus 7\", 600, 960, Ui.Icons.TABLET_ANDROID),\n      new Device(\"Nexus 10\", 800, 1280, Ui.Icons.TABLET_ANDROID),\n      new Device(\"Laptop\", 1280, 800, Ui.Icons.LAPTOP),\n      new Device(\"Laptop L\", 1440, 900, Ui.Icons.LAPTOP),\n      new Device(\"Laptop XL\", 1680, 1050, Ui.Icons.LAPTOP),\n      new Device(\"UHD 4k\", 3840, 2160, Ui.Icons.TV)\n    );\n\n    deviceListView.select(customDevice);\n\n    seekBarsLayout = new LinearLayout(context);\n    seekBarsLayout.setPadding(0, 10, 0, 0);\n    seekBarsLayout.setOrientation(LinearLayout.VERTICAL);\n    seekBarsLayout.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        LinearLayout.LayoutParams.MATCH_PARENT,\n        LinearLayout.LayoutParams.WRAP_CONTENT,\n        1\n      )\n    );\n\n    border = new View(context);\n    border.setBackgroundColor(theme.get(\"borderColor\"));\n    border.setLayoutParams(\n      new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 1)\n    );\n\n    main = new LinearLayout(context);\n    main.setOrientation(LinearLayout.HORIZONTAL);\n    main.setBackgroundColor(theme.get(\"primaryColor\"));\n    main.addView(seekBarsLayout);\n    main.addView(deviceListView);\n    main.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        LinearLayout.LayoutParams.MATCH_PARENT,\n        LinearLayout.LayoutParams.WRAP_CONTENT\n      )\n    );\n\n    addControl(\"width\", 50, \"Width\");\n    addControl(\"height\", 50, \"Height\");\n    addControl(\"scale\", 50, \"Scale\");\n    addView(border);\n    addView(main);\n    setOrientation(LinearLayout.VERTICAL);\n  }\n\n  public void setChangeListener(Callback listener) {\n    this.listener = listener;\n  }\n\n  public void setReference(View view) {\n    SeekBar widthSeekBar = seekBars.get(\"width\");\n    SeekBar heightSeekBar = seekBars.get(\"height\");\n    SeekBar scaleSeekBar = seekBars.get(\"scale\");\n    int maxWidth = view.getMeasuredWidth();\n    int maxHeight = view.getMeasuredHeight();\n    int width = widthSeekBar.getProgress();\n    int height = heightSeekBar.getProgress();\n    int minWidth = widthSeekBar.getMin();\n    int minHeight = heightSeekBar.getMin();\n\n    getViewTreeObserver()\n      .addOnGlobalLayoutListener(\n        new ViewTreeObserver.OnGlobalLayoutListener() {\n          @Override\n          public void onGlobalLayout() {\n            getViewTreeObserver().removeOnGlobalLayoutListener(this);\n            int correctedHeight = maxHeight - getHeight();\n            int iHeight = height;\n            int iWidth = width;\n\n            widthSeekBar.setMax(maxWidth);\n            heightSeekBar.setMax(correctedHeight);\n            if (width > maxWidth || !initialized) {\n              heightSeekBar.setProgress(maxHeight);\n              iHeight = maxHeight;\n            }\n\n            if (height > correctedHeight || !initialized) {\n              widthSeekBar.setProgress(maxWidth);\n              iWidth = maxWidth;\n            }\n\n            setMaxScale(iWidth, iHeight);\n            scaleSeekBar.setMin(100);\n            scaleSeekBar.setProgress(100);\n            if (listener != null) {\n              listener.onChange(iWidth, correctedHeight, 1);\n            }\n          }\n        }\n      );\n  }\n\n  public int getWidthProgress() {\n    return seekBars.get(\"width\").getProgress();\n  }\n\n  public int getHeightProgress() {\n    return seekBars.get(\"height\").getProgress();\n  }\n\n  public float getScaleProgress() {\n    return seekBars.get(\"scale\").getProgress() / 100f;\n  }\n\n  private void addControl(String id, int height, String label) {\n    LinearLayout linearLayout = new LinearLayout(context);\n    TextView textView = new TextView(context);\n    SeekBar seekBar = new SeekBar(context);\n\n    linearLayout.setOrientation(LinearLayout.VERTICAL);\n    linearLayout.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        LinearLayout.LayoutParams.MATCH_PARENT,\n        LinearLayout.LayoutParams.WRAP_CONTENT\n      )\n    );\n\n    seekBar.setMin(300);\n    seekBar.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        LinearLayout.LayoutParams.MATCH_PARENT,\n        height,\n        1\n      )\n    );\n\n    textView.setText(String.format(label, 0));\n    textView.setPadding(10, 0, 0, 0);\n\n    linearLayout.addView(textView);\n    linearLayout.addView(seekBar);\n    seekBarsLayout.addView(linearLayout);\n    seekBars.put(id, seekBar);\n\n    seekBar.setOnSeekBarChangeListener(\n      new SeekBar.OnSeekBarChangeListener() {\n        @Override\n        public void onProgressChanged(\n          SeekBar seekBar,\n          int progress,\n          boolean fromUser\n        ) {\n          if (!fromUser || listener == null) {\n            return;\n          }\n\n          String seekBarName = seekBar == seekBars.get(\"width\")\n            ? \"width\" //\n            : seekBar == seekBars.get(\"height\") //\n              ? \"height\" //\n              : \"scale\"; //\n\n          Log.d(\"Emulator\", seekBarName);\n\n          int height = getHeightProgress();\n          int width = getWidthProgress();\n          float scale = getScaleProgress();\n\n          if (seekBarName != \"scale\") {\n            SeekBar scaleSeekBar = seekBars.get(\"scale\");\n            setMaxScale(width, height);\n            scale = 1;\n          }\n\n          listener.onChange(width, height, scale);\n\n          if (\n            seekBarName == \"scale\" ||\n            selectedDevice == null ||\n            selectedDevice.id == customDevice.id\n          ) {\n            return;\n          }\n\n          selectDevice(customDevice);\n        }\n\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {}\n\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {}\n      }\n    );\n  }\n\n  private void selectDevice(Device device) {\n    if (selectedDevice != null) selectedDevice.deselect();\n    device.select();\n\n    selectedDevice = device;\n    if (device.id == customDevice.id) {\n      return;\n    }\n\n    SeekBar widthSeekBar = seekBars.get(\"width\");\n    SeekBar heightSeekBar = seekBars.get(\"height\");\n    SeekBar scaleSeekBar = seekBars.get(\"scale\");\n\n    int maxWidth = widthSeekBar.getMax();\n    int maxHeight = heightSeekBar.getMax();\n    int maxScale;\n\n    int width = device.width;\n    int height = device.height;\n\n    if (width > maxWidth) {\n      float ratio = (width - maxWidth) / (float) width;\n      width = maxWidth;\n      height = (int) (height - (height * ratio));\n    }\n\n    if (height > maxHeight) {\n      float ratio = (height - maxHeight) / (float) height;\n      height = maxHeight;\n      width = (int) (width - (width * ratio));\n    }\n\n    widthSeekBar.setProgress(width);\n    heightSeekBar.setProgress(height);\n    setMaxScale(width, height);\n    maxScale = scaleSeekBar.getMax();\n    scaleSeekBar.setProgress(maxScale);\n    listener.onChange(width, height, maxScale / 100f);\n  }\n\n  private void setMaxScale(int width, int height) {\n    SeekBar scaleSeekBar = seekBars.get(\"scale\");\n    SeekBar widthSeekBar = seekBars.get(\"width\");\n    SeekBar heightSeekBar = seekBars.get(\"height\");\n    int maxWidth = widthSeekBar.getMax();\n    int maxHeight = heightSeekBar.getMax();\n\n    float scaleX = maxWidth / (float) width;\n    float scaleY = maxHeight / (float) height;\n    int scale = (int) (Math.min(scaleX, scaleY) * 100);\n\n    scaleSeekBar.setMax(scale);\n    scaleSeekBar.setProgress(100);\n  }\n}\n\nclass Device {\n\n  public int id;\n  public int width;\n  public int height;\n  public String name;\n  public String icon;\n  public DeviceView view;\n  public boolean isDesktop;\n\n  public Device(\n    String name,\n    int width,\n    int height,\n    String icon,\n    boolean isDesktop\n  ) {\n    this.id = View.generateViewId();\n    this.name = name;\n    this.icon = icon;\n    this.width = width;\n    this.height = height;\n    this.isDesktop = isDesktop;\n  }\n\n  public Device(String name, int width, int height, String icon) {\n    this(name, width, height, icon, true);\n  }\n\n  public Device(String name, int width, int height) {\n    this(name, width, height, Ui.Icons.TUNE, true);\n  }\n\n  public void select() {\n    if (view != null) {\n      view.select();\n    }\n  }\n\n  public void deselect() {\n    if (view != null) {\n      view.deselect();\n    }\n  }\n}\n\nclass DeviceListView extends ScrollView {\n\n  DeviceView selectedDeviceView;\n  LinearLayout deviceListLayout;\n  Callback callback;\n  Context context;\n  Ui.Theme theme;\n\n  public abstract static class Callback {\n\n    public abstract void onSelect(Device device);\n  }\n\n  public DeviceListView(Context context, Ui.Theme theme) {\n    super(context);\n    this.context = context;\n    this.theme = theme;\n\n    deviceListLayout = new LinearLayout(context);\n    deviceListLayout.setOrientation(LinearLayout.VERTICAL);\n    deviceListLayout.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        LinearLayout.LayoutParams.MATCH_PARENT,\n        LinearLayout.LayoutParams.WRAP_CONTENT\n      )\n    );\n\n    addView(deviceListLayout);\n  }\n\n  public void add(Device... devices) {\n    for (Device device : devices) {\n      add(device);\n    }\n  }\n\n  public void select(Device device) {\n    DeviceView deviceView = (DeviceView) findViewById(device.id);\n    deviceView.select();\n    selectedDeviceView = deviceView;\n  }\n\n  public void add(Device device) {\n    DeviceView deviceView = new DeviceView(context, device, theme);\n    deviceListLayout.addView(deviceView);\n\n    deviceView.setOnSelect(\n      new DeviceView.Callback() {\n        @Override\n        public void onSelect(DeviceView view) {\n          if (callback == null) return;\n\n          if (selectedDeviceView != null) selectedDeviceView.deselect();\n          view.select();\n\n          callback.onSelect(view.device);\n          selectedDeviceView = view;\n        }\n      }\n    );\n  }\n\n  public void setOnSelect(Callback callback) {\n    this.callback = callback;\n  }\n}\n\nclass DeviceView extends LinearLayout {\n\n  Ui.Theme theme;\n  Device device;\n  TextView label;\n  ImageView icon;\n  Context context;\n  boolean isSelected = false;\n\n  public abstract static class Callback {\n\n    public abstract void onSelect(DeviceView device);\n  }\n\n  public DeviceView(Context context, Device device, Ui.Theme theme) {\n    super(context);\n    int primaryTextColor = theme.get(\"primaryTextColor\");\n    this.theme = theme;\n    this.device = device;\n    this.context = context;\n    this.device.view = this;\n\n    setId(device.id);\n    setClickable(true);\n    setPadding(0, 5, 0, 5);\n    setOrientation(LinearLayout.HORIZONTAL);\n\n    icon = new ImageView(context);\n    icon.setImageBitmap(Ui.Icons.get(context, device.icon, primaryTextColor));\n    icon.setPadding(0, 0, 5, 0);\n    icon.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        LinearLayout.LayoutParams.WRAP_CONTENT,\n        LinearLayout.LayoutParams.WRAP_CONTENT\n      )\n    );\n\n    label = new TextView(context);\n    label.setSingleLine(true);\n    label.setText(device.name);\n    label.setTextColor(primaryTextColor);\n\n    addView(icon);\n    addView(label);\n  }\n\n  public void setOnSelect(Callback callback) {\n    DeviceView self = this;\n    setOnClickListener(\n      new View.OnClickListener() {\n        @Override\n        public void onClick(View view) {\n          callback.onSelect(self);\n        }\n      }\n    );\n  }\n\n  public void deselect() {\n    int primaryTextColor = theme.get(\"primaryTextColor\");\n    icon.setImageBitmap(Ui.Icons.get(context, device.icon, primaryTextColor));\n    label.setTextColor(primaryTextColor);\n    label.setTypeface(null, Typeface.NORMAL);\n    isSelected = false;\n  }\n\n  public void select() {\n    int activeTextColor = theme.get(\"activeTextColor\");\n    icon.setImageBitmap(Ui.Icons.get(context, device.icon, activeTextColor));\n    label.setTextColor(activeTextColor);\n    label.setTypeface(null, Typeface.BOLD);\n    isSelected = true;\n  }\n}\n"
  },
  {
    "path": "src/plugins/browser/android/com/foxdebug/browser/Menu.java",
    "content": "package com.foxdebug.browser;\n\nimport android.content.Context;\nimport android.content.res.ColorStateList;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.GradientDrawable;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.CheckBox;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.PopupWindow;\nimport android.widget.ScrollView;\nimport android.widget.TextView;\nimport com.foxdebug.acode.R;\nimport com.foxdebug.system.Ui;\n\npublic class Menu extends PopupWindow {\n\n  private Ui.Theme theme;\n  private LinearLayout list;\n  private Callback callback;\n\n  Context context;\n  int itemHeight;\n  int imageSize;\n  int padding;\n\n  public abstract static class Callback {\n\n    public abstract void onSelect(String action, Boolean checked);\n  }\n\n  public Menu(Context context, Ui.Theme theme) {\n    super(context);\n    this.theme = theme;\n    this.context = context;\n\n    padding = Ui.dpToPixels(context, 5);\n    imageSize = Ui.dpToPixels(context, 30);\n    itemHeight = Ui.dpToPixels(context, 40);\n\n    MenuItem exit;\n    GradientDrawable border;\n    MenuItem toggleDisableCache;\n\n    border = new GradientDrawable();\n    border.setColor(theme.get(\"popupBackgroundColor\"));\n    border.setCornerRadius(Ui.dpToPixels(context, 8));\n\n    list = new LinearLayout(context);\n    list.setOrientation(LinearLayout.VERTICAL);\n    list.setBackgroundDrawable(border);\n    list.setPadding(padding, padding, padding, padding);\n    list.setLayoutParams(\n      new LinearLayout.LayoutParams(\n        ViewGroup.LayoutParams.WRAP_CONTENT,\n        ViewGroup.LayoutParams.WRAP_CONTENT\n      )\n    );\n\n    ScrollView scrollView = new ScrollView(context);\n    scrollView.addView(list);\n\n    setElevation(10f);\n    setFocusable(true);\n    setContentView(scrollView);\n    setBackgroundDrawable(border);\n    setAnimationStyle(R.style.MenuAnimation);\n  }\n\n  public void setCallback(Callback callback) {\n    this.callback = callback;\n  }\n\n  public void addItem(String icon, String text) {\n    addItem(icon, text, null);\n  }\n\n  public void addItem(String icon, String text, Boolean toggle) {\n    int textColor = theme.get(\"popupTextColor\");\n    int background = theme.get(\"popupBackgroundColor\");\n    MenuItem menuItem = new MenuItem(this, text);\n    menuItem.setBackgroundColor(background);\n    menuItem.setIcon(icon, textColor);\n    menuItem.setText(text, textColor);\n    menuItem.setOnClickListener(\n      new MenuItemCallback() {\n        @Override\n        public void onClick(MenuItem menuItem) {\n          callback.onSelect(menuItem.action, menuItem.checked);\n          hide();\n        }\n      }\n    );\n\n    if (toggle != null) {\n      menuItem.setChecked(toggle);\n    }\n\n    list.addView(menuItem);\n  }\n\n  public void setChecked(String action, Boolean checked) {\n    for (int i = 0; i < list.getChildCount(); i++) {\n      MenuItem menuItem = (MenuItem) list.getChildAt(i);\n      if (menuItem.action == action) {\n        menuItem.setChecked(checked);\n        break;\n      }\n    }\n  }\n\n  public void setVisible(String action, Boolean visible) {\n    for (int i = 0; i < list.getChildCount(); i++) {\n      MenuItem menuItem = (MenuItem) list.getChildAt(i);\n      if (menuItem.action == action) {\n        menuItem.setVisibility(visible ? View.VISIBLE : View.GONE);\n        break;\n      }\n    }\n  }\n\n  public void show(View view) {\n    int x = view.getLeft();\n    int y = view.getTop();\n    showAtLocation(view, Gravity.TOP | Gravity.RIGHT, padding, padding);\n  }\n\n  public void hide() {\n    dismiss();\n  }\n}\n\nabstract class MenuItemCallback {\n\n  public abstract void onClick(MenuItem menuItem);\n}\n\nclass MenuItem extends LinearLayout {\n\n  private Context context;\n\n  public Boolean checked;\n  public CheckBox checkBox;\n  public String action;\n  private int textColor;\n  private int itemHeight;\n  private int imageSize;\n  private int padding;\n  private int iconSize;\n  private int paddingLeft = 0;\n  private int paddingRight = 0;\n  private int paddingVertical = 0;\n\n  public MenuItem(Menu menu, String action) {\n    super(menu.context);\n    this.action = action;\n\n    context = menu.context;\n    padding = menu.padding;\n    imageSize = menu.imageSize;\n    itemHeight = menu.itemHeight;\n    iconSize = Ui.dpToPixels(context, 10);\n    paddingRight = imageSize;\n    paddingVertical = Ui.dpToPixels(context, 5);\n\n    setPadding();\n    setClickable(true);\n    setLayoutParams(\n      new LinearLayout.LayoutParams(\n        ViewGroup.LayoutParams.MATCH_PARENT,\n        itemHeight\n      )\n    );\n    setGravity(Gravity.CENTER_VERTICAL);\n    setOrientation(LinearLayout.HORIZONTAL);\n    addView(new TextView(context));\n  }\n\n  public void setPadding() {\n    setPadding(paddingLeft, paddingVertical, paddingRight, paddingVertical);\n  }\n\n  public void setIcon(String icon, int iconColor) {\n    ImageView imageView = new ImageView(context);\n    Bitmap iconBitmap = Ui.Icons.get(context, icon, iconColor);\n    imageView.setImageBitmap(iconBitmap);\n    imageView.setBackgroundDrawable(null);\n    imageView.setLayoutParams(\n      new LinearLayout.LayoutParams(imageSize, imageSize)\n    );\n    imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);\n    imageView.setAdjustViewBounds(true);\n    imageView.setPadding(padding, padding, padding, padding);\n    addView(imageView, 0);\n  }\n\n  public void setText(String text, int color) {\n    textColor = color;\n    TextView textView = new TextView(context);\n    LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(\n      ViewGroup.LayoutParams.FILL_PARENT,\n      ViewGroup.LayoutParams.MATCH_PARENT,\n      1\n    );\n    textView.setText(text);\n    textView.setTextColor(textColor);\n    textView.setGravity(Gravity.CENTER_VERTICAL);\n    textView.setLayoutParams(textViewParams);\n    addView(textView, 1);\n  }\n\n  public void setChecked(boolean checked) {\n    this.checked = checked;\n    if (checkBox != null) {\n      checkBox.setChecked(checked);\n      return;\n    }\n\n    checkBox = new CheckBox(context);\n    checkBox.setChecked(checked);\n    checkBox.setEnabled(false);\n    checkBox.setClickable(false);\n    checkBox.setButtonTintList(\n      new ColorStateList(\n        new int[][] {\n          new int[] { android.R.attr.state_checked },\n          new int[] { -android.R.attr.state_checked },\n        },\n        new int[] { textColor, textColor }\n      )\n    );\n\n    FrameLayout container = new FrameLayout(context);\n    FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(\n      imageSize,\n      imageSize\n    );\n    containerParams.gravity = Gravity.CENTER_VERTICAL;\n    container.setLayoutParams(containerParams);\n    container.addView(checkBox);\n\n    paddingRight = 0;\n    setPadding();\n    addView(container);\n  }\n\n  public void setOnClickListener(MenuItemCallback listener) {\n    MenuItem self = this;\n    this.setOnClickListener(\n        new View.OnClickListener() {\n          @Override\n          public void onClick(View v) {\n            if (checkBox != null) {\n              checked = !checked;\n              checkBox.setChecked(checked);\n            }\n\n            listener.onClick(self);\n          }\n        }\n      );\n  }\n}\n"
  },
  {
    "path": "src/plugins/browser/android/com/foxdebug/browser/Plugin.java",
    "content": "package com.foxdebug.browser;\n\nimport android.content.Intent;\nimport com.foxdebug.browser.BrowserActivity;\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaPlugin;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class Plugin extends CordovaPlugin {\n\n  @Override\n  public boolean execute(\n    String action,\n    JSONArray args,\n    CallbackContext callbackContext\n  ) throws JSONException {\n    if (action.equals(\"open\")) {\n      String url = args.getString(0);\n      JSONObject theme = args.getJSONObject(1);\n      boolean onlyConsole = args.optBoolean(2, false);\n      String themeString = theme.toString();\n      Intent intent = new Intent(cordova.getActivity(), BrowserActivity.class);\n\n      intent.putExtra(\"url\", url);\n      intent.putExtra(\"theme\", themeString);\n      intent.putExtra(\"onlyConsole\", onlyConsole);\n      cordova.getActivity().startActivity(intent);\n      callbackContext.success(\"Opened browser\");\n      return true;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/plugins/browser/index.js",
    "content": "import settings from 'lib/settings';\nimport themes from 'theme/list';\n\nconst SERVICE = 'Browser';\n\nfunction open(url, isConsole = false) {\n  const ACTION = 'open';\n  const success = () => { };\n  const error = () => { };\n  const theme = themes.get(settings.value.appTheme).toJSON('hex');\n  cordova.exec(success, error, SERVICE, ACTION, [url, theme, isConsole]);\n}\n\nexport default {\n  open,\n};"
  },
  {
    "path": "src/plugins/browser/package.json",
    "content": "{\r\n  \"name\": \"cordova-plugin-browser\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"\",\r\n  \"main\": \"\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  },\r\n  \"author\": \"\",\r\n  \"license\": \"ISC\"\r\n}"
  },
  {
    "path": "src/plugins/browser/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\r\n  xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-browser\" version=\"1.0.0\">\r\n  <name>cordova-plugin-browser</name>\r\n  <description>In app browser</description>\r\n  <license>Apache 2.0</license>\r\n\r\n  <platform name=\"android\">\r\n\r\n    <config-file target=\"res/xml/config.xml\" parent=\"/*\">\r\n      <feature name=\"Browser\">\r\n        <param name=\"android-package\" value=\"com.foxdebug.browser.Plugin\"/>\r\n      </feature>\r\n    </config-file>\r\n\r\n\r\n    <config-file parent=\"./application\" target=\"AndroidManifest.xml\">\r\n      <activity android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode\" android:name=\"com.foxdebug.browser.BrowserActivity\" android:theme=\"@style/Theme.App.Activity\" android:windowSoftInputMode=\"adjustResize\" android:resizeableActivity=\"true\">\r\n      </activity>\r\n    </config-file>\r\n\r\n    <hook type=\"after_prepare\" src=\"utils/updatePackage.js\" />\r\n\r\n    <resource-file src=\"res/android/menu_enter.xml\" target=\"res/anim/menu_enter.xml\" />\r\n    <resource-file src=\"res/android/menu_exit.xml\" target=\"res/anim/menu_exit.xml\" />\r\n    <resource-file src=\"res/android/styles.xml\" target=\"res/values/styles.xml\" />\r\n    <source-file src=\"android/com/foxdebug/browser/Browser.java\" target-dir=\"src/com/foxdebug/browser\"/>\r\n    <source-file src=\"android/com/foxdebug/browser/BrowserActivity.java\" target-dir=\"src/com/foxdebug/browser\"/>\r\n    <source-file src=\"android/com/foxdebug/browser/Emulator.java\" target-dir=\"src/com/foxdebug/browser\"/>\r\n    <source-file src=\"android/com/foxdebug/browser/Plugin.java\" target-dir=\"src/com/foxdebug/browser\"/>\r\n    <source-file src=\"android/com/foxdebug/browser/Menu.java\" target-dir=\"src/com/foxdebug/browser\"/>\r\n  </platform>\r\n</plugin>"
  },
  {
    "path": "src/plugins/browser/res/android/menu_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <!-- Scale from small to normal size -->\n  <scale android:duration=\"100\" android:fromXScale=\"0\" android:fromYScale=\"0.5\" android:pivotX=\"100%\" android:pivotY=\"0%\" android:toXScale=\"1.0\" android:toYScale=\"1.0\" />\n\n  <!-- Fade in from 0% opacity to 100% -->\n  <alpha android:duration=\"100\" android:fromAlpha=\"0.0\" android:toAlpha=\"1.0\" />\n</set>\n"
  },
  {
    "path": "src/plugins/browser/res/android/menu_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <!-- Scale from normal size to small -->\n  <scale android:duration=\"100\" android:fromXScale=\"1.0\" android:fromYScale=\"1.0\" android:pivotX=\"100%\" android:pivotY=\"0%\" android:toXScale=\"0.7\" android:toYScale=\"0.7\" />\n\n  <!-- Fade out from 100% opacity to 0% -->\n  <alpha android:duration=\"100\" android:fromAlpha=\"1.0\" android:toAlpha=\"0.0\" />\n</set>\n"
  },
  {
    "path": "src/plugins/browser/res/android/styles.xml",
    "content": "<resources>\n  <style name=\"MenuAnimation\">\n    <item name=\"android:windowEnterAnimation\">@anim/menu_enter</item>\n    <item name=\"android:windowExitAnimation\">@anim/menu_exit</item>\n  </style>\n</resources>"
  },
  {
    "path": "src/plugins/browser/utils/updatePackage.js",
    "content": "const fs = require(\"fs\");\r\nconst path = require(\"path\");\r\n\r\nconst configXML = path.resolve(__dirname, \"../../../config.xml\");\r\nconst menuJava = path.resolve(\r\n  __dirname,\r\n  \"../../../platforms/android/app/src/main/java/com/foxdebug/browser/Menu.java\"\r\n);\r\nconst docProvider = path.resolve(\r\n  __dirname,\r\n  \"../../../platforms/android/app/src/main/java/com/foxdebug/acode/rk/exec/terminal/AlpineDocumentProvider.java\"\r\n);\r\n\r\nconst repeatChar = (char, times) => char.repeat(times);\r\n\r\nfunction replaceImport(filePath, appName) {\r\n  if (!fs.existsSync(filePath)) {\r\n    console.warn(`⚠ File not found: ${filePath}`);\r\n    return;\r\n  }\r\n\r\n  const data = fs.readFileSync(filePath, \"utf8\");\r\n\r\n  const updated = data.replace(\r\n    /(import\\s+com\\.foxdebug\\.)(acode|acodefree)(\\.R;)/,\r\n    `$1${appName}$3`\r\n  );\r\n\r\n  fs.writeFileSync(filePath, updated);\r\n}\r\n\r\ntry {\r\n  if (!fs.existsSync(configXML)) {\r\n    throw new Error(\"config.xml not found\");\r\n  }\r\n\r\n  const config = fs.readFileSync(configXML, \"utf8\");\r\n  const match = /widget\\s+id=\"([0-9a-zA-Z.\\-_]+)\"/.exec(config);\r\n\r\n  if (!match) {\r\n    throw new Error(\"Could not extract widget id from config.xml\");\r\n  }\r\n\r\n  const appName = match[1].split(\".\").pop();\r\n\r\n  replaceImport(docProvider, appName);\r\n  replaceImport(menuJava, appName);\r\n\r\n  const msg = `==== Changed package to com.foxdebug.${appName} ====`;\r\n\r\n  console.log(\"\\n\" + repeatChar(\"=\", msg.length));\r\n  console.log(msg);\r\n  console.log(repeatChar(\"=\", msg.length) + \"\\n\");\r\n\r\n} catch (error) {\r\n  console.error(\"❌ Error:\", error.message);\r\n  process.exit(1);\r\n}"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/.gitignore",
    "content": ".DS_Store\nignore/\nnode_modules\n"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/.npmignore",
    "content": ".npmignore\n.travis.yml\n.DS_Store\nignore/\nnode_modules"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/.travis.yml",
    "content": "language: node_js\nsudo: false\nnode_js:\n    - \"4.2\"\n"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Mikihiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/package.json",
    "content": "{\n  \"name\": \"cordova-plugin-buildinfo\",\n  \"version\": \"4.0.0\",\n  \"description\": \"Cordova/PhoneGap Build Information Plugin. Get PackageName, Version, Debug and more...\",\n  \"cordova\": {\n    \"id\": \"cordova-plugin-buildinfo\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"debug\",\n    \"buildconfig\",\n    \"buildinfo\",\n    \"phonegap\",\n    \"cordova\",\n    \"ecosystem:cordova\",\n    \"cordova-android\"\n  ],\n  \"scripts\": {\n    \"test\": \"npm run jshint\",\n    \"jshint\": \"node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests\"\n  },\n  \"engines\": {\n    \"cordovaDependencies\": {\n      \"0.0.0\": {\n        \"cordova\": \">=4.0.0\"\n      },\n      \"2.0.0\": {\n        \"cordova\": \">=5.4.0\",\n        \"cordova-android\": \">=5.0.0\"\n      },\n      \"3.0.0\": {\n        \"cordova\": \">=5.4.0\",\n        \"cordova-android\": \">=5.0.0\"\n      },\n      \"4.0.0\": {\n        \"cordova\": \">=5.4.0\",\n        \"cordova-android\": \">=5.0.0\"\n      },\n      \"5.0.0\": {\n        \"cordova\": \">100\"\n      }\n    }\n  },\n  \"author\": \"Mikihiro Hayashi\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"jshint\": \"^2.6.0\"\n  }\n}\n"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin\n        xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        id=\"cordova-plugin-buildinfo\"\n        version=\"4.0.0\">\n    <name>BuildInfo</name>\n    <description>Cordova/Phonegap Build Information Plugin. Get PackageName, Version, Debug and more...</description>\n    <license>MIT</license>\n    <keywords>debug,buildconfig,buildinfo,phonegap,cordova</keywords>\n    <author>Mikihiro Hayashi</author>\n    <hook type=\"after_plugin_install\" src=\"scripts/after_install.js\" />\n    <hook type=\"before_plugin_uninstall\" src=\"scripts/before_uninstall.js\" />\n\n    <js-module src=\"www/buildinfo.js\" name=\"BuildInfo\">\n        <clobbers target=\"BuildInfo\"/>\n    </js-module>\n\n    <platform name=\"android\">\n        <config-file target=\"res/xml/config.xml\" parent=\"/*\">\n            <feature name=\"BuildInfo\">\n                <param name=\"android-package\" value=\"org.apache.cordova.buildinfo.BuildInfo\"/>\n            </feature>\n        </config-file>\n        <source-file src=\"src/android/BuildInfo.java\" target-dir=\"src/org/apache/cordova/buildinfo\"/>\n        <framework src=\"src/android/BuildInfo.gradle\" custom=\"true\" type=\"gradleReference\"/>\n    </platform>\n\n    <platform name=\"ios\">\n        <config-file target=\"config.xml\" parent=\"/*\">\n            <feature name=\"BuildInfo\">\n                <param name=\"ios-package\" value=\"CDVBuildInfo\"/>\n            </feature>\n        </config-file>\n        <header-file src=\"src/ios/CDVBuildInfo.h\"/>\n        <source-file src=\"src/ios/CDVBuildInfo.m\"/>\n    </platform>\n\n    <platform name=\"windows\">\n        <js-module src=\"src/windows/BuildInfoProxy.js\" name=\"BuildInfoProxy\">\n            <merges target=\"\"/>\n        </js-module>\n        <resource-file src=\"src/windows/buildinfo.resjson\" target=\"strings/buildinfo.resjson\" />\n    </platform>\n\n    <platform name=\"osx\">\n        <config-file target=\"config.xml\" parent=\"/*\">\n            <feature name=\"BuildInfo\">\n                <param name=\"osx-package\" value=\"CDVBuildInfo\"/>\n            </feature>\n        </config-file>\n        <header-file src=\"src/osx/CDVBuildInfo.h\"/>\n        <source-file src=\"src/osx/CDVBuildInfo.m\"/>\n    </platform>\n\n    <platform name=\"browser\">\n        <js-module src=\"src/browser/BuildInfoProxy.js\" name=\"BuildInfoProxy\">\n            <merges target=\"\"/>\n        </js-module>\n        <hook type=\"after_prepare\" src=\"scripts/browser_after_prepare.js\" />\n    </platform>\n    \n    <platform name=\"electron\">\n        <js-module src=\"src/browser/BuildInfoProxy.js\" name=\"BuildInfoProxy\">\n            <merges target=\"\"/>\n        </js-module>\n        <hook type=\"after_prepare\" src=\"scripts/browser_after_prepare.js\" />\n    </platform>\n</plugin>\n"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/scripts/after_install.js",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2019 Mikihiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n'use strict';\n\nconst path = require('path'),\n    fs = require('fs');\n\nfunction installWindows(windowsPath) {\n\n    const targetPath = path.join(windowsPath, 'CordovaApp.projitems');\n    let projitems = fs.readFileSync(targetPath).toString();\n    let changed = false;\n\n    // Replace <Content Include=\"strings\\buildinfo.resjson\"> to <PRIResource Include=\"strings\\buildinfo.resjson\">\n    if (projitems.match(/<ItemGroup>[\\s]*?<Content +.*?Include=\"strings\\/buildinfo.resjson\".+/m)) {\n        const search = /<ItemGroup>[\\s]*?<Content +.*?Include=\"strings\\/buildinfo.resjson\"[\\s\\S]*?<\\/ItemGroup>/m;\n\n        const replace\n            = \"<ItemGroup>\\r\\n\"\n            + \"        <PRIResource Include=\\\"strings\\/buildinfo.resjson\\\" />\\r\\n\"\n            + \"    </ItemGroup>\";\n\n        projitems = projitems.replace(search, replace);\n        changed = true;\n    }\n\n    // Add <Target Name=\"BuildInfo_Timestamp\" BeforeTargets=BeforeBuild\">\n    if (!projitems.match(/<Target +.*?Name=\"BuildInfo_Timestamp\".*?/)) {\n        const search = /<\\/Project>/;\n\n        const replace\n            = \"    <Target Name=\\\"BuildInfo_Timestamp\\\" BeforeTargets=\\\"BeforeBuild\\\">\\r\\n\"\n            + \"        <PropertyGroup>\\r\\n\"\n            + \"            <BuildInfoTimestamp>$([System.DateTime]::Now.ToString(\\\"yyyy-MM-dd\\THH:mm:sszzz\\\"))</BuildInfoTimestamp>\\r\\n\"\n            + \"        </PropertyGroup>\\r\\n\"\n            + \"        <ItemGroup>\\r\\n\"\n            + \"            <BuildInfoResJson Include=\\\"{\\\" />\\r\\n\"\n            + \"            <BuildInfoResJson Include=\\\"&quot;Timestamp&quot;: &quot;$(BuildInfoTimestamp)&quot;\\\" />\\r\\n\"\n            + \"            <BuildInfoResJson Include=\\\"}\\\" />\\r\\n\"\n            + \"        </ItemGroup>\\r\\n\"\n            + \"        <WriteLinesToFile File=\\\"strings\\\\buildinfo.resjson\\\" Lines=\\\"@(BuildInfoResJson)\\\" Overwrite=\\\"true\\\" Encoding=\\\"UTF-8\\\" />\\r\\n\"\n            + \"    </Target>\\r\\n\"\n            + \"</Project>\";\n\n        projitems = projitems.replace(search, replace);\n        changed = true;\n    }\n\n    // if variable \"changed\" is true, write to file.\n    if (changed) {\n        fs.writeFileSync(targetPath, projitems);\n    }\n}\n\nmodule.exports = function (context) {\n    const projectRoot = context.opts.projectRoot;\n\n    // Exists platform/windows\n    const windowsPath = path.join(projectRoot, 'platforms', 'windows');\n    if (fs.existsSync(windowsPath) && context.opts.plugin.platform == 'windows') {\n        installWindows(windowsPath);\n    }\n};"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/scripts/before_uninstall.js",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2019 Mikihiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n'use strict';\n\nconst path = require('path'),\n      fs = require('fs');\n\nfunction uninstallWindows(context, windowsPath) {\n    const targetPath = path.join(windowsPath, 'CordovaApp.projitems');\n    let projitems = fs.readFileSync(targetPath).toString();\n    let changed = false;\n\n    // Replace <PRIResource Include=\"strings\\buildinfo.resjson\"> to <Content Include=\"strings\\buildinfo.resjson\">\n    if (projitems.match(/<ItemGroup>[\\s]*?<PRIResource +.*?Include=\"strings\\/buildinfo.resjson\".+/m)) {\n\n        const search = /<ItemGroup>[\\s]*?<PRIResource +.*?Include=\"strings\\/buildinfo.resjson\"[\\s\\S]*?<\\/ItemGroup>/m;\n\n        const replace\n            = \"<ItemGroup>\\r\\n\"\n            + \"        <Content Include=\\\"strings\\/buildinfo.resjson\\\" />\\r\\n\"\n            + \"    </ItemGroup>\";\n\n        projitems = projitems.replace(search, replace);\n        changed = true;\n    }\n\n    // Remove <Target Name=\"BuildInfo_Timestamp\" BeforeTargets=BeforeBuild\">\n    if (projitems.match(/<Target +.*Name=\"BuildInfo_Timestamp\".*/)) {\n\n        const search = /[\\r\\n ]*<Target +.*Name=\"BuildInfo_Timestamp\"[\\s\\S]*?<\\/Target>/gm;\n\n        projitems = projitems.replace(search, '');\n        changed = true;\n    }\n    \n    // if variable \"changed\" is true, write to file.\n    if (changed) {\n        fs.writeFileSync(targetPath, projitems);\n    }\n}\n\nmodule.exports = function (context) {\n    const opts = context.opts || {};\n    const projectRoot = opts.projectRoot;\n\n    if ('string' != typeof projectRoot) {\n        return;\n    }\n\n    // Exists platform/windows\n    const windowsPath = path.join(projectRoot, 'platforms', 'windows');\n    if (context.opts.plugin.platform == 'windows' && fs.existsSync(windowsPath)) {\n        uninstallWindows(context, windowsPath);\n    }\n};"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/scripts/browser_after_prepare.js",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2019 Mikihiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n'use strict';\n\nconst path = require('path'),\n    fs = require('fs');\n\nfunction rewriteBuildInfoProxy(context, pathPrefix) {\n\n    const opts = context.opts || {};\n    const options = opts.options || {};\n    const release = options.release || false;\n    const debug = options.debug || !release;\n\n    const pluginId = (opts.plugin || {}).id || 'cordova-plugin-buildinfo';\n\n    const pathRewriteFile = path.join(pathPrefix, 'www', 'plugins', pluginId, 'src', 'browser', 'BuildInfoProxy.js');\n\n    if (!fs.existsSync(pathRewriteFile)) {\n        console.error('File not found: '.pathRewriteFile);\n        return;\n    }\n\n    const ConfigParser = context.requireCordovaModule('cordova-common').ConfigParser;\n    const cfg = new ConfigParser('config.xml');\n\n    const json = {\n        debug: debug,\n        packageName: cfg.packageName(),\n        basePackageName: cfg.packageName(),\n        name: cfg.name(),\n        displayName: cfg.shortName(),\n        version: cfg.version(),\n        versionCode: cfg.version()\n    };\n\n    const code = 'const json = ' + JSON.stringify(json) + ';';\n\n    const contentJs = fs.readFileSync(pathRewriteFile, 'utf8');\n    const outputJs = contentJs.replace(/(\\/\\* <EMBED_CODE> \\*\\/).*?(\\s*)(\\/\\* <\\/EMBED_CODE> \\*\\/)/s, '$1$2' + code + '$2$3');\n\n    fs.writeFileSync(pathRewriteFile, outputJs);\n}\n\n\nmodule.exports = function (context) {\n\n    const opts = context.opts || {};\n    const projectRoot = opts.projectRoot;\n    const platforms = opts.platforms || [];\n\n    if ('string' != typeof projectRoot) {\n        return;\n    }\n\n    ['browser', 'electron'].forEach((value, index, array) => {\n\n        if (!platforms.includes(value)) {\n            return;\n        }\n\n        const pathPrefix = path.join(projectRoot, 'platforms', value);\n        if (fs.existsSync(pathPrefix)) {\n            rewriteBuildInfoProxy(context, pathPrefix);\n        }\n    });\n};"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/src/android/BuildInfo.gradle",
    "content": "android {\r\n    defaultConfig {\r\n        //buildConfigField \"long\", \"_BUILDINFO_TIMESTAMP\", System.currentTimeMillis() + \"L\"\r\n    }\r\n}"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/src/android/BuildInfo.java",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Mikihiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\npackage org.apache.cordova.buildinfo;\n\nimport android.app.Activity;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.util.Log;\n\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaPlugin;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.lang.reflect.Field;\nimport java.text.SimpleDateFormat;\n\n/**\n * BuildInfo Cordova Plugin\n *\n * @author Mikihiro Hayashi\n * @since 1.0.0\n */\npublic class BuildInfo extends CordovaPlugin {\n\tprivate static final String TAG = \"BuildInfo\";\n\n\t/**\n\t * Cache of result JSON\n\t */\n\tprivate static JSONObject mBuildInfoCache;\n\n\t/**\n\t * Constructor\n\t */\n\tpublic BuildInfo() {\n\t}\n\n\t/**\n\t * execute\n\t * @param action          The action to execute.\n\t * @param args            The exec() arguments.\n\t * @param callbackContext The callback context used when calling back into JavaScript.\n\t * @return\n\t * @throws JSONException\n     */\n\tpublic boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {\n\n\t\tif (\"init\".equals(action)) {\n\t\t\tString buildConfigClassName = null;\n\t\t\tif (1 < args.length()) {\n\t\t\t\tbuildConfigClassName = args.getString(0);\n\t\t\t}\n\n\t\t\tinit(buildConfigClassName, callbackContext);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * init\n\t * @param buildConfigClassName null or specified BuildConfig class name\n\t * @param callbackContext\n\t */\n\tprivate void init(String buildConfigClassName, CallbackContext callbackContext) {\n\t\t// Cached check\n\t\tif (null != mBuildInfoCache) {\n\t\t\tcallbackContext.success(mBuildInfoCache);\n\t\t\treturn;\n\t\t}\n\n\t\t// Load PackageInfo\n\t\tActivity activity = cordova.getActivity();\n\t\tString packageName = activity.getPackageName();\n\t\tString basePackageName = packageName;\n\t\tCharSequence displayName = \"\";\n\t\tlong firstInstallTime = 0;\n\n\t\tPackageManager pm = activity.getPackageManager();\n\n\t\ttry {\n\t\t\tPackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);\n\t\t\tfirstInstallTime = pi.firstInstallTime;\n\n\t\t\tif (null != pi.applicationInfo) {\n\t\t\t\tdisplayName = pi.applicationInfo.loadLabel(pm);\n\t\t\t}\n\t\t} catch (PackageManager.NameNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\t// Load BuildConfig class\n\t\tClass c = null;\n\n\t\tif (null == buildConfigClassName) {\n\t\t\tbuildConfigClassName = packageName + \".BuildConfig\";\n\t\t}\n\n\t\ttry {\n\t\t\tc = Class.forName(buildConfigClassName);\n\t\t} catch (ClassNotFoundException e) {\n\t\t}\n\n\t\tif (null == c) {\n\t\t\tbasePackageName = activity.getClass().getPackage().getName();\n\t\t\tbuildConfigClassName = basePackageName + \".BuildConfig\";\n\n\t\t\ttry {\n\t\t\t\tc = Class.forName(buildConfigClassName);\n\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\tcallbackContext.error(\"BuildConfig ClassNotFoundException: \" + e.getMessage());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Create result\n\t\tmBuildInfoCache = new JSONObject();\n\t\ttry {\n\t\t\tboolean debug = getClassFieldBoolean(c, \"DEBUG\", false);\n\n\t\t\tmBuildInfoCache.put(\"packageName\"    , packageName);\n\t\t\tmBuildInfoCache.put(\"basePackageName\", basePackageName);\n\t\t\tmBuildInfoCache.put(\"displayName\"    , displayName);\n\t\t\tmBuildInfoCache.put(\"name\"           , displayName); // same as displayName\n\t\t\tmBuildInfoCache.put(\"version\"        , getClassFieldString(c, \"VERSION_NAME\", \"\"));\n\t\t\tmBuildInfoCache.put(\"versionCode\"    , getClassFieldInt(c, \"VERSION_CODE\", 0));\n\t\t\tmBuildInfoCache.put(\"debug\"          , debug);\n\t\t\t//mBuildInfoCache.put(\"buildDate\"      , convertLongToDateTimeString(getClassFieldLong(c, \"_BUILDINFO_TIMESTAMP\", 0L)));\n\t\t\tmBuildInfoCache.put(\"installDate\"    , convertLongToDateTimeString(firstInstallTime));\n\t\t\tmBuildInfoCache.put(\"buildType\"      , getClassFieldString(c, \"BUILD_TYPE\", \"\"));\n\t\t\tmBuildInfoCache.put(\"flavor\"         , getClassFieldString(c, \"FLAVOR\", \"\"));\n\n\t\t\tif (debug) {\n\t\t\t\tLog.d(TAG, \"packageName    : \\\"\" + mBuildInfoCache.getString(\"packageName\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"basePackageName: \\\"\" + mBuildInfoCache.getString(\"basePackageName\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"displayName    : \\\"\" + mBuildInfoCache.getString(\"displayName\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"name           : \\\"\" + mBuildInfoCache.getString(\"name\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"version        : \\\"\" + mBuildInfoCache.getString(\"version\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"versionCode    : \" + mBuildInfoCache.getInt(\"versionCode\"));\n\t\t\t\tLog.d(TAG, \"debug          : \" + (mBuildInfoCache.getBoolean(\"debug\") ? \"true\" : \"false\"));\n\t\t\t\tLog.d(TAG, \"buildType      : \\\"\" + mBuildInfoCache.getString(\"buildType\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"flavor         : \\\"\" + mBuildInfoCache.getString(\"flavor\") + \"\\\"\");\n\t\t\t\t//Log.d(TAG, \"buildDate      : \\\"\" + mBuildInfoCache.getString(\"buildDate\") + \"\\\"\");\n\t\t\t\tLog.d(TAG, \"installDate    : \\\"\" + mBuildInfoCache.getString(\"installDate\") + \"\\\"\");\n\t\t\t}\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t\tcallbackContext.error(\"JSONException: \" + e.getMessage());\n\t\t\treturn;\n\t\t}\n\n\t\tcallbackContext.success(mBuildInfoCache);\n\t}\n\n\t/**\n\t * Get boolean of field from Class\n\t * @param c\n\t * @param fieldName\n\t * @param defaultReturn\n     * @return\n     */\n\tprivate static boolean getClassFieldBoolean(Class c, String fieldName, boolean defaultReturn) {\n\t\tboolean ret = defaultReturn;\n\t\tField field = getClassField(c, fieldName);\n\n\t\tif (null != field) {\n\t\t\ttry {\n\t\t\t\tret = field.getBoolean(c);\n\t\t\t} catch (IllegalAccessException iae) {\n\t\t\t\tiae.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Get string of field from Class\n\t * @param c\n\t * @param fieldName\n\t * @param defaultReturn\n     * @return\n     */\n\tprivate static String getClassFieldString(Class c, String fieldName, String defaultReturn) {\n\t\tString ret = defaultReturn;\n\t\tField field = getClassField(c, fieldName);\n\n\t\tif (null != field) {\n\t\t\ttry {\n\t\t\t\tret = (String)field.get(c);\n\t\t\t} catch (IllegalAccessException iae) {\n\t\t\t\tiae.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Get int of field from Class\n\t * @param c\n\t * @param fieldName\n\t * @param defaultReturn\n     * @return\n     */\n\tprivate static int getClassFieldInt(Class c, String fieldName, int defaultReturn) {\n\t\tint ret = defaultReturn;\n\t\tField field = getClassField(c, fieldName);\n\n\t\tif (null != field) {\n\t\t\ttry {\n\t\t\t\tret = field.getInt(c);\n\t\t\t} catch (IllegalAccessException iae) {\n\t\t\t\tiae.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Get long of field from Class\n\t * @param c\n\t * @param fieldName\n\t * @param defaultReturn\n     * @return\n     */\n\tprivate static long getClassFieldLong(Class c, String fieldName, long defaultReturn) {\n\t\tlong ret = defaultReturn;\n\t\tField field = getClassField(c, fieldName);\n\n\t\tif (null != field) {\n\t\t\ttry {\n\t\t\t\tret = field.getLong(c);\n\t\t\t} catch (IllegalAccessException iae) {\n\t\t\t\tiae.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\t/**\n\t * Get field from Class\n\t * @param c\n\t * @param fieldName\n     * @return\n     */\n\tprivate static Field getClassField(Class c, String fieldName) {\n\t\tField field = null;\n\n\t\ttry {\n\t\t\tfield = c.getField(fieldName);\n\t\t} catch (NoSuchFieldException nsfe) {\n\t\t\tnsfe.printStackTrace();\n\t\t}\n\n\t\treturn field;\n\t}\n\n\tprivate static String convertLongToDateTimeString(long mills) {\n\t\tSimpleDateFormat formatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ssZ\");\n\t\treturn formatter.format(mills);\n\t}\n}\n"
  },
  {
    "path": "src/plugins/cordova-plugin-buildinfo/www/buildinfo.js",
    "content": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Mikihiro Hayashi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\nvar exec = require('cordova/exec');\nvar channel = require('cordova/channel');\n\nmodule.exports = {\n\tbaseUrl: null,\n\tpackageName: '',\n\tbasePackageName: '',\n\tdisplayName: '',\n\tname: '',\n\tversion: '',\n\tversionCode: 0,\n\tdebug: false,\n\t//buildDate: null,\n\tinstallDate: null,\n\tbuildType: '',\n\tflavor: ''\n};\n\nfunction _buldinfoCheckCordovaPlatform() {\n\tvar allowPlatforms = ['android', 'ios', 'windows', 'osx', 'browser', 'electron'];\n\tvar platformId = (cordova && cordova.platformId) ? cordova.platformId : null;\n\treturn (-1 !== allowPlatforms.indexOf(platformId));\n}\n\nfunction _findBaseUrl() {\n\tvar path = null;\n\tvar scripts = document.getElementsByTagName('script');\n\tvar findScriptPath = '/cordova.js';\n\tvar findScriptPathLen = findScriptPath.length;\n\n\tfor (var i = scripts.length - 1; i >= 0; i--) {\n\t\tvar src = scripts[i].src.replace(/\\?.*$/, '');\n\n\t\tif (src.length >= findScriptPathLen && src.substring(src.length - findScriptPathLen) == findScriptPath) {\n\t\t\tpath = src.substring(0, src.length - findScriptPathLen) + '/';\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn path;\n}\n\n\nif (_buldinfoCheckCordovaPlatform()) {\n\n\tchannel.onCordovaReady.subscribe(function () {\n\t\t// Platform Check\n\t\tvar allowPlatforms = ['android', 'ios', 'windows', 'osx', 'browser', 'electron'];\n\t\tvar platformId = (cordova && cordova.platformId) ? cordova.platformId : null;\n\t\tif (-1 == allowPlatforms.indexOf(platformId)) {\n\t\t\tconsole.debug('BuildInfo init skip.');\n\t\t\treturn;\n\t\t}\n\n\t\tvar args = [];\n\n\t\t// Android Only\n\t\t// defined buildInfoBuildConfigClassName variable\n\t\t// BuildConfig class name.\n\t\t// ex: <script>var buildInfoBuildConfigClassName = 'org.apache.cordova.sample.BuildConfig';</script>\n\t\tif ('undefined' !== typeof buildInfoBuildConfigClassName) {\n\t\t\targs.push(buildInfoBuildConfigClassName);\n\t\t}\n\n\t\texec(\n\t\t\tfunction (res) {\n\t\t\t\tif (!res) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tmodule.exports.baseUrl = _findBaseUrl();\n\n\t\t\t\tif ('undefined' !== typeof res.packageName) {\n\t\t\t\t\tmodule.exports.packageName = res.packageName;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.basePackageName) {\n\t\t\t\t\tmodule.exports.basePackageName = res.basePackageName;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.displayName) {\n\t\t\t\t\tmodule.exports.displayName = res.displayName;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.name) {\n\t\t\t\t\tmodule.exports.name = res.name;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.version) {\n\t\t\t\t\tmodule.exports.version = res.version;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.versionCode) {\n\t\t\t\t\tmodule.exports.versionCode = res.versionCode;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.debug) {\n\t\t\t\t\tmodule.exports.debug = res.debug;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.buildType) {\n\t\t\t\t\tmodule.exports.buildType = res.buildType;\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.flavor) {\n\t\t\t\t\tmodule.exports.flavor = res.flavor;\n\t\t\t\t}\n\n\t\t\t\t/*if ('undefined' !== typeof res.buildDate) {\n\t\t\t\t\tif (res.buildDate instanceof Date) {\n\t\t\t\t\t\tmodule.exports.buildDate = res.buildDate;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmodule.exports.buildDate = new Date(res.buildDate);\n\t\t\t\t\t}\n\t\t\t\t}*/\n\n\t\t\t\tif ('undefined' !== typeof res.installDate) {\n\t\t\t\t\tif (res.installDate instanceof Date) {\n\t\t\t\t\t\tmodule.exports.installDate = res.installDate;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmodule.exports.installDate = new Date(res.installDate);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ('undefined' !== typeof res.windows) {\n\t\t\t\t\tmodule.exports.windows = res.windows;\n\t\t\t\t}\n\t\t\t},\n\t\t\tfunction (msg) {\n\t\t\t\tconsole.error('BuildInfo init fail');\n\t\t\t\tconsole.error(msg);\n\t\t\t},\n\t\t\t'BuildInfo',\n\t\t\t'init',\n\t\t\targs\n\t\t);\n\t});\n}\n"
  },
  {
    "path": "src/plugins/custom-tabs/package.json",
    "content": "{\n  \"name\": \"com.foxdebug.acode.rk.customtabs\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Custom tabs api\",\n  \"cordova\": {\n    \"id\": \"com.foxdebug.acode.rk.customtabs\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"ecosystem:cordova\",\n    \"cordova-android\"\n  ],\n  \"author\": \"@RohitKushvaha01\",\n  \"license\": \"MIT\"\n}"
  },
  {
    "path": "src/plugins/custom-tabs/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\" xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"com.foxdebug.acode.rk.customtabs\" version=\"1.0.0\">\n    <name>Custom Tabs</name>\n\n\n    <js-module name=\"CustomTabs\" src=\"www/customtabs.js\">\n        <clobbers target=\"CustomTabs\"/>\n    </js-module>\n\n\n    <platform name=\"android\">\n\n         <config-file parent=\"/*\" target=\"res/xml/config.xml\">\n            <feature name=\"CustomTabs\">\n                <param name=\"android-package\" value=\"com.foxdebug.acode.rk.customtabs.CustomTabsPlugin\" />\n            </feature>\n           \n        </config-file>\n\n       <config-file parent=\"/*\" target=\"AndroidManifest.xml\">\n              <queries>\n                <intent>\n                    <action android:name=\"android.support.customtabs.action.CustomTabsService\" />\n                </intent>\n            </queries>\n        </config-file>\n\n        <framework src=\"androidx.browser:browser:1.8.0\"/>\n\n        <source-file\n            src=\"src/CustomTabsPlugin.java\"\n            target-dir=\"src/com/foxdebug/acode/rk/customtabs\"/>\n\n           \n    </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/custom-tabs/src/CustomTabsPlugin.java",
    "content": "package com.foxdebug.acode.rk.customtabs;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.graphics.Color;\n\nimport androidx.browser.customtabs.CustomTabsIntent;\n\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaPlugin;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\npublic class CustomTabsPlugin extends CordovaPlugin {\n\n    @Override\n    public boolean execute(\n            String action,\n            JSONArray args,\n            CallbackContext callbackContext\n    ) {\n\n        if (\"open\".equals(action)) {\n            try {\n                final String url = args.getString(0);\n                final JSONObject options = args.optJSONObject(1) != null\n                        ? args.optJSONObject(1)\n                        : new JSONObject();\n\n                cordova.getActivity().runOnUiThread(() -> {\n                    try {\n                        openCustomTab(url, options);\n                        callbackContext.success();\n                    } catch (Exception e) {\n                        callbackContext.error(e.getMessage());\n                    }\n                });\n\n                return true;\n\n            } catch (Exception e) {\n                callbackContext.error(e.getMessage());\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    private void openCustomTab(String url, JSONObject options) {\n        CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();\n\n        String toolbarColor = options.optString(\"toolbarColor\", null);\n        if (toolbarColor != null && !toolbarColor.isEmpty()) {\n            builder.setToolbarColor(Color.parseColor(toolbarColor));\n        }\n\n        CustomTabsIntent customTabsIntent = builder.build();\n\n        if (options.optBoolean(\"showTitle\", true)) {\n            customTabsIntent.intent.putExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE, CustomTabsIntent.SHOW_PAGE_TITLE);\n        }\n        \n        try {\n            customTabsIntent.launchUrl(\n                    cordova.getActivity(),\n                    Uri.parse(url)\n            );\n        } catch (ActivityNotFoundException e) {\n            // Fallback to default browser\n            Intent fallback = new Intent(\n                    Intent.ACTION_VIEW,\n                    Uri.parse(url)\n            );\n            cordova.getActivity().startActivity(fallback);\n        }\n    }\n}\n"
  },
  {
    "path": "src/plugins/custom-tabs/www/customtabs.js",
    "content": "var exec = require('cordova/exec');\n\nexports.open = function (url, options, success, error) {\n    exec(\n        success,\n        error,\n        'CustomTabs',\n        'open',\n        [url, options || {}]\n    );\n};\n"
  },
  {
    "path": "src/plugins/ftp/LICENSE.md",
    "content": "- GoldRaccoon is under [original author's license](https://github.com/albertodebortoli/GoldRaccoon/blob/master/LICENSE.markdown)\r\n- ftp4j is under [LGPL](http://opensource.org/licenses/LGPL-2.1)\r\n- All other codes (writen by me) are under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)\r\n"
  },
  {
    "path": "src/plugins/ftp/README.md",
    "content": "# cordova-plugin-ftp\r\n\r\n## Description\r\n\r\nThis cordova plugin is created to use ftp (client) in web/js.\r\n\r\nSupport both **iOS** and **Android** platform now.\r\n\r\nYou can do the following things:\r\n\r\n- List a directory\r\n- Create a directory\r\n- Delete a directory (must be empty)\r\n- Delete a file\r\n- Download a file (with percent info)\r\n- Upload a file (with percent info)\r\n- Cancel upload/download\r\n\r\n## Installation\r\n\r\n```sh\r\n$ cordova plugin add cordova-plugin-ftp\r\n$ cordova prepare\r\n```\r\n\r\nDependency:\r\n\r\n- For iOS, the plugin depends on *CFNetwork.framework*, which has been added to plugin.xml (and `cordova prepare` will add it to platform project), so you don't need to do anything.\r\n- But for Android, it depends on *com.android.support:support-v4:23.2.0*, which should be added to your platform project by hand.\r\n\r\n## Usage\r\n\r\nYou can access this plugin by js object `window.cordova.plugin.ftp`.\r\n\r\nExample:\r\n\r\n```js\r\ndocument.addEventListener(\"deviceready\", onDeviceReady, false);\r\n\r\nfunction onDeviceReady() {\r\n    // First of all, connect to ftp server address without protocol prefix. e.g. \"192.168.1.1:21\", \"ftp.xfally.github.io\"\r\n    // Notice: address port is only supported for Android, if not given, default port 21 will be used.\r\n    window.cordova.plugin.ftp.connect('ftp.xfally.github.io', 'username', 'password', function(ok) {\r\n        console.info(\"ftp: connect ok=\" + ok);\r\n\r\n        // You can do any ftp actions from now on...\r\n        window.cordova.plugin.ftp.upload('/localPath/localFile', '/remotePath/remoteFile', function(percent) {\r\n            if (percent == 1) {\r\n                console.info(\"ftp: upload finish\");\r\n            } else {\r\n                console.debug(\"ftp: upload percent=\" + percent * 100 + \"%\");\r\n            }\r\n        }, function(error) {\r\n            console.error(\"ftp: upload error=\" + error);\r\n        });\r\n\r\n    }, function(error) {\r\n        console.error(\"ftp: connect error=\" + error);\r\n    });\r\n}\r\n```\r\n\r\nPlease refer to [ftp.js](./www/ftp.js) for all available APIs and usages.\r\n\r\n## Notice\r\n\r\n1. For iOS, `ftp.connect` will always succeed (even if `username` and `password` are incorrect), but it does NOT mean the later actions, e.g. `ls`... `download` will succeed too! So always check their `errorCallback`.\r\n2. Want to upload/download multiple files? The plugin (Android part) inits just one connection and transmits all files via it. If you use asychronous syntax (e.g. `foreach`) to start multiple upload/download in a short time, it may mess the transfer. Instead, you can try [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or [async](https://github.com/caolan/async) to transmit one after one.\r\n\r\n## Thanks\r\n\r\n- The iOS native implementing is based on [GoldRaccoon](https://github.com/albertodebortoli/GoldRaccoon).\r\n- The Android native implementing is based on [ftp4j](http://www.sauronsoftware.it/projects/ftp4j/) jar (LGPL).\r\n\r\n## License\r\n\r\nApache License 2.0. Refer to [LICENSE.md](./LICENSE.md) for more info.\r\n\r\n"
  },
  {
    "path": "src/plugins/ftp/index.d.ts",
    "content": "interface FtpOptions {\n  connectionMode: 'passive' | 'active';\n  securityType: 'ftp' | 'ftps';\n  encoding: 'utf8' | 'binary';\n}\n\ntype SuccessCallback = (res: any) => void;\ntype ErrorCallback = (err: any) => void;\n\ninterface Ftp{\n  connect(\n    host: string, \n    port: number, \n    username: string, \n    password: string, \n    options: FtpOptions,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback\n  ): void;\n  listDirectory(\n    id: string, // connection id\n    path: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback\n  ): void;\n  execCommand(\n    id: string, // connection id\n    command: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n    ...args: string,\n  ): void;\n  isConnected(\n    id: string, // connection id\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  disconnect(\n    id: string, // connection id\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  downloadFile(\n    id: string, // connection id\n    remotePath: string,\n    localPath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  uploadFile(\n    id: string, // connection id\n    localPath: string,\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  deleteFile(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  deleteDirectory(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  createDirectory(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  createFile(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  getStat(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  exists(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  changeDirectory(\n    id: string, // connection id\n    remotePath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  changeToParentDirectory(\n    id: string, // connection id\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  getWorkingDirectory(\n    id: string, // connection id\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  rename(\n    id: string, // connection id\n    oldPath: string,\n    newPath: string,\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n  sendNoOp(\n    id: string, // connection id\n    onSuccess: SuccessCallback,\n    onError: ErrorCallback,\n  ): void;\n}\n\ndeclare var ftp: Ftp;"
  },
  {
    "path": "src/plugins/ftp/package.json",
    "content": "{\r\n  \"name\": \"cordova-plugin-ftp\",\r\n  \"version\": \"1.1.1\",\r\n  \"description\": \"This cordova plugin is created to use ftp (client) in web/js.\",\r\n  \"cordova\": {\r\n    \"id\": \"cordova-plugin-ftp\",\r\n    \"platforms\": [\r\n      \"android\",\r\n      \"ios\"\r\n    ]\r\n  },\r\n  \"repository\": {\r\n    \"type\": \"git\",\r\n    \"url\": \"https://github.com/xfally/cordova-plugin-ftp\"\r\n  },\r\n  \"keywords\": [\r\n    \"cordova\",\r\n    \"ftp\",\r\n    \"cordova-android\",\r\n    \"cordova-ios\"\r\n  ],\r\n  \"author\": \"pax\",\r\n  \"license\": \"Apache-2.0\",\r\n  \"bugs\": {\r\n    \"url\": \"https://github.com/xfally/cordova-plugin-ftp/issues\"\r\n  },\r\n  \"homepage\": \"https://github.com/xfally/cordova-plugin-ftp\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/plugins/ftp/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\r\n    xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-ftp\" version=\"1.1.1\">\r\n    <name>Ftp</name>\r\n    <description>Cordova Ftp Plugin</description>\r\n    <license>MIT</license>\r\n    <keywords>cordova,ftp</keywords>\r\n\r\n    <js-module src=\"www/ftp.js\" name=\"ftp\">\r\n        <clobbers target=\"window.ftp\" />\r\n    </js-module>\r\n\r\n    <!-- android -->\r\n    <platform name=\"android\">\r\n        <config-file target=\"res/xml/config.xml\" parent=\"/*\">\r\n            <feature name=\"Ftp\">\r\n                <param name=\"android-package\" value=\"com.foxdebug.ftp.Ftp\" />\r\n            </feature>\r\n        </config-file>\r\n\r\n        <source-file src=\"src/android/com/foxdebug/ftp/Ftp.java\" target-dir=\"src/com/foxdebug/ftp\" />\r\n        <framework src=\"commons-net:commons-net:3.12.0\" />\r\n    </platform>\r\n\r\n</plugin>\r\n"
  },
  {
    "path": "src/plugins/ftp/src/android/com/foxdebug/ftp/Ftp.java",
    "content": "package com.foxdebug.ftp;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.util.Log;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.io.UnsupportedEncodingException;\nimport java.lang.SecurityException;\nimport java.lang.reflect.Method;\nimport java.net.SocketException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URISyntaxException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport org.apache.commons.net.ftp.*;\nimport org.apache.commons.net.ftp.parser.ParserInitializationException;\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPlugin;\nimport org.apache.cordova.CordovaWebView;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class Ftp extends CordovaPlugin {\n\n  HashMap<String, FTPClient> ftpProfiles = new HashMap<String, FTPClient>();\n  Context context;\n  Activity activity;\n  String connectionID;\n\n  public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n    super.initialize(cordova, webView);\n    context = cordova.getContext();\n    activity = cordova.getActivity();\n  }\n\n  public boolean execute(\n    String action,\n    JSONArray args,\n    CallbackContext callback\n  ) {\n    try {\n      Method method =\n        this.getClass()\n          .getDeclaredMethod(action, JSONArray.class, CallbackContext.class);\n      if (method != null) {\n        method.invoke(this, args, callback);\n        return true;\n      }\n      return false;\n    } catch (NoSuchMethodException e) {\n      callback.error(e.getMessage());\n      return false;\n    } catch (SecurityException e) {\n      callback.error(e.getMessage());\n      return false;\n    } catch (Exception e) {\n      callback.error(e.getMessage());\n      return false;\n    }\n  }\n\n  public void connect(JSONArray args, CallbackContext callback) {\n    connect(args, callback, false);\n  }\n\n  public void connect(\n    JSONArray args,\n    CallbackContext callback,\n    boolean isRetry\n  ) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            int reply;\n            int port = args.optInt(1);\n            String host = args.optString(0);\n            String username = args.optString(2);\n            String password = args.optString(3);\n            String defaultPath = args.optString(4);\n            String securityType = args.optString(5);\n            String connectionMode = args.optString(6);\n            String encryption = args.optString(7);\n            String encoding = args.optString(8);\n            String ftpId = getFtpId(host, port, username);\n            FTPClient ftp = null;\n\n            try {\n              if (ftpProfiles.containsKey(ftpId)) {\n                ftp = ftpProfiles.get(ftpId);\n                reply = ftp.getReplyCode();\n                if (ftp.isConnected() && FTPReply.isPositiveCompletion(reply)) {\n                  ftp.setControlEncoding(\"UTF-8\");\n                  ftp.setAutodetectUTF8(true);\n                  System.setProperty(\"ftp.client.encoding\", \"UTF-8\");\n                  // test if connection is still valid\n                  ftp.sendNoOp();\n                  Log.d(\"FTP\", \"FTPClient (\" + ftpId + \") is connected\");\n                  callback.success(ftpId);\n                  return;\n                }\n                Log.d(\"FTP\", \"FTPClient (\" + ftpId + \") is not connected\");\n                ftp.disconnect();\n                Log.d(\"FTP\", \"FTPClient (\" + ftpId + \") disconnecting...\");\n              } else {\n                Log.d(\"FTP\", \"Creating new FTPClient (\" + ftpId + \")\");\n                ftp = new FTPClient();\n                ftpProfiles.put(ftpId, ftp);\n              }\n\n              Log.d(\"FTP\", \"FTPClient (\" + ftpId + \") connecting...\");\n              ftp.connect(host, port);\n              ftp.setControlKeepAliveTimeout(300);\n              if (connectionMode.equals(\"active\")) {\n                Log.d(\"FTP\", \"Entering Local Active mode\");\n                ftp.enterLocalActiveMode();\n              } else {\n                Log.d(\"FTP\", \"Entering Passive Active mode\");\n                ftp.enterLocalPassiveMode();\n              }\n\n              Log.d(\"FTP\", \"FTPClient (\" + ftpId + \") logging in...\");\n              ftp.login(username, password);\n\n              reply = ftp.getReplyCode();\n              if (!FTPReply.isPositiveCompletion(reply)) {\n                ftp.disconnect();\n                Log.d(\n                  \"FTP\",\n                  \"FTPClient (\" + ftpId + \") server refused connection.\"\n                );\n                callback.error(\"FTP server refused connection.\");\n                return;\n              }\n\n              ftp.setListHiddenFiles(true);\n              ftpProfiles.put(ftpId, ftp);\n              Log.d(\"FTP\", \"FTPClient (\" + ftpId + \") connected\");\n              callback.success(ftpId);\n            } catch (IOException e) {\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \")\", e);\n              if (ftp != null) {\n                ftpProfiles.remove(ftpId);\n              }\n\n              if (!isRetry) {\n                connect(args, callback, true);\n                return;\n              }\n\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \")\", e);\n\n              if (ftp != null) {\n                ftpProfiles.remove(ftpId);\n              }\n\n              if (!isRetry) {\n                connect(args, callback, true);\n                return;\n              }\n\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  private static String getBaseName(String path) {\n    int lastSepIndex = path.lastIndexOf('/');\n    if (lastSepIndex == path.length() - 1) {\n      return getBaseName(path.substring(0, lastSepIndex));\n    }\n\n    return path.substring(lastSepIndex + 1);\n  }\n\n  private static String getParentPath(String path) {\n    int lastSepIndex = path.lastIndexOf('/');\n    if (lastSepIndex == path.length() - 1) {\n      lastSepIndex = path.substring(0, lastSepIndex).lastIndexOf('/');\n    }\n\n    return path.substring(0, lastSepIndex);\n  }\n\n  public void listDirectory(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                path = \"/\";\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              FTPFile[] files = ftp.listFiles(path);\n              Log.d(\n                \"FTP\",\n                \"FTPClient (\" + ftpId + \") Listing files in \" + path\n              );\n              Log.d(\n                \"FTP\",\n                \"FTPClient (\" + ftpId + \") Found \" + files.length + \" files.\"\n              );\n\n              JSONArray jsonFiles = new JSONArray();\n\n              for (FTPFile file : files) {\n                String filename = file.getName();\n                if (filename.equals(\".\") || filename.equals(\"..\")) {\n                  continue;\n                }\n\n                JSONObject jsonFile = new JSONObject();\n                jsonFile.put(\"name\", filename);\n                jsonFile.put(\"length\", file.getSize());\n                jsonFile.put(\"url\", joinPath(path, filename));\n\n                if (file.isSymbolicLink()) {\n                  jsonFile.put(\"isLink\", true);\n                  String linkTarget = file.getLink();\n                  jsonFile.put(\"link\", linkTarget);\n                  String linkPath = linkTarget.startsWith(\"/\")\n                    ? linkTarget\n                    : joinPath(path, linkTarget);\n                  try {\n                    FTPFile[] targetFiles = ftp.listFiles(linkPath);\n                    if (targetFiles.length > 0) {\n                      FTPFile targetFile = targetFiles[0];\n                      jsonFile.put(\"isFile\", targetFile.isFile());\n                      jsonFile.put(\"isDirectory\", targetFile.isDirectory());\n                      jsonFile.put(\"url\", linkPath);\n                    } else {\n                      jsonFile.put(\"isFile\", false);\n                      jsonFile.put(\"isDirectory\", false);\n                    }\n                  } catch (Exception e) {\n                    // Handle broken symlink\n                    jsonFile.put(\"isFile\", false);\n                    jsonFile.put(\"isDirectory\", false);\n                  }\n                } else {\n                  jsonFile.put(\"isLink\", false);\n                  jsonFile.put(\"isDirectory\", file.isDirectory());\n                  jsonFile.put(\"isFile\", file.isFile());\n                  jsonFile.put(\"link\", null);\n                }\n\n                jsonFile.put(\n                  \"lastModified\",\n                  file.getTimestamp().getTimeInMillis()\n                );\n                jsonFile.put(\n                  \"canWrite\",\n                  file.hasPermission(\n                    FTPFile.USER_ACCESS,\n                    FTPFile.WRITE_PERMISSION\n                  )\n                );\n                jsonFile.put(\n                  \"canRead\",\n                  file.hasPermission(\n                    FTPFile.USER_ACCESS,\n                    FTPFile.READ_PERMISSION\n                  )\n                );\n                jsonFiles.put(jsonFile);\n              }\n              callback.success(jsonFiles);\n            } catch (ParserInitializationException e) {\n              callback.error(e.getMessage());\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void exists(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            String ftpId = args.optString(0);\n            String path = args.optString(1);\n            try {\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                path = \"/\";\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              // check if file or directory exists\n              FTPFile[] ftpFiles = ftp.listFiles(path);\n              if (ftpFiles.length > 0) {\n                callback.success(1);\n              } else {\n                callback.success(0);\n              }\n            } catch (ParserInitializationException e) {\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \") path: \" + path, e);\n              callback.error(e.getMessage());\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \") path: \" + path, e);\n            } catch (FTPConnectionClosedException e) {\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \") path: \" + path, e);\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \") path: \" + path, e);\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              Log.e(\"FTP\", \"FTPClient (\" + ftpId + \") path: \" + path, e);\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void sendNoOp(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n              ftp.sendNoOp();\n              callback.success();\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void deleteFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.deleteFile(path);\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void deleteDirectory(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              Log.d(\"FTP\", \"Deleting directory \" + path);\n              // delete all files in the directory\n              emptyDirectory(path, ftp);\n\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void rename(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String oldPath = args.optString(1);\n              String newPath = args.optString(2);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (oldPath == null || oldPath.isEmpty()) {\n                callback.error(\"Old path is required.\");\n                return;\n              }\n\n              if (newPath == null || newPath.isEmpty()) {\n                callback.error(\"New path is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              // get list of files in the parent directory\n              String parentPath = getParentPath(oldPath);\n              FTPFile[] ftpFiles = ftp.listFiles(parentPath);\n\n              Log.d(\"FTP\", \"Renaming \" + oldPath + \" to \" + newPath);\n              ftp.rename(oldPath, newPath);\n\n              // check if file is renamed successfully\n              FTPFile[] newFile = ftp.listFiles(newPath);\n              if (newFile.length > 0) {\n                callback.success(newPath);\n              } else {\n                // get latest list of files in the parent directory\n                FTPFile[] latestFtpFiles = ftp.listFiles(parentPath);\n                // some time src file is renamed and not moved to destination\n                // check if for changed file and rename it original name\n                FTPFile changedFile = null;\n                for (FTPFile file : latestFtpFiles) {\n                  boolean found = false;\n                  for (FTPFile oldFile : ftpFiles) {\n                    if (oldFile.getName().equals(file.getName())) {\n                      found = true;\n                      break;\n                    }\n                  }\n                  if (!found) {\n                    changedFile = file;\n                    break;\n                  }\n                }\n\n                if (changedFile != null) {\n                  String changedFilePath = joinPath(\n                    parentPath,\n                    changedFile.getName()\n                  );\n                  ftp.rename(changedFilePath, oldPath);\n                }\n                callback.error(\"Failed to rename file\");\n              }\n\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void downloadFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n              String localFilePath = args.optString(2);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              if (localFilePath == null || localFilePath.isEmpty()) {\n                callback.error(\"Local file is required.\");\n                return;\n              }\n\n              URI uri = new URI(localFilePath);\n              File localFile = new File(uri);\n              FTPClient ftp = ftpProfiles.get(ftpId);\n\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.setFileType(FTP.BINARY_FILE_TYPE);\n\n              // Delete existing cache file to prevent stale content\n              if (localFile.exists()) {\n                localFile.delete();\n              }\n\n              try (\n                InputStream inputStream = ftp.retrieveFileStream(path)\n              ) {\n                if (inputStream == null) {\n                  Log.d(\n                    \"FTP\",\n                    \"FTPClient (\" + ftpId + \") path: \" + path + \" - not found\"\n                  );\n                  callback.error(\"File not found.\");\n                  return;\n                }\n\n                try (\n                  FileOutputStream outputStream = new FileOutputStream(localFile)\n                ) {\n                  byte[] buffer = new byte[1024];\n                  int bytesRead = -1;\n                  while ((bytesRead = inputStream.read(buffer)) != -1) {\n                    outputStream.write(buffer, 0, bytesRead);\n                  }\n                }\n              }\n\n              if (!ftp.completePendingCommand()) {\n                ftp.logout();\n                ftp.disconnect();\n                callback.error(\"File transfer failed.\");\n                return;\n              }\n\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (URISyntaxException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void uploadFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String localFilePath = args.optString(1);\n              String remoteFilePath = args.optString(2);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (remoteFilePath == null || remoteFilePath.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              if (localFilePath == null || localFilePath.isEmpty()) {\n                callback.error(\"Local file is required.\");\n                return;\n              }\n\n              Log.d(\"FTPUpload\", \"uploadFile: \" + localFilePath);\n              URI uri = new URI(localFilePath);\n              File localFile = new File(uri);\n              FTPClient ftp = ftpProfiles.get(ftpId);\n\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.setFileType(FTP.BINARY_FILE_TYPE);\n\n              Log.d(\"FTPUpload\", \"Destination \" + remoteFilePath);\n\n              try (\n                InputStream inputStream = new FileInputStream(localFile);\n                OutputStream outputStream = ftp.storeFileStream(remoteFilePath)\n              ) {\n                if (outputStream == null) {\n                  callback.error(\"File not found.\");\n                  return;\n                }\n\n                byte[] buffer = new byte[1024];\n                int bytesRead = -1;\n                while ((bytesRead = inputStream.read(buffer)) != -1) {\n                  outputStream.write(buffer, 0, bytesRead);\n                }\n              }\n\n              if (!ftp.completePendingCommand()) {\n                ftp.logout();\n                ftp.disconnect();\n                callback.error(\"File transfer failed.\");\n                return;\n              }\n\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (URISyntaxException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void getKeepAlive(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              callback.success((int) ftp.getControlKeepAliveTimeout());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void execCommand(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String command = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (command == null || command.isEmpty()) {\n                callback.error(\"Command is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.sendCommand(command);\n              String reply = ftp.getReplyString();\n              callback.success(reply);\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void isConnected(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              boolean connected = ftp.isConnected();\n              callback.success(connected ? 1 : 0);\n            } catch (Exception e) {\n              Log.e(\"FTP\", \"FTPClient\", e);\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void disconnect(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp != null) {\n                ftp.disconnect();\n                ftpProfiles.remove(ftpId);\n              }\n              callback.success();\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void createDirectory(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.makeDirectory(path);\n              callback.success();\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void changeDirectory(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.changeWorkingDirectory(path);\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void changeToParentDirectory(\n    JSONArray args,\n    CallbackContext callback\n  ) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              ftp.changeToParentDirectory();\n              callback.success();\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void getWorkingDirectory(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              String workingDirectory = ftp.printWorkingDirectory();\n              callback.success(workingDirectory);\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  public void getStat(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String ftpId = args.optString(0);\n              String path = args.optString(1);\n\n              if (ftpId == null || ftpId.isEmpty()) {\n                callback.error(\"FTP ID is required.\");\n                return;\n              }\n\n              if (path == null || path.isEmpty()) {\n                callback.error(\"Path is required.\");\n                return;\n              }\n\n              FTPClient ftp = ftpProfiles.get(ftpId);\n              if (ftp == null) {\n                callback.error(\"FTP client not found.\");\n                return;\n              }\n\n              FTPFile[] files = ftp.listFiles(path);\n              if (files == null || files.length == 0) {\n                callback.error(\"File not found.\");\n                return;\n              }\n\n              FTPFile file = files[0];\n              JSONObject stat = new JSONObject();\n              stat.put(\"isFile\", file.isFile());\n              stat.put(\"isValid\", file.isValid());\n              stat.put(\"isUnknown\", file.isUnknown());\n              stat.put(\"isDirectory\", file.isDirectory());\n              stat.put(\"isLink\", file.isSymbolicLink());\n              stat.put(\"linkCount\", file.getHardLinkCount());\n              stat.put(\"length\", file.getSize());\n              stat.put(\"name\", getBaseName(file.getName()));\n              stat.put(\"lastModified\", file.getTimestamp().getTimeInMillis());\n              stat.put(\"link\", file.getLink());\n              stat.put(\"group\", file.getGroup());\n              stat.put(\"user\", file.getUser());\n              stat.put(\n                \"canWrite\",\n                file.hasPermission(\n                  FTPFile.USER_ACCESS,\n                  FTPFile.WRITE_PERMISSION\n                )\n              );\n              stat.put(\n                \"canRead\",\n                file.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION)\n              );\n\n              callback.success(stat);\n            } catch (ParserInitializationException e) {\n              callback.error(e.getMessage());\n            } catch (FTPConnectionClosedException e) {\n              callback.error(e.getMessage());\n            } catch (IOException e) {\n              callback.error(e.getMessage());\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  private String getFtpId(String host, int port, String username) {\n    return username + \"@\" + host + \":\" + port;\n  }\n\n  private String errMessage(Exception e) {\n    String res = e.getMessage();\n    if (res == null || res.equals(\"\")) {\n      return e.toString();\n    }\n\n    return res;\n  }\n\n  private void emptyDirectory(String directory, FTPClient client)\n    throws FTPConnectionClosedException, IOException {\n    FTPFile[] files = client.listFiles(directory);\n    for (FTPFile file : files) {\n      String filename = file.getName();\n      if (filename.equals(\".\") || filename.equals(\"..\")) {\n        continue;\n      }\n      if (file.isDirectory()) {\n        Log.d(\"FTP\", \"Removing directory: \" + file.getName());\n        emptyDirectory(directory + \"/\" + file.getName(), client);\n      } else {\n        Log.d(\"FTP\", \"Removing file: \" + file.getName());\n        client.deleteFile(directory + \"/\" + file.getName());\n      }\n    }\n    client.removeDirectory(directory);\n  }\n\n  private String joinPath(String p1, String p2) {\n    if (!p1.endsWith(\"/\")) {\n      p1 += \"/\";\n    }\n    return p1 + p2;\n  }\n}\n"
  },
  {
    "path": "src/plugins/ftp/www/ftp.js",
    "content": "module.exports = {\n  connect: function (host, port, username, password, options, onSuccess, onFail) {\n    if (typeof port != 'number') {\n      throw new Error('Port must be number');\n    }\n    port = Number.parseInt(port);\n\n    var connectionMode = \"passive\";\n    var securityType = \"ftp\";\n    var encoding = \"utf8\";\n\n    if (typeof options === 'function') {\n      onFail = onSuccess;\n      onSuccess = options;\n      options = {};\n    }\n\n    if (options) {\n      if (options.connectionMode) {\n        connectionMode = options.connectionMode;\n      }\n      if (options.securityType) {\n        securityType = options.securityType;\n      }\n      if (options.encoding) {\n        encoding = options.encoding;\n      }\n    }\n\n    cordova.exec(onSuccess, onFail, 'Ftp', 'connect', [\n      host,\n      port,\n      username,\n      password,\n      connectionMode,\n      securityType,\n      encoding\n    ]);\n  },\n  listDirectory: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'listDirectory', [id, path]);\n  },\n  execCommand: function (id, command, onSuccess, onFail, args) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'execCommand', [id, command, args]);\n  },\n  isConnected: function (id, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'isConnected', [id]);\n  },\n  disconnect: function (id, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'disconnect', [id]);\n  },\n  downloadFile: function (id, remotePath, localPath, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'downloadFile', [id, remotePath, localPath]);\n  },\n  uploadFile: function (id, localPath, remotePath, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'uploadFile', [id, localPath, remotePath]);\n  },\n  deleteFile: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'deleteFile', [id, path]);\n  },\n  deleteDirectory: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'deleteDirectory', [id, path]);\n  },\n  createDirectory: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'createDirectory', [id, path]);\n  },\n  createFile: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'createFile', [id, path]);\n  },\n  getStat: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'getStat', [id, path]);\n  },\n  exists: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'exists', [id, path]);\n  },\n  changeDirectory: function (id, path, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'changeDirectory', [id, path]);\n  },\n  changeToParentDirectory: function (id, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'changeToParentDirectory', [id]);\n  },\n  getWorkingDirectory: function (id, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'getWorkingDirectory', [id]);\n  },\n  rename: function (id, oldPath, newPath, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'rename', [id, oldPath, newPath]);\n  },\n  getKeepAlive: function (id, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'getKeepAlive', [id]);\n  },\n  sendNoOp: function (id, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Ftp', 'sendNoOp', [id]);\n  }\n}"
  },
  {
    "path": "src/plugins/iap/index.d.ts",
    "content": "interface Iap {\n  getProducts(\n    skuList: Array<string>,\n    onSuccess: (skuList: Array<Object>) => void,\n    onError: (err: String) => Error,\n  ): void;\n  setPurchaseUpdatedListener(\n    onSuccess: (purchase: Object) => void,\n    onError: (err: string) => void,\n  ): void;\n  startConnection(\n    onSuccess: (responseCode: number) => void,\n    onError: (err: string) => void,\n  ): void;\n  consume(\n    purchaseToken: string,\n    onSuccess: (responseCode: number) => void,\n    onError: (err: string) => void,\n  ): void;\n  purchase(\n    skuId: string,\n    onSuccess: (responseCode: number) => void,\n    onError: (err: string) => void,\n  ): void;\n  getPurchases(\n    onSuccess: (purchaseList: Array<Object>) => void,\n    onError: (err: string) => void,\n  ): void;\n  OK: 0;\n  BILLING_UNAVAILABLE: 3;\n  DEVELOPER_ERROR: 5;\n  ERROR: 6;\n  FEATURE_NOT_SUPPORTED: -2;\n  ITEM_ALREADY_OWNED: 7;\n  ITEM_NOT_OWNED: 8;\n  ITEM_UNAVAILABLE: 4;\n  SERVICE_DISCONNECTED: -1;\n  SERVICE_TIMEOUT: -3;\n  SERVICE_UNAVAILABLE: 2;\n  USER_CANCELED: 1;\n  PURCHASE_STATE_PURCHASED: 1;\n  PURCHASE_STATE_PENDING: 2;\n  PURCHASE_STATE_UNKNOWN: 0;\n}\n\ndeclare var iap: Iap;\n"
  },
  {
    "path": "src/plugins/iap/package.json",
    "content": "{\n  \"name\": \"cordova-plugin-iap\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}"
  },
  {
    "path": "src/plugins/iap/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\n  xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-iap\" version=\"0.0.1\">\n  <name>cordova-plugin-iap</name>\n  <description>In app purchase for Android.</description>\n  <license>Apache 2.0</license>\n  <keywords>cordova,plugin,in app purchase</keywords>\n\n  <js-module src=\"www/plugin.js\" name=\"iap\">\n    <clobbers target=\"window.iap\" />\n  </js-module>\n\n  <platform name=\"android\">\n\n    <config-file target=\"res/xml/config.xml\" parent=\"/*\">\n      <feature name=\"Iap\">\n        <param name=\"android-package\" value=\"com.foxdebug.iap.Iap\"/>\n      </feature>\n    </config-file>\n    <config-file target=\"AndroidManifest.xml\" parent=\"/manifest\">\n      <uses-permission android:name=\"com.android.vending.BILLING\" />\n    </config-file>\n\n    <source-file src=\"src/com/foxdebug/iap/Iap.java\" target-dir=\"src/com/foxdebug/iap\"/>\n    <framework src=\"com.android.billingclient:billing:8.0.0\" />\n  </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/iap/src/com/foxdebug/iap/Iap.java",
    "content": "package com.foxdebug.iap;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.util.Log;\nimport com.android.billingclient.api.AcknowledgePurchaseParams;\nimport com.android.billingclient.api.AcknowledgePurchaseResponseListener;\nimport com.android.billingclient.api.BillingClient;\nimport com.android.billingclient.api.BillingClient.BillingResponseCode;\nimport com.android.billingclient.api.BillingClientStateListener;\nimport com.android.billingclient.api.BillingFlowParams;\nimport com.android.billingclient.api.BillingResult;\nimport com.android.billingclient.api.ConsumeParams;\nimport com.android.billingclient.api.ConsumeResponseListener;\nimport com.android.billingclient.api.PendingPurchasesParams;\nimport com.android.billingclient.api.ProductDetails;\nimport com.android.billingclient.api.ProductDetailsResponseListener;\nimport com.android.billingclient.api.Purchase;\nimport com.android.billingclient.api.PurchasesResponseListener;\nimport com.android.billingclient.api.PurchasesUpdatedListener;\nimport com.android.billingclient.api.QueryProductDetailsParams;\nimport com.android.billingclient.api.QueryProductDetailsResult;\nimport com.android.billingclient.api.QueryPurchasesParams;\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPlugin;\nimport org.apache.cordova.CordovaWebView;\nimport org.apache.cordova.PluginResult;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class Iap extends CordovaPlugin {\n\n  private BillingClient billingClient;\n  private WeakReference<Context> contextRef;\n  private WeakReference<Activity> activityRef;\n  private CallbackContext purchaseUpdated;\n\n  public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n    super.initialize(cordova, webView);\n    contextRef = new WeakReference<>(cordova.getContext());\n    activityRef = new WeakReference<>(cordova.getActivity());\n    billingClient = getBillingClient();\n  }\n\n  @Override\n  public boolean execute(\n    String action,\n    JSONArray args,\n    CallbackContext callbackContext\n  ) throws JSONException {\n    String arg1 = getString(args, 0);\n    switch (action) {\n      case \"startConnection\":\n      case \"getProducts\":\n      case \"setPurchaseUpdatedListener\":\n      case \"purchase\":\n      case \"consume\":\n      case \"getPurchases\":\n      case \"acknowledgePurchase\":\n        break;\n      default:\n        return false;\n    }\n\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            switch (action) {\n              case \"startConnection\":\n                startConnection(callbackContext);\n                break;\n              case \"getProducts\":\n                getProducts(getStringList(args, 0), callbackContext);\n                break;\n              case \"setPurchaseUpdatedListener\":\n                setPurchaseUpdatedListener(callbackContext);\n                break;\n              case \"purchase\":\n                purchase(arg1, callbackContext);\n                break;\n              case \"consume\":\n                consume(arg1, callbackContext);\n                break;\n              case \"getPurchases\":\n                getPurchases(callbackContext);\n                break;\n              case \"acknowledgePurchase\":\n                acknowledgePurchase(arg1, callbackContext);\n                break;\n            }\n          }\n        }\n      );\n\n    return true;\n  }\n\n  private BillingClient getBillingClient() {\n    return BillingClient.newBuilder(this.contextRef.get())\n      .enablePendingPurchases(\n        PendingPurchasesParams.newBuilder().enableOneTimeProducts().build()\n      )\n      .setListener(\n        new PurchasesUpdatedListener() {\n          public void onPurchasesUpdated(\n            BillingResult billingResult,\n            List<Purchase> purchases\n          ) {\n            try {\n              int responseCode = billingResult.getResponseCode();\n              if (responseCode == BillingResponseCode.OK) {\n                JSONArray result = new JSONArray();\n                for (Purchase purchase : purchases) {\n                  result.put(purchaseToJson(purchase));\n                }\n                sendPurchasePluginResult(\n                  new PluginResult(PluginResult.Status.OK, result)\n                );\n              } else {\n                sendPurchasePluginResult(\n                  new PluginResult(PluginResult.Status.ERROR, responseCode)\n                );\n              }\n            } catch (JSONException e) {\n              sendPurchasePluginResult(\n                new PluginResult(PluginResult.Status.ERROR, e.getMessage())\n              );\n            }\n          }\n        }\n      )\n      .build();\n  }\n\n  private void setPurchaseUpdatedListener(CallbackContext callbackContext) {\n    purchaseUpdated = callbackContext;\n  }\n\n  private void consume(String token, CallbackContext callbackContext) {\n    ConsumeParams consumeParams = ConsumeParams.newBuilder()\n      .setPurchaseToken(token)\n      .build();\n    billingClient.consumeAsync(\n      consumeParams,\n      new ConsumeResponseListener() {\n        public void onConsumeResponse(\n          BillingResult billingResult,\n          String purchaseToken\n        ) {\n          int responseCode = billingResult.getResponseCode();\n          if (responseCode == BillingResponseCode.OK) {\n            callbackContext.success(responseCode);\n          } else {\n            callbackContext.error(responseCode);\n          }\n        }\n      }\n    );\n  }\n\n  private void startConnection(CallbackContext callbackContext) {\n    try {\n      if (billingClient == null) {\n        billingClient = getBillingClient();\n      }\n      billingClient.startConnection(\n        new BillingClientStateListener() {\n          public void onBillingSetupFinished(BillingResult billingResult) {\n            int responseCode = billingResult.getResponseCode();\n            if (responseCode == BillingResponseCode.OK) {\n              callbackContext.success(responseCode);\n            } else {\n              callbackContext.error(responseCode);\n            }\n          }\n\n          public void onBillingServiceDisconnected() {\n            callbackContext.error(\"Billing service disconnected\");\n          }\n        }\n      );\n    } catch (SecurityException e) {\n      callbackContext.error(e.getMessage());\n    }\n  }\n\n  private void getProducts(\n    List<String> idList,\n    CallbackContext callbackContext\n  ) {\n    if (billingClient == null) {\n      billingClient = getBillingClient();\n      callbackContext.error(\"Billing client is not connected\");\n      return;\n    }\n    List<QueryProductDetailsParams.Product> productList = new ArrayList<>();\n    for (String productId : idList) {\n      productList.add(\n        QueryProductDetailsParams.Product.newBuilder()\n          .setProductId(productId)\n          .setProductType(BillingClient.ProductType.INAPP)\n          .build()\n      );\n    }\n    QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()\n      .setProductList(productList)\n      .build();\n\n    billingClient.queryProductDetailsAsync(\n      params,\n      new ProductDetailsResponseListener() {\n        public void onProductDetailsResponse(\n          BillingResult billingResult,\n          QueryProductDetailsResult queryProductDetailsResult\n        ) {\n          try {\n            int responseCode = billingResult.getResponseCode();\n            if (responseCode == BillingResponseCode.OK) {\n              List<ProductDetails> productDetailsList = queryProductDetailsResult.getProductDetailsList();\n              JSONArray products = new JSONArray();\n              for (ProductDetails productDetails : productDetailsList) {\n                JSONObject product = new JSONObject();\n                ProductDetails.OneTimePurchaseOfferDetails offerDetails = productDetails.getOneTimePurchaseOfferDetails();\n                if (offerDetails != null) {\n                  product.put(\"productId\", productDetails.getProductId());\n                  product.put(\"title\", productDetails.getTitle());\n                  product.put(\"description\", productDetails.getDescription());\n                  product.put(\"price\", offerDetails.getFormattedPrice());\n                  product.put(\n                    \"priceAmountMicros\",\n                    offerDetails.getPriceAmountMicros()\n                  );\n                  product.put(\n                    \"priceCurrencyCode\",\n                    offerDetails.getPriceCurrencyCode()\n                  );\n                  product.put(\"type\", productDetails.getProductType());\n                }\n                products.put(product);\n              }\n              callbackContext.success(products);\n            } else {\n              callbackContext.error(responseCode);\n            }\n          } catch (JSONException e) {\n            callbackContext.error(e.getMessage());\n          }\n        }\n      }\n    );\n  }\n\n  private void purchase(String productIdOrJson, CallbackContext callbackContext) {\n    try {\n      if (productIdOrJson == null || productIdOrJson.trim().isEmpty()) {\n        callbackContext.error(\"Product ID cannot be null or empty\");\n        return;\n      }\n      \n      final String productId = productIdOrJson;\n      \n      List<QueryProductDetailsParams.Product> productList = new ArrayList<>();\n      productList.add(\n        QueryProductDetailsParams.Product.newBuilder()\n          .setProductId(productId)\n          .setProductType(BillingClient.ProductType.INAPP)\n          .build()\n      );\n      QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()\n        .setProductList(productList)\n        .build();\n        \n      billingClient.queryProductDetailsAsync(\n        params,\n        new ProductDetailsResponseListener() {\n          public void onProductDetailsResponse(\n            BillingResult billingResult,\n            QueryProductDetailsResult queryProductDetailsResult\n          ) {\n            if (billingResult.getResponseCode() == BillingResponseCode.OK) {\n              List<ProductDetails> productDetailsList = queryProductDetailsResult.getProductDetailsList();\n              if (!productDetailsList.isEmpty()) {\n                ProductDetails productDetails = productDetailsList.get(0);\n                BillingResult result = billingClient.launchBillingFlow(\n                  activityRef.get(),\n                  BillingFlowParams.newBuilder().setProductDetailsParamsList(\n                    Arrays.asList(\n                      BillingFlowParams.ProductDetailsParams.newBuilder()\n                        .setProductDetails(productDetails)\n                        .build()\n                    )\n                  ).build()\n                );\n                int responseCode = result.getResponseCode();\n                if (responseCode == BillingResponseCode.OK) {\n                  callbackContext.success();\n                } else {\n                  callbackContext.error(responseCode);\n                }\n              } else {\n                callbackContext.error(\"No product details found for: \" + productId);\n              }\n            } else {\n              callbackContext.error(billingResult.getResponseCode());\n            }\n          }\n        }\n      );\n    } catch (Exception e) {\n      callbackContext.error(\"Purchase error: \" + e.getMessage());\n    }\n  }\n\n  private void getPurchases(CallbackContext callbackContext) {\n    if (billingClient == null) {\n      billingClient = getBillingClient();\n      callbackContext.error(\"Billing client is not connected\");\n      return;\n    }\n\n    QueryPurchasesParams params = QueryPurchasesParams.newBuilder()\n      .setProductType(BillingClient.ProductType.INAPP)\n      .build();\n    billingClient.queryPurchasesAsync(\n      params,\n      new PurchasesResponseListener() {\n        public void onQueryPurchasesResponse(\n          BillingResult billingResult,\n          List<Purchase> purchasesList\n        ) {\n          try {\n            int responseCode = billingResult.getResponseCode();\n            if (responseCode == BillingResponseCode.OK) {\n              JSONArray purchases = new JSONArray();\n              for (Purchase purchase : purchasesList) {\n                purchases.put(purchaseToJson(purchase));\n              }\n              callbackContext.success(purchases);\n            } else {\n              callbackContext.error(responseCode);\n            }\n          } catch (JSONException e) {\n            callbackContext.error(e.getMessage());\n          }\n        }\n      }\n    );\n  }\n\n  private void acknowledgePurchase(\n    String purchaseToken,\n    CallbackContext callbackContext\n  ) {\n    if (billingClient == null) {\n      billingClient = getBillingClient();\n      callbackContext.error(\"Billing client is not connected\");\n      return;\n    }\n\n    AcknowledgePurchaseParams params = AcknowledgePurchaseParams.newBuilder()\n      .setPurchaseToken(purchaseToken)\n      .build();\n\n    billingClient.acknowledgePurchase(\n      params,\n      new AcknowledgePurchaseResponseListener() {\n        public void onAcknowledgePurchaseResponse(BillingResult billingResult) {\n          int responseCode = billingResult.getResponseCode();\n          if (responseCode == BillingResponseCode.OK) {\n            callbackContext.success();\n          } else {\n            callbackContext.error(responseCode);\n          }\n        }\n      }\n    );\n  }\n\n  private JSONObject purchaseToJson(Purchase purchase) throws JSONException {\n    JSONObject item = new JSONObject();\n    List<String> skuList = purchase.getSkus();\n    JSONArray skuArray = new JSONArray();\n    for (String sku : skuList) {\n      skuArray.put(sku);\n    }\n    item.put(\"productIds\", skuArray);\n    item.put(\"orderId\", purchase.getOrderId());\n    item.put(\"signature\", purchase.getSignature());\n    item.put(\"purchaseTime\", purchase.getPurchaseTime());\n    item.put(\"purchaseToken\", purchase.getPurchaseToken());\n    item.put(\"purchaseState\", purchase.getPurchaseState());\n    item.put(\"isAcknowledged\", purchase.isAcknowledged());\n    item.put(\"developerPayload\", purchase.getDeveloperPayload());\n    return item;\n  }\n\n  private void sendPurchasePluginResult(PluginResult result) {\n    if (purchaseUpdated != null) {\n      result.setKeepCallback(true);\n      purchaseUpdated.sendPluginResult(result);\n    }\n  }\n\n  private String getString(JSONArray args, int index) {\n    try {\n      return args.getString(index);\n    } catch (JSONException e) {\n      return null;\n    }\n  }\n\n  private List<String> getStringList(JSONArray args, int index) {\n    try {\n      JSONArray array = args.getJSONArray(index);\n      List<String> list = new ArrayList<String>();\n      for (int i = 0; i < array.length(); i++) {\n        list.add(array.getString(i));\n      }\n      return list;\n    } catch (JSONException e) {\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "src/plugins/iap/www/plugin.js",
    "content": "module.exports = {\n  getProducts: function (productIds, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'getProducts', [productIds]);\n  },\n  setPurchaseUpdatedListener: function (onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'setPurchaseUpdatedListener', []);\n  },\n  startConnection: function (onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'startConnection', []);\n  },\n  consume: function (purchaseToken, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'consume', [purchaseToken]);\n  },\n  purchase: function (productId, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'purchase', [productId]);\n  },\n  getPurchases: function (onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'getPurchases', []);\n  },\n  acknowledgePurchase: function (purchaseToken, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'Iap', 'acknowledgePurchase', [purchaseToken]);\n  },\n  BILLING_UNAVAILABLE: 3,\n  DEVELOPER_ERROR: 5,\n  ERROR: 6,\n  FEATURE_NOT_SUPPORTED: -2,\n  ITEM_ALREADY_OWNED: 7,\n  ITEM_NOT_OWNED: 8,\n  ITEM_UNAVAILABLE: 4,\n  OK: 0,\n  SERVICE_DISCONNECTED: -1,\n  SERVICE_TIMEOUT: -3,\n  SERVICE_TIMEOUT: 2,\n  USER_CANCELED: 1,\n  PURCHASE_STATE_PURCHASED: 1,\n  PURCHASE_STATE_PENDING: 2,\n  PURCHASE_STATE_UNKNOWN: 0,\n};"
  },
  {
    "path": "src/plugins/pluginContext/package.json",
    "content": "{\n  \"name\": \"com.foxdebug.acode.rk.plugin.plugincontext\",\n  \"version\": \"1.0.0\",\n  \"description\": \"PluginContext\",\n  \"cordova\": {\n    \"id\": \"com.foxdebug.acode.rk.plugin.plugincontext\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"ecosystem:cordova\",\n    \"cordova-android\"\n  ],\n  \"author\": \"@RohitKushvaha01\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "src/plugins/pluginContext/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\" xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"com.foxdebug.acode.rk.plugin.plugincontext\" version=\"1.0.0\">\n    <name>PluginContext</name>\n\n\n        <js-module name=\"PluginContext\" src=\"www/PluginContext.js\">\n        <clobbers target=\"window.PluginContext\" />\n    </js-module>\n\n\n    <platform name=\"android\">\n        <config-file parent=\"/*\" target=\"res/xml/config.xml\">\n            <feature name=\"Tee\">\n                <param name=\"android-package\" value=\"com.foxdebug.acode.rk.plugin.Tee\" />\n            </feature>\n        </config-file>\n\n        <source-file src=\"src/android/Tee.java\" target-dir=\"src/com/foxdebug/acode/rk/plugin\" />\n\n           \n    </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/pluginContext/src/android/Tee.java",
    "content": "package com.foxdebug.acode.rk.plugin;\n\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaPlugin;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.UUID;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport android.content.Context;\nimport org.apache.cordova.*;\n\n//auth plugin\nimport com.foxdebug.acode.rk.auth.EncryptedPreferenceManager;\n\npublic class Tee extends CordovaPlugin {\n\n    // pluginId : token\n    private /*static*/ final Map<String, String> tokenStore = new ConcurrentHashMap<>();\n\n    //assigned tokens\n    private /*static*/ final Set<String> disclosed = ConcurrentHashMap.newKeySet();\n\n    // token : list of permissions\n    private /*static*/ final Map<String, List<String>> permissionStore = new ConcurrentHashMap<>();\n\n\n\n    private Context context;\n\n\n    public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n        super.initialize(cordova, webView);\n        this.context = cordova.getContext();\n    }\n\n    @Override\n    public boolean execute(String action, JSONArray args, CallbackContext callback)\n            throws JSONException {\n\n\n        if (\"get_secret\".equals(action)) {\n            String token = args.getString(0);\n            String key = args.getString(1);\n            String defaultValue = args.getString(2);\n\n            String pluginId = getPluginIdFromToken(token);\n\n            if (pluginId == null) {\n                callback.error(\"INVALID_TOKEN\");\n                return true;\n            }\n\n            EncryptedPreferenceManager prefs =\n                    new EncryptedPreferenceManager(context, pluginId);\n\n            String value = prefs.getString(key, defaultValue);\n            callback.success(value);\n            return true;\n        }\n\n        if (\"set_secret\".equals(action)) {\n            String token = args.getString(0);\n            String key = args.getString(1);\n            String value = args.getString(2);\n\n            String pluginId = getPluginIdFromToken(token);\n\n            if (pluginId == null) {\n                callback.error(\"INVALID_TOKEN\");\n                return true;\n            }\n\n            EncryptedPreferenceManager prefs =\n                    new EncryptedPreferenceManager(context, pluginId);\n\n            prefs.setString(key, value);\n            callback.success();\n            return true;\n        }\n\n\n        if (\"requestToken\".equals(action)) {\n            String pluginId = args.getString(0);\n            String pluginJson = args.getString(1);\n            handleTokenRequest(pluginId, pluginJson, callback);\n            return true;\n        }\n\n        if (\"grantedPermission\".equals(action)) {\n            String token = args.getString(0);\n            String permission = args.getString(1);\n\n            if (!permissionStore.containsKey(token)) {\n                callback.error(\"INVALID_TOKEN\");\n                return true;\n            }\n\n            boolean granted = grantedPermission(token, permission);\n            callback.success(granted ? 1 : 0);\n            return true;\n        }\n\n        if (\"listAllPermissions\".equals(action)) {\n            String token = args.getString(0);\n\n            if (!permissionStore.containsKey(token)) {\n                callback.error(\"INVALID_TOKEN\");\n                return true;\n            }\n\n            List<String> permissions = listAllPermissions(token);\n            JSONArray result = new JSONArray(permissions);\n\n            callback.success(result);\n            return true;\n        }\n\n        return false;\n    }\n\n\n    private String getPluginIdFromToken(String token) {\n        for (Map.Entry<String, String> entry : tokenStore.entrySet()) {\n            if (entry.getValue().equals(token)) {\n                return entry.getKey();\n            }\n        }\n        return null;\n    }\n\n    //============================================================\n    //do not change function signatures\n    public boolean isTokenValid(String token, String pluginId) {\n        String storedToken = tokenStore.get(pluginId);\n        return storedToken != null && token.equals(storedToken);\n    }\n\n\n    public boolean grantedPermission(String token, String permission) {\n        List<String> permissions = permissionStore.get(token);\n        return permissions != null && permissions.contains(permission);\n    }\n\n    public List<String> listAllPermissions(String token) {\n        List<String> permissions = permissionStore.get(token);\n\n        if (permissions == null) {\n            return new ArrayList<>();\n        }\n\n        return new ArrayList<>(permissions); // return copy (safe)\n    }\n    //============================================================\n\n\n    private synchronized void handleTokenRequest(\n            String pluginId,\n            String pluginJson,\n            CallbackContext callback\n    ) {\n\n        if (disclosed.contains(pluginId)) {\n            callback.error(\"TOKEN_ALREADY_ISSUED\");\n            return;\n        }\n\n        String token = tokenStore.get(pluginId);\n\n        if (token == null) {\n            token = UUID.randomUUID().toString();\n            tokenStore.put(pluginId, token);\n        }\n\n        try {\n            JSONObject json = new JSONObject(pluginJson);\n            JSONArray permissions = json.optJSONArray(\"permissions\");\n\n            List<String> permissionList = new ArrayList<>();\n\n            if (permissions != null) {\n                for (int i = 0; i < permissions.length(); i++) {\n                    permissionList.add(permissions.getString(i));\n                }\n            }\n\n            // Bind permissions to token\n            permissionStore.put(token, permissionList);\n\n        } catch (JSONException e) {\n            callback.error(\"INVALID_PLUGIN_JSON\");\n            return;\n        }\n\n        disclosed.add(pluginId);\n        callback.success(token);\n    }\n}\n"
  },
  {
    "path": "src/plugins/pluginContext/www/PluginContext.js",
    "content": "var exec = require(\"cordova/exec\");\n\nconst PluginContext = (function () {\n  //=============================\n  class _PluginContext {\n    constructor(uuid) {\n      this.created_at = Date.now();\n      this.uuid = uuid;\n      Object.freeze(this);\n    }\n\n    toString() {\n      return this.uuid;\n    }\n\n    [Symbol.toPrimitive](hint) {\n      if (hint === \"number\") {\n        return NaN; // prevent numeric coercion\n      }\n      return this.uuid;\n    }\n\n    grantedPermission(permission) {\n      return new Promise((resolve, reject) => {\n        exec(resolve, reject, \"Tee\", \"grantedPermission\", [\n          this.uuid,\n          permission,\n        ]);\n      });\n    }\n\n    listAllPermissions() {\n      return new Promise((resolve, reject) => {\n        exec(resolve, reject, \"Tee\", \"listAllPermissions\", [this.uuid]);\n      });\n    }\n\n    getSecret(key, defaultValue = \"\") {\n      return new Promise((resolve, reject) => {\n        exec(\n          resolve,\n          reject,\n          \"Tee\",             \n          \"get_secret\",       \n          [this.uuid, key, defaultValue]\n        );\n      });\n    }\n\n\n    setSecret(key, value) {\n      return new Promise((resolve, reject) => {\n        exec(\n          resolve,\n          reject,\n          \"Tee\",\n          \"set_secret\",\n          [this.uuid, key, value]\n        );\n      });\n    }\n  }\n\n  //Object.freeze(this);\n\n  //===============================\n\n  return {\n    generate: async function (pluginId, pluginJson) {\n      try {\n        function requestToken(pluginId) {\n          return new Promise((resolve, reject) => {\n            exec(resolve, reject, \"Tee\", \"requestToken\", [\n              pluginId,\n              pluginJson,\n            ]);\n          });\n        }\n\n        const uuid = await requestToken(pluginId);\n        return new _PluginContext(uuid);\n      } catch (err) {\n        console.warn(`PluginContext creation failed for pluginId ${pluginId}:`, err);\n        return null;\n      }\n    },\n  };\n})();\n\nmodule.exports = PluginContext;\n"
  },
  {
    "path": "src/plugins/proot/package.json",
    "content": "{\n  \"name\": \"com.foxdebug.acode.rk.exec.proot\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Proot stuff\",\n  \"cordova\": {\n    \"id\": \"com.foxdebug.acode.rk.exec.proot\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"ecosystem:cordova\",\n    \"cordova-android\"\n  ],\n  \"author\": \"@RohitKushvaha01\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "src/plugins/proot/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\" xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"com.foxdebug.acode.rk.exec.proot\" version=\"1.0.0\">\n    <name>Proot</name>\n\n\n    <platform name=\"android\">\n        <!-- Use flavors if F-Droid complains about native libs -->\n\n        <!-- x86_64 / x64 only (32bit is not supported) -->\n        <source-file src=\"libs/x64/libproot.so\" target-dir=\"libs/x86_64\" />\n        <source-file src=\"libs/x64/libproot32.so\" target-dir=\"libs/x86_64\" />\n        <source-file src=\"libs/x64/libproot-xed.so\" target-dir=\"libs/x86_64\" />\n        <source-file src=\"libs/x64/libtalloc.so\" target-dir=\"libs/x86_64\" />\n\n        <!-- arm64 / arm-v8a -->\n        <source-file src=\"libs/arm64/libproot.so\" target-dir=\"libs/arm64-v8a\" />\n        <source-file src=\"libs/arm64/libproot32.so\" target-dir=\"libs/arm64-v8a\" />\n        <source-file src=\"libs/arm64/libproot-xed.so\" target-dir=\"libs/arm64-v8a\" />\n        <source-file src=\"libs/arm64/libtalloc.so\" target-dir=\"libs/arm64-v8a\" />\n\n\n        <!-- armhf / armeabi-v7a (armeabi / armeabi-v6a not supported)-->\n        <source-file src=\"libs/arm32/libproot.so\" target-dir=\"libs/armeabi-v7a\" />\n        <source-file src=\"libs/arm32/libproot-xed.so\" target-dir=\"libs/armeabi-v7a\" />       \n        <source-file src=\"libs/arm32/libtalloc.so\" target-dir=\"libs/armeabi-v7a\" />\n\n           \n    </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/sdcard/index.d.ts",
    "content": "interface Storage {\n  /**\n   * Name of the storage\n   */\n  name: string;\n  /**\n   * UUID of the storage\n   */\n  uuid: string;\n}\n\ninterface DirListItem {\n  name: string;\n  mime: string;\n  isDirectory: Boolean;\n  isFile: Boolean;\n  uri: string;\n}\n\ninterface Stats {\n  canRead: boolean;\n  canWrite: boolean;\n  exists: boolean; //indicates if file can be found on device storage\n  isDirectory: boolean;\n  isFile: boolean;\n  isVirtual: boolean;\n  lastModified: number;\n  length: number;\n  name: string;\n  type: string;\n  uri: string;\n}\n\ninterface DocumentFile {\n  canWrite: boolean;\n  filename: string;\n  length: number;\n  type: string;\n  uri: string;\n}\n\ninterface SDcard {\n  /**\n   * Copy file/directory to given destination\n   * @param src Source url\n   * @param dest Destination url\n   * @param onSuccess Callback function on success returns url of copied file/dir\n   * @param onFail Callback function on error returns error object\n   */\n  copy(\n    src: string,\n    dest: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Creates new directory at given source url.\n   * @param src Source url\n   * @param dirName New directory name\n   * @param onSuccess Callback function on success returns url of new directory\n   * @param onFail callback function on error returns error object\n   */\n  createDir(\n    src: string,\n    dirName: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Creates new file at given source url.\n   * @param src Source url\n   * @param fileName New file name\n   * @param onSuccess Callback function on success returns url of new directory\n   * @param onFail Callback function on error returns error object\n   */\n  createFile(\n    src: string,\n    fileName: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Deletes file/directory\n   * @param src Source url of file/directory\n   * @param onSuccess Callback function on success returns source url\n   * @param onFail Callback function on error returns error object\n   */\n  delete(\n    src: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Checks if given file/directory\n   * @param src File/Directory url\n   * @param onSuccess Callback function on success returns string \"TRUE\" or \"FALSE\"\n   * @param onFail Callback function on error returns error object\n   */\n  exists(\n    src: string,\n    onSuccess: (exists: 'TRUE' | 'FALSE') => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Converts virtual URL to actual url\n   * @param src Virtual address returned by other methods\n   * @param onSuccess Callback function on success returns actual url\n   * @param onFail Callback function on error returns error object\n   */\n  formatUri(\n    src: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Gets actual url for relative path to src\n   * e.g. getPath(src, \"../path/to/file.txt\") => actual url\n   * @param src Directory url\n   * @param path Relative file/directory path\n   * @param onSuccess Callback function on success returns actual url\n   * @param onFail Callback function on error returns error object\n   */\n  getPath(\n    src: string,\n    path: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Requests user for storage permission\n   * @param uuid UUID returned from listStorages method\n   * @param onSuccess Callback function on success returns url for the storage root\n   * @param onFail Callback function on error returns error object\n   */\n  getStorageAccessPermission(\n    uuid: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Lists all the storages\n   * @param onSuccess Callback function on success returns list of storages\n   * @param onFail Callback function on error returns error object\n   */\n  listStorages(\n    onSuccess: (storages: Array<Storage>) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Gets list of files/directory in the given directory\n   * @param src Directory url\n   * @param onSuccess Callback function on success returns list of files/directory\n   * @param onFail Callback function on error returns error object\n   */\n  listDir(\n    src: string,\n    onSuccess: (list: Array<DirListItem>) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Move file/directory to given destination\n   * @param src Source url\n   * @param dest Destination url\n   * @param onSuccess Callback function on success returns url of moved file/dir\n   * @param onFail Callback function on error returns error object\n   */\n  move(\n    src: string,\n    dest: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Opens file provider to select file\n   * @param onSuccess Callback function on success returns url of selected file\n   * @param onFail Callback function on error returns error object\n   * @param mimeType MimeType of file to be selected\n   */\n  openDocumentFile(\n    onSuccess: (url: DocumentFile) => void,\n    onFail: (err: any) => void,\n    mimeType: string,\n  ): void;\n  /**\n   * Opens gallery to select image\n   * @param onSuccess Callback function on success returns url of selected file\n   * @param onFail Callback function on error returns error object\n   * @param mimeType MimeType of file to be selected\n   */\n  getImage(\n    onSuccess: (url: DocumentFile) => void,\n    onFail: (err: any) => void,\n    mimeType: string,\n  ): void;\n  /**\n   * Renames the given file/directory to given new name\n   * @param src Url of file/directory\n   * @param newname New name\n   * @param onSuccess Callback function on success returns url of renamed file\n   * @param onFail Callback function on error returns error object\n   */\n  rename(\n    src: string,\n    newname: string,\n    onSuccess: (url: string) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Writes new content to the given file.\n   * @param src file url\n   * @param content new file content\n   * @param onSuccess Callback function on success returns \"OK\"\n   * @param onFail Callback function on error returns error object\n   */\n  write(\n    src: string,\n    content: string,\n    onSuccess: (res: 'OK') => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Writes new content to the given file.\n   * @param src file url\n   * @param content new file content\n   * @param isBinary is data binary\n   * @param onSuccess Callback function on success returns \"OK\"\n   * @param onFail Callback function on error returns error object\n   */\n  write(\n    src: string,\n    content: string,\n    isBinary: Boolean,\n    onSuccess: (res: 'OK') => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Gets stats of given file\n   * @param src file/directory url\n   * @param onSuccess Callback function on success returns file/directory stats\n   * @param onFail Callback function on error returns error object\n   */\n  stats(\n    src: string,\n    onSuccess: (stats: Stats) => void,\n    onFail: (err: any) => void,\n  ): void;\n  /**\n   * Listens for file changes\n   * @param src File url\n   * @param listener Callback function on file change returns file stats\n   */\n  watchFile(\n    src: string,\n    listener: () => void,\n  ): {\n    unwatch: () => void;\n  };\n}\n\ndeclare var sdcard: SDcard;\n"
  },
  {
    "path": "src/plugins/sdcard/package.json",
    "content": "{\n  \"author\": \"\",\n  \"bundleDependencies\": false,\n  \"deprecated\": false,\n  \"description\": \"Using this plugin, cordova apps can check for external storages and write/modify files.\",\n  \"keywords\": [\n    \"cordova\",\n    \"plugin\",\n    \"sdcard\",\n    \"external storage\",\n    \"access external storage\",\n    \"android\"\n  ],\n  \"license\": \"ISC\",\n  \"main\": \"index.js\",\n  \"name\": \"cordova-plugin-sdcard\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"version\": \"1.1.0\"\n}"
  },
  {
    "path": "src/plugins/sdcard/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\n  xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-sdcard\" version=\"0.0.1\">\n  <name>cordova-plugin-sdcard</name>\n  <description>Choose folder to get read/write access to the document tree</description>\n  <license>Apache 2.0</license>\n  <keywords>cordova,plugin,Folder</keywords>\n\n  <js-module src=\"www/plugin.js\" name=\"sdcard\">\n    <clobbers target=\"window.sdcard\" />\n  </js-module>\n  <platform name=\"android\">\n    <config-file target=\"res/xml/config.xml\" parent=\"/*\">\n      <feature name=\"SDcard\">\n        <param name=\"android-package\" value=\"com.foxdebug.sdcard.SDcard\"/>\n      </feature>\n    </config-file>\n\n    <framework src=\"commons-io:commons-io:2.11.0\" />\n    <framework src=\"commons-codec:commons-codec:1.10\" />\n    <framework src=\"androidx.documentfile:documentfile:1.0.1\" />\n\n    <source-file src=\"src/android/SDcard.java\" target-dir=\"src/com/foxdebug/sdcard\"/>\n    <config-file target=\"AndroidManifest.xml\" parent=\"/manifest\"></config-file>\n  </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/sdcard/readme.md",
    "content": "# Write/modify content on external storage\r\n\r\nUsing this plugin, cordova apps can check for external storages and write/modify files.\r\n\r\n## Usage\r\n\r\n```ts\r\ninterface SDcard {\r\n\r\n  /**\r\n   * Copy file/directory to given destination\r\n   * @param src Source url\r\n   * @param dest Destination url\r\n   * @param onSuccess Callback function on success returns url of copied file/dir\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  copy(src: String, dest: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Creates new directory at given source url.\r\n   * @param src Source url\r\n   * @param dirName New directory name\r\n   * @param onSuccess Callback function on success returns url of new directory\r\n   * @param onFail callback function on error returns error object\r\n   */\r\n  createDir(src: String, dirName: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Creates new file at given source url.\r\n   * @param src Source url\r\n   * @param dirName New file name\r\n   * @param onSuccess Callback function on success returns url of new directory\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  createFile(src: String, fileName: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Deletes file/directory\r\n   * @param src Source url of file/directory\r\n   * @param onSuccess Callback function on success returns source url\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  delete(src: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Checks if given file/directory\r\n   * @param src File/Directory url\r\n   * @param onSuccess Callback function on success returns string \"TRUE\" or \"FALSE\"\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  exists(src: String, onSuccess: (exists: \"TRUE\" | \"FALSE\") => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Converts virtual URL to actual url\r\n   * @param src Virtual address returned by other methods\r\n   * @param onSuccess Callback function on success returns actual url\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  formatUri(src: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Gets actual url for relative path to src\r\n   * e.g. getPath(src, \"../path/to/file.txt\") => actual url\r\n   * @param src Directory url\r\n   * @param path Relative file/direcotry path\r\n   * @param onSuccess Callback function on success returns actual url\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  getPath(src: String, path: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Requests user for storage permission\r\n   * @param uuid UUID returned from listStorages method\r\n   * @param onSuccess Callback function on success returns url for the storage root\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  getStorageAccessPermission(uuid: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Lists all the storages\r\n   * @param onSuccess Callback function on success returns list of storages\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  listStorages(onSuccess: (storages: Array<Storage>) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Gets list of files/directory in the given directory\r\n   * @param src Directory url\r\n   * @param onSuccess Callback function on success returns list of files/directory\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  listDir(src: String, onSuccess: (list: Array<DirListItem>) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Move file/directory to given destination\r\n   * @param src Source url\r\n   * @param dest Destination url\r\n   * @param onSuccess Callback function on success returns url of moved file/dir\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  move(src: String, dest: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Opens file provider to select file\r\n   * @param onSuccess Callback function on success returns url of selected file\r\n   * @param onFail Callback function on error returns error object\r\n   * @param mimeType MimeType of file to be selected\r\n   */\r\n  openDocumentFile(onSuccess: (url: String) => void, onFail: (err: any) => void, mimeType: String): void;\r\n  /**\r\n   * Renames the given file/directory to given new name\r\n   * @param src Url of file/directory\r\n   * @param newname New name\r\n   * @param onSuccess Callback function on success returns url of renamed file\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  rename(src: String, newname: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Writes new content to the given file.\r\n   * @param src file url\r\n   * @param content new file content\r\n   * @param onSuccess Callback function on success returns \"OK\"\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  write(src: String, content: String, onSuccess: (res: \"OK\") => void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Gets stats of given file\r\n   * @param src file/directory url\r\n   * @param onSuccess Callback function on success returns file/directory stats\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  stats(src: String, onSuccess: (stats: Stats) => void, onFail: (err: any) => void): void;\r\n}\r\n\r\ninterface Storage {\r\n  /**\r\n   * Name of the storage\r\n   */\r\n  name: String;\r\n  /**\r\n   * UUID of the storage\r\n   */\r\n  uuid: String;\r\n}\r\n\r\ninterface DirListItem {\r\n  name: String;\r\n  mime: String;\r\n  isDirectory: Boolean;\r\n  isFile: Boolean;\r\n  uri: String;\r\n}\r\n\r\ninterface Stats {\r\n  canRead: boolean;\r\n  canWrite: boolean;\r\n  exists: boolean; //indicates if file can be found on device storage\r\n  isDirectory: boolean;\r\n  isFile: boolean;\r\n  isVirtual: boolean;\r\n  lastModified: number;\r\n  length: number;\r\n  name: string;\r\n  type: string;\r\n  uri: string;\r\n}\r\n```\r\n"
  },
  {
    "path": "src/plugins/sdcard/src/android/SDcard.java",
    "content": "package com.foxdebug.sdcard;\n\nimport android.app.Activity;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.FileObserver;\nimport android.os.storage.StorageManager;\nimport android.os.storage.StorageVolume;\nimport android.provider.DocumentsContract;\nimport android.provider.DocumentsContract.Document;\nimport android.text.TextUtils;\nimport android.util.Base64;\nimport android.util.Log;\nimport androidx.documentfile.provider.DocumentFile;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.net.URLConnection;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPlugin;\nimport org.apache.cordova.CordovaWebView;\nimport org.apache.cordova.PluginResult;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class SDcard extends CordovaPlugin {\n\n  private int mode;\n  private int SDK_INT = android.os.Build.VERSION.SDK_INT;\n  private int REQUEST_CODE;\n  private final int ACCESS_INTENT = 6000;\n  private final int DOCUMENT_TREE = 6001;\n  private final int OPEN_DOCUMENT = 6002;\n  private final int PICK_FROM_GALLERY = 6003;\n  private final String SEPARATOR = \"::\";\n  private StorageManager storageManager;\n  private Context context;\n  private Activity activity;\n  private ContentResolver contentResolver;\n  private DocumentFile originalRootFile;\n  private CallbackContext activityResultCallback;\n  private HashMap<String, MyFileObserver> fileObservers = new HashMap();\n\n  public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n    super.initialize(cordova, webView);\n    this.REQUEST_CODE = this.ACCESS_INTENT;\n    this.context = cordova.getContext();\n    this.activity = cordova.getActivity();\n    this.storageManager = (StorageManager) this.activity.getSystemService(\n        Context.STORAGE_SERVICE\n      );\n  }\n\n  public boolean execute(\n    String action,\n    JSONArray args,\n    CallbackContext callback\n  ) throws JSONException {\n    String arg1 = null, arg2 = null, arg3 = null, arg4 = null;\n    int argLen = args.length();\n\n    if (argLen > 0) arg1 = args.getString(0);\n\n    if (argLen > 1) arg2 = args.getString(1);\n\n    if (argLen > 2) arg3 = args.getString(2);\n\n    switch (action) {\n      case \"create directory\":\n        createDir(arg1, arg2, callback);\n        break;\n      case \"create file\":\n        createFile(arg1, arg2, callback);\n        break;\n      case \"open document file\":\n        openDocumentFile(arg1, callback);\n        break;\n      case \"get image\":\n        getImage(arg1, callback);\n        break;\n      case \"list volumes\":\n        getStorageVolumes(callback);\n        break;\n      case \"storage permission\":\n        getStorageAccess(arg1, callback);\n        break;\n      case \"read\":\n        readFile(arg1, callback);\n        break;\n      case \"write\":\n        writeFile(\n          formatUri(arg1),\n          args.optString(1),\n          args.optBoolean(2),\n          callback\n        );\n        break;\n      case \"rename\":\n        rename(arg1, arg2, callback);\n        break;\n      case \"delete\":\n        delete(formatUri(arg1), callback);\n        break;\n      case \"copy\":\n        copy(arg1, arg2, callback);\n        break;\n      case \"move\":\n        move(arg1, arg2, callback);\n        break;\n      case \"get path\":\n        getPath(formatUri(arg1), arg2, callback);\n        break;\n      case \"exists\":\n        exists(formatUri(arg1), callback);\n        break;\n      case \"format uri\":\n        callback.success(formatUri(arg1));\n        break;\n      case \"list directory\":\n        if (arg1.contains(SEPARATOR)) {\n          String splittedStr[] = arg1.split(SEPARATOR, 2);\n          arg1 = splittedStr[0];\n          arg2 = splittedStr[1];\n        }\n\n        listDir(arg1, arg2, callback);\n\n        break;\n      case \"stats\":\n        getStats(arg1, callback);\n        break;\n      case \"watch file\":\n        watchFile(arg1, arg2, callback);\n        break;\n      case \"unwatch file\":\n        unwatchFile(arg1);\n        break;\n      default:\n        return false;\n    }\n\n    return true;\n  }\n\n  private String formatUri(String filename) {\n    if (filename.contains(SEPARATOR)) {\n      String splittedStr[] = filename.split(SEPARATOR, 2);\n      String rootUri = splittedStr[0];\n      String docId = splittedStr[1];\n\n      Uri uri = getUri(rootUri, docId);\n\n      return uri.toString();\n    } else {\n      return filename;\n    }\n  }\n\n  private void watchFile(\n    final String fileUri,\n    final String id,\n    final CallbackContext listener\n  ) {\n    activity.runOnUiThread(\n      new Runnable() {\n        @Override\n        public void run() {\n          MyFileObserver observer;\n          Uri uri = Uri.parse(fileUri);\n          File file = new File(uri.getPath());\n\n          if (!file.exists()) {\n            listener.error(\"File not found\");\n            return;\n          }\n\n          if (SDK_INT >= 29) {\n            observer = new MyFileObserver(file, listener);\n          } else {\n            observer = new MyFileObserver(fileUri, listener);\n          }\n\n          observer.startObserving();\n          fileObservers.put(id, observer);\n        }\n      }\n    );\n  }\n\n  private void unwatchFile(String id) {\n    MyFileObserver observer = fileObservers.get(id);\n    if (observer == null) return;\n    observer.stopObserving();\n    fileObservers.remove(id);\n  }\n\n  public void openDocumentFile(String mimeType, CallbackContext callback) {\n    Intent intent = new Intent();\n    if (mimeType == null) mimeType = \"*/*\";\n    intent.setAction(Intent.ACTION_OPEN_DOCUMENT);\n    intent.addCategory(Intent.CATEGORY_OPENABLE);\n    intent.setType(mimeType);\n    activityResultCallback = callback;\n    cordova.startActivityForResult(this, intent, this.OPEN_DOCUMENT);\n  }\n\n  public void getImage(String mimeType, CallbackContext callback) {\n    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);\n    if (mimeType == null) mimeType = \"image/*\";\n\n    intent.setType(mimeType);\n    activityResultCallback = callback;\n    cordova.startActivityForResult(this, intent, this.PICK_FROM_GALLERY);\n  }\n\n  public void getStorageVolumes(CallbackContext callback) {\n    try {\n      JSONArray result = new JSONArray();\n\n      if (SDK_INT >= 24) {\n        for (StorageVolume volume : this.storageManager.getStorageVolumes()) {\n          String name = volume.getDescription(this.context);\n          String uuid = volume.getUuid();\n          JSONObject volumeData = new JSONObject();\n          if (name != null && uuid != null) {\n            volumeData.put(\"uuid\", uuid);\n            volumeData.put(\"name\", name);\n\n            if (SDK_INT >= 30) {\n              File file = volume.getDirectory();\n              String path = file.getAbsolutePath();\n              volumeData.put(\"path\", path);\n            }\n\n            result.put(volumeData);\n          }\n        }\n      } else {\n        File file = Environment.getExternalStorageDirectory();\n        if (\n          Environment.getExternalStorageState(file) == Environment.MEDIA_MOUNTED\n        ) {\n          String name = file.getName();\n          String path = file.getPath();\n          String absolutePath = file.getAbsolutePath();\n          JSONObject volumeData = new JSONObject();\n          volumeData.put(\"name\", name);\n          volumeData.put(\"path\", path);\n          volumeData.put(\"absolutePath\", absolutePath);\n          result.put(volumeData);\n        }\n      }\n\n      callback.success(result);\n    } catch (JSONException e) {\n      callback.error(e.toString());\n    }\n  }\n\n  public void getStorageAccess(String SDCardUUID, CallbackContext callback) {\n    Intent intent = null;\n\n    if (SDK_INT >= 24) {\n      StorageVolume sdCard = null;\n\n      for (StorageVolume volume : this.storageManager.getStorageVolumes()) {\n        String uuid = volume.getUuid();\n        if (uuid != null && uuid.equals(SDCardUUID)) {\n          sdCard = volume;\n        }\n      }\n\n      if (sdCard != null) {\n        if (SDK_INT < 29) {\n          intent = sdCard.createAccessIntent(null);\n        } else if (SDK_INT >= 29) {\n          intent = sdCard.createOpenDocumentTreeIntent();\n        }\n      }\n    }\n\n    if (intent == null) {\n      REQUEST_CODE = DOCUMENT_TREE;\n      intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);\n    }\n\n    activityResultCallback = callback;\n    cordova.startActivityForResult(this, intent, REQUEST_CODE);\n  }\n\n  public void onActivityResult(int requestCode, int resultCode, Intent data) {\n    super.onActivityResult(requestCode, resultCode, data);\n\n    if (activityResultCallback == null) {\n      Log.e(\"SDcard\", \"activityResultCallback is null\");\n      return;\n    }\n\n    if (data == null) return;\n\n    if (resultCode == Activity.RESULT_CANCELED) {\n      activityResultCallback.error(\"Operation cancelled\");\n      return;\n    }\n\n    if (requestCode == PICK_FROM_GALLERY) {\n      if (resultCode == Activity.RESULT_OK) {\n        Uri uri = data.getData();\n        if (uri == null) {\n          activityResultCallback.error(\"No file selected\");\n        } else {\n          try {\n            takePermission(uri);\n            activityResultCallback.success(uri.toString());\n          } catch (Exception e) {\n            activityResultCallback.error(\n              \"Error taking permission: \" + e.getMessage()\n            );\n          }\n        }\n        //activityResultCallback.success(uri.toString());\n      } else {\n        activityResultCallback.error(\"Operation cancelled\");\n      }\n      return;\n    }\n\n    if (requestCode == OPEN_DOCUMENT) {\n      if (resultCode == Activity.RESULT_OK) {\n        try {\n          Uri uri = data.getData();\n\n          if (uri == null) {\n            activityResultCallback.error(\"No file selected\");\n            return;\n          }\n\n          takePermission(uri);\n          DocumentFile file = DocumentFile.fromSingleUri(context, uri);\n          JSONObject res = new JSONObject();\n\n          res.put(\"length\", file.length());\n          res.put(\"type\", file.getType());\n          res.put(\"filename\", file.getName());\n          res.put(\"canWrite\", canWrite(file.getUri()));\n          res.put(\"uri\", uri.toString());\n          activityResultCallback.success(res);\n        } catch (JSONException e) {\n          activityResultCallback.error(e.toString());\n        }\n      }\n\n      return;\n    }\n\n    if (requestCode == DOCUMENT_TREE || requestCode == ACCESS_INTENT) {\n      if (\n        requestCode == ACCESS_INTENT && resultCode == Activity.RESULT_CANCELED\n      ) {\n        activityResultCallback.error(\"Canceled\");\n        return;\n      }\n\n      try {\n        Uri uri = data.getData();\n        if (uri == null) {\n          activityResultCallback.error(\"Empty uri\");\n          return;\n        }\n\n        takePermission(uri);\n        DocumentFile file = DocumentFile.fromTreeUri(context, uri);\n        if (file != null && file.canWrite()) {\n          activityResultCallback.success(uri.toString());\n        } else {\n          activityResultCallback.error(\n            \"No write permission: \" + uri.toString()\n          );\n        }\n      } catch (Exception error) {\n        activityResultCallback.error(error.toString());\n      }\n\n      return;\n    }\n  }\n\n  private void readFile(String filename, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              Uri uri = Uri.parse(filename);\n              InputStream is = context\n                .getContentResolver()\n                .openInputStream(uri);\n\n              if (is == null) {\n                callback.error(\"File not found\");\n                return;\n              }\n              ByteArrayOutputStream outputStream = new ByteArrayOutputStream();\n              final int bufferSize = 1024;\n              byte[] buffer = new byte[bufferSize];\n              int bytesRead = 0;\n              while ((bytesRead = is.read(buffer, 0, bufferSize)) != -1) {\n                outputStream.write(buffer, 0, bytesRead);\n              }\n              is.close();\n              callback.success(outputStream.toByteArray());\n            } catch (Exception e) {\n              callback.error(e.toString());\n            }\n          }\n        }\n      );\n  }\n\n  private void writeFile(\n    final String filename,\n    final String content,\n    final Boolean isArrayBuffer,\n    final CallbackContext callback\n  ) {\n    final Context context = this.context;\n\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              DocumentFile file = getFile(filename);\n              if (file == null) {\n                callback.error(\"File not found.\");\n                return;\n              }\n              if (canWrite(file.getUri())) {\n                OutputStream op = context\n                  .getContentResolver()\n                  .openOutputStream(file.getUri(), \"rwt\");\n\n                PrintWriter pw = new PrintWriter(op, true);\n\n                if (isArrayBuffer) {\n                  byte[] bytes = Base64.decode(content, Base64.DEFAULT);\n                  // write bytes to file\n                  op.write(bytes);\n                } else {\n                  pw.print(content);\n                }\n\n                pw.flush();\n                pw.close();\n                op.close();\n                callback.success(\"OK\");\n              } else {\n                callback.error(\"No write permission\");\n              }\n            } catch (Exception e) {\n              callback.error(e.toString());\n            }\n          }\n        }\n      );\n  }\n\n  private void createDir(String parent, String name, CallbackContext callback) {\n    create(parent, name, Document.MIME_TYPE_DIR, callback);\n  }\n\n  private void createFile(\n    String parent,\n    String name,\n    CallbackContext callback\n  ) {\n    String mimeType = URLConnection.guessContentTypeFromName(name);\n    String ext = FilenameUtils.getExtension(name);\n\n    if (mimeType == null && ext != null) mimeType = \"text/\" + ext;\n    else mimeType = \"text/plain\";\n\n    create(parent, name, mimeType, callback);\n  }\n\n  private void create(\n    String parent,\n    String name,\n    String mimeType,\n    CallbackContext callback\n  ) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String srcUri = null, docId = null;\n              Uri parentUri = null;\n\n              if (parent.contains(SEPARATOR)) {\n                String splittedStr[] = parent.split(SEPARATOR, 2);\n                srcUri = splittedStr[0];\n                docId = splittedStr[1];\n                parentUri = getUri(srcUri, docId);\n              } else {\n                srcUri = parent;\n                parentUri = Uri.parse(srcUri);\n                docId = DocumentsContract.getTreeDocumentId(parentUri);\n                parentUri = DocumentsContract.buildDocumentUriUsingTree(\n                  parentUri,\n                  docId\n                );\n              }\n\n              ContentResolver contentResolver = context.getContentResolver();\n              Uri newDocumentUri = DocumentsContract.createDocument(\n                contentResolver,\n                parentUri,\n                mimeType,\n                name\n              );\n              DocumentFile file = DocumentFile.fromTreeUri(\n                context,\n                newDocumentUri\n              );\n              Log.i(\"SDcard\", \"Uri: \" + newDocumentUri.toString());\n              if (!name.equals(file.getName()) && file.renameTo(name)) {\n                newDocumentUri = file.getUri();\n              }\n\n              docId = DocumentsContract.getDocumentId(newDocumentUri);\n              if (newDocumentUri != null) {\n                callback.success(srcUri + SEPARATOR + docId);\n              } else {\n                callback.error(\"Unable to create \" + parent);\n              }\n            } catch (Exception e) {\n              Log.e(\"CREATE_FILE\", \"Unable to create file\", e);\n              callback.error(e.toString());\n            }\n          }\n        }\n      );\n  }\n\n  private void rename(\n    String filename,\n    String newFile,\n    CallbackContext callback\n  ) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            String srcUri = null, docId = null;\n            Uri fileUri = null;\n            if (filename.contains(SEPARATOR)) {\n              String splittedStr[] = filename.split(SEPARATOR, 2);\n              srcUri = splittedStr[0];\n              docId = splittedStr[1];\n              fileUri = getUri(srcUri, docId);\n            } else {\n              srcUri = filename;\n              fileUri = Uri.parse(filename);\n            }\n\n            try {\n              DocumentFile file = DocumentFile.fromTreeUri(context, fileUri);\n              // If only case change, OS adds '(<number>)' as suffix, to avoid that we need to rename to a temporary name first\n              if (newFile.equalsIgnoreCase(file.getName())) {\n                file.renameTo(newFile + \"_temp\");\n              }\n\n              if (file.renameTo(newFile)) {\n                String name = file.getName();\n                docId = DocumentsContract.getDocumentId(file.getUri());\n                callback.success(srcUri + SEPARATOR + docId);\n                return;\n              }\n\n              callback.error(\"Unable to rename: \" + filename);\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  private void delete(String filename, CallbackContext callback) {\n    final ContentResolver contentResolver = context.getContentResolver();\n\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            Uri fileUri = Uri.parse(filename);\n\n            try {\n              boolean fileDeleted = DocumentsContract.deleteDocument(\n                contentResolver,\n                fileUri\n              );\n\n              if (fileDeleted) {\n                callback.success(filename);\n              } else {\n                callback.error(\"Unable to delete file \" + filename);\n              }\n            } catch (FileNotFoundException e) {\n              callback.error(e.toString());\n            }\n          }\n        }\n      );\n  }\n\n  private void move(String src, String dest, final CallbackContext callback) {\n    final ContentResolver contentResolver = this.context.getContentResolver();\n    final String splittedStr[] = src.split(SEPARATOR, 2);\n    final String rootUri = splittedStr[0];\n    final String srcId = splittedStr[1];\n    final String destId = dest.split(SEPARATOR, 2)[1];\n\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          @Override\n          public void run() {\n            try {\n              Uri newUri = copy(rootUri, srcId, destId);\n              if (newUri == null) callback.error(\"Unable to copy \" + src);\n              else {\n                DocumentsContract.deleteDocument(\n                  contentResolver,\n                  getUri(rootUri, srcId)\n                );\n                callback.success(\n                  rootUri + SEPARATOR + DocumentsContract.getDocumentId(newUri)\n                );\n              }\n            } catch (Exception e) {\n              callback.error(e.toString());\n            }\n          }\n        }\n      );\n  }\n\n  private void copy(String src, String dest, final CallbackContext callback) {\n    final String splittedStr[] = src.split(SEPARATOR, 2);\n    final String srcUri = splittedStr[0];\n    final String srcId = splittedStr[1];\n    final String destId = dest.split(SEPARATOR, 2)[1];\n\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          @Override\n          public void run() {\n            try {\n              Uri newUri = copy(srcUri, srcId, destId);\n              if (newUri == null) {\n                callback.error(\"Unable to copy \" + src);\n              } else {\n                callback.success(\n                  srcUri + SEPARATOR + DocumentsContract.getDocumentId(newUri)\n                );\n              }\n            } catch (Exception e) {\n              callback.error(e.toString());\n            }\n          }\n        }\n      );\n  }\n\n  private Uri copy(String root, String srcId, String destId)\n    throws IOException, FileNotFoundException {\n    Uri srcUri = getUri(root, srcId);\n    Uri destUri = getUri(root, destId);\n    DocumentFile src = getFile(srcUri);\n    DocumentFile dest = getFile(destUri);\n    ContentResolver contentResolver = context.getContentResolver();\n\n    if (src.isFile()) {\n      Uri newUri = copyFile(src, dest);\n      if (newUri == null) return null;\n      else return newUri;\n    } else {\n      destUri = DocumentsContract.createDocument(\n        contentResolver,\n        destUri,\n        Document.MIME_TYPE_DIR,\n        src.getName()\n      );\n      destId = DocumentsContract.getDocumentId(destUri);\n\n      Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(\n        Uri.parse(root),\n        srcId\n      );\n      Cursor c = contentResolver.query(\n        childrenUri,\n        new String[] { Document.COLUMN_DOCUMENT_ID },\n        null,\n        null,\n        null\n      );\n      if (c != null) {\n        while (c.moveToNext()) {\n          String docId = c.getString(0);\n          copy(root, docId, destId);\n        }\n        c.close();\n        return destUri;\n      } else {\n        DocumentsContract.deleteDocument(contentResolver, destUri);\n        return null;\n      }\n    }\n  }\n\n  private Uri copyFile(DocumentFile src, DocumentFile dest)\n    throws IOException, FileNotFoundException {\n    ContentResolver contentResolver = this.context.getContentResolver();\n\n    Uri newFileUri = DocumentsContract.createDocument(\n      contentResolver,\n      dest.getUri(),\n      src.getType(),\n      src.getName()\n    );\n    DocumentFile newFile = getFile(newFileUri);\n    InputStream is = contentResolver.openInputStream(src.getUri());\n    OutputStream os = contentResolver.openOutputStream(newFile.getUri(), \"rwt\");\n\n    if (is == null || os == null) {\n      DocumentsContract.deleteDocument(contentResolver, newFileUri);\n      return null;\n    }\n\n    IOUtils.copy(is, os);\n\n    is.close();\n    os.close();\n\n    if (src.length() == newFile.length()) return newFile.getUri();\n    else {\n      DocumentsContract.deleteDocument(contentResolver, newFileUri);\n      return null;\n    }\n  }\n\n  private void listDir(String src, String parentId, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            Uri srcUri = Uri.parse(src);\n            ContentResolver contentResolver = context.getContentResolver();\n            String parentDocId = parentId;\n\n            if (parentDocId == null) {\n              parentDocId = DocumentsContract.getTreeDocumentId(srcUri);\n            }\n\n            Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(\n              srcUri,\n              parentDocId\n            );\n\n            JSONArray result = new JSONArray();\n            Cursor cursor = null;\n\n            try {\n              cursor = contentResolver.query(\n                childrenUri,\n                new String[] {\n                  Document.COLUMN_DOCUMENT_ID,\n                  Document.COLUMN_DISPLAY_NAME,\n                  Document.COLUMN_MIME_TYPE,\n                },\n                null,\n                null,\n                null\n              );\n            } catch (\n              NullPointerException\n              | SecurityException\n              | IllegalArgumentException\n              | Error e\n            ) {\n              Log.d(\"sdCard\", \"lsDir: \" + src);\n              Log.e(\"sdCard\", \"lsDir\", e);\n              callback.error(\"Cannot read directory.\");\n              return;\n            }\n\n            if (cursor == null) {\n              callback.error(\"Cannot read directory.\");\n              return;\n            }\n\n            try {\n              while (cursor.moveToNext()) {\n                JSONObject fileData = new JSONObject();\n                String docId = cursor.getString(0);\n                String name = cursor.getString(1);\n                String mime = cursor.getString(2);\n                boolean isDirectory = isDirectory(mime);\n\n                fileData.put(\"name\", name);\n                fileData.put(\"mime\", mime);\n                fileData.put(\"isDirectory\", isDirectory);\n                fileData.put(\"isFile\", !isDirectory);\n                fileData.put(\"uri\", src + SEPARATOR + docId); // TODO: Deprecate in future\n                fileData.put(\"url\", src + SEPARATOR + docId);\n                result.put(fileData);\n              }\n\n              callback.success(result);\n            } catch (JSONException e) {\n              callback.error(e.toString());\n            } finally {\n              if (cursor != null) {\n                try {\n                  cursor.close();\n                } catch (RuntimeException re) {\n                  throw re;\n                } catch (Exception ignore) {\n                  // ignore exception\n                }\n              }\n            }\n          }\n        }\n      );\n  }\n\n  private boolean isDirectory(String mimeType) {\n    return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);\n  }\n\n  private void getStats(String filename, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            String fileUri = formatUri(filename);\n\n            try {\n              DocumentFile file = getFile(fileUri);\n\n              JSONObject result = new JSONObject();\n              result.put(\"exists\", file.exists());\n              result.put(\"canRead\", file.canRead());\n              result.put(\"canWrite\", canWrite(file.getUri()));\n              result.put(\"name\", file.getName());\n              result.put(\"length\", file.length());\n              result.put(\"type\", file.getType());\n              result.put(\"isFile\", file.isFile());\n              result.put(\"isDirectory\", file.isDirectory());\n              result.put(\"isVirtual\", file.isVirtual());\n              result.put(\"lastModified\", file.lastModified());\n              result.put(\"url\", file.getUri().toString());\n\n              callback.success(result);\n            } catch (Exception e) {\n              callback.error(e.getMessage());\n            }\n          }\n        }\n      );\n  }\n\n  private Uri getUri(String src, String docId) {\n    Uri srcUri = Uri.parse(src);\n    String srcId = DocumentsContract.getTreeDocumentId(srcUri);\n    srcUri = DocumentsContract.buildDocumentUriUsingTree(srcUri, srcId);\n    return DocumentsContract.buildDocumentUriUsingTree(srcUri, docId);\n  }\n\n  private void exists(String path, CallbackContext callback) {\n    DocumentFile file = DocumentFile.fromSingleUri(context, Uri.parse(path));\n\n    if (file == null) {\n      callback.error(\"Unable to get file\");\n    } else {\n      if (file.exists()) {\n        callback.success(\"TRUE\");\n      } else {\n        callback.success(\"FALSE\");\n      }\n    }\n  }\n\n  private void error(String err, CallbackContext callback) {\n    callback.error(\"ERROR: \" + err);\n  }\n\n  private void getPath(String uriString, String src, CallbackContext callback) {\n    try {\n      DocumentFile file = geRelativeDocumentFile(uriString, src);\n\n      if (file == null) {\n        callback.error(\"Unable to get file\");\n      } else {\n        Uri uri = file.getUri();\n        String path = uri.getPath();\n\n        if (path != null) {\n          callback.success(uri.toString());\n        } else {\n          callback.error(\"Unable to get path\");\n        }\n      }\n    } catch (Exception e) {\n      callback.error(e.getMessage());\n    }\n  }\n\n  private DocumentFile geRelativeDocumentFile(String uri, String filename) {\n    List<String> paths = new ArrayList<String>();\n    DocumentFile file = null;\n\n    file = DocumentFile.fromTreeUri(context, Uri.parse(uri));\n    if (!canWrite(file.getUri())) {\n      throw new RuntimeException(\"Cannot write file\");\n    }\n\n    paths.addAll(Arrays.asList(filename.split(\"/\")));\n\n    while (paths.size() > 0) {\n      String path = paths.remove(0);\n      filename = TextUtils.join(\"/\", paths);\n\n      if (!path.equals(\"\")) {\n        file = file.findFile(path);\n\n        if (file == null) return null;\n      }\n    }\n\n    return file;\n  }\n\n  private DocumentFile getFile(Uri uri) {\n    return getFile(uri.toString());\n  }\n\n  private DocumentFile getFile(String filePath) {\n    Uri fileUri = Uri.parse(filePath);\n    DocumentFile documentFile = null;\n\n    if (filePath.matches(\"file:///(.*)\")) {\n      File file = new File(fileUri.getPath());\n      documentFile = DocumentFile.fromFile(file);\n    } else {\n      documentFile = DocumentFile.fromSingleUri(context, fileUri);\n    }\n\n    return documentFile;\n  }\n\n  private void takePermission(Uri uri) {\n    contentResolver = context.getContentResolver();\n    contentResolver.takePersistableUriPermission(\n      uri,\n      Intent.FLAG_GRANT_WRITE_URI_PERMISSION |\n      Intent.FLAG_GRANT_READ_URI_PERMISSION\n    );\n  }\n\n  public boolean canWrite(Uri uri) {\n    boolean canWrite = false;\n    try {\n      // if the file is not writable this throws a SecurityException\n      OutputStream os = context\n        .getContentResolver()\n        .openOutputStream(uri, \"wa\");\n\n      if (os == null) return false;\n\n      os.close(); // we don't actually want to write anything, so we close immediately\n      canWrite = true;\n    } catch (\n      SecurityException | IllegalArgumentException | IOException ignored\n    ) {\n      // if we don't have write-permission or the file doesn't exist, canWrite can stay on false\n    }\n    return canWrite;\n  }\n}\n\nclass MyFileObserver extends FileObserver {\n\n  private CallbackContext listener;\n  private static final int mask =\n    (FileObserver.DELETE_SELF | FileObserver.MODIFY | FileObserver.MOVE_SELF);\n\n  public MyFileObserver(String path, CallbackContext listener) {\n    super(path, mask);\n    this.listener = listener;\n    Log.d(\"MyFileObserver\", \"MyFileObserver: \" + path);\n  }\n\n  public MyFileObserver(File file, CallbackContext listener) {\n    super(file, mask);\n    this.listener = listener;\n    Log.d(\"MyFileObserver\", \"MyFileObserver: \" + file.getAbsolutePath());\n  }\n\n  @Override\n  public void onEvent(int event, String path) {\n    Log.d(\"MyFileObserver\", \"onEvent: \" + event);\n    PluginResult result = new PluginResult(PluginResult.Status.OK);\n    result.setKeepCallback(true);\n    listener.sendPluginResult(result);\n  }\n\n  public void startObserving() {\n    startWatching();\n  }\n\n  public void stopObserving() {\n    stopWatching();\n  }\n}\n"
  },
  {
    "path": "src/plugins/sdcard/www/plugin.js",
    "content": "module.exports = {\n  copy: function (srcPathname, destPathname, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'copy', [srcPathname, destPathname]);\n  },\n  createDir: function (pathname, dir, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'create directory', [pathname, dir]);\n  },\n  createFile: function (pathname, file, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'create file', [pathname, file]);\n  },\n  delete: function (pathname, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'delete', [pathname]);\n  },\n  exists: function (pathName, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'exists', [pathName]);\n  },\n  formatUri: function (pathName, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'format uri', [pathName]);\n  },\n  getPath: function (uri, filename, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'get path', [uri, filename]);\n  },\n  getStorageAccessPermission: function (uuid, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'storage permission', [uuid]);\n  },\n  listStorages: function (onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'list volumes', []);\n  },\n  listDir: function (src, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'list directory', [src]);\n  },\n  move: function (srcPathname, destPathname, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'move', [srcPathname, destPathname]);\n  },\n  openDocumentFile: function (onSuccess, onFail, mimeType) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'open document file', mimeType ? [mimeType] : []);\n  },\n  getImage: function (onSuccess, onFail, mimeType) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'get image', mimeType ? [mimeType] : []);\n  },\n  rename: function (pathname, newFilename, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'rename', [pathname, newFilename]);\n  },\n  read: function (filename, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'read', [filename]);\n  },\n  write: function (filename, content, onSuccess, onFail) {\n    var _isBuffer = content instanceof ArrayBuffer;\n    cordova.exec(onSuccess, onFail, 'SDcard', 'write', [filename, content, _isBuffer]);\n  },\n  stats: function (filename, onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'stats', [filename]);\n  },\n  watchFile: function (filename, listener, onFail) {\n    var id = parseInt(Date.now() + Math.random() * 1000000) + '';\n    cordova.exec(listener, onFail, 'SDcard', 'watch file', [filename, id]);\n    return {\n      unwatch: function () {\n        cordova.exec(null, null, 'SDcard', 'unwatch file', [id]);\n      }\n    };\n  },\n  listEncodings: function (onSuccess, onFail) {\n    cordova.exec(onSuccess, onFail, 'SDcard', 'list encodings', []);\n  }\n};"
  },
  {
    "path": "src/plugins/server/index.d.ts",
    "content": "interface Server{\n  stop(onSuccess: () => void, onError: (error: any) => void): void;\n  send(id: string, data: any, onSuccess: () => void, onError: (error: any) => void): void;\n  port: number;\n}\n\ndeclare var CreateServer: (port: number, onSuccess: (msg: any) => void, onError: (err: any) => void) => Server;"
  },
  {
    "path": "src/plugins/server/package.json",
    "content": "{\r\n  \"name\": \"cordova-plugin-server\",\r\n  \"version\": \"1.0.0\",\r\n  \"main\": \"index.js\",\r\n  \"license\": \"MIT\"\r\n}"
  },
  {
    "path": "src/plugins/server/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n<plugin xmlns=\"http://cordova.apache.org/ns/plugins/1.0\"\r\n  xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-server\" version=\"1.1.1\">\r\n  <engines>\r\n    <engine name=\"cordova\" version=\">=6.5.0\" />\r\n  </engines>\r\n\r\n  <name>Webserver for Cordova Apps</name>\r\n  <keywords>HTTP server, webserver</keywords>\r\n\r\n  <js-module src=\"www/server.js\" name=\"CreateServer\">\r\n    <clobbers target=\"window.CreateServer\" />\r\n  </js-module>\r\n\r\n  <platform name=\"android\">\r\n    <framework src=\"org.nanohttpd:nanohttpd:+\" />\r\n    <framework src=\"androidx.documentfile:documentfile:1.0.1\" />\r\n    <source-file src=\"src/android/com/foxdebug/server/Server.java\" target-dir=\"src/com/foxdebug/server\"/>\r\n    <source-file src=\"src/android/com/foxdebug/server/NanoHTTPDWebserver.java\" target-dir=\"src/com/foxdebug/server\"/>\r\n    <config-file target=\"config.xml\" parent=\"/*\">\r\n      <feature name=\"Server\">\r\n        <param name=\"android-package\" value=\"com.foxdebug.server.Server\"/>\r\n      </feature>\r\n    </config-file>\r\n  </platform>\r\n</plugin>\r\n"
  },
  {
    "path": "src/plugins/server/src/android/com/foxdebug/server/NanoHTTPDWebserver.java",
    "content": "package com.foxdebug.server;\r\n\r\nimport android.content.ContentResolver;\r\nimport android.content.Context;\r\nimport android.net.Uri;\r\nimport android.util.Log;\r\nimport androidx.documentfile.provider.DocumentFile;\r\nimport fi.iki.elonen.NanoHTTPD;\r\nimport java.io.File;\r\nimport java.io.FileNotFoundException;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.lang.reflect.Method;\r\nimport java.net.URLConnection;\r\nimport java.util.HashMap;\r\nimport java.util.Iterator;\r\nimport java.util.Map;\r\nimport java.util.UUID;\r\nimport org.apache.cordova.CallbackContext;\r\nimport org.apache.cordova.PluginResult;\r\nimport org.json.JSONException;\r\nimport org.json.JSONObject;\r\n\r\npublic class NanoHTTPDWebserver extends NanoHTTPD {\r\n\r\n  public HashMap<String, Object> responses;\r\n  public CallbackContext onRequestCallbackContext;\r\n  Context context;\r\n\r\n  public NanoHTTPDWebserver(int port, Context context) {\r\n    super(port);\r\n    this.context = context;\r\n    this.responses = new HashMap<String, Object>();\r\n  }\r\n\r\n  private String getBodyText(IHTTPSession session) {\r\n    Map<String, String> files = new HashMap<String, String>();\r\n    Method method = session.getMethod();\r\n    if (Method.PUT.equals(method) || Method.POST.equals(method)) {\r\n      try {\r\n        session.parseBody(files);\r\n      } catch (IOException ioe) {\r\n        return \"{}\";\r\n      } catch (ResponseException re) {\r\n        return \"{}\";\r\n      }\r\n    }\r\n    // get the POST body\r\n    return files.get(\"postData\");\r\n  }\r\n\r\n  /**\r\n   * Create a request object\r\n   * <p>\r\n   * [ \"requestId\": requestUUID, \" body\": request.jsonObject ?? \"\", \" headers\":\r\n   * request.headers, \" method\": request.method, \" path\": request.url.path, \"\r\n   * query\": request.url.query ?? \"\" ]\r\n   *\r\n   * @param session\r\n   * @return\r\n   */\r\n  private JSONObject createJSONRequest(String requestId, IHTTPSession session)\r\n    throws JSONException {\r\n    JSONObject jsonRequest = new JSONObject();\r\n    jsonRequest.put(\"requestId\", requestId);\r\n    jsonRequest.put(\"body\", this.getBodyText(session));\r\n    jsonRequest.put(\"headers\", session.getHeaders());\r\n    jsonRequest.put(\"method\", session.getMethod());\r\n    jsonRequest.put(\"path\", session.getUri());\r\n    jsonRequest.put(\"query\", session.getQueryParameterString());\r\n    return jsonRequest;\r\n  }\r\n\r\n  private String getContentType(JSONObject responseObject)\r\n    throws JSONException {\r\n    if (\r\n      responseObject.has(\"headers\") &&\r\n      responseObject.getJSONObject(\"headers\").has(\"Content-Type\")\r\n    ) {\r\n      return responseObject.getJSONObject(\"headers\").getString(\"Content-Type\");\r\n    } else {\r\n      return \"text/plain\";\r\n    }\r\n  }\r\n\r\n  private Response newFixedFileResponse(DocumentFile file, String mime)\r\n    throws FileNotFoundException, IOException {\r\n    Response res;\r\n    res =\r\n      newFixedLengthResponse(\r\n        Response.Status.OK,\r\n        mime,\r\n        getInputStream(file),\r\n        (int) file.length()\r\n      );\r\n    res.addHeader(\"Accept-Ranges\", \"bytes\");\r\n    return res;\r\n  }\r\n\r\n  Response serveFile(\r\n    Map<String, String> header,\r\n    DocumentFile file,\r\n    String mime\r\n  ) {\r\n    Response res;\r\n    try {\r\n      // Calculate etag\r\n      long fileLen = file.length();\r\n      String path = file.getUri().toString();\r\n      String etag = Integer.toHexString(\r\n        (path + file.lastModified() + \"\" + fileLen).hashCode()\r\n      );\r\n\r\n      // Support (simple) skipping:\r\n      long startFrom = 0;\r\n      long endAt = -1;\r\n      String range = header.get(\"range\");\r\n      if (range != null) {\r\n\t        if (range.startsWith(\"bytes=\")) {\n\t          range = range.substring(\"bytes=\".length());\n\t          int minus = range.indexOf('-');\n\t          try {\n\t            if (minus > 0) {\n\t              startFrom = Long.parseLong(range.substring(0, minus));\n\t              endAt = Long.parseLong(range.substring(minus + 1));\n\t            }\n\t          } catch (NumberFormatException error) {\n\t            Log.w(\"NanoHTTPDWebserver\", \"Invalid range header: \" + range, error);\n\t          }\n\t        }\n\t      }\n\r\n      // get if-range header. If present, it must match etag or else we\r\n      // should ignore the range request\r\n      String ifRange = header.get(\"if-range\");\r\n      boolean headerIfRangeMissingOrMatching =\r\n        (ifRange == null || etag.equals(ifRange));\r\n\r\n      String ifNoneMatch = header.get(\"if-none-match\");\r\n      boolean headerIfNoneMatchPresentAndMatching =\r\n        ifNoneMatch != null &&\r\n        (\"*\".equals(ifNoneMatch) || ifNoneMatch.equals(etag));\r\n\r\n      if (\r\n        headerIfRangeMissingOrMatching &&\r\n        range != null &&\r\n        startFrom >= 0 &&\r\n        startFrom < fileLen\r\n      ) {\r\n        // range request that matches current etag\r\n        // and the startFrom of the range is satisfiable\r\n        if (headerIfNoneMatchPresentAndMatching) {\r\n          // range request that matches current etag\r\n          // and the startFrom of the range is satisfiable\r\n          // would return range from file\r\n          // respond with not-modified\r\n          res = newFixedLengthResponse(Response.Status.NOT_MODIFIED, mime, \"\");\r\n          res.addHeader(\"ETag\", etag);\r\n        } else {\r\n          if (endAt < 0) {\r\n            endAt = fileLen - 1;\r\n          }\r\n          long newLen = endAt - startFrom + 1;\r\n          if (newLen < 0) {\r\n            newLen = 0;\r\n          }\r\n\r\n          InputStream is = getInputStream(file);\r\n          is.skip(startFrom);\r\n\r\n          res =\r\n            newFixedLengthResponse(\r\n              Response.Status.PARTIAL_CONTENT,\r\n              mime,\r\n              is,\r\n              newLen\r\n            );\r\n          res.addHeader(\"Accept-Ranges\", \"bytes\");\r\n          res.addHeader(\"Content-Length\", \"\" + newLen);\r\n\r\n          String contentRange =\r\n            \"bytes \" + startFrom + \"-\" + endAt + \"/\" + fileLen;\r\n          res.addHeader(\"Content-Range\", contentRange);\r\n          res.addHeader(\"ETag\", etag);\r\n        }\r\n      } else {\r\n        if (\r\n          headerIfRangeMissingOrMatching &&\r\n          range != null &&\r\n          startFrom >= fileLen\r\n        ) {\r\n          // return the size of the file\r\n          // 4xx responses are not trumped by if-none-match\r\n          res =\r\n            newFixedLengthResponse(\r\n              Response.Status.RANGE_NOT_SATISFIABLE,\r\n              NanoHTTPD.MIME_PLAINTEXT,\r\n              \"\"\r\n            );\r\n          res.addHeader(\"Content-Range\", \"bytes */\" + fileLen);\r\n          res.addHeader(\"ETag\", etag);\r\n        } else if (range == null && headerIfNoneMatchPresentAndMatching) {\r\n          // full-file-fetch request\r\n          // would return entire file\r\n          // respond with not-modified\r\n          res = newFixedLengthResponse(Response.Status.NOT_MODIFIED, mime, \"\");\r\n          res.addHeader(\"ETag\", etag);\r\n        } else if (\r\n          !headerIfRangeMissingOrMatching && headerIfNoneMatchPresentAndMatching\r\n        ) {\r\n          // range request that doesn't match current etag\r\n          // would return entire (different) file\r\n          // respond with not-modified\r\n\r\n          res = newFixedLengthResponse(Response.Status.NOT_MODIFIED, mime, \"\");\r\n          res.addHeader(\"ETag\", etag);\r\n        } else {\r\n          // supply the file\r\n          res = newFixedFileResponse(file, mime);\r\n          res.addHeader(\"Content-Length\", \"\" + fileLen);\r\n          res.addHeader(\"ETag\", etag);\r\n        }\r\n      }\r\n    } catch (Exception e) {\r\n      Log.d(\"ServeFileError\", e.getMessage());\r\n      String type = e.getClass().getName();\r\n      if (type.equals(\"FileNotFoundException\")) res =\r\n        newFixedLengthResponse(\r\n          Response.Status.NOT_FOUND,\r\n          NanoHTTPD.MIME_PLAINTEXT,\r\n          e.getMessage()\r\n        ); else res =\r\n        newFixedLengthResponse(\r\n          Response.Status.FORBIDDEN,\r\n          NanoHTTPD.MIME_PLAINTEXT,\r\n          e.getMessage()\r\n        );\r\n    }\r\n\r\n    return res;\r\n  }\r\n\r\n  @Override\r\n  public Response serve(IHTTPSession session) {\r\n    String requestUUID = UUID.randomUUID().toString();\r\n\r\n    PluginResult pluginResult = null;\r\n    try {\r\n      pluginResult =\r\n        new PluginResult(\r\n          PluginResult.Status.OK,\r\n          this.createJSONRequest(requestUUID, session)\r\n        );\r\n    } catch (JSONException e) {\r\n      e.printStackTrace();\r\n    }\r\n    pluginResult.setKeepCallback(true);\r\n    this.onRequestCallbackContext.sendPluginResult(pluginResult);\r\n\r\n    while (!this.responses.containsKey(requestUUID)) {\r\n      try {\r\n        Thread.sleep(1);\r\n      } catch (InterruptedException e) {\r\n        e.printStackTrace();\r\n      }\r\n    }\r\n\r\n    JSONObject responseObject = (JSONObject) this.responses.get(requestUUID);\r\n    Response response = null;\r\n\r\n    if (responseObject.has(\"path\")) {\r\n      try {\r\n        String path = responseObject.getString(\"path\");\r\n        DocumentFile file = getFile(path);\r\n        String mimeType = URLConnection.guessContentTypeFromName(path);\r\n        Response res = serveFile(session.getHeaders(), file, mimeType);\r\n        JSONObject headers = getJSONObject(responseObject, \"headers\");\r\n        // JSONObject headers = responseObject.getJSONObject(\"headers\");\r\n        if (headers != null) {\r\n          Iterator<String> keys = headers.keys();\r\n          while (keys.hasNext()) {\r\n            String key = (String) keys.next();\r\n            res.addHeader(\r\n              key,\r\n              responseObject.getJSONObject(\"headers\").getString(key)\r\n            );\r\n          }\r\n        }\r\n        return res;\r\n      } catch (JSONException e) {\r\n        e.printStackTrace();\r\n      }\r\n      return response;\r\n    } else {\r\n      try {\r\n        response =\r\n          newFixedLengthResponse(\r\n            Response.Status.lookup(responseObject.getInt(\"status\")),\r\n            getContentType(responseObject),\r\n            responseObject.getString(\"body\")\r\n          );\r\n\r\n        Iterator<?> keys = responseObject.getJSONObject(\"headers\").keys();\r\n        while (keys.hasNext()) {\r\n          String key = (String) keys.next();\r\n          response.addHeader(\r\n            key,\r\n            responseObject.getJSONObject(\"headers\").getString(key)\r\n          );\r\n        }\r\n      } catch (JSONException e) {\r\n        e.printStackTrace();\r\n      }\r\n      return response;\r\n    }\r\n  }\r\n\r\n  private DocumentFile getFile(String filePath) {\r\n    Uri fileUri = Uri.parse(filePath);\r\n    DocumentFile documentFile = null;\r\n    if (filePath.matches(\"file:///(.*)\")) {\r\n      File file = new File(fileUri.getPath());\r\n      documentFile = DocumentFile.fromFile(file);\r\n    } else {\r\n      documentFile =\r\n        DocumentFile.fromSingleUri(this.context, Uri.parse(filePath));\r\n    }\r\n\r\n    return documentFile;\r\n  }\r\n\r\n  private InputStream getInputStream(DocumentFile file)\r\n    throws FileNotFoundException, IOException {\r\n    Uri uri = file.getUri();\r\n    ContentResolver contentResolver = context.getContentResolver();\r\n    return contentResolver.openInputStream(uri);\r\n  }\r\n\r\n  private JSONObject getJSONObject(JSONObject ob, String key) {\n    JSONObject jsonObject = null;\n    try {\n      jsonObject = ob.getJSONObject(key);\n    } catch (JSONException e) {\n      Log.w(\"NanoHTTPDWebserver\", \"Missing or invalid JSON object for key: \" + key, e);\n    }\n    return jsonObject;\n  }\n}\n"
  },
  {
    "path": "src/plugins/server/src/android/com/foxdebug/server/Server.java",
    "content": "package com.foxdebug.server;\r\n\r\nimport java.io.IOException;\r\nimport java.util.HashMap;\r\nimport org.apache.cordova.*;\r\nimport org.json.JSONArray;\r\nimport org.json.JSONException;\r\n\r\npublic class Server extends CordovaPlugin {\r\n\r\n  public HashMap<Integer, NanoHTTPDWebserver> servers;\r\n\r\n  @Override\r\n  public void initialize(CordovaInterface cordova, CordovaWebView webView) {\r\n    super.initialize(cordova, webView);\r\n    servers = new HashMap<Integer, NanoHTTPDWebserver>();\r\n  }\r\n\r\n  @Override\r\n  public boolean execute(\r\n    String action,\r\n    JSONArray args,\r\n    CallbackContext callbackContext\r\n  )\r\n    throws JSONException {\r\n    if (\"start\".equals(action)) {\r\n      try {\r\n        this.start(args, callbackContext);\r\n      } catch (IOException e) {\r\n        e.printStackTrace();\r\n      }\r\n      return true;\r\n    }\r\n    if (\"setOnRequestHandler\".equals(action)) {\r\n      this.setOnRequestHandler(args, callbackContext);\r\n      return true;\r\n    }\r\n    if (\"stop\".equals(action)) {\r\n      this.stop(args, callbackContext);\r\n      return true;\r\n    }\r\n    if (\"send\".equals(action)) {\r\n      this.send(args, callbackContext);\r\n      return true;\r\n    }\r\n    return false; // Returning false results in a \"MethodNotFound\" error.\r\n  }\r\n\r\n  /**\r\n   * Starts the server\r\n   *\r\n   * @param args\r\n   * @param callbackContext\r\n   */\r\n  private void start(JSONArray args, CallbackContext callbackContext)\r\n    throws JSONException, IOException {\r\n    Integer port = 8080;\r\n\r\n    if (args.length() == 1) {\r\n      port = args.getInt(0);\r\n    }\r\n\r\n    NanoHTTPDWebserver server = servers.get(port);\r\n    if (server != null) {\r\n      callbackContext.success(\"Server started on port \" + port);\r\n      return;\r\n    }\r\n\r\n    try {\r\n      server = new NanoHTTPDWebserver(port, cordova.getContext());\r\n      server.start();\r\n      servers.put(port, server);\r\n      callbackContext.success(\"Server started on port \" + port);\r\n    } catch (Exception e) {\r\n      callbackContext.sendPluginResult(\r\n        new PluginResult(PluginResult.Status.ERROR, e.getMessage())\r\n      );\r\n    }\r\n  }\r\n\r\n  private void setOnRequestHandler(\r\n    JSONArray args,\r\n    CallbackContext callbackContext\r\n  )\r\n    throws JSONException {\r\n    Integer port = args.getInt(0);\r\n    NanoHTTPDWebserver server = servers.get(port);\r\n    if (server == null) {\r\n      callbackContext.error(\"Server not started on port \" + port);\r\n      return;\r\n    }\r\n    server.onRequestCallbackContext = callbackContext;\r\n  }\r\n\r\n  /**\r\n   * Stops the server\r\n   *\r\n   * @param args\r\n   * @param callbackContext\r\n   */\r\n  private void stop(JSONArray args, CallbackContext callbackContext)\r\n    throws JSONException {\r\n    Integer port = args.getInt(0);\r\n    NanoHTTPDWebserver server = servers.get(port);\r\n    if (server == null) {\r\n      callbackContext.error(\"Server not started on port \" + port);\r\n      return;\r\n    }\r\n    server.stop();\r\n    servers.remove(port);\r\n    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));\r\n  }\r\n\r\n  /**\r\n   * Will be called if the js context sends an response to the webserver\r\n   *\r\n   * @param args            {UUID: {...}}\r\n   * @param callbackContext\r\n   * @throws JSONException\r\n   */\r\n  private void send(JSONArray args, CallbackContext callbackContext)\r\n    throws JSONException {\r\n    Integer port = args.getInt(0);\r\n    NanoHTTPDWebserver server = this.servers.get(port);\r\n    if (server == null) {\r\n      callbackContext.sendPluginResult(\r\n        new PluginResult(PluginResult.Status.ERROR, \"Server not running\")\r\n      );\r\n      return;\r\n    }\r\n    server.responses.put(args.getString(1), args.get(2));\r\n    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/plugins/server/www/server.js",
    "content": "module.exports = function (port, onRequest, onError) {\r\n  cordova.exec(onRequest, onError, 'Server', 'start', [port]);\r\n  return {\r\n    stop: function (onSuccess, onError) {\r\n      onSuccess = onSuccess || function () { };\r\n      onError = onError || console.error.bind(console);\r\n      cordova.exec(onSuccess, onError, 'Server', 'stop', [port]);\r\n    },\r\n    send: function (req_id, data, onSuccess, onError) {\r\n      onSuccess = onSuccess || function () { };\r\n      onError = onError || console.error.bind(console);\r\n      cordova.exec(onSuccess, onError, 'Server', 'send', [port, req_id, data]);\r\n    },\r\n    setOnRequestHandler: function (onRequest, onError) {\r\n      onError = onError || console.error.bind(console);\r\n      cordova.exec(onRequest, onError, 'Server', 'setOnRequestHandler', [port]);\r\n    },\r\n    port: port\r\n  }\r\n}"
  },
  {
    "path": "src/plugins/sftp/LICENSE.md",
    "content": "- GoldRaccoon is under [original author's license](https://github.com/albertodebortoli/GoldRaccoon/blob/master/LICENSE.markdown)\r\n- ftp4j is under [LGPL](http://opensource.org/licenses/LGPL-2.1)\r\n- All other codes (writen by me) are under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)\r\n"
  },
  {
    "path": "src/plugins/sftp/README.md",
    "content": "# cordova-plugin-sftp\r\n\r\n## Description\r\n\r\nThis cordova plugin is created to use sftp (client) in Cordova apps.\r\n\r\nSupport **Android** platform for now.\r\n\r\nYou can do the following things:\r\n\r\n- Execute a command\r\n- Download a file (with percent info)\r\n- Upload a file (with percent info)\r\n- Connect using private key or password\r\n\r\n## Installation\r\n\r\n```sh\r\n$ cordova plugin add cordova-plugin-sftp\r\n$ cordova prepare\r\n```\r\n"
  },
  {
    "path": "src/plugins/sftp/index.d.ts",
    "content": "interface Stats {\r\n  canRead: boolean;\r\n  canWrite: boolean;\r\n  exists: boolean; //indicates if file can be found on device storage\r\n  isDirectory: boolean;\r\n  isFile: boolean;\r\n  isVirtual: boolean;\r\n  lastModified: number;\r\n  length: number;\r\n  name: string;\r\n  type: string;\r\n  uri: string;\r\n}\r\n\r\ninterface ExecResult{\r\n  code: Number;\r\n  result: String;\r\n}\r\n\r\ninterface Sftp {\r\n  /**\r\n   * Executes command on ssh-server\r\n   * @param command \r\n   * @param onSucess \r\n   * @param onFail \r\n   */\r\n  exec(command: String, onSucess: (res: ExecResult)=>void, onFail: (err: any) => void): void;\r\n  /**\r\n   * Connects to SFTP server\r\n   * @param host Hostname of the server\r\n   * @param port port numer\r\n   * @param username Username \r\n   * @param password Password or private key file to authenticate the server\r\n   * @param onSuccess Callback function on success returns url of copied file/dir\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  connectUsingPassoword(host: String, port: Number, username: String, password: String, onSuccess: () => void, onFail: (err: any) => void): void;\r\n  \r\n  /**\r\n   * Connects to SFTP server\r\n   * @param host Hostname of the server\r\n   * @param port port numer\r\n   * @param username Username \r\n   * @param keyFile Password or private key file to authenticate the server\r\n   * @param passphrase Passphrase for keyfile\r\n   * @param onSuccess Callback function on success returns url of copied file/dir\r\n   * @param onFail Callback function on error returns error object\r\n   */\r\n  connectUsingKeyFile(host: String, port: Number, username: String, keyFile: String, passphrase: String, onSuccess: () => void, onFail: (err: any) => void): void;\r\n\r\n  /**\r\n   * Gets file from the server.\r\n   * @param filename \r\n   * @param localFilename copy/shadow of remote file.\r\n   * @param onSuccess \r\n   * @param onFail \r\n   */\r\n  getFile(filename: String, localFilename: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  \r\n  /**\r\n   * Uploaded the file to server\r\n   * @param filename \r\n   * @param localFilename copy/shadow of remote file.\r\n   * @param onSuccess \r\n   * @param onFail \r\n   */\r\n  putFile(filename: String, localFilename: String, onSuccess: (url: String) => void, onFail: (err: any) => void): void;\r\n  \r\n  /**\r\n   * Closes the connection\r\n   * @param onSuccess \r\n   * @param onFail \r\n   */\r\n  close(onSuccess: () => void, onFail: (err: any) => void): void;\r\n  \r\n  /**\r\n   * Gets wether server is connected or not.\r\n   * @param onSuccess \r\n   * @param onFail \r\n   */\r\n  isConnected(onSuccess: (connectionId: String) => void, onFail: (err: any) => void): void;\r\n}\r\n\r\ndeclare var sftp: Sftp;"
  },
  {
    "path": "src/plugins/sftp/package.json",
    "content": "{\r\n  \"name\": \"cordova-plugin-sftp\",\r\n  \"version\": \"1.1.1\",\r\n  \"description\": \"This cordova plugin is created to use sftp (client) in web/js.\",\r\n  \"cordova\": {\r\n    \"id\": \"cordova-plugin-sftp\",\r\n    \"platforms\": [\r\n      \"android\"\r\n    ]\r\n  },\r\n  \"keywords\": [\r\n    \"cordova\",\r\n    \"sftp\",\r\n    \"cordova-android\"\r\n  ],\r\n  \"author\": \"pax\",\r\n  \"license\": \"Apache-2.0\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  }\r\n}"
  },
  {
    "path": "src/plugins/sftp/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n\r\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\r\n    xmlns:rim=\"http://www.blackberry.com/ns/widgets\"\r\n    xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-sftp\" version=\"1.1.1\">\r\n    <name>Sftp</name>\r\n    <description>Cordova Sftp Plugin</description>\r\n    <license>MIT</license>\r\n    <keywords>cordova,ftp</keywords>\r\n    <repo>https://github.com/foxdebug/cordova-plugin-sftp.git</repo>\r\n\r\n    <js-module src=\"www/sftp.js\" name=\"sftp\">\r\n        <clobbers target=\"window.sftp\" />\r\n    </js-module>\r\n\r\n    <!-- android -->\r\n    <platform name=\"android\">\r\n        <config-file target=\"res/xml/config.xml\" parent=\"/*\">\r\n            <feature name=\"Sftp\">\r\n                <param name=\"android-package\" value=\"com.foxdebug.sftp.Sftp\" />\r\n            </feature>\r\n        </config-file>\r\n\r\n        <source-file src=\"src/com/foxdebug/sftp/Sftp.java\" target-dir=\"src/com/foxdebug/sftp\" />\r\n    </platform>\r\n\r\n    <framework src=\"commons-io:commons-io:2.11.0\" />\r\n    <framework src=\"com.sshtools:maverick-synergy-client:3.1.4\" />\r\n<!--    <framework src=\"com.sshtools:maverick-bc:3.1.2\" />-->\r\n    <framework src=\"org.bouncycastle:bcprov-jdk15to18:1.83\" />\r\n    <framework src=\"org.bouncycastle:bcpkix-jdk15to18:1.83\" />\r\n</plugin>\r\n"
  },
  {
    "path": "src/plugins/sftp/src/com/foxdebug/sftp/Sftp.java",
    "content": "package com.foxdebug.sftp;\n\nimport android.app.Activity;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.net.Uri;\nimport android.util.Log;\nimport androidx.documentfile.provider.DocumentFile;\nimport com.sshtools.client.SshClient;\nimport com.sshtools.client.SshClient.SshClientBuilder;\nimport com.sshtools.client.sftp.SftpClient;\nimport com.sshtools.client.sftp.SftpClient.SftpClientBuilder;\nimport com.sshtools.client.sftp.SftpFile;\nimport com.sshtools.client.sftp.TransferCancelledException;\nimport com.sshtools.common.permissions.PermissionDeniedException;\nimport com.sshtools.common.publickey.InvalidPassphraseException;\nimport com.sshtools.common.publickey.SshKeyUtils;\nimport com.sshtools.common.sftp.SftpFileAttributes;\nimport com.sshtools.common.sftp.SftpStatusException;\nimport com.sshtools.common.ssh.SshException;\nimport com.sshtools.common.ssh.components.SshKeyPair;\nimport com.sshtools.common.ssh.components.jce.JCEProvider;\nimport com.sshtools.common.util.FileUtils;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.lang.SecurityException;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.channels.UnresolvedAddressException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Security;\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPlugin;\nimport org.apache.cordova.CordovaWebView;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport java.util.Arrays;\nimport org.bouncycastle.jce.provider.BouncyCastleProvider;\n\npublic class Sftp extends CordovaPlugin {\n\n  private static final String TAG = \"SFTP\";\n  private SshClient ssh;\n  private SftpClient sftp;\n  private Context context;\n  private Activity activity;\n  private String connectionID;\n\n  public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n    super.initialize(cordova, webView);\n    context = cordova.getContext();\n    activity = cordova.getActivity();\n    System.setProperty(\"maverick.log.nothread\", \"true\");\n  }\n\n  public boolean execute(\n    String action,\n    JSONArray args,\n    CallbackContext callback\n  ) {\n    try {\n      Method method = getClass()\n        .getDeclaredMethod(action, JSONArray.class, CallbackContext.class);\n\n      if (method != null) {\n        method.invoke(this, args, callback);\n        return true;\n      }\n    } catch (NoSuchMethodException e) {\n      callback.error(\"Method not found: \" + action);\n      return false;\n    } catch (SecurityException e) {\n      callback.error(\"Security exception: \" + e.getMessage());\n      return false;\n    } catch (Exception e) {\n      callback.error(\"Exception: \" + e.getMessage());\n      return false;\n    }\n\n    return false;\n  }\n\n  public void connectUsingPassword(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String host = args.optString(0);\n              int port = args.optInt(1);\n              String username = args.optString(2);\n              String password = args.optString(3);\n              JCEProvider.enableBouncyCastle(true);\n              Log.d(\n                TAG,\n                \"Connecting to \" + host + \":\" + port + \" as \" + username\n              );\n              ssh = SshClientBuilder.create()\n                .withHostname(host)\n                .withPort(port)\n                .withUsername(username)\n                .withPassword(password)\n                .build();\n\n              if (ssh.isConnected()) {\n                connectionID = username + \"@\" + host;\n\n                try {\n                  sftp = SftpClientBuilder.create().withClient(ssh).build();\n                } catch (IOException | SshException e) {\n                  ssh.close();\n                  callback.error(\n                    \"Failed to initialize SFTP subsystem: \" + errMessage(e)\n                  );\n                  Log.e(TAG, \"Failed to initialize SFTP subsystem\", e);\n                  return;\n                }\n\n                try {\n                  sftp.getSubsystemChannel().setCharsetEncoding(\"UTF-8\");\n                } catch (UnsupportedEncodingException | SshException e) {\n                  // Fallback to default encoding if UTF-8 fails\n                  Log.w(\n                    TAG,\n                    \"Failed to set UTF-8 encoding, falling back to default\",\n                    e\n                  );\n                }\n                callback.success();\n                Log.d(TAG, \"Connected successfully to \" + connectionID);\n                return;\n              }\n\n              callback.error(\"Failed to establish SSH connection\");\n            } catch (UnresolvedAddressException e) {\n              callback.error(\"Cannot resolve host address\");\n              Log.e(TAG, \"Cannot resolve host address\", e);\n            } catch (PermissionDeniedException e) {\n              callback.error(\"Authentication failed: \" + e.getMessage());\n              Log.e(TAG, \"Authentication failed\", e);\n            } catch (SshException e) {\n              callback.error(\"SSH error: \" + errMessage(e));\n              Log.e(TAG, \"SSH error\", e);\n            } catch (IOException e) {\n              callback.error(\"I/O error: \" + errMessage(e));\n              Log.e(TAG, \"I/O error\", e);\n            } catch (Exception e) {\n              callback.error(\"Unexpected error: \" + errMessage(e));\n              Log.e(TAG, \"Unexpected error\", e);\n            }\n          }\n        }\n      );\n  }\n\n  public void connectUsingKeyFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String host = args.optString(0);\n              int port = args.optInt(1);\n              String username = args.optString(2);\n              String keyFile = args.optString(3);\n              String passphrase = args.optString(4);\n              DocumentFile file = DocumentFile.fromSingleUri(\n                context,\n                Uri.parse(keyFile)\n              );\n              Uri uri = file.getUri();\n              ContentResolver contentResolver = context.getContentResolver();\n              InputStream in = contentResolver.openInputStream(uri);\n\n//            for `appDataDirectory`, Ref: https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String)\n//            the absolute path to application-specific directory. May return *null* if shared storage is not currently available.\n              File appDataDirectory = context.getExternalFilesDir(null);\n              if (appDataDirectory != null) {\n                com.sshtools.common.logger.Log.getDefaultContext().enableFile(com.sshtools.common.logger.Log.Level.DEBUG, new File(appDataDirectory,\"synergy.log\"));\n              }\n//            JCEProvider.enableBouncyCastle(false);\n\n              Log.i(TAG, \"All Available Security Providers (Security.getProviders() : \" + Arrays.toString(Security.getProviders()));\n              Log.i(TAG, \"All Available Security Providers for ED25519 (Security.getProviders(\\\"KeyPairGenerator.Ed25519\\\"\\\") : \" + Arrays.toString(Security.getProviders(\"KeyPairGenerator.Ed25519\")));\n              Log.i(TAG, \"BC Security Provider Name (`Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)`) : \" + Security.getProvider(BouncyCastleProvider.PROVIDER_NAME));\n              Security.removeProvider(\"BC\");\n              Security.insertProviderAt(new BouncyCastleProvider(), 1);\n\n              Log.i(TAG, \"(After Inserting BC) All Available Security Providers (Security.getProviders() : \" + Arrays.toString(Security.getProviders()));\n              Log.i(TAG, \"(After Inserting BC) All Available Security Providers for ED25519 (Security.getProviders(\\\"KeyPairGenerator.Ed25519\\\"\\\") : \" + Arrays.toString(Security.getProviders(\"KeyPairGenerator.Ed25519\")));\n              Log.i(TAG, \"(After Inserting BC) BC Security Provider Name (`Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)`) : \" + Security.getProvider(BouncyCastleProvider.PROVIDER_NAME));\n\n              SshKeyPair keyPair = null;\n              try {\n                keyPair = SshKeyUtils.getPrivateKey(in, passphrase);\n              } catch (InvalidPassphraseException e) {\n                callback.error(\"Invalid passphrase for key file\");\n                Log.e(TAG, \"Invalid passphrase for key file\", e);\n                return;\n              } catch (IOException e) {\n                callback.error(\"Could not read key file: \" + errMessage(e));\n                Log.e(TAG, \"Could not read key file\", e);\n                return;\n              }\n\n              ssh = SshClientBuilder.create()\n                .withHostname(host)\n                .withPort(port)\n                .withUsername(username)\n                .withIdentities(keyPair)\n                .build();\n\n              if (ssh.isConnected()) {\n                connectionID = username + \"@\" + host;\n                try {\n                  sftp = SftpClientBuilder.create().withClient(ssh).build();\n                } catch (IOException | SshException e) {\n                  ssh.close();\n                  callback.error(\n                    \"Failed to initialize SFTP subsystem: \" + errMessage(e)\n                  );\n                  Log.e(TAG, \"Failed to initialize SFTP subsystem\", e);\n                  return;\n                }\n\n                try {\n                  sftp.getSubsystemChannel().setCharsetEncoding(\"UTF-8\");\n                } catch (UnsupportedEncodingException | SshException e) {\n                  // Fallback to default encoding if UTF-8 fails\n                  Log.w(\n                    TAG,\n                    \"Failed to set UTF-8 encoding, falling back to default\",\n                    e\n                  );\n                }\n                callback.success();\n                Log.d(TAG, \"Connected successfully to \" + connectionID);\n                return;\n              }\n\n              callback.error(\"Failed to establish SSH connection\");\n            } catch (UnresolvedAddressException e) {\n              callback.error(\"Cannot resolve host address\");\n              Log.e(TAG, \"Cannot resolve host address\", e);\n            } catch (PermissionDeniedException e) {\n              callback.error(\"Authentication failed: \" + e.getMessage());\n              Log.e(TAG, \"Authentication failed\", e);\n            } catch (SshException e) {\n              callback.error(\"SSH error: \" + errMessage(e));\n              Log.e(TAG, \"SSH error\", e);\n            } catch (IOException e) {\n              callback.error(\"I/O error: \" + errMessage(e));\n              Log.e(TAG, \"I/O error\", e);\n            } catch (SecurityException e) {\n              callback.error(\"Security error: \" + errMessage(e));\n              Log.e(TAG, \"Security error\", e);\n            } catch (Exception e) {\n              callback.error(\"Unexpected error: \" + errMessage(e));\n              Log.e(TAG, \"Unexpected error\", e);\n            }\n          }\n        }\n      );\n  }\n\n  public void exec(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String command = args.optString(0);\n              if (ssh != null) {\n                JSONObject res = new JSONObject();\n                StringBuffer buffer = new StringBuffer();\n                int code = ssh.executeCommandWithResult(command, buffer);\n                String result = buffer.toString();\n                res.put(\"code\", code);\n                res.put(\"result\", result);\n                callback.success(res);\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (IOException | JSONException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void getFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String filename = args.optString(0);\n              String localFilename = args.optString(1);\n              if (ssh != null && sftp != null) {\n                URI uri = new URI(localFilename);\n                DocumentFile file = DocumentFile.fromSingleUri(\n                  context,\n                  Uri.parse(localFilename)\n                );\n                Uri fileUri = file.getUri();\n                ContentResolver contentResolver = context.getContentResolver();\n\n                try (\n                  InputStream inputStream = sftp.getInputStream(filename);\n                  java.io.OutputStream outputStream =\n                    contentResolver.openOutputStream(fileUri, \"wt\")\n                ) {\n                  byte[] buffer = new byte[32768];\n                  int bytesRead;\n\n                  while ((bytesRead = inputStream.read(buffer)) != -1) {\n                    outputStream.write(buffer, 0, bytesRead);\n                  }\n\n                  outputStream.flush();\n                  callback.success();\n                  return;\n                } catch (SftpStatusException e) {\n                  callback.error(\"SFTP transfer error: \" + errMessage(e));\n                  return;\n                }\n              }\n              Log.d(\"getFile\", \"ssh or sftp is null\");\n              callback.error(\"Not connected\");\n            } catch (\n              IOException\n              | URISyntaxException\n              | SecurityException\n              | SshException e\n            ) {\n              Log.e(\"getFile\", \"Error downloading file\", e);\n              callback.error(\"File transfer error: \" + errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void putFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String remoteFilename = args.optString(0);\n              String localFilename = args.optString(1);\n\n              if (ssh == null || sftp == null) {\n                callback.error(\"Not connected\");\n                return;\n              }\n\n              if (remoteFilename == null || remoteFilename.isEmpty()) {\n                callback.error(\"Remote filename is required\");\n                return;\n              }\n\n              if (localFilename == null || localFilename.isEmpty()) {\n                callback.error(\"Local filename is required\");\n                return;\n              }\n\n              File localFile;\n              try {\n                URI uri = new URI(localFilename);\n                localFile = new File(uri);\n              } catch (URISyntaxException e) {\n                callback.error(\"Invalid local URI: \" + errMessage(e));\n                return;\n              }\n\n              if (!localFile.exists() || !localFile.canRead()) {\n                callback.error(\"Local file does not exist or is not readable\");\n                return;\n              }\n\n              try {\n                sftp.put(localFile.getAbsolutePath(), remoteFilename);\n                callback.success(\"File uploaded successfully\");\n              } catch (IOException e) {\n                callback.error(\"Error uploading file: \" + errMessage(e));\n              }\n            } catch (Exception e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void lsDir(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String path = args.optString(0);\n              if (ssh != null && sftp != null) {\n                JSONArray files = new JSONArray();\n                for (SftpFile file : sftp.ls(path)) {\n                  String filename = file.getFilename();\n                  if (filename.equals(\".\") || filename.equals(\"..\")) {\n                    continue;\n                  }\n                  SftpFileAttributes fileAttributes = file.attributes();\n                  JSONObject fileInfo = new JSONObject();\n                  fileInfo.put(\"name\", filename);\n                  fileInfo.put(\"exists\", true);\n\n                  if (fileAttributes != null) {\n                    String permissions = fileAttributes.toPermissionsString();\n                    boolean canRead = permissions.charAt(1) == 'r';\n                    boolean canWrite = permissions.charAt(2) == 'w';\n                    fileInfo.put(\"canRead\", canRead);\n                    fileInfo.put(\"canWrite\", canWrite);\n                    fileInfo.put(\"permissions\", permissions);\n                    fileInfo.put(\"length\", fileAttributes.size());\n                    fileInfo.put(\"url\", file.getAbsolutePath());\n                    fileInfo.put(\n                      \"lastModified\",\n                      fileAttributes.lastModifiedTime()\n                    );\n\n                    if (permissions.charAt(0) == 'l') {\n                      fileInfo.put(\"isLink\", true);\n                      try {\n                        String linkTarget = sftp.getSymbolicLinkTarget(\n                          file.getAbsolutePath()\n                        );\n                        fileInfo.put(\"linkTarget\", linkTarget);\n                        SftpFileAttributes linkAttributes = sftp.stat(\n                          linkTarget\n                        );\n                        fileInfo.put(\"isFile\", linkAttributes.isFile());\n                        fileInfo.put(\n                          \"isDirectory\",\n                          linkAttributes.isDirectory()\n                        );\n                      } catch (SftpStatusException | SshException e) {\n                        // Handle broken symlink\n                        fileInfo.put(\"isFile\", false);\n                        fileInfo.put(\"isDirectory\", false);\n                        fileInfo.put(\"isLink\", false);\n                      }\n                    } else {\n                      fileInfo.put(\"isLink\", false);\n                      fileInfo.put(\"isDirectory\", fileAttributes.isDirectory());\n                      fileInfo.put(\"isFile\", fileAttributes.isFile());\n                    }\n                  }\n\n                  files.put(fileInfo);\n                }\n                callback.success(files);\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (SftpStatusException | JSONException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void stat(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String path = sanitizePath(args.optString(0));\n              if (ssh != null && sftp != null) {\n                URI uri = new URI(path);\n                JSONObject fileStat = new JSONObject();\n\n                try {\n                  SftpFileAttributes fileAttributes = sftp.stat(uri.getPath());\n                  if (fileAttributes != null) {\n                    String permissions = fileAttributes.toPermissionsString();\n                    boolean canRead = permissions.charAt(1) == 'r';\n                    boolean canWrite = permissions.charAt(2) == 'w';\n\n                    fileStat.put(\"exists\", true);\n                    fileStat.put(\"canRead\", canRead);\n                    fileStat.put(\"canWrite\", canWrite);\n                    fileStat.put(\"isLink\", fileAttributes.isLink());\n                    fileStat.put(\"isDirectory\", fileAttributes.isDirectory());\n                    fileStat.put(\"isFile\", fileAttributes.isFile());\n                    fileStat.put(\"length\", fileAttributes.size());\n                    fileStat.put(\n                      \"permissions\",\n                      fileAttributes.toPermissionsString()\n                    );\n                    fileStat.put(\n                      \"lastModified\",\n                      fileAttributes.lastModifiedTime()\n                    );\n                    String[] pathSegments = uri.getPath().split(\"/\");\n                    String filename = pathSegments[pathSegments.length - 1];\n\n                    fileStat.put(\"name\", filename);\n                    fileStat.put(\"url\", uri.getPath());\n                    if (permissions.charAt(0) == 'l') {\n                      fileStat.put(\"isLink\", true);\n                      try {\n                        String linkTarget = sftp.getSymbolicLinkTarget(\n                          uri.getPath()\n                        );\n                        fileStat.put(\"linkTarget\", linkTarget);\n                        SftpFileAttributes linkAttributes = sftp.stat(\n                          linkTarget\n                        );\n                        fileStat.put(\"isFile\", linkAttributes.isFile());\n                        fileStat.put(\n                          \"isDirectory\",\n                          linkAttributes.isDirectory()\n                        );\n                      } catch (SftpStatusException | SshException e) {\n                        // Handle broken symlink\n                        fileStat.put(\"isFile\", false);\n                        fileStat.put(\"isDirectory\", false);\n                        fileStat.put(\"isLink\", false);\n                        fileStat.put(\"exists\", false);\n                      }\n                    } else {\n                      fileStat.put(\"isLink\", false);\n                      fileStat.put(\"isDirectory\", fileAttributes.isDirectory());\n                      fileStat.put(\"isFile\", fileAttributes.isFile());\n                    }\n                  }\n                } catch (SftpStatusException e) {\n                  fileStat.put(\"exists\", false);\n                  fileStat.put(\"url\", uri.getPath());\n                }\n\n                callback.success(fileStat);\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (URISyntaxException | JSONException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void mkdir(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String path = args.optString(0);\n              if (ssh != null && sftp != null) {\n                sftp.mkdir(path);\n                callback.success();\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (SftpStatusException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void rm(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String path = args.optString(0);\n              boolean force = args.optBoolean(1, false);\n              boolean recurse = args.optBoolean(2, false);\n\n              if (ssh != null && sftp != null) {\n                sftp.rm(path, force, recurse);\n                callback.success();\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (SftpStatusException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void createFile(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String path = args.optString(0);\n              String content = args.optString(1, \"\");\n\n              if (ssh != null && sftp != null) {\n                try {\n                  SftpFileAttributes attrs = sftp.stat(path);\n                  if (attrs != null && attrs.isFile()) {\n                    callback.error(\"File already exists\");\n                    return;\n                  }\n                } catch (SftpStatusException e) {\n                  // File doesn't exist, continue with creation\n                }\n\n                java.io.ByteArrayInputStream inputStream;\n                if (content.isEmpty()) {\n                  inputStream = new java.io.ByteArrayInputStream(new byte[0]);\n                } else {\n                  inputStream = new java.io.ByteArrayInputStream(\n                    content.getBytes(StandardCharsets.UTF_8)\n                  );\n                }\n                sftp.put(inputStream, path);\n                callback.success();\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (\n              SftpStatusException | SshException | TransferCancelledException e\n            ) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void rename(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              String oldpath = args.optString(0);\n              String newpath = args.optString(1);\n\n              if (ssh != null && sftp != null) {\n                sftp.rename(oldpath, newpath);\n                callback.success();\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (SftpStatusException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void pwd(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              if (ssh != null && sftp != null) {\n                String pwd = sftp.pwd();\n                callback.success(pwd);\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (SftpStatusException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  private String sanitizePath(String path) {\n    try {\n      String decodedPath = URLDecoder.decode(\n        path,\n        StandardCharsets.UTF_8.toString()\n      );\n      String encodedPath = URLEncoder.encode(\n        decodedPath,\n        StandardCharsets.UTF_8.toString()\n      )\n        .replace(\"+\", \"%20\") // Replace + with %20 for spaces\n        .replace(\"%2F\", \"/\") // Preserve forward slashes\n        .replace(\"%5C\", \"\\\\\"); // Preserve backslashes if needed\n\n      return encodedPath;\n    } catch (UnsupportedEncodingException e) {\n      return path; // Return original if encoding fails\n    }\n  }\n\n  public void close(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            try {\n              if (ssh != null) {\n                ssh.close();\n                sftp.quit();\n                callback.success();\n                return;\n              }\n              callback.error(\"Not connected\");\n            } catch (IOException | SshException e) {\n              callback.error(errMessage(e));\n            }\n          }\n        }\n      );\n  }\n\n  public void isConnected(JSONArray args, CallbackContext callback) {\n    cordova\n      .getThreadPool()\n      .execute(\n        new Runnable() {\n          public void run() {\n            if (\n              ssh != null &&\n              ssh.isConnected() &&\n              sftp != null &&\n              !sftp.isClosed()\n            ) {\n              callback.success(connectionID);\n              return;\n            }\n\n            callback.success(0);\n          }\n        }\n      );\n  }\n\n  public String errMessage(Exception e) {\n    String res = e.getMessage();\n    if (res == null || res.equals(\"\")) {\n      return e.toString();\n    }\n\n    return res;\n  }\n}\n"
  },
  {
    "path": "src/plugins/sftp/www/sftp.js",
    "content": "module.exports = {\r\n  exec: function (command, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'exec', [command]);\r\n  },\r\n  connectUsingPassword: function (host, port, username, password, onSuccess, onFail) {\r\n    if (typeof port != 'number') {\r\n      throw new Error('Port must be number');\r\n    }\r\n\r\n    port = Number.parseInt(port);\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'connectUsingPassword', [host, port, username, password]);\r\n  },\r\n  connectUsingKeyFile: function (host, port, username, keyFile, passphrase, onSuccess, onFail) {\r\n    if (typeof port != 'number') {\r\n      throw new Error('Port must be number');\r\n    }\r\n\r\n    port = Number.parseInt(port);\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'connectUsingKeyFile', [host, port, username, keyFile, passphrase]);\r\n  },\r\n  getFile: function (filename, localFilename, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'getFile', [filename, localFilename]);\r\n  },\r\n  putFile: function (filename, localFilename, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'putFile', [filename, localFilename]);\r\n  },\r\n  lsDir: function (path, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'lsDir', [path]);\r\n  },\r\n  stat: function (path, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'stat', [path]);\r\n  },\r\n  mkdir: function (path, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'mkdir', [path]);\r\n  },\r\n  rm: function (path, force, recurse, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'rm', [path, force, recurse]);\r\n  },\r\n  createFile: function (path, content, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'createFile', [path, content]);\r\n  },\r\n  rename: function (oldpath, newpath, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'rename', [oldpath, newpath]);\r\n  },\r\n  pwd: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'pwd', []);\r\n  },\r\n  close: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'close', []);\r\n  },\r\n  isConnected: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'Sftp', 'isConnected', []);\r\n  }\r\n};\r\n"
  },
  {
    "path": "src/plugins/system/android/com/foxdebug/system/RewardPassManager.java",
    "content": "package com.foxdebug.system;\n\nimport android.content.Context;\nimport android.util.Log;\nimport com.foxdebug.acode.rk.auth.EncryptedPreferenceManager;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.Random;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic class RewardPassManager {\n    private static final String TAG = \"SystemRewardPass\";\n    private static final String ADS_PREFS_FILENAME = \"ads\";\n    private static final String KEY_REWARD_STATE = \"reward_state\";\n    private static final long ONE_HOUR_MS = 60L * 60L * 1000L;\n    private static final long MAX_ACTIVE_PASS_MS = 10L * ONE_HOUR_MS;\n    private static final int MAX_REDEMPTIONS_PER_DAY = 3;\n\n    private final EncryptedPreferenceManager adsPrefManager;\n    private final Random random = new Random();\n\n    public RewardPassManager(Context context) {\n        this.adsPrefManager = new EncryptedPreferenceManager(context, ADS_PREFS_FILENAME);\n    }\n\n    public String getRewardStatus() throws JSONException {\n        JSONObject state = syncRewardState(loadRewardState());\n        JSONObject status = buildRewardStatus(state);\n\n        if (status.optBoolean(\"hasPendingExpiryNotice\")) {\n            state.put(\"expiryNoticePendingUntil\", 0L);\n        }\n\n        saveRewardState(state);\n        return status.toString();\n    }\n\n    public String redeemReward(String offerId) throws JSONException {\n        JSONObject state = syncRewardState(loadRewardState());\n        int redemptionsToday = state.optInt(\"redemptionsToday\", 0);\n        long now = java.lang.System.currentTimeMillis();\n        long adFreeUntil = state.optLong(\"adFreeUntil\", 0L);\n        long remainingMs = Math.max(0L, adFreeUntil - now);\n\n        if (redemptionsToday >= MAX_REDEMPTIONS_PER_DAY) {\n            throw new JSONException(\n                \"Daily limit reached. You can redeem up to \" + MAX_REDEMPTIONS_PER_DAY + \" rewards per day.\"\n            );\n        }\n\n        if (remainingMs >= MAX_ACTIVE_PASS_MS) {\n            throw new JSONException(\"You already have the maximum 10 hours of ad-free time active.\");\n        }\n\n        long grantedDurationMs = resolveRewardDuration(offerId);\n        long baseTime = Math.max(now, adFreeUntil);\n        long newAdFreeUntil = Math.min(baseTime + grantedDurationMs, now + MAX_ACTIVE_PASS_MS);\n        long appliedDurationMs = Math.max(0L, newAdFreeUntil - baseTime);\n\n        state.put(\"adFreeUntil\", newAdFreeUntil);\n        state.put(\"lastExpiredRewardUntil\", 0L);\n        state.put(\"expiryNoticePendingUntil\", 0L);\n        state.put(\"redemptionDay\", getTodayKey());\n        state.put(\"redemptionsToday\", redemptionsToday + 1);\n        saveRewardState(state);\n\n        JSONObject status = buildRewardStatus(state);\n        status.put(\"grantedDurationMs\", grantedDurationMs);\n        status.put(\"appliedDurationMs\", appliedDurationMs);\n        status.put(\"offerId\", offerId);\n        return status.toString();\n    }\n\n    private JSONObject loadRewardState() {\n        String raw = adsPrefManager.getString(KEY_REWARD_STATE, \"\");\n        if (raw == null || raw.isEmpty()) {\n            return defaultRewardState();\n        }\n\n        try {\n            return mergeRewardState(new JSONObject(raw));\n        } catch (JSONException error) {\n            Log.w(TAG, \"Failed to parse reward state, resetting.\", error);\n            return defaultRewardState();\n        }\n    }\n\n    private JSONObject defaultRewardState() {\n        JSONObject state = new JSONObject();\n        try {\n            state.put(\"adFreeUntil\", 0L);\n            state.put(\"lastExpiredRewardUntil\", 0L);\n            state.put(\"expiryNoticePendingUntil\", 0L);\n            state.put(\"redemptionDay\", getTodayKey());\n            state.put(\"redemptionsToday\", 0);\n        } catch (JSONException ignored) {\n        }\n        return state;\n    }\n\n    private JSONObject mergeRewardState(JSONObject parsed) {\n        JSONObject state = defaultRewardState();\n        try {\n            state.put(\"adFreeUntil\", parsed.optLong(\"adFreeUntil\", 0L));\n            state.put(\"lastExpiredRewardUntil\", parsed.optLong(\"lastExpiredRewardUntil\", 0L));\n            state.put(\"expiryNoticePendingUntil\", parsed.optLong(\"expiryNoticePendingUntil\", 0L));\n            state.put(\"redemptionDay\", parsed.optString(\"redemptionDay\", getTodayKey()));\n            state.put(\"redemptionsToday\", parsed.optInt(\"redemptionsToday\", 0));\n        } catch (JSONException ignored) {\n        }\n        return state;\n    }\n\n    private void saveRewardState(JSONObject state) {\n        adsPrefManager.setString(KEY_REWARD_STATE, state.toString());\n    }\n\n    private JSONObject syncRewardState(JSONObject state) throws JSONException {\n        String todayKey = getTodayKey();\n        if (!todayKey.equals(state.optString(\"redemptionDay\", todayKey))) {\n            state.put(\"redemptionDay\", todayKey);\n            state.put(\"redemptionsToday\", 0);\n        }\n\n        long adFreeUntil = state.optLong(\"adFreeUntil\", 0L);\n        long now = java.lang.System.currentTimeMillis();\n        if (adFreeUntil > 0L && adFreeUntil <= now) {\n            if (state.optLong(\"expiryNoticePendingUntil\", 0L) != adFreeUntil) {\n                state.put(\"expiryNoticePendingUntil\", adFreeUntil);\n            }\n            state.put(\"lastExpiredRewardUntil\", adFreeUntil);\n            state.put(\"adFreeUntil\", 0L);\n        }\n\n        return state;\n    }\n\n    private JSONObject buildRewardStatus(JSONObject state) throws JSONException {\n        long now = java.lang.System.currentTimeMillis();\n        long adFreeUntil = state.optLong(\"adFreeUntil\", 0L);\n        int redemptionsToday = state.optInt(\"redemptionsToday\", 0);\n        long remainingMs = Math.max(0L, adFreeUntil - now);\n        int remainingRedemptions = Math.max(0, MAX_REDEMPTIONS_PER_DAY - redemptionsToday);\n\n        JSONObject status = new JSONObject();\n        status.put(\"adFreeUntil\", adFreeUntil);\n        status.put(\"lastExpiredRewardUntil\", state.optLong(\"lastExpiredRewardUntil\", 0L));\n        status.put(\"isActive\", adFreeUntil > now);\n        status.put(\"remainingMs\", remainingMs);\n        status.put(\"redemptionsToday\", redemptionsToday);\n        status.put(\"remainingRedemptions\", remainingRedemptions);\n        status.put(\"maxRedemptionsPerDay\", MAX_REDEMPTIONS_PER_DAY);\n        status.put(\"maxActivePassMs\", MAX_ACTIVE_PASS_MS);\n        status.put(\"hasPendingExpiryNotice\", state.optLong(\"expiryNoticePendingUntil\", 0L) > 0L);\n        status.put(\"expiryNoticePendingUntil\", state.optLong(\"expiryNoticePendingUntil\", 0L));\n\n        boolean canRedeem = remainingRedemptions > 0 && remainingMs < MAX_ACTIVE_PASS_MS;\n        status.put(\"canRedeem\", canRedeem);\n        status.put(\"redeemDisabledReason\", getRedeemDisabledReason(remainingRedemptions, remainingMs));\n        return status;\n    }\n\n    private String getRedeemDisabledReason(int remainingRedemptions, long remainingMs) {\n        if (remainingRedemptions <= 0) {\n            return \"Daily limit reached. You can redeem up to \" + MAX_REDEMPTIONS_PER_DAY + \" rewards per day.\";\n        }\n        if (remainingMs >= MAX_ACTIVE_PASS_MS) {\n            return \"You already have the maximum 10 hours of ad-free time active.\";\n        }\n        return \"\";\n    }\n\n    private long resolveRewardDuration(String offerId) throws JSONException {\n        if (\"quick\".equals(offerId)) {\n            return ONE_HOUR_MS;\n        }\n        if (\"focus\".equals(offerId)) {\n            int selectedHours = 4 + random.nextInt(3);\n            return selectedHours * ONE_HOUR_MS;\n        }\n        throw new JSONException(\"Unknown reward offer.\");\n    }\n\n    private String getTodayKey() {\n        return new SimpleDateFormat(\"yyyy-MM-dd\", Locale.US).format(new Date());\n    }\n}\n"
  },
  {
    "path": "src/plugins/system/android/com/foxdebug/system/SoftInputAssist.java",
    "content": "package com.foxdebug.system;\n\nimport android.app.Activity;\nimport android.view.View;\nimport java.util.List;\n\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport androidx.core.view.WindowInsetsAnimationCompat;\n\npublic class SoftInputAssist {\n\n    private boolean animationRunning = false;\n\n    public SoftInputAssist(Activity activity) {\n        View contentView = activity.findViewById(android.R.id.content);\n\n        ViewCompat.setOnApplyWindowInsetsListener(contentView, (v, insets) -> {\n\n            if (!animationRunning) {\n                Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());\n                Insets nav = insets.getInsets(WindowInsetsCompat.Type.navigationBars());\n\n                int keyboardHeight = Math.max(0, ime.bottom - nav.bottom);\n\n                v.setPadding(0, 0, 0, keyboardHeight);\n            }\n\n            return insets;\n        });\n\n        ViewCompat.setWindowInsetsAnimationCallback(\n            contentView,\n            new WindowInsetsAnimationCompat.Callback(\n                WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE\n            ) {\n\n                @Override\n                public void onPrepare(WindowInsetsAnimationCompat animation) {\n                    animationRunning = true;\n                }\n\n                @Override\n                public void onEnd(WindowInsetsAnimationCompat animation) {\n                    animationRunning = false;\n                }\n\n                @Override\n                public WindowInsetsCompat onProgress(\n                        WindowInsetsCompat insets,\n                        List<WindowInsetsAnimationCompat> runningAnimations) {\n\n                    Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime());\n                    Insets nav = insets.getInsets(WindowInsetsCompat.Type.navigationBars());\n\n                    int keyboardHeight = Math.max(0, ime.bottom - nav.bottom);\n\n                    contentView.setPadding(contentView.getPaddingLeft(), contentView.getPaddingTop(), contentView.getPaddingRight(), keyboardHeight);\n\n                    return insets;\n                }\n            }\n        );\n    }\n}"
  },
  {
    "path": "src/plugins/system/android/com/foxdebug/system/System.java",
    "content": "package com.foxdebug.system;\n\nimport static android.os.Build.VERSION.SDK_INT;\n\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.io.IOException;\nimport android.app.Activity;\nimport android.app.PendingIntent;\nimport android.content.ClipData;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.content.*;\nimport android.content.pm.*;\nimport java.util.*;\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\nimport android.graphics.ImageDecoder;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.PowerManager;\nimport android.provider.Settings.Global;\nimport android.util.Base64;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowInsetsController;\nimport android.view.inputmethod.InputMethodManager;\nimport android.webkit.WebView;\nimport androidx.core.content.FileProvider;\nimport androidx.core.content.pm.ShortcutInfoCompat;\nimport androidx.core.content.pm.ShortcutManagerCompat;\nimport androidx.core.graphics.drawable.IconCompat;\nimport com.foxdebug.system.Ui.Theme;\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPlugin;\nimport org.apache.cordova.CordovaWebView;\nimport org.apache.cordova.PluginResult;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.Path;\nimport android.content.Context;\nimport android.net.Uri;\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.IOException;\nimport android.webkit.MimeTypeMap;\n\n// DocumentFile import (AndroidX library)\nimport androidx.documentfile.provider.DocumentFile;\n\n// Java I/O imports\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.IOException;\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\n\nimport android.os.Build;\nimport android.os.Environment;\nimport android.Manifest;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.provider.DocumentsContract;\nimport android.provider.Settings;\n\nimport androidx.core.content.ContextCompat;\n\n\nimport android.graphics.Paint;\nimport android.graphics.RectF;\nimport android.graphics.Typeface;\nimport android.graphics.Canvas;\nimport android.util.TypedValue;\n\nimport android.provider.MediaStore;\nimport android.graphics.Bitmap;\nimport android.os.Build;\nimport android.graphics.ImageDecoder;\n\n\nimport java.security.MessageDigest;\nimport java.security.MessageDigest;\n\n\n\npublic class System extends CordovaPlugin {\n    private static final String TAG = \"SystemPlugin\";\n\n    private CallbackContext requestPermissionCallback;\n    private Activity activity;\n    private Context context;\n    private int REQ_PERMISSIONS = 1;\n    private int REQ_PERMISSION = 2;\n    private int systemBarColor = 0xFF000000;\n    private Theme theme;\n    private CallbackContext intentHandler;\n    private CordovaWebView webView;\n    private String fileProviderAuthority;\n    private RewardPassManager rewardPassManager;\n\n    public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n        super.initialize(cordova, webView);\n        this.context = cordova.getContext();\n        this.activity = cordova.getActivity();\n        this.webView = webView;\n        this.rewardPassManager = new RewardPassManager(this.context);\n        this.activity.runOnUiThread(\n            new Runnable() {\n                @Override\n                public void run() {\n                    setNativeContextMenuDisabled(false);\n                }\n            }\n        );\n\n        // Set up global exception handler\n        Thread.setDefaultUncaughtExceptionHandler(\n            new Thread.UncaughtExceptionHandler() {\n                @Override\n                public void uncaughtException(Thread thread, Throwable ex) {\n                    StringWriter sw = new StringWriter();\n                    PrintWriter pw = new PrintWriter(sw);\n                    ex.printStackTrace(pw);\n                    String stackTrace = sw.toString();\n\n                    String errorMsg = String.format(\n                        \"Uncaught Exception: %s\\nStack trace: %s\",\n                        ex.getMessage(),\n                        stackTrace\n                    );\n\n                    sendLogToJavaScript(\"error\", errorMsg);\n\n                    // rethrow to the default handler\n                    Thread.getDefaultUncaughtExceptionHandler()\n                        .uncaughtException(thread, ex);\n                }\n            }\n        );\n    }\n\n    public boolean execute(\n        String action,\n        final JSONArray args,\n        final CallbackContext callbackContext\n    ) throws JSONException {\n        final String arg1 = args.optString(0);\n        final String arg2 = args.optString(1);\n        final String arg3 = args.optString(2);\n        final String arg4 = args.optString(3);\n        final String arg5 = args.optString(4);\n        final String arg6 = args.optString(5);\n\n        switch (action) {\n            case \"get-webkit-info\":\n            case \"file-action\":\n            case \"checksumText\":\n            case \"is-powersave-mode\":\n            case \"get-app-info\":\n            case \"add-shortcut\":\n            case \"remove-shortcut\":\n            case \"pin-shortcut\":\n            case \"get-android-version\":\n            case \"request-permissions\":\n            case \"request-permission\":\n            case \"has-permission\":\n            case \"open-in-browser\":\n            case \"launch-app\":\n            case \"get-global-setting\":\n            case \"get-available-encodings\":\n            case \"decode\":\n            case \"encode\":\n            case \"copyToUri\":\n            case \"compare-file-text\":\n            case \"compare-texts\":\n            case \"pin-file-shortcut\":\n                break;\n            case \"get-configuration\":\n                getConfiguration(callbackContext);\n                return true;\n            case \"set-input-type\":\n                setInputType(arg1);\n                callbackContext.success();\n                return true;\n            case \"set-native-context-menu-disabled\":\n                this.cordova.getActivity()\n                    .runOnUiThread(\n                        new Runnable() {\n                            @Override\n                            public void run() {\n                                setNativeContextMenuDisabled(Boolean.parseBoolean(arg1));\n                                callbackContext.success();\n                            }\n                        }\n                    );\n                return true;\n            case \"get-cordova-intent\":\n                getCordovaIntent(callbackContext);\n                return true;\n            case \"set-intent-handler\":\n                setIntentHandler(callbackContext);\n                return true;\n            case \"set-ui-theme\":\n                this.cordova.getActivity()\n                    .runOnUiThread(\n                        new Runnable() {\n                            public void run() {\n                                setUiTheme(arg1, args.optJSONObject(1), callbackContext);\n                            }\n                        }\n                    );\n                return true;\n            case \"clear-cache\":\n                this.cordova.getActivity()\n                    .runOnUiThread(\n                        new Runnable() {\n                            public void run() {\n                                clearCache(callbackContext);\n                            }\n                        }\n                    );\n                return true;\n            case \"fileExists\":\n                callbackContext.success(fileExists(args.getString(0), args.getString(1)) ? 1 : 0);\n                return true;\n\n            case \"createSymlink\":\n                boolean success = createSymlink(args.getString(0), args.getString(1));\n                callbackContext.success(success ? 1 : 0);\n                return true;\n\n            case \"getNativeLibraryPath\":\n                callbackContext.success(getNativeLibraryPath());\n                return true;\n\n            case \"getFilesDir\":\n                callbackContext.success(getFilesDir());\n                return true;\n            case \"getRewardStatus\":\n                callbackContext.success(rewardPassManager.getRewardStatus());\n                return true;\n            case \"redeemReward\":\n                callbackContext.success(rewardPassManager.redeemReward(args.getString(0)));\n                return true;\n\n            case \"getParentPath\":\n                callbackContext.success(getParentPath(args.getString(0)));\n                return true;\n\n            case \"listChildren\":\n                callbackContext.success(listChildren(args.getString(0)));\n                return true;\n            case \"writeText\":\n                {\n                    try {\n                        String filePath = args.getString(0);\n                        String content = args.getString(1);\n\n                        Files.write(Paths.get(filePath),\n                            Collections.singleton(content),\n                            StandardOpenOption.CREATE,\n                            StandardOpenOption.TRUNCATE_EXISTING);\n\n                        callbackContext.success(\"File written successfully\");\n                    } catch (Exception e) {\n                        callbackContext.error(\"Failed to write file: \" + e.getMessage());\n                    }\n                    return true;\n                }\n\n            case \"getArch\":\n                String arch;\n\n                if (android.os.Build.VERSION.SDK_INT >= 21) {\n                    arch = android.os.Build.SUPPORTED_ABIS[0];\n                } else {\n                    arch = android.os.Build.CPU_ABI;\n                }\n\n                callbackContext.success(arch);\n                return true;\n\n            case \"requestStorageManager\":\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                    try {\n                        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);\n                        intent.setData(Uri.parse(\"package:\" + context.getPackageName()));\n                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                        context.startActivity(intent);\n                        callbackContext.success(\"true\");\n                    } catch (Exception e) {\n                        // Fallback to general settings if specific one fails\n                        Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);\n                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                        context.startActivity(intent);\n                        callbackContext.success(\"true\");\n                    }\n                } else {\n                    callbackContext.success(\"false\"); // Not needed on Android < 11\n                }\n                return true;\n\n\n            case \"hasGrantedStorageManager\":\n                boolean granted;\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                    granted = Environment.isExternalStorageManager();\n                } else {\n                    // Fallback for Android 10 and below\n                    granted = ContextCompat.checkSelfPermission(\n                        context,\n                        Manifest.permission.READ_EXTERNAL_STORAGE\n                    ) == PackageManager.PERMISSION_GRANTED;\n                }\n                callbackContext.success(String.valueOf(granted));\n                return true;\n\n            case \"isManageExternalStorageDeclared\":\n                PackageManager pm = context.getPackageManager();\n                try {\n                    PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);\n                    String[] permissions = info.requestedPermissions;\n                    String isDeclared = \"false\";\n\n                    if (permissions != null) {\n                        for (String perm: permissions) {\n                            if (perm.equals(\"android.permission.MANAGE_EXTERNAL_STORAGE\")) {\n                                isDeclared = \"true\";\n                                break;\n                            }\n                        }\n                    }\n                    callbackContext.success(isDeclared);\n\n                } catch (PackageManager.NameNotFoundException e) {\n                    e.printStackTrace();\n                    callbackContext.error(e.toString());\n                }\n\n                return true;\n            case \"mkdirs\":\n                if (new File(args.getString(0)).mkdirs()) {\n                    callbackContext.success();\n                } else {\n                    callbackContext.error(\"mkdirs failed\");\n                }\n                return true;\n            case \"deleteFile\":\n                if (new File(args.getString(0)).delete()) {\n                    callbackContext.success();\n                } else {\n                    callbackContext.error(\"delete failed\");\n                }\n                return true;\n            case \"setExec\":\n                if (new File(args.getString(0)).setExecutable(Boolean.parseBoolean(args.getString(1)))) {\n                    callbackContext.success();\n                } else {\n                    callbackContext.error(\"set exec failed\");\n                }\n\n                return true;\n            default:\n                return false;\n        }\n\n        cordova\n            .getThreadPool()\n            .execute(\n                new Runnable() {\n                    public void run() {\n                        switch (action) {\n                            case \"copyToUri\":\n                                try {\n                                    //srcUri is a file\n                                    Uri srcUri = Uri.parse(args.getString(0));\n\n                                    //destUri is a directory\n                                    Uri destUri = Uri.parse(args.getString(1));\n\n                                    //create a file named this into the dest Directory and copy the srcUri into it\n                                    String fileName = args.getString(2);\n\n                                    InputStream in = null;\n                                    OutputStream out = null;\n                                    try {\n                                        // Open input stream from the source URI\n                                        if (\"file\".equalsIgnoreCase(srcUri.getScheme())) {\n                                            File file = new File(srcUri.getPath()); in = new FileInputStream(file);\n                                        } else { in = context.getContentResolver().openInputStream(srcUri);\n                                        }\n\n                                        // Create the destination file using DocumentFile for better URI handling\n                                        DocumentFile destFile = null;\n\n                                        if (\"file\".equalsIgnoreCase(destUri.getScheme())) {\n                                            // Handle file:// scheme using DocumentFile\n                                            File destDir = new File(destUri.getPath());\n                                            if (!destDir.exists()) {\n                                                destDir.mkdirs(); // Create directory if it doesn't exist\n                                            }\n                                            DocumentFile destDocDir = DocumentFile.fromFile(destDir);\n\n                                            // Check if file already exists and delete it\n                                            DocumentFile existingFile = destDocDir.findFile(fileName);\n                                            if (existingFile != null && existingFile.exists()) {\n                                                existingFile.delete();\n                                            }\n\n                                            // Create new file\n                                            String mimeType = getMimeTypeFromExtension(fileName);\n                                            destFile = destDocDir.createFile(mimeType, fileName);\n                                        } else {\n                                            // Handle content:// scheme using DocumentFile\n                                            DocumentFile destDocDir = DocumentFile.fromTreeUri(context, destUri);\n\n                                            if (destDocDir == null || !destDocDir.exists() || !destDocDir.isDirectory()) {\n                                                callbackContext.error(\"Destination directory does not exist or is not accessible\");\n                                                return;\n                                            }\n\n                                            // Check if file already exists and delete it\n                                            DocumentFile existingFile = destDocDir.findFile(fileName);\n                                            if (existingFile != null && existingFile.exists()) {\n                                                existingFile.delete();\n                                            }\n\n                                            // Create new file\n                                            String mimeType = getMimeTypeFromExtension(fileName);\n                                            destFile = destDocDir.createFile(mimeType, fileName);\n                                        }\n\n                                        if (destFile == null || !destFile.exists()) {\n                                            callbackContext.error(\"Failed to create destination file\");\n                                            return;\n                                        }\n\n                                        // Open output stream to the created file\n                                        out = context.getContentResolver().openOutputStream(destFile.getUri());\n\n                                        if ( in == null || out == null) {\n                                            callbackContext.error(\"uri streams are null\");\n                                            return;\n                                        }\n\n                                        // Copy stream\n                                        byte[] buffer = new byte[8192];\n                                        int len;\n                                        while ((len = in .read(buffer)) > 0) {\n                                            out.write(buffer, 0, len);\n                                        }\n\n                                        out.flush();\n                                        callbackContext.success();\n                                    } catch (IOException e) {\n                                        e.printStackTrace();\n                                        callbackContext.error(e.toString());\n                                    } finally {\n                                        try {\n                                            if ( in != null) in .close();\n                                            if (out != null) out.close();\n                                        } catch (IOException e) {\n                                            e.printStackTrace();\n                                            callbackContext.error(e.toString());\n                                        }\n                                    }\n                                } catch (Exception e) {\n                                    e.printStackTrace();\n                                    callbackContext.error(e.toString());\n                                }\n                                break;\n                            case \"get-webkit-info\":\n                                getWebkitInfo(callbackContext);\n                                break;\n                            case \"file-action\":\n                                fileAction(arg1, arg2, arg3, arg4, callbackContext);\n                                break;\n                            case \"is-powersave-mode\":\n                                isPowerSaveMode(callbackContext);\n                                break;\n                            case \"get-app-info\":\n                                getAppInfo(callbackContext);\n                                break;\n                            case \"pin-file-shortcut\":\n                                pinFileShortcut(args.optJSONObject(0), callbackContext);\n                                break;\n                            case \"add-shortcut\":\n                                addShortcut(\n                                    arg1,\n                                    arg2,\n                                    arg3,\n                                    arg4,\n                                    arg5,\n                                    arg6,\n                                    callbackContext\n                                );\n                                break;\n                            case \"remove-shortcut\":\n                                removeShortcut(arg1, callbackContext);\n                                break;\n                            case \"pin-shortcut\":\n                                pinShortcut(arg1, callbackContext);\n                                break;\n                            case \"get-android-version\":\n                                getAndroidVersion(callbackContext);\n                                break;\n                            case \"request-permissions\":\n                                requestPermissions(args.optJSONArray(0), callbackContext);\n                                break;\n                            case \"request-permission\":\n                                requestPermission(arg1, callbackContext);\n                                break;\n                            case \"has-permission\":\n                                hasPermission(arg1, callbackContext);\n                                break;\n                            case \"open-in-browser\":\n                                openInBrowser(arg1, callbackContext);\n                                break;\n                            case \"launch-app\":\n                                launchApp(arg1, arg2, args.optJSONObject(2), callbackContext);\n                                break;\n                            case \"get-global-setting\":\n                                getGlobalSetting(arg1, callbackContext);\n                                break;\n                            case \"get-available-encodings\":\n                                getAvailableEncodings(callbackContext);\n                                break;\n                            case \"decode\":\n                                decode(arg1, arg2, callbackContext);\n                                break;\n                            case \"encode\":\n                                encode(arg1, arg2, callbackContext);\n                                break;\n                            case \"compare-file-text\":\n                                compareFileText(arg1, arg2, arg3, callbackContext);\n                                break;\n                            case \"compare-texts\":\n                                compareTexts(arg1, arg2, callbackContext);\n                                break;\n                            case \"checksumText\":\n                            \n                                cordova.getThreadPool().execute(() -> {\n                                    try {\n                                        \n                                        MessageDigest digest = MessageDigest.getInstance(\"SHA-256\");\n\n                                        byte[] hash = digest.digest(args.getString(0).getBytes(\"UTF-8\"));\n\n                                        StringBuilder hexString = new StringBuilder();\n\n                                        for (byte b : hash) {\n                                            String hex = Integer.toHexString(0xff & b);\n\n                                            if (hex.length() == 1) hexString.append('0');\n\n                                            hexString.append(hex);\n                                        }\n\n\n                                        callbackContext.success(hexString.toString());\n                                    } catch (Exception e) {\n                                        callbackContext.error(e.getMessage());\n                                    }\n                                });\n\n                                break;\n                            default:\n                                break;\n                        }\n                    }\n                }\n            );\n\n        return true;\n    }\n\n    private void sendLogToJavaScript(String level, String message) {\n        final String js =\n            \"window.log('\" + level + \"', \" + JSONObject.quote(message) + \");\";\n        cordova\n            .getActivity()\n            .runOnUiThread(\n                new Runnable() {\n                    @Override\n                    public void run() {\n                        webView.loadUrl(\"javascript:\" + js);\n                    }\n                }\n            );\n    }\n\n    // Helper method to determine MIME type using Android's built-in MimeTypeMap\n    private String getMimeTypeFromExtension(String fileName) {\n        String extension = \"\";\n        int lastDotIndex = fileName.lastIndexOf('.');\n        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {\n            extension = fileName.substring(lastDotIndex + 1).toLowerCase();\n        }\n\n        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);\n        return mimeType != null ? mimeType : \"application/octet-stream\";\n    }\n\n    private void getConfiguration(CallbackContext callback) {\n        try {\n            JSONObject result = new JSONObject();\n            Configuration config = context.getResources().getConfiguration();\n            InputMethodManager imm = (InputMethodManager) context.getSystemService(\n                Context.INPUT_METHOD_SERVICE\n            );\n            Method method =\n                InputMethodManager.class.getMethod(\"getInputMethodWindowVisibleHeight\");\n\n            result.put(\"isAcceptingText\", imm.isAcceptingText());\n            result.put(\"keyboardHeight\", method.invoke(imm));\n            result.put(\"locale\", config.locale.toString());\n            result.put(\"fontScale\", config.fontScale);\n            result.put(\"keyboard\", config.keyboard);\n            result.put(\"keyboardHidden\", config.keyboardHidden);\n            result.put(\"hardKeyboardHidden\", config.hardKeyboardHidden);\n            result.put(\"navigationHidden\", config.navigationHidden);\n            result.put(\"navigation\", config.navigation);\n            result.put(\"orientation\", config.orientation);\n            callback.success(result);\n        } catch (\n            JSONException |\n            NoSuchMethodException |\n            IllegalAccessException |\n            InvocationTargetException error\n        ) {\n            callback.error(error.toString());\n        }\n    }\n\n    private void decode(\n        String content, // base64 encoded string\n        String charSetName,\n        CallbackContext callback\n    ) {\n        try {\n            byte[] bytes = Base64.decode(content, Base64.DEFAULT);\n\n            if (Charset.isSupported(charSetName) == false) {\n                callback.error(\"Charset not supported: \" + charSetName);\n                return;\n            }\n\n            Charset charSet = Charset.forName(charSetName);\n            CharBuffer charBuffer = charSet.decode(ByteBuffer.wrap(bytes));\n            String result = String.valueOf(charBuffer);\n            callback.success(result);\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void encode(\n        String content, // string to encode\n        String charSetName,\n        CallbackContext callback\n    ) {\n        try {\n            if (Charset.isSupported(charSetName) == false) {\n                callback.error(\"Charset not supported: \" + charSetName);\n                return;\n            }\n\n            Charset charSet = Charset.forName(charSetName);\n            ByteBuffer byteBuffer = charSet.encode(content);\n            byte[] bytes = new byte[byteBuffer.remaining()];\n            byteBuffer.get(bytes);\n            callback.success(bytes);\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    /**\n     * Compares file content with provided text.\n     * This method runs in a background thread to avoid blocking the UI.\n     * \n     * @param fileUri The URI of the file to read (file:// or content://)\n     * @param encoding The character encoding to use when reading the file\n     * @param currentText The text to compare against the file content\n     * @param callback Returns 1 if texts are different, 0 if same\n     */\n    private void compareFileText(\n        String fileUri,\n        String encoding,\n        String currentText,\n        CallbackContext callback\n    ) {\n        try {\n            if (fileUri == null || fileUri.isEmpty()) {\n                callback.error(\"File URI is required\");\n                return;\n            }\n\n            if (encoding == null || encoding.isEmpty()) {\n                encoding = \"UTF-8\";\n            }\n\n            if (!Charset.isSupported(encoding)) {\n                callback.error(\"Charset not supported: \" + encoding);\n                return;\n            }\n\n            Uri uri = Uri.parse(fileUri);\n            Charset charset = Charset.forName(encoding);\n            String fileContent;\n\n            // Handle file:// URIs\n            if (\"file\".equalsIgnoreCase(uri.getScheme())) {\n                File file = new File(uri.getPath());\n                \n                // Validate file\n                if (!file.exists()) {\n                    callback.error(\"File does not exist\");\n                    return;\n                }\n                if (!file.isFile()) {\n                    callback.error(\"Path is not a file\");\n                    return;\n                }\n                if (!file.canRead()) {\n                    callback.error(\"File is not readable\");\n                    return;\n                }\n\n                Path path = file.toPath();\n                fileContent = new String(Files.readAllBytes(path), charset);\n\n            } else if (\"content\".equalsIgnoreCase(uri.getScheme())) {\n                // Handle content:// URIs (including SAF tree URIs)\n                InputStream inputStream = null;\n                try {\n                    String uriString = fileUri;\n                    Uri resolvedUri = uri;\n                    \n                    // Check if this is a SAF tree URI with :: separator\n                    if (uriString.contains(\"::\")) {\n                        try {\n                            // Split into tree URI and document ID\n                            String[] parts = uriString.split(\"::\", 2);\n                            String treeUriStr = parts[0];\n                            String docId = parts[1];\n                            \n                            // Build document URI directly from tree URI and document ID\n                            Uri treeUri = Uri.parse(treeUriStr);\n                            resolvedUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, docId);\n                        } catch (Exception e) {\n                            callback.error(\"SAF_FALLBACK: Invalid SAF URI format - \" + e.getMessage());\n                            return;\n                        }\n                    }\n                    \n                    // Try to open the resolved URI\n                    inputStream = context.getContentResolver().openInputStream(resolvedUri);\n                    \n                    if (inputStream == null) {\n                        callback.error(\"Cannot open file\");\n                        return;\n                    }\n\n                    StringBuilder sb = new StringBuilder();\n                    try (BufferedReader reader = new BufferedReader(\n                            new InputStreamReader(inputStream, charset))) {\n                        char[] buffer = new char[8192];\n                        int charsRead;\n                        while ((charsRead = reader.read(buffer)) != -1) {\n                            sb.append(buffer, 0, charsRead);\n                        }\n                    }\n                    fileContent = sb.toString();\n                    \n                } finally {\n                    if (inputStream != null) {\n                        try {\n                            inputStream.close();\n                        } catch (IOException closeError) {\n                            Log.w(TAG, \"Failed to close input stream while reading file.\", closeError);\n                        }\n                    }\n                }\n            } else {\n                callback.error(\"Unsupported URI scheme: \" + uri.getScheme());\n                return;\n            }\n\n            // check length first \n            if (fileContent.length() != currentText.length()) {\n                callback.success(1); // Changed\n                return;\n            }\n\n            // Full comparison\n            if (fileContent.equals(currentText)) {\n                callback.success(0); // Not changed\n            } else {\n                callback.success(1); // Changed\n            }\n\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    /**\n     * Compares two text strings.\n     * This method runs in a background thread to avoid blocking the UI\n     * for large string comparisons.\n     * \n     * @param text1 First text to compare\n     * @param text2 Second text to compare\n     * @param callback Returns 1 if texts are different, 0 if same\n     */\n    private void compareTexts(\n        String text1,\n        String text2,\n        CallbackContext callback\n    ) {\n        try {\n            if (text1 == null) text1 = \"\";\n            if (text2 == null) text2 = \"\";\n\n            // check length first\n            if (text1.length() != text2.length()) {\n                callback.success(1); // Changed\n                return;\n            }\n\n            // Full comparison\n            if (text1.equals(text2)) {\n                callback.success(0); // Not changed\n            } else {\n                callback.success(1); // Changed\n            }\n\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void getAvailableEncodings(CallbackContext callback) {\n        try {\n            Map < String, Charset > charsets = Charset.availableCharsets();\n            JSONObject result = new JSONObject();\n            for (Map.Entry < String, Charset > entry: charsets.entrySet()) {\n                JSONObject obj = new JSONObject();\n                Charset charset = entry.getValue();\n                obj.put(\"label\", charset.displayName());\n                obj.put(\"aliases\", new JSONArray(charset.aliases()));\n                obj.put(\"name\", charset.name());\n                result.put(charset.name(), obj);\n            }\n            callback.success(result);\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void requestPermissions(JSONArray arr, CallbackContext callback) {\n        try {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n                int[] res = new int[arr.length()];\n                for (int i = 0; i < res.length; ++i) {\n                    res[i] = 1;\n                }\n                callback.success(1);\n                return;\n            }\n\n            String[] permissions = checkPermissions(arr);\n\n            if (permissions.length > 0) {\n                requestPermissionCallback = callback;\n                cordova.requestPermissions(this, REQ_PERMISSIONS, permissions);\n                return;\n            }\n            callback.success(new JSONArray());\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void requestPermission(String permission, CallbackContext callback) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            callback.success(1);\n            return;\n        }\n\n        if (permission != null || !permission.equals(\"\")) {\n            if (!cordova.hasPermission(permission)) {\n                requestPermissionCallback = callback;\n                cordova.requestPermission(this, REQ_PERMISSION, permission);\n                return;\n            }\n\n            callback.success(1);\n            return;\n        }\n\n        callback.error(\"No permission passed to request.\");\n    }\n\n    private void hasPermission(String permission, CallbackContext callback) {\n        if (permission != null || !permission.equals(\"\")) {\n            int res = 0;\n            if (cordova.hasPermission(permission)) {\n                res = 1;\n            }\n\n            callback.success(res);\n            return;\n        }\n        callback.error(\"No permission passed to check.\");\n    }\n\n    public boolean fileExists(String path, String countSymlinks) {\n        boolean followSymlinks = !Boolean.parseBoolean(countSymlinks);\n        File file = new File(path);\n\n        // Android < O does not implement File#toPath(), fall back to legacy checks\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n            if (!file.exists()) return false;\n            if (followSymlinks) {\n                try {\n                    // If canonical and absolute paths differ, it's a symlink\n                    return file.getCanonicalPath().equals(file.getAbsolutePath());\n                } catch (IOException ignored) {\n                    return false;\n                }\n            }\n            return true;\n        }\n\n        Path p = file.toPath();\n        try {\n            if (followSymlinks) {\n                return Files.exists(p) && !Files.isSymbolicLink(p);\n            } else {\n                return Files.exists(p, LinkOption.NOFOLLOW_LINKS);\n            }\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    public boolean createSymlink(String target, String linkPath) {\n        try {\n            Process process = Runtime.getRuntime().exec(new String[] {\n                \"ln\",\n                \"-s\",\n                target,\n                linkPath\n            });\n            return process.waitFor() == 0;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    public String getNativeLibraryPath() {\n        ApplicationInfo appInfo = context.getApplicationInfo();\n        return appInfo.nativeLibraryDir;\n    }\n\n    public String getFilesDir() {\n        return context.getFilesDir().getAbsolutePath();\n    }\n\n    public String getParentPath(String path) {\n        File file = new File(path);\n        File parent = file.getParentFile();\n        return parent != null ? parent.getAbsolutePath() : null;\n    }\n\n    public JSONArray listChildren(String path) throws JSONException {\n        File dir = new File(path);\n        JSONArray result = new JSONArray();\n        if (dir.exists() && dir.isDirectory()) {\n            File[] files = dir.listFiles();\n            if (files != null) {\n                for (File file: files) {\n                    result.put(file.getAbsolutePath());\n                }\n            }\n        }\n        return result;\n    }\n\n    public void onRequestPermissionResult(\n        int code,\n        String[] permissions,\n        int[] resCodes\n    ) {\n        if (requestPermissionCallback == null) return;\n\n        if (code == REQ_PERMISSIONS) {\n            JSONArray resAr = new JSONArray();\n            for (int res: resCodes) {\n                if (res == PackageManager.PERMISSION_DENIED) {\n                    resAr.put(0);\n                }\n                resAr.put(1);\n            }\n\n            requestPermissionCallback.success(resAr);\n            requestPermissionCallback = null;\n            return;\n        }\n\n        if (\n            resCodes.length >= 1 && resCodes[0] == PackageManager.PERMISSION_DENIED\n        ) {\n            requestPermissionCallback.success(0);\n            requestPermissionCallback = null;\n            return;\n        }\n        requestPermissionCallback.success(1);\n        requestPermissionCallback = null;\n        return;\n    }\n\n    private String[] checkPermissions(JSONArray arr) throws Exception {\n        List < String > list = new ArrayList < String > ();\n        for (int i = 0; i < arr.length(); i++) {\n            try {\n                String permission = arr.getString(i);\n                if (permission == null || permission.equals(\"\")) {\n                    throw new Exception(\"Permission cannot be null or empty\");\n                }\n                if (!cordova.hasPermission(permission)) {\n                    list.add(permission);\n                }\n            } catch (JSONException e) {\n                Log.w(TAG, \"Invalid permission entry at index \" + i, e);\n            }\n        }\n\n        String[] res = new String[list.size()];\n        return list.toArray(res);\n    }\n\n    private void getAndroidVersion(CallbackContext callback) {\n        callback.success(Build.VERSION.SDK_INT);\n    }\n\n    private void getWebkitInfo(CallbackContext callback) {\n        PackageInfo info = null;\n        JSONObject res = new JSONObject();\n\n        try {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                info = WebView.getCurrentWebViewPackage();\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                Class webViewFactory = Class.forName(\"android.webkit.WebViewFactory\");\n                Method method = webViewFactory.getMethod(\"getLoadedPackageInfo\");\n                info = (PackageInfo) method.invoke(null);\n            } else {\n                PackageManager packageManager = activity.getPackageManager();\n\n                try {\n                    info = packageManager.getPackageInfo(\"com.google.android.webview\", 0);\n                } catch (PackageManager.NameNotFoundException e) {\n                    callback.error(\"Package not found\");\n                }\n\n                return;\n            }\n\n            res.put(\"packageName\", info.packageName);\n            res.put(\"versionName\", info.versionName);\n            res.put(\"versionCode\", info.versionCode);\n\n            callback.success(res);\n        } catch (\n            JSONException |\n            InvocationTargetException |\n            ClassNotFoundException |\n            NoSuchMethodException |\n            IllegalAccessException e\n        ) {\n            callback.error(\n                \"Cannot determine current WebView engine. (\" + e.getMessage() + \")\"\n            );\n\n            return;\n        }\n    }\n\n    private void isPowerSaveMode(CallbackContext callback) {\n        PowerManager powerManager = (PowerManager) context.getSystemService(\n            Context.POWER_SERVICE\n        );\n        boolean powerSaveMode = powerManager.isPowerSaveMode();\n\n        callback.success(powerSaveMode ? 1 : 0);\n    }\n\n    private void pinFileShortcut(JSONObject shortcutJson, CallbackContext callback) {\n        if (shortcutJson == null) {\n            callback.error(\"Invalid shortcut data\");\n            return;\n        }\n\n        String id = shortcutJson.optString(\"id\", \"\");\n        String label = shortcutJson.optString(\"label\", \"\");\n        String description = shortcutJson.optString(\"description\", label);\n        String iconSrc = shortcutJson.optString(\"icon\", \"\");\n        String uriString = shortcutJson.optString(\"uri\", \"\");\n\n        if (id.isEmpty() || label.isEmpty() || uriString.isEmpty()) {\n            callback.error(\"Missing required shortcut fields\");\n            return;\n        }\n\n        if (!ShortcutManagerCompat.isRequestPinShortcutSupported(context)) {\n            callback.error(\"Pin shortcut not supported on this launcher\");\n            return;\n        }\n\n        try {\n            Uri dataUri = Uri.parse(uriString);\n            String packageName = context.getPackageName();\n            PackageManager pm = context.getPackageManager();\n\n            Intent launchIntent = pm.getLaunchIntentForPackage(packageName);\n            if (launchIntent == null) {\n                callback.error(\"Launch intent not found for package: \" + packageName);\n                return;\n            }\n\n            ComponentName componentName = launchIntent.getComponent();\n            if (componentName == null) {\n                callback.error(\"ComponentName is null\");\n                return;\n            }\n\n            Intent intent = new Intent(Intent.ACTION_VIEW);\n            intent.setComponent(componentName);\n            intent.setData(dataUri);\n            intent.putExtra(\"acodeFileUri\", uriString);\n\n            IconCompat icon;\n\n            if (iconSrc != null && !iconSrc.isEmpty()) {\n                try {\n                    Uri iconUri = Uri.parse(iconSrc);\n                    Bitmap bitmap;\n\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                        // API 28+\n                        ImageDecoder.Source source =\n                                ImageDecoder.createSource(\n                                        context.getContentResolver(),\n                                        iconUri\n                                );\n                        bitmap = ImageDecoder.decodeBitmap(source);\n                    } else {\n                        // Below API 28\n                        bitmap = MediaStore.Images.Media.getBitmap(\n                                context.getContentResolver(),\n                                iconUri\n                        );\n                    }\n\n                    icon = IconCompat.createWithBitmap(bitmap);\n\n                } catch (Exception e) {\n                    icon = getFileShortcutIcon(label);\n                }\n            } else {\n                icon = getFileShortcutIcon(label);\n            }\n\n            ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(context, id)\n                .setShortLabel(label)\n                .setLongLabel(\n                    description != null && !description.isEmpty() ? description : label\n                )\n                .setIcon(icon)\n                .setIntent(intent)\n                .build();\n\n            ShortcutManagerCompat.pushDynamicShortcut(context, shortcut);\n\n            boolean requested = ShortcutManagerCompat.requestPinShortcut(\n                context,\n                shortcut,\n                null\n            );\n\n            if (!requested) {\n                callback.error(\"Failed to request pin shortcut\");\n                return;\n            }\n\n            callback.success();\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private IconCompat getFileShortcutIcon(String filename) {\n        Bitmap fallback = createFileShortcutBitmap(filename);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            return IconCompat.createWithAdaptiveBitmap(fallback);\n        }\n        return IconCompat.createWithBitmap(fallback);\n    }\n\n    private Bitmap createFileShortcutBitmap(String filename) {\n        final float baseSizeDp = 72f;\n        float sizePx = TypedValue.applyDimension(\n            TypedValue.COMPLEX_UNIT_DIP,\n            baseSizeDp,\n            context.getResources().getDisplayMetrics()\n        );\n        if (sizePx <= 0) {\n            sizePx = baseSizeDp;\n        }\n        int size = Math.round(sizePx);\n        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);\n        Canvas canvas = new Canvas(bitmap);\n        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);\n\n        int backgroundColor = pickShortcutColor(filename);\n        paint.setColor(backgroundColor);\n        float radius = size * 0.24f;\n        RectF bounds = new RectF(0, 0, size, size);\n        canvas.drawRoundRect(bounds, radius, radius, paint);\n\n        paint.setColor(Color.WHITE);\n        paint.setTextAlign(Paint.Align.CENTER);\n        paint.setTypeface(Typeface.create(\"sans-serif-medium\", Typeface.BOLD));\n\n        String label = getShortcutLabel(filename);\n        float textLength = Math.max(1, label.length());\n        float factor = textLength > 4 ? 0.22f : textLength > 3 ? 0.26f : 0.34f;\n        paint.setTextSize(size * factor);\n        Paint.FontMetrics metrics = paint.getFontMetrics();\n        float baseline = (size - metrics.bottom - metrics.top) / 2f;\n        canvas.drawText(label, size / 2f, baseline, paint);\n\n        return bitmap;\n    }\n\n    private String getFileExtension(String filename) {\n        if (filename == null) return \"\";\n        int dot = filename.lastIndexOf('.');\n        if (dot < 0 || dot == filename.length() - 1) return \"\";\n        return filename.substring(dot + 1).toLowerCase(Locale.getDefault());\n    }\n\n    private String getShortcutLabel(String filename) {\n        String ext = getFileExtension(filename);\n        if (!ext.isEmpty()) {\n            switch (ext) {\n                case \"js\":\n                case \"jsx\":\n                    return \"JS\";\n                case \"ts\":\n                case \"tsx\":\n                    return \"TS\";\n                case \"md\":\n                case \"markdown\":\n                    return \"MD\";\n                case \"json\":\n                    return \"JSON\";\n                case \"html\":\n                case \"htm\":\n                    return \"HTML\";\n                case \"css\":\n                    return \"CSS\";\n                case \"java\":\n                    return \"JAVA\";\n                case \"kt\":\n                case \"kts\":\n                    return \"KOT\";\n                case \"py\":\n                    return \"PY\";\n                case \"rb\":\n                    return \"RB\";\n                case \"c\":\n                    return \"C\";\n                case \"cpp\":\n                case \"cc\":\n                case \"cxx\":\n                    return \"CPP\";\n                case \"h\":\n                case \"hpp\":\n                    return \"HDR\";\n                case \"go\":\n                    return \"GO\";\n                case \"rs\":\n                    return \"RS\";\n                case \"php\":\n                    return \"PHP\";\n                case \"xml\":\n                    return \"XML\";\n                case \"yml\":\n                case \"yaml\":\n                    return \"YML\";\n                case \"txt\":\n                    return \"TXT\";\n                case \"sh\":\n                case \"bash\":\n                    return \"SH\";\n                default:\n                    String label = ext.replaceAll(\"[^A-Za-z0-9]\", \"\");\n                    if (label.isEmpty()) label = ext;\n                    if (label.length() > 4) {\n                        label = label.substring(0, 4);\n                    }\n                    return label.toUpperCase(Locale.getDefault());\n            }\n        }\n\n        if (filename != null && !filename.trim().isEmpty()) {\n            String cleaned = filename.replaceAll(\"[^A-Za-z0-9]\", \"\");\n            if (!cleaned.isEmpty()) {\n                if (cleaned.length() > 3) cleaned = cleaned.substring(0, 3);\n                return cleaned.toUpperCase(Locale.getDefault());\n            }\n            return filename.substring(0, 1).toUpperCase(Locale.getDefault());\n        }\n\n        return \"FILE\";\n    }\n\n    private int pickShortcutColor(String filename) {\n        String ext = getFileExtension(filename);\n        switch (ext) {\n            case \"js\":\n            case \"jsx\":\n                return 0xFFF7DF1E;\n            case \"ts\":\n            case \"tsx\":\n                return 0xFF3178C6;\n            case \"md\":\n            case \"markdown\":\n                return 0xFF546E7A;\n            case \"json\":\n                return 0xFF4CAF50;\n            case \"html\":\n            case \"htm\":\n                return 0xFFF4511E;\n            case \"css\":\n                return 0xFF2962FF;\n            case \"java\":\n                return 0xFFEC6F2D;\n            case \"kt\":\n            case \"kts\":\n                return 0xFF7F52FF;\n            case \"py\":\n                return 0xFF306998;\n            case \"rb\":\n                return 0xFFCC342D;\n            case \"c\":\n                return 0xFF546E7A;\n            case \"cpp\":\n            case \"cc\":\n            case \"cxx\":\n                return 0xFF00599C;\n            case \"h\":\n            case \"hpp\":\n                return 0xFF8D6E63;\n            case \"go\":\n                return 0xFF00ADD8;\n            case \"rs\":\n                return 0xFFB7410E;\n            case \"php\":\n                return 0xFF8892BF;\n            case \"xml\":\n                return 0xFF5C6BC0;\n            case \"yml\":\n            case \"yaml\":\n                return 0xFF757575;\n            case \"txt\":\n                return 0xFF546E7A;\n            case \"sh\":\n            case \"bash\":\n                return 0xFF388E3C;\n            default:\n                final int[] colors = new int[] {\n                    0xFF1E88E5,\n                    0xFF6D4C41,\n                    0xFF00897B,\n                    0xFF8E24AA,\n                    0xFF3949AB,\n                    0xFF039BE5,\n                    0xFFD81B60,\n                    0xFF43A047\n                };\n                String key = ext.isEmpty()\n                    ? (filename == null ? \"file\" : filename)\n                    : ext;\n                int hash = Math.abs(key.hashCode());\n                return colors[hash % colors.length];\n        }\n    }\n\n    private void fileAction(\n        String fileURI,\n        String filename,\n        String action,\n        String mimeType,\n        CallbackContext callback\n    ) {\n        Activity activity = this.activity;\n        Context context = this.context;\n        Uri uri = this.getContentProviderUri(fileURI, filename);\n        if (uri == null) {\n            callback.error(\"Unable to access file for action \" + action);\n            return;\n        }\n        try {\n            Intent intent = new Intent(action);\n\n            if (mimeType.equals(\"\")) {\n                mimeType = \"text/plain\";\n            }\n\n            mimeType = resolveMimeType(mimeType, uri, filename);\n\n            String clipLabel = null;\n            if (filename != null && !filename.isEmpty()) {\n                clipLabel = new File(filename).getName();\n            }\n            if (clipLabel == null || clipLabel.isEmpty()) {\n                clipLabel = uri.getLastPathSegment();\n            }\n            if (clipLabel == null || clipLabel.isEmpty()) {\n                clipLabel = \"shared-file\";\n            }\n            if (action.equals(Intent.ACTION_SEND)) {\n                intent.setType(mimeType);\n                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                intent.setClipData(\n                    ClipData.newUri(\n                        context.getContentResolver(),\n                        clipLabel,\n                        uri\n                    )\n                );\n                intent.putExtra(Intent.EXTRA_STREAM, uri);\n                intent.putExtra(Intent.EXTRA_TITLE, clipLabel);\n                intent.putExtra(Intent.EXTRA_SUBJECT, clipLabel);\n                if (filename != null && !filename.isEmpty()) {\n                    intent.putExtra(Intent.EXTRA_TEXT, filename);\n                }\n            } else {\n                int flags =\n                    Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |\n                    Intent.FLAG_GRANT_READ_URI_PERMISSION;\n\n                if (action.equals(Intent.ACTION_EDIT)) {\n                    flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;\n                }\n\n                intent.setFlags(flags);\n                intent.setDataAndType(uri, mimeType);\n                intent.setClipData(\n                    ClipData.newUri(\n                        context.getContentResolver(),\n                        clipLabel,\n                        uri\n                    )\n                );\n                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                if (!clipLabel.equals(\"shared-file\")) {\n                    intent.putExtra(Intent.EXTRA_TITLE, clipLabel);\n                }\n                if (action.equals(Intent.ACTION_EDIT)) {\n                    intent.putExtra(Intent.EXTRA_STREAM, uri);\n                }\n            }\n\n            int permissionFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION;\n            if (action.equals(Intent.ACTION_EDIT)) {\n                permissionFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;\n            }\n            grantUriPermissions(intent, uri, permissionFlags);\n\n            if (action.equals(Intent.ACTION_SEND)) {\n                Intent chooserIntent = Intent.createChooser(intent, null);\n                chooserIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                activity.startActivity(chooserIntent);\n            } else if (action.equals(Intent.ACTION_EDIT) || action.equals(Intent.ACTION_VIEW)) {\n                Intent chooserIntent = Intent.createChooser(intent, null);\n                chooserIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                if (action.equals(Intent.ACTION_EDIT)) {\n                    chooserIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n                }\n                activity.startActivity(chooserIntent);\n            } else {\n                activity.startActivity(intent);\n            }\n            callback.success(uri.toString());\n        } catch (Exception e) {\n            callback.error(e.getMessage());\n        }\n    }\n\n    private void getAppInfo(CallbackContext callback) {\n        JSONObject res = new JSONObject();\n        try {\n            PackageManager pm = activity.getPackageManager();\n            PackageInfo pInfo = pm.getPackageInfo(context.getPackageName(), 0);\n            ApplicationInfo appInfo = context.getApplicationInfo();\n            int isDebuggable = appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE;\n\n            res.put(\"firstInstallTime\", pInfo.firstInstallTime);\n            res.put(\"lastUpdateTime\", pInfo.lastUpdateTime);\n            res.put(\"label\", appInfo.loadLabel(pm).toString());\n            res.put(\"packageName\", pInfo.packageName);\n            res.put(\"versionName\", pInfo.versionName);\n            res.put(\"versionCode\", pInfo.getLongVersionCode());\n            res.put(\"isDebuggable\", isDebuggable);\n\n            callback.success(res);\n        } catch (JSONException e) {\n            callback.error(e.getMessage());\n        } catch (Exception e) {\n            callback.error(e.getMessage());\n        }\n    }\n\n    private void openInBrowser(String src, CallbackContext callback) {\n        Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(src));\n        activity.startActivity(browserIntent);\n    }\n\n    private void launchApp(\n        String appId,\n        String className,\n        JSONObject extras,\n        CallbackContext callback\n    ) {\n        if (appId == null || appId.equals(\"\")) {\n            callback.error(\"No package name provided.\");\n            return;\n        }\n\n        if (className == null || className.equals(\"\")) {\n            callback.error(\"No activity class name provided.\");\n            return;\n        }\n\n        try {\n            Intent intent = new Intent(Intent.ACTION_MAIN);\n            intent.addCategory(Intent.CATEGORY_LAUNCHER);\n            intent.setPackage(appId);\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            intent.setClassName(appId, className);\n\n            if (extras != null) {\n                Iterator<String> keys = extras.keys();\n\n                while (keys.hasNext()) {\n                    String key = keys.next();\n                    Object value = extras.get(key);\n\n                    if (value instanceof Integer) {\n                        intent.putExtra(key, (Integer) value);\n                    } else if (value instanceof Boolean) {\n                        intent.putExtra(key, (Boolean) value);\n                    } else if (value instanceof Double) {\n                        intent.putExtra(key, (Double) value);\n                    } else if (value instanceof Long) {\n                        intent.putExtra(key, (Long) value);\n                    } else if (value instanceof String) {\n                        intent.putExtra(key, (String) value);\n                    } else {\n                        intent.putExtra(key, value.toString());\n                    }\n                }\n            }\n\n            activity.startActivity(intent);\n            callback.success(\"Launched \" + appId);\n\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n\n    }\n\n\n    private void addShortcut(\n        String id,\n        String label,\n        String description,\n        String iconSrc,\n        String action,\n        String data,\n        CallbackContext callback\n    ) {\n        try {\n            Intent intent;\n            ImageDecoder.Source imgSrc;\n            Bitmap bitmap;\n            IconCompat icon;\n\n            imgSrc = ImageDecoder.createSource(\n                context.getContentResolver(),\n                Uri.parse(iconSrc)\n            );\n            bitmap = ImageDecoder.decodeBitmap(imgSrc);\n            icon = IconCompat.createWithBitmap(bitmap);\n            intent = activity\n                .getPackageManager()\n                .getLaunchIntentForPackage(activity.getPackageName());\n            intent.putExtra(\"action\", action);\n            intent.putExtra(\"data\", data);\n\n            ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(context, id)\n                .setShortLabel(label)\n                .setLongLabel(description)\n                .setIcon(icon)\n                .setIntent(intent)\n                .build();\n\n            ShortcutManagerCompat.pushDynamicShortcut(context, shortcut);\n            callback.success();\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void pinShortcut(String id, CallbackContext callback) {\n        ShortcutManager shortcutManager = context.getSystemService(\n            ShortcutManager.class\n        );\n\n        if (shortcutManager.isRequestPinShortcutSupported()) {\n            ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(\n                context,\n                id\n            ).build();\n\n            Intent pinnedShortcutCallbackIntent =\n                shortcutManager.createShortcutResultIntent(pinShortcutInfo);\n\n            PendingIntent successCallback = PendingIntent.getBroadcast(\n                context,\n                0,\n                pinnedShortcutCallbackIntent,\n                PendingIntent.FLAG_IMMUTABLE\n            );\n\n            shortcutManager.requestPinShortcut(\n                pinShortcutInfo,\n                successCallback.getIntentSender()\n            );\n\n            callback.success();\n            return;\n        }\n\n        callback.error(\"Not supported\");\n    }\n\n    private void removeShortcut(String id, CallbackContext callback) {\n        try {\n            List < String > list = new ArrayList < String > ();\n            list.add(id);\n            ShortcutManagerCompat.removeDynamicShortcuts(context, list);\n            callback.success();\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void setUiTheme(\n            final String systemBarColor,\n            final JSONObject scheme,\n            final CallbackContext callback\n    ) {\n        try {\n            this.systemBarColor = Color.parseColor(systemBarColor);\n            this.theme = new Theme(scheme);\n\n            preferences.set(\"BackgroundColor\", this.systemBarColor);\n            webView.getPluginManager().postMessage(\"updateSystemBars\", null);\n            applySystemBarTheme();\n\n            callback.success();\n        } catch (IllegalArgumentException e) {\n            callback.error(\"Invalid color: \" + systemBarColor);\n        } catch (Exception e) {\n            callback.error(e.toString());\n        }\n    }\n\n    private void applySystemBarTheme() {\n        final Window window = activity.getWindow();\n        final View decorView = window.getDecorView();\n\n        // Keep Cordova's BackgroundColor flow for API 36+, but also apply the\n        // window colors directly so OEM variants do not leave stale system-bar\n        // colors behind after a theme switch.\n        window.clearFlags(0x04000000 | 0x08000000); // FLAG_TRANSLUCENT_STATUS | FLAG_TRANSLUCENT_NAVIGATION\n        window.addFlags(0x80000000); // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            window.setNavigationBarContrastEnforced(false);\n            window.setStatusBarContrastEnforced(false);\n        }\n\n        decorView.setBackgroundColor(this.systemBarColor);\n\n        View rootView = activity.findViewById(android.R.id.content);\n        if (rootView != null) {\n            rootView.setBackgroundColor(this.systemBarColor);\n        }\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            window.setStatusBarColor(this.systemBarColor);\n            window.setNavigationBarColor(this.systemBarColor);\n        }\n\n        setStatusBarStyle(window);\n        setNavigationBarStyle(window);\n    }\n\n    private void setStatusBarStyle(final Window window) {\n        String themeType = theme.getType();\n        View decorView = window.getDecorView();\n        int uiOptions;\n        int lightStatusBar;\n\n        if (SDK_INT <= 30) {\n            uiOptions = getDeprecatedSystemUiVisibility(decorView);\n            lightStatusBar = deprecatedFlagUiLightStatusBar();\n\n            if (themeType.equals(\"light\")) {\n                setDeprecatedSystemUiVisibility(decorView, uiOptions | lightStatusBar);\n                return;\n            }\n            setDeprecatedSystemUiVisibility(decorView, uiOptions & ~lightStatusBar);\n            return;\n        }\n\n        uiOptions = Objects.requireNonNull(decorView.getWindowInsetsController()).getSystemBarsAppearance();\n        lightStatusBar = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;\n\n        if (themeType.equals(\"light\")) {\n            decorView.getWindowInsetsController().setSystemBarsAppearance(uiOptions | lightStatusBar, lightStatusBar);\n            return;\n        }\n\n        decorView.getWindowInsetsController().setSystemBarsAppearance(uiOptions & ~lightStatusBar, lightStatusBar);\n    }\n\n    private void setNavigationBarStyle(final Window window) {\n        String themeType = theme.getType();\n        View decorView = window.getDecorView();\n        int uiOptions;\n        int lightNavigationBar;\n\n        if (SDK_INT <= 30) {\n            uiOptions = getDeprecatedSystemUiVisibility(decorView);\n            lightNavigationBar = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;\n\n            if (themeType.equals(\"light\")) {\n                setDeprecatedSystemUiVisibility(decorView, uiOptions | lightNavigationBar);\n                return;\n            }\n            setDeprecatedSystemUiVisibility(decorView, uiOptions & ~lightNavigationBar);\n            return;\n        }\n\n        uiOptions = Objects.requireNonNull(decorView.getWindowInsetsController()).getSystemBarsAppearance();\n        lightNavigationBar = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;\n\n        if (themeType.equals(\"light\")) {\n            decorView.getWindowInsetsController().setSystemBarsAppearance(uiOptions | lightNavigationBar, lightNavigationBar);\n            return;\n        }\n\n        decorView.getWindowInsetsController().setSystemBarsAppearance(uiOptions & ~lightNavigationBar, lightNavigationBar);\n    }\n\n    private int deprecatedFlagUiLightStatusBar() {\n        return View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;\n    }\n\n    private int getDeprecatedSystemUiVisibility(View decorView) {\n        return decorView.getSystemUiVisibility();\n    }\n\n    private void setDeprecatedSystemUiVisibility(View decorView, int visibility) {\n        decorView.setSystemUiVisibility(visibility);\n    }\n\n    private void getCordovaIntent(CallbackContext callback) {\n        Intent intent = activity.getIntent();\n        callback.sendPluginResult(\n            new PluginResult(PluginResult.Status.OK, getIntentJson(intent))\n        );\n    }\n\n    private void setIntentHandler(CallbackContext callback) {\n        intentHandler = callback;\n        PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);\n        result.setKeepCallback(true);\n        callback.sendPluginResult(result);\n    }\n\n    @Override\n    public void onNewIntent(Intent intent) {\n        if (intentHandler != null) {\n            PluginResult result = new PluginResult(\n                PluginResult.Status.OK,\n                getIntentJson(intent)\n            );\n            result.setKeepCallback(true);\n            intentHandler.sendPluginResult(result);\n        }\n    }\n\n    private JSONObject getIntentJson(Intent intent) {\n        JSONObject json = new JSONObject();\n        try {\n            json.put(\"action\", intent.getAction());\n            json.put(\"data\", intent.getDataString());\n            json.put(\"type\", intent.getType());\n            json.put(\"package\", intent.getPackage());\n            json.put(\"extras\", getExtrasJson(intent.getExtras()));\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n        return json;\n    }\n\n    private JSONObject getExtrasJson(Bundle extras) {\n        JSONObject json = new JSONObject();\n        if (extras != null) {\n            for (String key: extras.keySet()) {\n                try {\n                    Object value = extras.get(key);\n                    if (value instanceof String) {\n                        json.put(key, (String) value);\n                    } else if (value instanceof Integer) {\n                        json.put(key, (Integer) value);\n                    } else if (value instanceof Long) {\n                        json.put(key, (Long) value);\n                    } else if (value instanceof Double) {\n                        json.put(key, (Double) value);\n                    } else if (value instanceof Float) {\n                        json.put(key, (Float) value);\n                    } else if (value instanceof Boolean) {\n                        json.put(key, (Boolean) value);\n                    } else if (value instanceof Bundle) {\n                        json.put(key, getExtrasJson((Bundle) value));\n                    } else {\n                        json.put(key, value.toString());\n                    }\n                } catch (JSONException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return json;\n    }\n\n    private Uri getContentProviderUri(String fileUri) {\n        return this.getContentProviderUri(fileUri, \"\");\n    }\n\n    private Uri getContentProviderUri(String fileUri, String filename) {\n        if (fileUri == null || fileUri.isEmpty()) {\n            return null;\n        }\n\n        Uri uri = Uri.parse(fileUri);\n        if (uri == null) {\n            return null;\n        }\n\n        if (\"file\".equalsIgnoreCase(uri.getScheme())) {\n            File originalFile = new File(uri.getPath());\n            if (!originalFile.exists()) {\n                Log.e(\"System\", \"File does not exist for URI: \" + fileUri);\n                return null;\n            }\n\n            String authority = getFileProviderAuthority();\n            if (authority == null) {\n                Log.e(\"System\", \"No FileProvider authority available.\");\n                return null;\n            }\n\n            try {\n                return FileProvider.getUriForFile(context, authority, originalFile);\n            } catch (IllegalArgumentException | SecurityException ex) {\n                try {\n                    File cacheCopy = ensureShareableCopy(originalFile, filename);\n                    return FileProvider.getUriForFile(context, authority, cacheCopy);\n                } catch (Exception copyError) {\n                    Log.e(\"System\", \"Failed to expose file via FileProvider\", copyError);\n                    return null;\n                }\n            }\n        }\n        return uri;\n    }\n\n    private File ensureShareableCopy(File source, String displayName) throws IOException {\n        File cacheRoot = new File(context.getCacheDir(), \"shared\");\n        if (!cacheRoot.exists() && !cacheRoot.mkdirs()) {\n            throw new IOException(\"Unable to create shared cache directory\");\n        }\n\n        if (displayName != null && !displayName.isEmpty()) {\n            displayName = new File(displayName).getName();\n        }\n        if (displayName == null || displayName.isEmpty()) {\n            displayName = source.getName();\n        }\n        if (displayName == null || displayName.isEmpty()) {\n            displayName = \"shared-file\";\n        }\n\n        File target = new File(cacheRoot, displayName);\n        target = ensureUniqueFile(target);\n        copyFile(source, target);\n        return target;\n    }\n\n    private File ensureUniqueFile(File target) {\n        if (!target.exists()) {\n            return target;\n        }\n\n        String name = target.getName();\n        String prefix = name;\n        String suffix = \"\";\n        int dotIndex = name.lastIndexOf('.');\n        if (dotIndex > 0) {\n            prefix = name.substring(0, dotIndex);\n            suffix = name.substring(dotIndex);\n        }\n\n        int index = 1;\n        File candidate = target;\n        while (candidate.exists()) {\n            candidate = new File(target.getParentFile(), prefix + \"-\" + index + suffix);\n            index++;\n        }\n        return candidate;\n    }\n\n    private void copyFile(File source, File destination) throws IOException {\n        try (\n            InputStream in = new FileInputStream(source);\n            OutputStream out = new FileOutputStream(destination)\n        ) {\n            byte[] buffer = new byte[8192];\n            int length;\n            while ((length = in.read(buffer)) != -1) {\n                out.write(buffer, 0, length);\n            }\n            out.flush();\n        }\n    }\n\n    private void grantUriPermissions(Intent intent, Uri uri, int flags) {\n        if (uri == null) return;\n        PackageManager pm = context.getPackageManager();\n        List<ResolveInfo> resInfoList = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);\n        for (ResolveInfo resolveInfo: resInfoList) {\n            String packageName = resolveInfo.activityInfo.packageName;\n            context.grantUriPermission(packageName, uri, flags);\n        }\n    }\n\n    private String resolveMimeType(String currentMime, Uri uri, String filename) {\n        if (currentMime != null && !currentMime.isEmpty() && !currentMime.equals(\"*/*\")) {\n            return currentMime;\n        }\n\n        String mime = null;\n        if (uri != null) {\n            mime = context.getContentResolver().getType(uri);\n        }\n\n        if ((mime == null || mime.isEmpty()) && filename != null) {\n            mime = getMimeTypeFromExtension(filename);\n        }\n\n        if ((mime == null || mime.isEmpty()) && uri != null) {\n            String path = uri.getPath();\n            if (path != null) {\n                mime = getMimeTypeFromExtension(path);\n            }\n        }\n\n        return (mime != null && !mime.isEmpty()) ? mime : \"*/*\";\n    }\n\n    private String getFileProviderAuthority() {\n        if (fileProviderAuthority != null && !fileProviderAuthority.isEmpty()) {\n            return fileProviderAuthority;\n        }\n\n        try {\n            PackageManager pm = context.getPackageManager();\n            PackageInfo packageInfo = pm.getPackageInfo(\n                context.getPackageName(),\n                PackageManager.GET_PROVIDERS\n            );\n            if (packageInfo.providers != null) {\n                for (ProviderInfo providerInfo: packageInfo.providers) {\n                    if (\n                        providerInfo != null &&\n                        providerInfo.name != null &&\n                        providerInfo.name.equals(FileProvider.class.getName())\n                    ) {\n                        fileProviderAuthority = providerInfo.authority;\n                        break;\n                    }\n                }\n            }\n        } catch (PackageManager.NameNotFoundException error) {\n            Log.w(TAG, \"Unable to inspect package providers for FileProvider authority.\", error);\n        }\n\n        if (fileProviderAuthority == null || fileProviderAuthority.isEmpty()) {\n            fileProviderAuthority = context.getPackageName() + \".provider\";\n        }\n\n        return fileProviderAuthority;\n    }\n\n    private boolean isPackageInstalled(\n        String packageName,\n        PackageManager packageManager,\n        CallbackContext callback\n    ) {\n        try {\n            packageManager.getPackageInfo(packageName, 0);\n            return true;\n        } catch (PackageManager.NameNotFoundException e) {\n            return false;\n        }\n    }\n\n    private void getGlobalSetting(String setting, CallbackContext callback) {\n        int value = (int) Global.getFloat(\n            context.getContentResolver(),\n            setting, -1\n        );\n        callback.success(value);\n    }\n\n    private void clearCache(CallbackContext callback) {\n        webView.clearCache(true);\n        callback.success(\"Cache cleared\");\n    }\n\n    private void setInputType(String type) {\n        int mode = -1;\n        if (type.equals(\"NO_SUGGESTIONS\")) {\n            mode = 0;\n        } else if (type.equals(\"NO_SUGGESTIONS_AGGRESSIVE\")) {\n            mode = 1;\n        }\n        webView.setInputType(mode);\n    }\n\n    private void setNativeContextMenuDisabled(boolean disabled) {\n        if (webView == null) {\n            return;\n        }\n        webView.setNativeContextMenuDisabled(disabled);\n    }\n}\n"
  },
  {
    "path": "src/plugins/system/android/com/foxdebug/system/Ui.java",
    "content": "package com.foxdebug.system;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Typeface;\nimport android.util.Log;\nimport android.util.TypedValue;\nimport org.json.JSONObject;\n\npublic class Ui {\n\n  public static class Icons {\n\n    public static final String LOGO = \"\\uE922\";\n    public static final String TUNE = \"\\uE927\";\n    public static final String EXIT = \"\\uE902\";\n    public static final String REFRESH = \"\\uE91B\";\n    public static final String TERMINAL = \"\\uE923\";\n    public static final String NO_CACHE = \"\\uE901\";\n    public static final String MORE_VERT = \"\\uE91A\";\n    public static final String OPEN_IN_BROWSER = \"\\uE91f\";\n\n    public static final String PHONE_APPLE = \"\\uE928\";\n    public static final String PHONE_ANDROID = \"\\uE90E\";\n    public static final String TABLET_ANDROID = \"\\uE90F\";\n    public static final String TABLET_APPLE = \"\\uE92A\";\n    public static final String DESKTOP = \"\\uE90A\";\n    public static final String DEVICES = \"\\uE907\";\n    public static final String LAPTOP = \"\\uE90D\";\n    public static final String TV = \"\\uE929\";\n\n    private static Paint paint;\n    private static int size = 24;\n    private static int color = Color.parseColor(\"#FFFFFF\");\n    private static final String FONT_PATH = \"font/icon.ttf\";\n\n    public static Bitmap get(\n      Context context,\n      String code,\n      int size,\n      int color\n    ) {\n      if (paint == null) {\n        paint = new Paint();\n        paint.setAntiAlias(true);\n        paint.setTypeface(\n          Typeface.createFromAsset(context.getAssets(), FONT_PATH)\n        );\n        paint.setTextAlign(Paint.Align.CENTER);\n      }\n\n      paint.setTextSize(size);\n      paint.setColor(color);\n\n      float baseline = -paint.ascent();\n      int width = (int) paint.measureText(code, 0, code.length());\n      int height = (int) (baseline + paint.descent());\n      Bitmap bitmap = Bitmap.createBitmap(\n        width,\n        height,\n        Bitmap.Config.ARGB_8888\n      );\n      Canvas canvas = new Canvas(bitmap);\n\n      canvas.drawText(code, width / 2, baseline, paint);\n      return bitmap;\n    }\n\n    public static Bitmap get(\n      Context context,\n      String code,\n      int size,\n      String color\n    ) {\n      int intColor = Color.parseColor(color);\n      return get(context, code, size, intColor);\n    }\n\n    public static Bitmap get(Context context, String code) {\n      return get(context, code, size, color);\n    }\n\n    public static Bitmap get(Context context, String code, String color) {\n      int intColor = Color.parseColor(color);\n      return get(context, code, size, intColor);\n    }\n\n    public static Bitmap get(Context context, String code, int color) {\n      return get(context, code, size, color);\n    }\n\n    public static void setSize(int size) {\n      Icons.size = size;\n    }\n\n    public static void setColor(int color) {\n      Icons.color = color;\n    }\n  }\n\n  public static class Theme {\n\n    private JSONObject theme;\n\n    public Theme(JSONObject theme) {\n      this.theme = theme;\n    }\n\n    public int get(String color) {\n      return get(color, \"#000000\");\n    }\n\n    public int get(String color, String fallback) {\n      String hex = theme.optString(color, fallback);\n      return Color.parseColor(hex);\n    }\n\n    public String getType() {\n      return theme.optString(\"type\", \"light\");\n    }\n  }\n\n  public static int dpToPixels(Context context, int dipValue) {\n    int value = (int) TypedValue.applyDimension(\n      TypedValue.COMPLEX_UNIT_DIP,\n      (float) dipValue,\n      context.getResources().getDisplayMetrics()\n    );\n\n    return value;\n  }\n}\n"
  },
  {
    "path": "src/plugins/system/package.json",
    "content": "{\r\n  \"name\": \"cordova-plugin-system\",\r\n  \"version\": \"1.0.3\",\r\n  \"description\": \"\",\r\n  \"main\": \"index.js\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\r\n  },\r\n  \"author\": \"\",\r\n  \"license\": \"ISC\"\r\n}\r\n"
  },
  {
    "path": "src/plugins/system/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\"\r\n  xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"cordova-plugin-system\" version=\"0.0.1\">\r\n  <name>cordova-plugin-system</name>\r\n  <description>Utility methods for Android.</description>\r\n  <license>Apache 2.0</license>\r\n  <keywords>cordova,plugin,system</keywords>\r\n\r\n  <js-module src=\"www/plugin.js\" name=\"system\">\r\n    <clobbers target=\"window.system\" />\r\n  </js-module>\r\n  <platform name=\"android\">\r\n\r\n    <config-file target=\"res/xml/config.xml\" parent=\"/*\">\r\n      <feature name=\"System\">\r\n        <param name=\"android-package\" value=\"com.foxdebug.system.System\"/>\r\n      </feature>\r\n    </config-file>\r\n\r\n    <config-file target=\"AndroidManifest.xml\" parent=\"./application\">\r\n      <provider android:name=\"androidx.core.content.FileProvider\" android:authorities=\"com.foxdebug.provider\" android:exported=\"false\" android:grantUriPermissions=\"true\">\r\n        <meta-data android:name=\"android.support.FILE_PROVIDER_PATHS\" android:resource=\"@xml/file_provider\"/>\r\n      </provider>\r\n    </config-file>\r\n\r\n\r\n    <config-file parent=\"./application/activity\" target=\"AndroidManifest.xml\">\r\n      <intent-filter>\r\n        <action android:name=\"android.intent.action.MAIN\"/>\r\n      </intent-filter>\r\n    </config-file>\r\n\r\n    <hook type=\"before_prepare\" src=\"utils/resetProvider.js\" />\r\n    <hook type=\"after_prepare\" src=\"utils/fixProvider.js\" />\r\n\r\n    <resource-file src=\"res/android/file_provider.xml\" target=\"res/xml/file_provider.xml\" />\r\n    <resource-file src=\"res/android/icon.ttf\" target=\"assets/font/icon.ttf\" />\r\n    <source-file src=\"android/com/foxdebug/system/Ui.java\" target-dir=\"src/com/foxdebug/system\"/>\r\n    <source-file src=\"android/com/foxdebug/system/System.java\" target-dir=\"src/com/foxdebug/system\"/>\r\n    <source-file src=\"android/com/foxdebug/system/SoftInputAssist.java\" target-dir=\"src/com/foxdebug/system\"/>\r\n\r\n    <framework src=\"androidx.core:core:1.6.0\" />\r\n    <framework src=\"androidx.core:core-google-shortcuts:1.0.0\" />\r\n    <framework src=\"androidx.documentfile:documentfile:1.0.1\" />\r\n    <source-file src=\"android/com/foxdebug/system/RewardPassManager.java\" target-dir=\"src/com/foxdebug/system\"/>\r\n  </platform>\r\n</plugin>\r\n"
  },
  {
    "path": "src/plugins/system/readme.md",
    "content": "# Util plugin for cordova apps\r\n\r\nUsing this plugin, cordova apps can:\r\n\r\n- Enable/disable full screen\r\n- Share file\r\n- Get webview information\r\n- Send email\r\n- Clear cache\r\n\r\n## Installation\r\n"
  },
  {
    "path": "src/plugins/system/res/android/file_provider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n  <external-cache-path name=\"external_cache\" path=\".\" />\r\n  <external-files-path name=\"external_files\" path=\".\" />\r\n  <external-path name=\"ext_storage\" path=\".\" />\r\n  <cache-path name=\"cache\" path=\".\" />\r\n  <files-path name=\"files\" path=\".\" />\r\n</paths>"
  },
  {
    "path": "src/plugins/system/system.d.ts",
    "content": "interface Info {\r\n  versionName: string;\r\n  packageName: string;\r\n  versionCode: number;\r\n}\r\n\r\ninterface AppInfo extends Info {\r\n  label: string;\r\n  firstInstallTime: number;\r\n  lastUpdateTime: number;\r\n}\r\n\r\ninterface ShortCut {\r\n  id: string;\r\n  label: string;\r\n  description: string;\r\n  icon: string;\r\n  action: string;\r\n  data: string;\r\n}\r\n\r\ninterface FileShortcut {\r\n  id: string;\r\n  label: string;\r\n  description?: string;\r\n  icon?: string;\r\n  uri: string;\r\n}\r\n\r\ninterface Intent {\r\n  action: string;\r\n  data: string;\r\n  type: string;\r\n  package: string;\r\n  extras: {\r\n    [key: string]: any;\r\n  };\r\n}\r\n\r\ninterface RewardStatus {\r\n  adFreeUntil: number;\r\n  lastExpiredRewardUntil: number;\r\n  isActive: boolean;\r\n  remainingMs: number;\r\n  redemptionsToday: number;\r\n  remainingRedemptions: number;\r\n  maxRedemptionsPerDay: number;\r\n  maxActivePassMs: number;\r\n  hasPendingExpiryNotice: boolean;\r\n  expiryNoticePendingUntil: number;\r\n  canRedeem: boolean;\r\n  redeemDisabledReason: string;\r\n  grantedDurationMs?: number;\r\n  appliedDurationMs?: number;\r\n  offerId?: string;\r\n}\r\n\r\ntype FileAction = 'VIEW' | 'EDIT' | 'SEND' | 'RUN';\r\ntype OnFail = (err: string) => void;\r\ntype OnSuccessBool = (res: boolean) => void;\r\n\r\ninterface System {\r\n  /**\r\n   * Get information about current webview\r\n   */\r\n  getWebviewInfo(onSuccess: (res: Info) => void, onFail: OnFail): void;\r\n  /**\r\n   * Checks if power saving mode is on\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  isPowerSaveMode(onSuccess: OnSuccessBool, onFail: OnFail): void;\r\n  /**\r\n   * File action using Apps content provider\r\n   * @param fileUri File uri\r\n   * @param filename file name\r\n   * @param action file name\r\n   * @param onFail\r\n   */\r\n  fileAction(\r\n    fileUri: string,\r\n    filename: string,\r\n    action: FileAction,\r\n    mimeType: string,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * File action using Apps content provider\r\n   * @param fileUri File uri\r\n   * @param filename file name\r\n   * @param action file name\r\n   */\r\n  fileAction(\r\n    fileUri: string,\r\n    filename: string,\r\n    action: FileAction,\r\n    mimeType: string,\r\n  ): void;\r\n  /**\r\n   * File action using Apps content provider\r\n   * @param fileUri File uri\r\n   * @param action file name\r\n   * @param onFail\r\n   */\r\n  fileAction(\r\n    fileUri: string,\r\n    action: FileAction,\r\n    mimeType: string,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * File action using Apps content provider\r\n   * @param fileUri File uri\r\n   * @param action file name\r\n   */\r\n  fileAction(fileUri: string, action: FileAction, mimeType: string): void;\r\n  /**\r\n   * File action using Apps content provider\r\n   * @param fileUri File uri\r\n   * @param action file name\r\n   */\r\n  fileAction(fileUri: string, action: FileAction, onFail: OnFail): void;\r\n  /**\r\n   * File action using Apps content provider\r\n   * @param fileUri File uri\r\n   * @param action file name\r\n   */\r\n  fileAction(fileUri: string, action: FileAction): void;\r\n  /**\r\n   * Gets app information\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  getAppInfo(onSuccess: (info: AppInfo) => void, onFail: OnFail): void;\r\n  /**\r\n   * Add shortcut to app context menu\r\n   * @param shortCut\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  addShortcut(\r\n    shortCut: ShortCut,\r\n    onSuccess: OnSuccessBool,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Removes shortcut\r\n   * @param id\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  removeShortcut(id: string, onSuccess: OnSuccessBool, onFail: OnFail): void;\r\n  /**\r\n   * Pins a shortcut\r\n   * @param id\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  pinShortcut(id: string, onSuccess: OnSuccessBool, onFail: OnFail): void;\r\n\r\n  /**\r\n   * Pin a shortcut for a specific file to the home screen\r\n   * @param shortcut Shortcut configuration\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  pinFileShortcut(\r\n    shortcut: FileShortcut,\r\n    onSuccess: OnSuccessBool,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Gets android version\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  getAndroidVersion(onSuccess: (res: Number) => void, onFail: OnFail): void;\r\n  /**\r\n   * Open settings which lets user change app settings to manage all files\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  manageAllFiles(onSuccess: OnSuccessBool, onFail: OnFail): void;\r\n  /**\r\n   * Opens settings to allow to grant the app permission manage all files on device\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  isExternalStorageManager(onSuccess: OnSuccessBool, onFail: OnFail): void;\r\n  /**\r\n   * Requests user to grant the provided permissions\r\n   * @param permissions constant value of the permission required @see https://developer.android.com/reference/android/Manifest.permission\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  requestPermissions(\r\n    permissions: string[],\r\n    onSuccess: OnSuccessBool,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Requests user to grant the provided permission\r\n   * @param permission constant value of the permission required @see https://developer.android.com/reference/android/Manifest.permission\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  requestPermission(\r\n    permission: string,\r\n    onSuccess: OnSuccessBool,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Checks whether the app has provided permission\r\n   * @param permission constant value of the permission required @see https://developer.android.com/reference/android/Manifest.permission\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  hasPermission(\r\n    permission: string,\r\n    onSuccess: OnSuccessBool,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Opens src in browser\r\n   * @param src\r\n   */\r\n  openInBrowser(src: string): void;\r\n  /**\r\n   * Launch an Android application activity.\r\n   *\r\n   * @param app Package name of the application (e.g. `com.example.app`)\r\n   * @param className Fully qualified activity class name (e.g. `com.example.app.MainActivity`)\r\n   * @param extras Optional key-value pairs passed as Android Intent extras\r\n   * @param onSuccess Called when the activity launches successfully\r\n   * @param onFail Called if launching the activity fails\r\n   */\r\n  launchApp(\r\n    app: string,\r\n    className: string,\r\n    extras?: Record<string, string | number | boolean>,\r\n    onSuccess?: OnSuccessBool,\r\n    onFail?: OnFail,\r\n  ): void;\r\n\r\n  /**\r\n   * Opens a link within the app\r\n   * @param url Url to open\r\n   * @param title Title of the page\r\n   * @param showButtons Set to true to show buttons like console, open in browser, etc\r\n   */\r\n  inAppBrowser(url: string, title: string, showButtons: boolean): void;\r\n  /**\r\n   * Sets the color of status bar and navigation bar\r\n   * @param systemBarColor Color of status bar and navigation bar\r\n   * @param theme Theme as object\r\n   * @param onSuccess Callback on success\r\n   * @param onFail Callback on fail\r\n   */\r\n  setUiTheme(\r\n    systemBarColor: string,\r\n    theme: object,\r\n    onSuccess: OnSuccessBool,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Sets intent handler for the app\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  setIntentHandler(onSuccess: (intent: Intent) => void, onFail: OnFail): void;\r\n  /**\r\n   * Gets the launch intent\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  getCordovaIntent(onSuccess: (intent: Intent) => void, onFail: OnFail): void;\r\n  getRewardStatus(\r\n    onSuccess: (status: RewardStatus | string) => void,\r\n    onFail: OnFail,\r\n  ): void;\r\n  redeemReward(\r\n    offerId: string,\r\n    onSuccess: (status: RewardStatus | string) => void,\r\n    onFail: OnFail,\r\n  ): void;\r\n  /**\r\n   * Enable/disable native WebView long-press context behavior.\r\n   * Use this when rendering a custom editor context menu.\r\n   * @param disabled\r\n   * @param onSuccess\r\n   * @param onFail\r\n   */\r\n  setNativeContextMenuDisabled(\r\n    disabled: boolean,\r\n    onSuccess?: () => void,\r\n    onFail?: OnFail,\r\n  ): void;\r\n}\r\n\r\ninterface Window{\r\n  system: System;\r\n}\r\n"
  },
  {
    "path": "src/plugins/system/utils/changeProvider.js",
    "content": "module.exports = {\r\n  changeProvider(reset) {\r\n    const fs = require('fs');\r\n    const path = require('path');\r\n\r\n\r\n    const androidManifest = path.resolve(__dirname, \"../../../platforms/android/app/src/main/AndroidManifest.xml\");\r\n    const configXML = path.resolve(__dirname, \"../../../config.xml\");\r\n    const repeatChar = (char, times) => {\r\n      let res = \"\";\r\n      while (--times >= 0) res += char;\r\n      return res;\r\n    };\r\n\r\n    try {\r\n      const fileData = fs.readFileSync(configXML, \"utf8\");\r\n      const manifest = fs.readFileSync(androidManifest, \"utf8\");\r\n      const ID = reset ? \"com.foxdebug\" : /widget id=\"([0-9a-zA-Z\\.\\-_]*)\"/.exec(fileData)[1];\r\n      const newFileData = manifest.replace(\r\n        /(android:authorities=\")([0-9a-zA-Z\\.\\-_]*)(\")/,\r\n        `$1${reset ? \"com.foxdebug\" : ID}.provider$3`\r\n      );\r\n      fs.writeFileSync(androidManifest, newFileData);\r\n\r\n      const msg = \"==== Changed provider to \" + ID + \".provider ====\";\r\n\r\n      console.log(\"\");\r\n      console.log(repeatChar(\"=\", msg.length));\r\n      console.log(msg);\r\n      console.log(repeatChar(\"=\", msg.length));\r\n      console.log(\"\");\r\n\r\n    } catch (error) {\r\n      console.error(error);\r\n      process.exit(1);\r\n    }\r\n  }\r\n};"
  },
  {
    "path": "src/plugins/system/utils/fixProvider.js",
    "content": "const {\r\n  changeProvider\r\n} = require(\"./changeProvider\");\r\n\r\nchangeProvider();"
  },
  {
    "path": "src/plugins/system/utils/resetProvider.js",
    "content": "const {\r\n  changeProvider\r\n} = require(\"./changeProvider\");\r\n\r\nchangeProvider(true);"
  },
  {
    "path": "src/plugins/system/www/plugin.js",
    "content": "module.exports = {\r\n  isManageExternalStorageDeclared: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'isManageExternalStorageDeclared', []);\r\n  },\r\n  hasGrantedStorageManager: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'hasGrantedStorageManager', []);\r\n  },\r\n  requestStorageManager: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'requestStorageManager', []);\r\n  },\r\n  copyToUri: function (srcUri, destUri, fileName, success, error) {\r\n    cordova.exec(success, error, 'System', 'copyToUri', [srcUri, destUri, fileName]);\r\n  },\r\n  fileExists: function (path, countSymlinks, success, error) {\r\n    cordova.exec(success, error, 'System', 'fileExists', [path, String(countSymlinks)]);\r\n  },\r\n\r\n  createSymlink: function (target, linkPath, success, error) {\r\n    cordova.exec(success, error, 'System', 'createSymlink', [target, linkPath]);\r\n  },\r\n  writeText: function (path, content, success, error) {\r\n    cordova.exec(success, error, 'System', 'writeText', [path, content]);\r\n  },\r\n  deleteFile: function (path, success, error) {\r\n    cordova.exec(success, error, 'System', 'deleteFile', [path]);\r\n  },\r\n  setExec: function (path, executable, success, error) {\r\n    cordova.exec(success, error, 'System', 'setExec', [path, String(executable)]);\r\n  },\r\n\r\n\r\n  getNativeLibraryPath: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'getNativeLibraryPath', []);\r\n  },\r\n\r\n  getFilesDir: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'getFilesDir', []);\r\n  },\r\n  getRewardStatus: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'getRewardStatus', []);\r\n  },\r\n  redeemReward: function (offerId, success, error) {\r\n    cordova.exec(success, error, 'System', 'redeemReward', [offerId]);\r\n  },\r\n\r\n  getParentPath: function (path, success, error) {\r\n    cordova.exec(success, error, 'System', 'getParentPath', [path]);\r\n  },\r\n\r\n  listChildren: function (path, success, error) {\r\n    cordova.exec(success, error, 'System', 'listChildren', [path]);\r\n  },\r\n  mkdirs: function (path, success, error) {\r\n    cordova.exec(success, error, 'System', 'mkdirs', [path]);\r\n  },\r\n  getArch: function (success, error) {\r\n    cordova.exec(success, error, 'System', 'getArch', []);\r\n  },\r\n\r\n  clearCache: function (success, fail) {\r\n    return cordova.exec(success, fail, \"System\", \"clearCache\", []);\r\n  },\r\n  getWebviewInfo: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'get-webkit-info', []);\r\n  },\r\n  isPowerSaveMode: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'is-powersave-mode', []);\r\n  },\r\n  fileAction: function (fileUri, filename, action, mimeType, onFail) {\r\n    if (typeof action !== 'string') {\r\n      onFail = action || function () { };\r\n      action = filename;\r\n      filename = '';\r\n    } else if (typeof mimeType !== 'string') {\r\n      onFail = mimeType || function () { };\r\n      mimeType = action;\r\n      action = filename;\r\n      filename = '';\r\n    } else if (typeof onFail !== 'function') {\r\n      onFail = function () { };\r\n    }\r\n\r\n    action = \"android.intent.action.\" + action;\r\n    cordova.exec(function () { }, onFail, 'System', 'file-action', [fileUri, filename, action, mimeType]);\r\n  },\r\n  getAppInfo: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'get-app-info', []);\r\n  },\r\n  addShortcut: function (shortcut, onSuccess, onFail) {\r\n    var id, label, description, icon, data;\r\n    id = shortcut.id;\r\n    label = shortcut.label;\r\n    description = shortcut.description;\r\n    icon = shortcut.icon;\r\n    data = shortcut.data;\r\n    action = shortcut.action;\r\n    cordova.exec(onSuccess, onFail, 'System', 'add-shortcut', [id, label, description, icon, action, data]);\r\n  },\r\n  removeShortcut: function (id, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'remove-shortcut', [id]);\r\n  },\r\n  pinShortcut: function (id, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'pin-shortcut', [id]);\r\n  },\r\n  pinFileShortcut: function (shortcut, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'pin-file-shortcut', [shortcut]);\r\n  },\r\n  manageAllFiles: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'manage-all-files', []);\r\n  },\r\n  getAndroidVersion: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'get-android-version', []);\r\n  },\r\n  isExternalStorageManager: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'is-external-storage-manager', []);\r\n  },\r\n  requestPermission: function (permission, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'request-permission', [permission]);\r\n  },\r\n  requestPermissions: function (permissions, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'request-permissions', [permissions]);\r\n  },\r\n  hasPermission: function (permission, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'has-permission', [permission]);\r\n  },\r\n  openInBrowser: function (src) {\r\n    cordova.exec(null, null, 'System', 'open-in-browser', [src]);\r\n  },\r\n  /**\r\n   * Launch an Android application activity.\r\n   *\r\n   * @param {string} app - Package name of the application (e.g. `com.example.app`).\r\n   * @param {string} className - Fully qualified activity class name (e.g. `com.example.app.MainActivity`).\r\n   * @param {Object<string, (string|number|boolean)>} [extras] - Optional key-value pairs passed as Intent extras.\r\n   * @param {(message: string) => void} [onSuccess] - Callback invoked when the activity launches successfully.\r\n   * @param {(error: any) => void} [onFail] - Callback invoked if launching the activity fails.\r\n   *\r\n   * @example\r\n   * System.launchApp(\r\n   *   \"com.example.app\",\r\n   *   \"com.example.app.MainActivity\",\r\n   *   {\r\n   *     user: \"example\",\r\n   *     age: 20,\r\n   *     premium: true\r\n   *   },\r\n   *   (msg) => console.log(msg),\r\n   *   (err) => console.error(err)\r\n   * );\r\n   */\r\n  launchApp: function (app, className, extras, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'launch-app', [app, className, extras]);\r\n  },\r\n  inAppBrowser: function (url, title, showButtons, disableCache) {\r\n    var myInAppBrowser = {\r\n      onOpenExternalBrowser: null,\r\n      onError: null,\r\n    };\r\n\r\n    cordova.exec(function (data) {\r\n      if (typeof data !== 'string') {\r\n        console.warn('System.inAppBrowser: invalid callback payload', data);\r\n        return;\r\n      }\r\n      var separatorIndex = data.indexOf(':');\r\n      if (separatorIndex < 0) {\r\n        console.warn('System.inAppBrowser: malformed callback payload', data);\r\n        return;\r\n      }\r\n      var dataTag = data.slice(0, separatorIndex);\r\n      var dataUrl = data.slice(separatorIndex + 1);\r\n      if (dataTag === 'onOpenExternalBrowser') {\r\n        if (typeof myInAppBrowser.onOpenExternalBrowser === 'function') {\r\n          myInAppBrowser.onOpenExternalBrowser(dataUrl);\r\n        } else {\r\n          console.warn('System.inAppBrowser: onOpenExternalBrowser handler is not set');\r\n        }\r\n      }\r\n    }, function (err) {\r\n      if (typeof myInAppBrowser.onError === 'function') {\r\n        myInAppBrowser.onError(err);\r\n        return;\r\n      }\r\n      console.warn('System.inAppBrowser error callback not handled', err);\r\n    }, 'System', 'in-app-browser', [url, title, !!showButtons, disableCache]);\r\n    return myInAppBrowser;\r\n  },\r\n  setUiTheme: function (systemBarColor, theme, onSuccess, onFail) {\r\n    const color = systemBarColor.toLowerCase();\r\n\r\n    if (color === '#ffffff' || color === '#ffffffff') {\r\n      systemBarColor = '#fffffe';\r\n    }\r\n\r\n    cordova.exec((out) => {\r\n      window.statusbar.setBackgroundColor(systemBarColor);\r\n\r\n      if (typeof onSuccess === \"function\") {\r\n        onSuccess(out);\r\n      }\r\n\r\n    }, onFail, 'System', 'set-ui-theme', [systemBarColor, theme]);\r\n  },\r\n  setIntentHandler: function (handler, onerror) {\r\n    cordova.exec(handler, onerror, 'System', 'set-intent-handler', []);\r\n  },\r\n  getCordovaIntent: function (onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'get-cordova-intent', []);\r\n  },\r\n  setInputType: function (type, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'set-input-type', [type]);\r\n  },\r\n  setNativeContextMenuDisabled: function (disabled, onSuccess, onFail) {\r\n    cordova.exec(\r\n      onSuccess,\r\n      onFail,\r\n      'System',\r\n      'set-native-context-menu-disabled',\r\n      [String(!!disabled)],\r\n    );\r\n  },\r\n  getGlobalSetting: function (key, onSuccess, onFail) {\r\n    cordova.exec(onSuccess, onFail, 'System', 'get-global-setting', [key]);\r\n  },\r\n  /**\r\n   * Compare file content with provided text in a background thread.\r\n   * @param {string} fileUri - The URI of the file to read\r\n   * @param {string} encoding - The character encoding to use\r\n   * @param {string} currentText - The text to compare against\r\n   * @returns {Promise<boolean>} - Resolves to true if content differs, false if same\r\n   */\r\n  compareFileText: function (fileUri, encoding, currentText) {\r\n    return new Promise((resolve, reject) => {\r\n      cordova.exec(\r\n        function(result) {\r\n          resolve(result === 1);\r\n        },\r\n        reject,\r\n        'System',\r\n        'compare-file-text',\r\n        [fileUri, encoding, currentText]\r\n      );\r\n    });\r\n  },\r\n  /**\r\n   * Compare two text strings in a background thread.\r\n   * @param {string} text1 - First text to compare\r\n   * @param {string} text2 - Second text to compare\r\n   * @returns {Promise<boolean>} - Resolves to true if texts differ, false if same\r\n   */\r\n  compareTexts: function (text1, text2) {\r\n    return new Promise((resolve, reject) => {\r\n      cordova.exec(\r\n        function(result) {\r\n          resolve(result === 1);\r\n        },\r\n        reject,\r\n        'System',\r\n        'compare-texts',\r\n        [text1, text2]\r\n      );\r\n    });\r\n  }\r\n};\r\n"
  },
  {
    "path": "src/plugins/terminal/package.json",
    "content": "{\n  \"name\": \"com.foxdebug.acode.rk.exec.terminal\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Terminal stuff\",\n  \"cordova\": {\n    \"id\": \"com.foxdebug.acode.rk.exec.terminal\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"ecosystem:cordova\",\n    \"cordova-android\"\n  ],\n  \"author\": \"@RohitKushvaha01\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "src/plugins/terminal/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\" xmlns:android=\"http://schemas.android.com/apk/res/android\" id=\"com.foxdebug.acode.rk.exec.terminal\" version=\"1.0.0\">\n    <name>Terminal</name>\n   \n    <js-module name=\"Terminal\" src=\"www/Terminal.js\">\n        <clobbers target=\"window.Terminal\" />\n    </js-module>\n\n    <!-- executor api -->\n    <js-module name=\"Executor\" src=\"www/Executor.js\">\n        <clobbers target=\"window.Executor\" />\n    </js-module>\n\n    <platform name=\"android\">\n\n        <framework src=\"org.java-websocket:Java-WebSocket:1.6.0\" />\n\n        <config-file parent=\"/*\" target=\"res/xml/config.xml\">\n            <feature name=\"Executor\">\n                <param name=\"android-package\" value=\"com.foxdebug.acode.rk.exec.terminal.Executor\" />\n            </feature>\n            <feature name=\"BackgroundExecutor\">\n                <param name=\"android-package\" value=\"com.foxdebug.acode.rk.exec.terminal.BackgroundExecutor\" />\n            </feature>\n        </config-file>\n        <config-file parent=\"/*\" target=\"AndroidManifest.xml\" />\n\n        <source-file src=\"src/android/BackgroundExecutor.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n        <source-file src=\"src/android/ProcessManager.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n        <source-file src=\"src/android/ProcessUtils.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n        <source-file src=\"src/android/StreamHandler.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n\n        <source-file src=\"src/android/Executor.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n        <source-file src=\"src/android/ProcessServer.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n\n        <source-file src=\"src/android/TerminalService.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n        \n        <source-file src=\"src/android/AlpineDocumentProvider.java\" target-dir=\"src/com/foxdebug/acode/rk/exec/terminal\" />\n\n        <source-file src=\"scripts/init-sandbox.sh\" target-dir=\"assets\"/>\n        <source-file src=\"scripts/init-alpine.sh\" target-dir=\"assets\"/>\n        <source-file src=\"scripts/rm-wrapper.sh\" target-dir=\"assets\"/>\n\n\n        <config-file target=\"AndroidManifest.xml\" parent=\"./application\">\n            <provider\n                android:name=\"com.foxdebug.acode.rk.exec.terminal.AlpineDocumentProvider\"\n                android:authorities=\"${applicationId}.documents\"\n                android:exported=\"true\"\n                android:grantUriPermissions=\"true\"\n                android:icon=\"@mipmap/ic_launcher\"\n                android:permission=\"android.permission.MANAGE_DOCUMENTS\">\n            <intent-filter>\n                <action android:name=\"android.content.action.DOCUMENTS_PROVIDER\" />\n            </intent-filter>\n            </provider>\n\n            <service\n                android:name=\"com.foxdebug.acode.rk.exec.terminal.TerminalService\"\n                android:enabled=\"true\"\n                android:foregroundServiceType=\"specialUse\"\n                android:exported=\"true\" />\n\n        </config-file>\n\n        <config-file target=\"AndroidManifest.xml\" parent=\"/manifest\">\n            <uses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n            <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>\n            <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_SPECIAL_USE\"/>\n            <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\"/>\n        </config-file>\n\n\n        <!-- DO NOT EDIT OR REMOVE-->\n        <config-file target=\"AndroidManifest.xml\" parent=\"/manifest\"></config-file>\n\n    </platform>\n</plugin>"
  },
  {
    "path": "src/plugins/terminal/scripts/init-alpine.sh",
    "content": "export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/share/bin:/usr/share/sbin:/usr/local/bin:/usr/local/sbin:/system/bin:/system/xbin:$PREFIX/local/bin\nexport PS1=\"\\[\\e[38;5;46m\\]\\u\\[\\033[39m\\]@localhost \\[\\033[39m\\]\\w \\[\\033[0m\\]\\\\$ \"\nexport HOME=/home\nexport TERM=xterm-256color\n\n\nrequired_packages=\"bash command-not-found tzdata wget\"\nmissing_packages=\"\"\n\nfor pkg in $required_packages; do\n    if ! apk info -e \"$pkg\" >/dev/null 2>&1; then\n        missing_packages=\"$missing_packages $pkg\"\n    fi\ndone\n\nif [ -n \"$missing_packages\" ]; then\n    echo -e \"\\e[34;1m[*] \\e[0mInstalling important packages\\e[0m\"\n    apk update && apk upgrade\n    apk add $missing_packages\n    if [ $? -eq 0 ]; then\n        echo -e \"\\e[32;1m[+] \\e[0mSuccessfully installed\\e[0m\"\n    fi\n    echo -e \"\\e[34m[*] \\e[0mUse \\e[32mapk\\e[0m to install new packages\\e[0m\"\nfi\n\n\nif [ ! -f /linkerconfig/ld.config.txt ]; then\n    mkdir -p /linkerconfig\n    touch /linkerconfig/ld.config.txt\nfi\n\n\nif [ \"$1\" = \"--installing\" ]; then\n    echo \"Configuring timezone...\"\n    \n    if [ -n \"$ANDROID_TZ\" ] && [ -f \"/usr/share/zoneinfo/$ANDROID_TZ\" ]; then\n        ln -sf \"/usr/share/zoneinfo/$ANDROID_TZ\" /etc/localtime\n        echo \"$ANDROID_TZ\" > /etc/timezone\n        echo \"Timezone set to: $ANDROID_TZ\"\n    else\n        echo \"Failed to detect timezone\"\n    fi\n\n    mkdir -p \"$PREFIX/.configured\"\n    echo \"Installation completed.\"\n    exit 0\nfi\n\n\nif [ \"$#\" -eq 0 ]; then\n    echo \"$$\" > \"$PREFIX/pid\"\n    chmod +x \"$PREFIX/axs\"\n\n    if [ ! -e \"$PREFIX/alpine/etc/acode_motd\" ]; then\n        cat <<EOF > \"$PREFIX/alpine/etc/acode_motd\"\nWelcome to Alpine Linux in Acode!\n\nWorking with packages:\n\n - Search:  apk search <query>\n - Install: apk add <package>\n - Uninstall: apk del <package>\n - Upgrade: apk update && apk upgrade\n\nEOF\n    fi\n\n    # Create acode CLI tool\n    if [ ! -e \"$PREFIX/alpine/usr/local/bin/acode\" ]; then\n        mkdir -p \"$PREFIX/alpine/usr/local/bin\"\n        cat <<'ACODE_CLI' > \"$PREFIX/alpine/usr/local/bin/acode\"\n#!/bin/bash\n# acode - Open files/folders in Acode editor\n# Uses OSC escape sequences to communicate with the Acode terminal\n\nusage() {\n    echo \"Usage: acode [file/folder...]\"\n    echo \"\"\n    echo \"Open files or folders in Acode editor.\"\n    echo \"\"\n    echo \"Examples:\"\n    echo \"  acode file.txt      # Open a file\"\n    echo \"  acode .             # Open current folder\"\n    echo \"  acode ~/project     # Open a folder\"\n    echo \"  acode -h, --help    # Show this help\"\n}\n\nget_abs_path() {\n    local path=\"$1\"\n    local abs_path=\"\"\n\n    if command -v realpath >/dev/null 2>&1; then\n        abs_path=$(realpath -- \"$path\" 2>/dev/null)\n    fi\n\n    if [[ -z \"$abs_path\" ]]; then\n        if [[ -d \"$path\" ]]; then\n            abs_path=$(cd -- \"$path\" 2>/dev/null && pwd -P)\n        elif [[ -e \"$path\" ]]; then\n            local dir_name file_name\n            dir_name=$(dirname -- \"$path\")\n            file_name=$(basename -- \"$path\")\n            abs_path=\"$(cd -- \"$dir_name\" 2>/dev/null && pwd -P)/$file_name\"\n        elif [[ \"$path\" == /* ]]; then\n            abs_path=\"$path\"\n        else\n            abs_path=\"$PWD/$path\"\n        fi\n    fi\n\n    echo \"$abs_path\"\n}\n\nopen_in_acode() {\n    local path=$(get_abs_path \"$1\")\n    local type=\"file\"\n    [[ -d \"$path\" ]] && type=\"folder\"\n    \n    # Send OSC 7777 escape sequence: \\e]7777;cmd;type;path\\a\n    # The terminal component will intercept and handle this\n    printf '\\e]7777;open;%s;%s\\a' \"$type\" \"$path\"\n}\n\nif [[ $# -eq 0 ]]; then\n    open_in_acode \".\"\n    exit 0\nfi\n\nfor arg in \"$@\"; do\n    case \"$arg\" in\n        -h|--help)\n            usage\n            exit 0\n            ;;\n        *)\n            if [[ -e \"$arg\" ]]; then\n                open_in_acode \"$arg\"\n            else\n                echo \"Error: '$arg' does not exist\" >&2\n                exit 1\n            fi\n            ;;\n    esac\ndone\nACODE_CLI\n        chmod +x \"$PREFIX/alpine/usr/local/bin/acode\"\n    fi\n\n    # Create initrc if it doesn't exist\n    #initrc runs in bash so we can use bash features \nif [ ! -e \"$PREFIX/alpine/initrc\" ]; then\n    cat <<'EOF' > \"$PREFIX/alpine/initrc\"\n# Source rc files if they exist\n\nif [ -f \"/etc/profile\" ]; then\n    source \"/etc/profile\"\nfi\n\n# Environment setup\nexport PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/share/bin:/usr/share/sbin:/usr/local/bin:/usr/local/sbin\n\nexport HOME=/home \nexport TERM=xterm-256color \nSHELL=/bin/bash\nexport PIP_BREAK_SYSTEM_PACKAGES=1\n\n# Default prompt with fish-style path shortening (~/p/s/components)\n# To use custom prompts (Starship, Oh My Posh, etc.), just init them in ~/.bashrc:\n#   eval \"$(starship init bash)\"\n_shorten_path() {\n    local path=\"$PWD\"\n    \n    if [[ \"$HOME\" != \"/\" && \"$path\" == \"$HOME\" ]]; then\n        echo \"~\"\n        return\n    elif [[ \"$HOME\" != \"/\" && \"$path\" == \"$HOME/\"* ]]; then\n        path=\"~${path#$HOME}\"\n    fi\n    \n    [[ \"$path\" == \"~\" ]] && echo \"~\" && return\n    \n    local parts result=\"\"\n    IFS='/' read -ra parts <<< \"$path\"\n    local len=${#parts[@]}\n    \n    for ((i=0; i<len; i++)); do\n        [[ -z \"${parts[i]}\" ]] && continue\n        if [[ $i -lt $((len-1)) ]]; then\n            result+=\"${parts[i]:0:1}/\"\n        else\n            result+=\"${parts[i]}\"\n        fi\n    done\n    \n    [[ \"$path\" == /* ]] && echo \"/$result\" || echo \"$result\"\n}\n\nPROMPT_COMMAND='_PS1_PATH=$(_shorten_path); _PS1_EXIT=$?'\n\n# Source user configs AFTER defaults (so user can override PROMPT_COMMAND)\nif [ -f \"$HOME/.bashrc\" ]; then\n    source \"$HOME/.bashrc\"\nfi\n\nif [ -f /etc/bash/bashrc ]; then\n    source /etc/bash/bashrc\nfi\n\n\n# Display MOTD if available\nif [ -s /etc/acode_motd ]; then\n    cat /etc/acode_motd\nfi\n\n# Command-not-found handler\ncommand_not_found_handle() {\n    cmd=\"$1\"\n    pkg=\"\"\n    green=\"\\e[1;32m\"\n    reset=\"\\e[0m\"\n\n    pkg=$(apk search -x \"cmd:$cmd\" 2>/dev/null | awk -F'-[0-9]' '{print $1}' | head -n 1)\n\n    if [ -n \"$pkg\" ]; then\n        echo -e \"The program '$cmd' is not installed.\\nInstall it by executing:\\n ${green}apk add $pkg${reset}\" >&2\n    else\n        echo \"The program '$cmd' is not installed and no package provides it.\" >&2\n    fi\n\n    return 127\n}\n\nEOF\nfi\n\n# Add PS1 only if not already present\nif ! grep -q 'PS1=' \"$PREFIX/alpine/initrc\"; then\n    # Smart path shortening (fish-style: ~/p/s/components)\n    echo 'PS1=\"\\[\\033[1;32m\\]\\u\\[\\033[0m\\]@localhost \\[\\033[1;34m\\]\\$_PS1_PATH\\[\\033[0m\\] \\[\\$([ \\$_PS1_EXIT -ne 0 ] && echo \\\"\\033[31m\\\")\\]\\$\\[\\033[0m\\] \"' >> \"$PREFIX/alpine/initrc\"\n    # Simple prompt (uncomment below and comment above if you prefer full paths)\n    # echo 'PS1=\"\\[\\033[1;32m\\]\\u\\[\\033[0m\\]@localhost \\[\\033[1;34m\\]\\w\\[\\033[0m\\] \\$ \"' >> \"$PREFIX/alpine/initrc\"\nfi\n\nchmod +x \"$PREFIX/alpine/initrc\"\n\n#actual source\n#everytime a terminal is started initrc will run\n\"$PREFIX/axs\" -c \"bash --rcfile /initrc -i\"\n\nelse\n    exec \"$@\"\nfi\n"
  },
  {
    "path": "src/plugins/terminal/scripts/init-sandbox.sh",
    "content": "export LD_LIBRARY_PATH=$PREFIX\n\nmkdir -p \"$PREFIX/tmp\"\nmkdir -p \"$PREFIX/alpine/tmp\"\nmkdir -p \"$PREFIX/public\"\n\nexport PROOT_TMP_DIR=$PREFIX/tmp\n\nif [ \"$FDROID\" = \"true\" ]; then\n\n    if [ -f \"$PREFIX/libproot.so\" ]; then\n        export PROOT_LOADER=\"$PREFIX/libproot.so\"\n    fi\n\n    if [ -f \"$PREFIX/libproot32.so\" ]; then\n        export PROOT_LOADER32=\"$PREFIX/libproot32.so\"\n    fi\n\n\n    export PROOT=\"$PREFIX/libproot-xed.so\"\n    chmod +x $PREFIX/*\nelse\n    if [ -f \"$NATIVE_DIR/libproot.so\" ]; then\n        export PROOT_LOADER=\"$NATIVE_DIR/libproot.so\"\n    fi\n\n    if [ -f \"$NATIVE_DIR/libproot32.so\" ]; then\n        export PROOT_LOADER32=\"$NATIVE_DIR/libproot32.so\"\n    fi\n\n\n    if [ -e \"$PREFIX/libtalloc.so.2\" ] || [ -L \"$PREFIX/libtalloc.so.2\" ]; then\n        rm \"$PREFIX/libtalloc.so.2\"\n    fi\n\n    ln -s \"$NATIVE_DIR/libtalloc.so\" \"$PREFIX/libtalloc.so.2\"\n    export PROOT=\"$NATIVE_DIR/libproot-xed.so\"\nfi\n\nARGS=\"--kill-on-exit\"\n\n\n\nfor system_mnt in /apex /odm /product /system /system_ext /vendor /linkerconfig/ld.config.txt /linkerconfig/com.android.art/ld.config.txt /plat_property_contexts /property_contexts; do\n\n if [ -e \"$system_mnt\" ]; then\n  system_mnt=$(realpath \"$system_mnt\")\n  ARGS=\"$ARGS -b ${system_mnt}\"\n fi\ndone\n\n\n\n\nunset system_mnt\n\nARGS=\"$ARGS -b /sdcard\"\nARGS=\"$ARGS -b /storage\"\nARGS=\"$ARGS -b /dev\"\nARGS=\"$ARGS -b /data\"\nARGS=\"$ARGS -b /dev/urandom:/dev/random\"\nARGS=\"$ARGS -b /proc\"\nARGS=\"$ARGS -b /sys\"\nARGS=\"$ARGS -b $PREFIX\"\nARGS=\"$ARGS -b $PREFIX/public:/public\"\nARGS=\"$ARGS -b $PREFIX/alpine/tmp:/dev/shm\"\n\n\nif [ -e \"/proc/self/fd\" ]; then\n  ARGS=\"$ARGS -b /proc/self/fd:/dev/fd\"\nfi\n\nif [ -e \"/proc/self/fd/0\" ]; then\n  ARGS=\"$ARGS -b /proc/self/fd/0:/dev/stdin\"\nfi\n\nif [ -e \"/proc/self/fd/1\" ]; then\n  ARGS=\"$ARGS -b /proc/self/fd/1:/dev/stdout\"\nfi\n\nif [ -e \"/proc/self/fd/2\" ]; then\n  ARGS=\"$ARGS -b /proc/self/fd/2:/dev/stderr\"\nfi\n\n\nARGS=\"$ARGS -r $PREFIX/alpine\"\nARGS=\"$ARGS -0\"\nARGS=\"$ARGS --link2symlink\"\nARGS=\"$ARGS --sysvipc\"\nARGS=\"$ARGS -L\"\n\n\n$PROOT $ARGS /bin/sh $PREFIX/init-alpine.sh \"$@\"\n"
  },
  {
    "path": "src/plugins/terminal/scripts/rm-wrapper.sh",
    "content": "#!/bin/sh\n\nunlink_recursive() {\n    path=\"$1\"\n\n    # Try to recurse into it as a directory first\n    for entry in \"$path\"/* \"$path\"/.[!.]* \"$path\"/..?*; do\n        case \"$entry\" in\n            *'*'*|*'?'*) continue ;;\n        esac\n        unlink_recursive \"$entry\"\n    done 2>/dev/null\n\n    unlink \"$path\" 2>/dev/null || :\n}\n\nfor target in \"$@\"; do\n    unlink_recursive \"$target\"\ndone\n\n# Run busybox rm, capture stderr, and filter out the \"No such file or directory\" message\nerr=\"$(busybox rm \"$@\" 2>&1 >/dev/null)\"\n\n# Print only real errors\nprintf \"%s\\n\" \"$err\" | grep -v \"No such file or directory\""
  },
  {
    "path": "src/plugins/terminal/src/android/AlpineDocumentProvider.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.content.res.AssetFileDescriptor;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.graphics.Point;\nimport android.os.CancellationSignal;\nimport android.os.ParcelFileDescriptor;\nimport android.provider.DocumentsContract;\nimport android.provider.DocumentsProvider;\nimport android.util.Log;\nimport android.webkit.MimeTypeMap;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.LinkedList;\nimport java.util.Locale;\nimport com.foxdebug.acode.R;\nimport com.foxdebug.acode.rk.exec.terminal.*;\n\npublic class AlpineDocumentProvider extends DocumentsProvider {\n    \n    private static final String ALL_MIME_TYPES = \"*/*\";\n\n    \n    // The default columns to return information about a root if no specific\n    // columns are requested in a query.\n    private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{\n        DocumentsContract.Root.COLUMN_ROOT_ID,\n        DocumentsContract.Root.COLUMN_MIME_TYPES,\n        DocumentsContract.Root.COLUMN_FLAGS,\n        DocumentsContract.Root.COLUMN_ICON,\n        DocumentsContract.Root.COLUMN_TITLE,\n        DocumentsContract.Root.COLUMN_SUMMARY,\n        DocumentsContract.Root.COLUMN_DOCUMENT_ID,\n        DocumentsContract.Root.COLUMN_AVAILABLE_BYTES\n    };\n    \n    // The default columns to return information about a document if no specific\n    // columns are requested in a query.\n    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{\n        DocumentsContract.Document.COLUMN_DOCUMENT_ID,\n        DocumentsContract.Document.COLUMN_MIME_TYPE,\n        DocumentsContract.Document.COLUMN_DISPLAY_NAME,\n        DocumentsContract.Document.COLUMN_LAST_MODIFIED,\n        DocumentsContract.Document.COLUMN_FLAGS,\n        DocumentsContract.Document.COLUMN_SIZE\n    };\n\n    @Override\n    public Cursor queryRoots(String[] projection) {\n        File BASE_DIR = new File(getContext().getFilesDir(),\"public\");\n        if (!BASE_DIR.exists()) {\n            BASE_DIR.mkdirs();\n        }\n\n        MatrixCursor result = new MatrixCursor(\n            projection != null ? projection : DEFAULT_ROOT_PROJECTION\n        );\n        String applicationName = getApplicationLabel();\n\n        MatrixCursor.RowBuilder row = result.newRow();\n        row.add(DocumentsContract.Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR));\n        row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocIdForFile(BASE_DIR));\n        row.add(DocumentsContract.Root.COLUMN_SUMMARY, null);\n        row.add(\n            DocumentsContract.Root.COLUMN_FLAGS,\n            DocumentsContract.Root.FLAG_SUPPORTS_CREATE | \n            DocumentsContract.Root.FLAG_SUPPORTS_SEARCH | \n            DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD\n        );\n        row.add(DocumentsContract.Root.COLUMN_TITLE, applicationName);\n        row.add(DocumentsContract.Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES);\n        row.add(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace());\n        row.add(DocumentsContract.Root.COLUMN_ICON, resolveLauncherIcon());\n        return result;\n    }\n\n    @Override\n    public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {\n        MatrixCursor result = new MatrixCursor(\n            projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION\n        );\n        includeFile(result, documentId, null);\n        return result;\n    }\n\n    @Override\n    public Cursor queryChildDocuments(\n        String parentDocumentId,\n        String[] projection,\n        String sortOrder\n    ) throws FileNotFoundException {\n        MatrixCursor result = new MatrixCursor(\n            projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION\n        );\n        File parent = getFileForDocId(parentDocumentId);\n        File[] files = parent.listFiles();\n        if (files != null) {\n            for (File file : files) {\n                includeFile(result, null, file);\n            }\n        } else {\n            Log.e(\"DocumentsProvider\", \"Unable to list files in \" + parentDocumentId);\n        }\n        return result;\n    }\n\n    @Override\n    public ParcelFileDescriptor openDocument(\n        String documentId,\n        String mode,\n        CancellationSignal signal\n    ) throws FileNotFoundException {\n        File file = getFileForDocId(documentId);\n        int accessMode = ParcelFileDescriptor.parseMode(mode);\n        return ParcelFileDescriptor.open(file, accessMode);\n    }\n\n    @Override\n    public AssetFileDescriptor openDocumentThumbnail(\n        String documentId,\n        Point sizeHint,\n        CancellationSignal signal\n    ) throws FileNotFoundException {\n        File file = getFileForDocId(documentId);\n        ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);\n        return new AssetFileDescriptor(pfd, 0, file.length());\n    }\n\n    @Override\n    public boolean onCreate() {\n        return true;\n    }\n\n    @Override\n    public String createDocument(\n        String parentDocumentId,\n        String mimeType,\n        String displayName\n    ) throws FileNotFoundException {\n        File parent = getFileForDocId(parentDocumentId);\n        File newFile = new File(parent, displayName);\n        int noConflictId = 2;\n        while (newFile.exists()) {\n            newFile = new File(parent, displayName + \" (\" + noConflictId++ + \")\");\n        }\n        try {\n            boolean succeeded;\n            if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {\n                succeeded = newFile.mkdir();\n            } else {\n                succeeded = newFile.createNewFile();\n            }\n            if (!succeeded) {\n                throw new FileNotFoundException(\"Failed to create document with id \" + newFile.getAbsolutePath());\n            }\n        } catch (IOException e) {\n            throw new FileNotFoundException(\"Failed to create document with id \" + newFile.getAbsolutePath());\n        }\n        return getDocIdForFile(newFile);\n    }\n\n    @Override\n    public void deleteDocument(String documentId) throws FileNotFoundException {\n        File file = getFileForDocId(documentId);\n        if (!file.delete()) {\n            throw new FileNotFoundException(\"Failed to delete document with id \" + documentId);\n        }\n    }\n\n    @Override\n    public String renameDocument(String documentId, String displayName) throws FileNotFoundException {\n        File file = getFileForDocId(documentId);\n        File parent = file.getParentFile();\n        if (parent == null) {\n            throw new FileNotFoundException(\"Failed to rename root document with id \" + documentId);\n        }\n        if (displayName == null || displayName.trim().isEmpty()) {\n            throw new FileNotFoundException(\"Failed to rename document with id \" + documentId);\n        }\n        if (displayName.equals(file.getName())) {\n            return documentId;\n        }\n        if (displayName.contains(File.separator)) {\n            throw new FileNotFoundException(\"Invalid display name for rename: \" + displayName);\n        }\n\n        File target = new File(parent, displayName);\n        if (target.exists()) {\n            throw new FileNotFoundException(\"Target already exists: \" + target.getAbsolutePath());\n        }\n        if (!file.renameTo(target)) {\n            throw new FileNotFoundException(\"Failed to rename document with id \" + documentId);\n        }\n        return getDocIdForFile(target);\n    }\n\n    @Override\n    public String getDocumentType(String documentId) throws FileNotFoundException {\n        File file = getFileForDocId(documentId);\n        return getMimeType(file);\n    }\n\n    @Override\n    public Cursor querySearchDocuments(\n        String rootId,\n        String query,\n        String[] projection\n    ) throws FileNotFoundException {\n        File BASE_DIR = new File(getContext().getFilesDir(),\"public\");\n        if (!BASE_DIR.exists()) {\n            BASE_DIR.mkdirs();\n        }\n        MatrixCursor result = new MatrixCursor(\n            projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION\n        );\n        File parent = getFileForDocId(rootId);\n\n        // This example implementation searches file names for the query and doesn't rank search\n        // results, so we can stop as soon as we find a sufficient number of matches.  Other\n        // implementations might rank results and use other data about files, rather than the file\n        // name, to produce a match.\n        LinkedList<File> pending = new LinkedList<>();\n        pending.add(parent);\n\n        final int MAX_SEARCH_RESULTS = 50;\n        while (!pending.isEmpty() && result.getCount() < MAX_SEARCH_RESULTS) {\n            File file = pending.removeFirst();\n            // Avoid directories outside the $HOME directory linked with symlinks (to avoid e.g. search\n            // through the whole SD card).\n            boolean isInsideHome;\n            try {\n                isInsideHome = file.getCanonicalPath().startsWith(BASE_DIR.getCanonicalPath());\n            } catch (IOException e) {\n                isInsideHome = true;\n            }\n            if (isInsideHome) {\n                if (file.isDirectory()) {\n                    File[] files = file.listFiles();\n                    if (files != null) {\n                        Collections.addAll(pending, files);\n                    }\n                } else {\n                    if (file.getName().toLowerCase(Locale.getDefault()).contains(query)) {\n                        includeFile(result, null, file);\n                    }\n                }\n            }\n        }\n\n        return result;\n    }\n\n    @Override\n    public boolean isChildDocument(String parentDocumentId, String documentId) {\n        return documentId.startsWith(parentDocumentId);\n    }\n\n    /**\n     * Add a representation of a file to a cursor.\n     *\n     * @param result the cursor to modify\n     * @param docId  the document ID representing the desired file (may be null if given file)\n     * @param file   the File object representing the desired file (may be null if given docID)\n     */\n    private void includeFile(MatrixCursor result, String docId, File file) throws FileNotFoundException {\n        if (docId == null) {\n            docId = getDocIdForFile(file);\n        } else {\n            file = getFileForDocId(docId);\n        }\n\n        int flags = 0;\n        if (file.isDirectory()) {\n            if (file.canWrite()) {\n                flags = flags | DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;\n            }\n        } else if (file.canWrite()) {\n            flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_WRITE;\n        }\n        File parentFile = file.getParentFile();\n        if (parentFile != null && parentFile.canWrite()) {\n            flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_DELETE;\n            flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_RENAME;\n        }\n\n        String displayName = file.getName();\n        String mimeType = getMimeType(file);\n        if (mimeType.startsWith(\"image/\")) {\n            flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL;\n        }\n\n        MatrixCursor.RowBuilder row = result.newRow();\n        row.add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, docId);\n        row.add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, displayName);\n        row.add(DocumentsContract.Document.COLUMN_SIZE, file.length());\n        row.add(DocumentsContract.Document.COLUMN_MIME_TYPE, mimeType);\n        row.add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, file.lastModified());\n        row.add(DocumentsContract.Document.COLUMN_FLAGS, flags);\n        row.add(DocumentsContract.Document.COLUMN_ICON, R.mipmap.ic_launcher);\n    }\n\n    public static boolean isDocumentProviderEnabled(Context context) {\n        ComponentName componentName = new ComponentName(context, AlpineDocumentProvider.class);\n        int state = context.getPackageManager().getComponentEnabledSetting(componentName);\n        return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED ||\n               state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;\n    }\n\n    public static void setDocumentProviderEnabled(Context context, boolean enabled) {\n        if (isDocumentProviderEnabled(context) == enabled) {\n            return;\n        }\n        ComponentName componentName = new ComponentName(context, AlpineDocumentProvider.class);\n        int newState = enabled ? \n            PackageManager.COMPONENT_ENABLED_STATE_ENABLED : \n            PackageManager.COMPONENT_ENABLED_STATE_DISABLED;\n\n        context.getPackageManager().setComponentEnabledSetting(\n            componentName,\n            newState,\n            PackageManager.DONT_KILL_APP\n        );\n    }\n\n    /**\n     * Get the document id given a file. This document id must be consistent across time as other\n     * applications may save the ID and use it to reference documents later.\n     *\n     * The reverse of {@link #getFileForDocId}.\n     */\n    private static String getDocIdForFile(File file) {\n        return file.getAbsolutePath();\n    }\n\n    /**\n     * Get the file given a document id (the reverse of {@link #getDocIdForFile}).\n     */\n    private static File getFileForDocId(String docId) throws FileNotFoundException {\n        File f = new File(docId);\n        if (!f.exists()) {\n            throw new FileNotFoundException(f.getAbsolutePath() + \" not found\");\n        }\n        return f;\n    }\n\n    private static String getMimeType(File file) {\n        if (file.isDirectory()) {\n            return DocumentsContract.Document.MIME_TYPE_DIR;\n        } else {\n            String name = file.getName();\n            int lastDot = name.lastIndexOf('.');\n            if (lastDot >= 0) {\n                String extension = name.substring(lastDot + 1).toLowerCase(Locale.getDefault());\n                String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);\n                if (mime != null) {\n                    return mime;\n                }\n            }\n            return \"application/octet-stream\";\n        }\n    }\n    private int resolveLauncherIcon() {\n        Context context = getContext();\n        if (context == null) return android.R.mipmap.sym_def_app_icon;\n        int icon = context.getResources().getIdentifier(\"ic_launcher\", \"mipmap\", context.getPackageName());\n        return icon != 0 ? icon : android.R.mipmap.sym_def_app_icon;\n    }\n\n    private String getApplicationLabel() {\n        Context context = getContext();\n        if (context == null) return \"Acode\";\n        PackageManager pm = context.getPackageManager();\n        try {\n            CharSequence label = pm.getApplicationLabel(context.getApplicationInfo());\n            return label != null ? label.toString() : \"Acode\";\n        } catch (Exception ignored) {\n            return \"Acode\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/plugins/terminal/src/android/BackgroundExecutor.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport org.apache.cordova.*;\nimport org.json.*;\nimport java.io.*;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport com.foxdebug.acode.rk.exec.terminal.*;\n\npublic class BackgroundExecutor extends CordovaPlugin {\n\n    private final Map<String, Process> processes = new ConcurrentHashMap<>();\n    private final Map<String, OutputStream> processInputs = new ConcurrentHashMap<>();\n    private final Map<String, CallbackContext> processCallbacks = new ConcurrentHashMap<>();\n    private ProcessManager processManager;\n\n    @Override\n    public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n        super.initialize(cordova, webView);\n        this.processManager = new ProcessManager(cordova.getContext());\n    }\n\n    @Override\n    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {\n        switch (action) {\n            case \"start\":\n                String pid = UUID.randomUUID().toString();\n                startProcess(pid, args.getString(0), args.getString(1).equals(\"true\"), callbackContext);\n                return true;\n            case \"write\":\n                writeToProcess(args.getString(0), args.getString(1), callbackContext);\n                return true;\n            case \"stop\":\n                stopProcess(args.getString(0), callbackContext);\n                return true;\n            case \"exec\":\n                exec(args.getString(0), args.getString(1).equals(\"true\"), callbackContext);\n                return true;\n            case \"isRunning\":\n                isProcessRunning(args.getString(0), callbackContext);\n                return true;\n            case \"loadLibrary\":\n                loadLibrary(args.getString(0), callbackContext);\n                return true;\n            default:\n                callbackContext.error(\"Unknown action: \" + action);\n                return false;\n        }\n    }\n\n    private void exec(String cmd, boolean useAlpine, CallbackContext callbackContext) {\n        cordova.getThreadPool().execute(() -> {\n            try {\n                ProcessManager.ExecResult result = processManager.executeCommand(cmd, useAlpine);\n                \n                if (result.isSuccess()) {\n                    callbackContext.success(result.stdout);\n                } else {\n                    callbackContext.error(result.getErrorMessage());\n                }\n            } catch (Exception e) {\n                callbackContext.error(\"Exception: \" + e.getMessage());\n            }\n        });\n    }\n\n    private void startProcess(String pid, String cmd, boolean useAlpine, CallbackContext callbackContext) {\n        cordova.getThreadPool().execute(() -> {\n            try {\n                ProcessBuilder builder = processManager.createProcessBuilder(cmd, useAlpine);\n                Process process = builder.start();\n\n                processes.put(pid, process);\n                processInputs.put(pid, process.getOutputStream());\n                processCallbacks.put(pid, callbackContext);\n\n                sendPluginResult(callbackContext, pid, true);\n\n                // Stream stdout\n                new Thread(() -> StreamHandler.streamOutput(\n                    process.getInputStream(), \n                    line -> sendPluginMessage(pid, \"stdout:\" + line)\n                )).start();\n                \n                // Stream stderr\n                new Thread(() -> StreamHandler.streamOutput(\n                    process.getErrorStream(), \n                    line -> sendPluginMessage(pid, \"stderr:\" + line)\n                )).start();\n\n                int exitCode = process.waitFor();\n                sendPluginMessage(pid, \"exit:\" + exitCode);\n                cleanup(pid);\n            } catch (Exception e) {\n                callbackContext.error(\"Failed to start process: \" + e.getMessage());\n            }\n        });\n    }\n\n    private void writeToProcess(String pid, String input, CallbackContext callbackContext) {\n        try {\n            OutputStream os = processInputs.get(pid);\n            if (os != null) {\n                StreamHandler.writeToStream(os, input);\n                callbackContext.success(\"Written to process\");\n            } else {\n                callbackContext.error(\"Process not found or closed\");\n            }\n        } catch (IOException e) {\n            callbackContext.error(\"Write error: \" + e.getMessage());\n        }\n    }\n\n    private void stopProcess(String pid, CallbackContext callbackContext) {\n        Process process = processes.get(pid);\n        if (process != null) {\n            ProcessUtils.killProcessTree(process);\n            cleanup(pid);\n            callbackContext.success(\"Process terminated\");\n        } else {\n            callbackContext.error(\"No such process\");\n        }\n    }\n\n    private void isProcessRunning(String pid, CallbackContext callbackContext) {\n        Process process = processes.get(pid);\n        \n        if (process != null) {\n            String status = ProcessUtils.isAlive(process) ? \"running\" : \"exited\";\n            if (status.equals(\"exited\")) cleanup(pid);\n            callbackContext.success(status);\n        } else {\n            callbackContext.success(\"not_found\");\n        }\n    }\n\n    private void loadLibrary(String path, CallbackContext callbackContext) {\n        try {\n            System.load(path);\n            callbackContext.success(\"Library loaded successfully.\");\n        } catch (Exception e) {\n            callbackContext.error(\"Failed to load library: \" + e.getMessage());\n        }\n    }\n\n    private void sendPluginResult(CallbackContext ctx, String message, boolean keepCallback) {\n        PluginResult result = new PluginResult(PluginResult.Status.OK, message);\n        result.setKeepCallback(keepCallback);\n        ctx.sendPluginResult(result);\n    }\n\n    private void sendPluginMessage(String pid, String message) {\n        CallbackContext ctx = processCallbacks.get(pid);\n        if (ctx != null) {\n            sendPluginResult(ctx, message, true);\n        }\n    }\n\n    private void cleanup(String pid) {\n        processes.remove(pid);\n        processInputs.remove(pid);\n        processCallbacks.remove(pid);\n    }\n}"
  },
  {
    "path": "src/plugins/terminal/src/android/Executor.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport org.apache.cordova.*;\nimport org.json.*;\n\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Message;\nimport android.os.Messenger;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport java.util.UUID;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport android.Manifest;\nimport android.content.pm.PackageManager;\nimport android.os.Build;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.app.ActivityCompat;\nimport androidx.core.content.ContextCompat;\nimport android.app.Activity;\nimport com.foxdebug.acode.rk.exec.terminal.*;\n\nimport java.net.ServerSocket;\n\n\n\npublic class Executor extends CordovaPlugin {\n\n    private Messenger serviceMessenger;\n    private boolean isServiceBound;\n    private boolean isServiceBinding; // Track if binding is in progress\n    private Context context;\n    private Activity activity;\n    private final Messenger handlerMessenger = new Messenger(new IncomingHandler());\n    private CountDownLatch serviceConnectedLatch;\n    private final java.util.Map<String, CallbackContext> callbackContextMap = new java.util.concurrent.ConcurrentHashMap<>();\n\n    private static final int REQUEST_POST_NOTIFICATIONS = 1001;\n    \n    \n    \n    private void askNotificationPermission(Activity context) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            if (ContextCompat.checkSelfPermission(\n                    context, Manifest.permission.POST_NOTIFICATIONS) ==\n                    PackageManager.PERMISSION_GRANTED) {\n            } else if (ActivityCompat.shouldShowRequestPermissionRationale(\n                    context, Manifest.permission.POST_NOTIFICATIONS)) {\n                ActivityCompat.requestPermissions(\n                        context,\n                        new String[]{Manifest.permission.POST_NOTIFICATIONS},\n                        REQUEST_POST_NOTIFICATIONS\n                );\n            } else {\n                ActivityCompat.requestPermissions(\n                        context,\n                        new String[]{Manifest.permission.POST_NOTIFICATIONS},\n                        REQUEST_POST_NOTIFICATIONS\n                );\n            }\n        }\n    }\n\n    @Override\n    public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n        super.initialize(cordova, webView);\n        this.context = cordova.getContext();\n        this.activity = cordova.getActivity();\n        askNotificationPermission(activity);\n        \n        // Don't bind service immediately - wait until needed\n        Log.d(\"Executor\", \"Plugin initialized - service will be started when needed\");\n    }\n\n    /**\n     * Ensure service is bound and ready for communication\n     * Returns true if service is ready, false if binding failed\n     */\n    private boolean ensureServiceBound(CallbackContext callbackContext) {\n        // If already bound, return immediately\n        if (isServiceBound && serviceMessenger != null) {\n            return true;\n        }\n\n        // If binding is already in progress, wait for it\n        if (isServiceBinding) {\n            try {\n                if (serviceConnectedLatch != null && \n                    serviceConnectedLatch.await(10, TimeUnit.SECONDS)) {\n                    return isServiceBound;\n                } else {\n                    callbackContext.error(\"Service binding timeout\");\n                    return false;\n                }\n            } catch (InterruptedException e) {\n                callbackContext.error(\"Service binding interrupted: \" + e.getMessage());\n                return false;\n            }\n        }\n\n        // Start binding process\n        Log.d(\"Executor\", \"Starting service binding...\");\n        return bindServiceNow(callbackContext);\n    }\n\n    /**\n     * Immediately bind to service\n     */\n    private boolean bindServiceNow(CallbackContext callbackContext) {\n        if (isServiceBinding) {\n            return false; // Already binding\n        }\n\n        isServiceBinding = true;\n        serviceConnectedLatch = new CountDownLatch(1);\n\n        Intent intent = new Intent(context, TerminalService.class);\n        \n        // Start the service first\n        context.startService(intent);\n        \n        // Then bind to it\n        boolean bindResult = context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);\n        \n        if (!bindResult) {\n            Log.e(\"Executor\", \"Failed to bind to service\");\n            isServiceBinding = false;\n            callbackContext.error(\"Failed to bind to service\");\n            return false;\n        }\n\n        // Wait for connection\n        try {\n            if (serviceConnectedLatch.await(10, TimeUnit.SECONDS)) {\n                Log.d(\"Executor\", \"Service bound successfully\");\n                return isServiceBound;\n            } else {\n                Log.e(\"Executor\", \"Service binding timeout\");\n                callbackContext.error(\"Service binding timeout\");\n                isServiceBinding = false;\n                return false;\n            }\n        } catch (InterruptedException e) {\n            Log.e(\"Executor\", \"Service binding interrupted: \" + e.getMessage());\n            callbackContext.error(\"Service binding interrupted: \" + e.getMessage());\n            isServiceBinding = false;\n            return false;\n        }\n    }\n\n    private final ServiceConnection serviceConnection = new ServiceConnection() {\n        @Override\n        public void onServiceConnected(ComponentName name, IBinder service) {\n            Log.d(\"Executor\", \"Service connected\");\n            serviceMessenger = new Messenger(service);\n            isServiceBound = true;\n            isServiceBinding = false;\n            if (serviceConnectedLatch != null) {\n                serviceConnectedLatch.countDown();\n            }\n        }\n\n        @Override\n        public void onServiceDisconnected(ComponentName name) {\n            Log.w(\"Executor\", \"Service disconnected\");\n            serviceMessenger = null;\n            isServiceBound = false;\n            isServiceBinding = false;\n            serviceConnectedLatch = new CountDownLatch(1);\n        }\n    };\n\n    private class IncomingHandler extends Handler {\n        @Override\n        public void handleMessage(Message msg) {\n            Bundle bundle = msg.getData();\n            String id = bundle.getString(\"id\");\n            String action = bundle.getString(\"action\");\n            String data = bundle.getString(\"data\");\n\n            if (action.equals(\"exec_result\")) {\n                CallbackContext callbackContext = getCallbackContext(id);\n                if (callbackContext != null) {\n                    if (bundle.getBoolean(\"isSuccess\", false)) {\n                        callbackContext.success(data);\n                    } else {\n                        callbackContext.error(data);\n                    }\n                    cleanupCallback(id);\n                }\n            } else {\n                String pid = id;\n                CallbackContext callbackContext = getCallbackContext(pid);\n\n                if (callbackContext != null) {\n                    switch (action) {\n                        case \"stdout\":\n                        case \"stderr\":\n                            PluginResult result = new PluginResult(PluginResult.Status.OK, action + \":\" + data);\n                            result.setKeepCallback(true);\n                            callbackContext.sendPluginResult(result);\n                            break;\n                        case \"exit\":\n                            cleanupCallback(pid);\n                            callbackContext.success(\"exit:\" + data);\n                            break;\n                        case \"isRunning\":\n                            callbackContext.success(data);\n                            cleanupCallback(pid);\n                            break;\n                    }\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {\n        // For actions that don't need the service, handle them directly\n        if (action.equals(\"loadLibrary\")) {\n            try {\n                System.load(args.getString(0));\n                callbackContext.success(\"Library loaded successfully.\");\n            } catch (Exception e) {\n                callbackContext.error(\"Failed to load library: \" + e.getMessage());\n            }\n            return true;\n        }\n\n        if (action.equals(\"stopService\")) {\n            stopServiceNow();\n            callbackContext.success(\"Service stopped\");\n            return true;\n        }\n\n        if (action.equals(\"moveToBackground\")) {\n            Intent intent = new Intent(context, TerminalService.class);\n            intent.setAction(TerminalService.MOVE_TO_BACKGROUND);\n            context.startService(intent);\n            callbackContext.success(\"Service moved to background mode\");\n            return true;\n        }\n\n        if (action.equals(\"moveToForeground\")) {\n            Intent intent = new Intent(context, TerminalService.class);\n            intent.setAction(TerminalService.MOVE_TO_FOREGROUND);\n            context.startService(intent);\n            callbackContext.success(\"Service moved to foreground mode\");\n            return true;\n        }\n\n        if (action.equals(\"spawn\")) {\n            try {\n                JSONArray cmdArr = args.getJSONArray(0);\n                String[] cmd = new String[cmdArr.length()];\n                for (int i = 0; i < cmdArr.length(); i++) {\n                    cmd[i] = cmdArr.getString(i);\n                }\n\n                int port;\n                try (ServerSocket socket = new ServerSocket(0)) {\n                    port = socket.getLocalPort();\n                }\n\n                ProcessServer server = new ProcessServer(port, cmd);\n                server.startAndAwait(); // blocks until onStart() fires — server is listening before port is returned\n\n                callbackContext.success(port);\n            } catch (Exception e) {\n                e.printStackTrace();\n                callbackContext.error(\"Failed to spawn process: \" + e.getMessage());\n            }\n\n            return true;\n        }\n\n\n        // For all other actions, ensure service is bound first\n        if (!ensureServiceBound(callbackContext)) {\n            // Error already sent by ensureServiceBound\n            return false;\n        }\n\n        switch (action) {\n            case \"start\":\n                String cmdStart = args.getString(0);\n                String pid = UUID.randomUUID().toString();\n                callbackContextMap.put(pid, callbackContext);\n                startProcess(pid, cmdStart, args.getString(1));\n                return true;\n            case \"write\":\n                String pidWrite = args.getString(0);\n                String input = args.getString(1);\n                writeToProcess(pidWrite, input, callbackContext);\n                return true;\n            case \"stop\":\n                String pidStop = args.getString(0);\n                stopProcess(pidStop, callbackContext);\n                return true;\n            case \"exec\":\n                String execId = UUID.randomUUID().toString();\n                callbackContextMap.put(execId, callbackContext);\n                exec(execId, args.getString(0), args.getString(1));\n                return true;\n            case \"isRunning\":\n                String pidCheck = args.getString(0);\n                callbackContextMap.put(pidCheck, callbackContext);\n                isProcessRunning(pidCheck);\n                return true;\n            default:\n                callbackContext.error(\"Unknown action: \" + action);\n                return false;\n        }\n    }\n\n    private void stopServiceNow() {\n        if (isServiceBound) {\n            try {\n                context.unbindService(serviceConnection);\n                Log.d(\"Executor\", \"Service unbound\");\n            } catch (IllegalArgumentException ignored) {\n                // already unbound\n            }\n            isServiceBound = false;\n        }\n        isServiceBinding = false;\n\n        Intent intent = new Intent(context, TerminalService.class);\n        boolean stopped = context.stopService(intent);\n        Log.d(\"Executor\", \"Service stop result: \" + stopped);\n        \n        serviceMessenger = null;\n        if (serviceConnectedLatch == null) {\n            serviceConnectedLatch = new CountDownLatch(1);\n        }\n    }\n\n    private void startProcess(String pid, String cmd, String alpine) {\n        CallbackContext callbackContext = getCallbackContext(pid);\n        if (callbackContext != null) {\n            PluginResult result = new PluginResult(PluginResult.Status.OK, pid);\n            result.setKeepCallback(true);\n            callbackContext.sendPluginResult(result);\n        }\n\n        Message msg = Message.obtain(null, TerminalService.MSG_START_PROCESS);\n        msg.replyTo = handlerMessenger;\n        Bundle bundle = new Bundle();\n        bundle.putString(\"id\", pid);\n        bundle.putString(\"cmd\", cmd);\n        bundle.putString(\"alpine\", alpine);\n        msg.setData(bundle);\n        try {\n            serviceMessenger.send(msg);\n        } catch (RemoteException e) {\n            CallbackContext errorContext = getCallbackContext(pid);\n            if (errorContext != null) {\n                errorContext.error(\"Failed to start process: \" + e.getMessage());\n                cleanupCallback(pid);\n            }\n        }\n    }\n\n    private void exec(String execId, String cmd, String alpine) {\n        Message msg = Message.obtain(null, TerminalService.MSG_EXEC);\n        msg.replyTo = handlerMessenger;\n        Bundle bundle = new Bundle();\n        bundle.putString(\"id\", execId);\n        bundle.putString(\"cmd\", cmd);\n        bundle.putString(\"alpine\", alpine);\n        msg.setData(bundle);\n        try {\n            serviceMessenger.send(msg);\n        } catch (RemoteException e) {\n            CallbackContext callbackContext = getCallbackContext(execId);\n            if (callbackContext != null) {\n                callbackContext.error(\"Failed to execute command: \" + e.getMessage());\n                cleanupCallback(execId);\n            }\n        }\n    }\n\n    private void writeToProcess(String pid, String input, CallbackContext callbackContext) {\n        Message msg = Message.obtain(null, TerminalService.MSG_WRITE_TO_PROCESS);\n        Bundle bundle = new Bundle();\n        bundle.putString(\"id\", pid);\n        bundle.putString(\"input\", input);\n        msg.setData(bundle);\n        try {\n            serviceMessenger.send(msg);\n            callbackContext.success(\"Written to process\");\n        } catch (RemoteException e) {\n            callbackContext.error(\"Write error: \" + e.getMessage());\n        }\n    }\n\n    private void stopProcess(String pid, CallbackContext callbackContext) {\n        Message msg = Message.obtain(null, TerminalService.MSG_STOP_PROCESS);\n        Bundle bundle = new Bundle();\n        bundle.putString(\"id\", pid);\n        msg.setData(bundle);\n        try {\n            serviceMessenger.send(msg);\n            callbackContext.success(\"Process terminated\");\n        } catch (RemoteException e) {\n            callbackContext.error(\"Stop error: \" + e.getMessage());\n        }\n    }\n\n    private void isProcessRunning(String pid) {\n        Message msg = Message.obtain(null, TerminalService.MSG_IS_RUNNING);\n        msg.replyTo = handlerMessenger;\n        Bundle bundle = new Bundle();\n        bundle.putString(\"id\", pid);\n        msg.setData(bundle);\n        try {\n            serviceMessenger.send(msg);\n        } catch (RemoteException e) {\n            CallbackContext callbackContext = getCallbackContext(pid);\n            if (callbackContext != null) {\n                callbackContext.error(\"Check running error: \" + e.getMessage());\n                cleanupCallback(pid);\n            }\n        }\n    }\n\n    private CallbackContext getCallbackContext(String id) {\n        return callbackContextMap.get(id);\n    }\n\n    private void cleanupCallback(String id) {\n        callbackContextMap.remove(id);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n    }\n}"
  },
  {
    "path": "src/plugins/terminal/src/android/ProcessManager.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport java.io.*;\nimport java.util.Map;\nimport java.util.TimeZone;\nimport com.foxdebug.acode.rk.exec.terminal.*;\n\npublic class ProcessManager {\n    \n    private final Context context;\n    \n    public ProcessManager(Context context) {\n        this.context = context;\n    }\n    \n    /**\n     * Creates a ProcessBuilder with common environment setup\n     */\n    public ProcessBuilder createProcessBuilder(String cmd, boolean useAlpine) {\n        String xcmd = useAlpine ? \"source $PREFIX/init-sandbox.sh \" + cmd : cmd;\n        ProcessBuilder builder = new ProcessBuilder(\"sh\", \"-c\", xcmd);\n        setupEnvironment(builder.environment());\n        return builder;\n    }\n    \n    /**\n     * Sets up common environment variables\n     */\n    private void setupEnvironment(Map<String, String> env) {\n        env.put(\"PREFIX\", context.getFilesDir().getAbsolutePath());\n        env.put(\"NATIVE_DIR\", context.getApplicationInfo().nativeLibraryDir);\n        \n        TimeZone tz = TimeZone.getDefault();\n        env.put(\"ANDROID_TZ\", tz.getID());\n        \n        try {\n            int target = context.getPackageManager()\n                .getPackageInfo(context.getPackageName(), 0)\n                .applicationInfo.targetSdkVersion;\n            env.put(\"FDROID\", String.valueOf(target <= 28));\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n    }\n    \n    /**\n     * Reads all output from a stream\n     */\n    public static String readStream(InputStream stream) throws IOException {\n        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));\n        StringBuilder output = new StringBuilder();\n        String line;\n        while ((line = reader.readLine()) != null) {\n            output.append(line).append(\"\\n\");\n        }\n        return output.toString();\n    }\n    \n    /**\n     * Executes a command and returns the result\n     */\n    public ExecResult executeCommand(String cmd, boolean useAlpine) throws Exception {\n        ProcessBuilder builder = createProcessBuilder(cmd, useAlpine);\n        Process process = builder.start();\n        \n        String stdout = readStream(process.getInputStream());\n        String stderr = readStream(process.getErrorStream());\n        int exitCode = process.waitFor();\n        \n        return new ExecResult(exitCode, stdout.trim(), stderr.trim());\n    }\n    \n    /**\n     * Result container for command execution\n     */\n    public static class ExecResult {\n        public final int exitCode;\n        public final String stdout;\n        public final String stderr;\n        \n        public ExecResult(int exitCode, String stdout, String stderr) {\n            this.exitCode = exitCode;\n            this.stdout = stdout;\n            this.stderr = stderr;\n        }\n        \n        public boolean isSuccess() {\n            return exitCode == 0;\n        }\n        \n        public String getErrorMessage() {\n            if (!stderr.isEmpty()) {\n                return stderr;\n            }\n            return \"Command exited with code: \" + exitCode;\n        }\n    }\n}"
  },
  {
    "path": "src/plugins/terminal/src/android/ProcessServer.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport org.java_websocket.WebSocket;\nimport org.java_websocket.handshake.ClientHandshake;\nimport org.java_websocket.server.WebSocketServer;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicReference;\n\nclass ProcessServer extends WebSocketServer {\n\n    private final String[] cmd;\n    private final CountDownLatch readyLatch = new CountDownLatch(1);\n    private final AtomicReference<Exception> startError = new AtomicReference<>();\n\n    private static final class ConnState {\n        final Process process;\n        final OutputStream stdin;\n\n        ConnState(Process process, OutputStream stdin) {\n            this.process = process;\n            this.stdin   = stdin;\n        }\n    }\n\n    ProcessServer(int port, String[] cmd) {\n        super(new InetSocketAddress(\"127.0.0.1\", port));\n        this.cmd = cmd;\n    }\n\n    void startAndAwait() throws Exception {\n        start();\n        readyLatch.await();\n        Exception err = startError.get();\n        if (err != null) throw err;\n    }\n\n    @Override\n    public void onStart() {\n        readyLatch.countDown();\n    }\n\n    @Override\n    public void onError(WebSocket conn, Exception ex) {\n        if (conn == null) {\n            // Bind/startup failure — unblock startAndAwait() so it can throw.\n            startError.set(ex);\n            readyLatch.countDown();\n        }\n        // Per-connection errors: do nothing. onClose fires immediately after\n        // for the same connection, which is the single place cleanup happens.\n    }\n\n    @Override\n    public void onOpen(WebSocket conn, ClientHandshake handshake) {\n        try {\n            Process process = new ProcessBuilder(cmd).redirectErrorStream(true).start();\n            InputStream  stdout = process.getInputStream();\n            OutputStream stdin  = process.getOutputStream();\n\n            conn.setAttachment(new ConnState(process, stdin));\n\n            new Thread(() -> {\n                try {\n                    byte[] buf = new byte[8192];\n                    int len;\n                    while ((len = stdout.read(buf)) != -1) {\n                        conn.send(ByteBuffer.wrap(buf, 0, len));\n                    }\n                } catch (Exception ignored) {}\n                conn.close(1000, \"process exited\");\n            }).start();\n\n        } catch (Exception e) {\n            conn.close(1011, \"Failed to start process: \" + e.getMessage());\n        }\n    }\n\n    @Override\n    public void onMessage(WebSocket conn, ByteBuffer msg) {\n        try {\n            ConnState state = conn.getAttachment();\n            state.stdin.write(msg.array(), msg.position(), msg.remaining());\n            state.stdin.flush();\n        } catch (Exception ignored) {}\n    }\n\n    @Override\n    public void onMessage(WebSocket conn, String message) {\n        try {\n            ConnState state = conn.getAttachment();\n            state.stdin.write(message.getBytes(StandardCharsets.UTF_8));\n            state.stdin.flush();\n        } catch (Exception ignored) {}\n    }\n\n    @Override\n    public void onClose(WebSocket conn, int code, String reason, boolean remote) {\n        try {\n            ConnState state = conn.getAttachment();\n            if (state != null) state.process.destroy();\n        } catch (Exception ignored) {}\n\n        // stop() calls w.join() on every worker thread. If called directly from\n        // onClose (which runs on a WebSocketWorker thread), it deadlocks waiting\n        // for itself to finish. A separate thread sidesteps that entirely.\n        new Thread(() -> {\n            try {\n                stop();\n            } catch (Exception ignored) {}\n        }).start();\n    }\n}"
  },
  {
    "path": "src/plugins/terminal/src/android/ProcessUtils.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport java.lang.reflect.Field;\nimport android.util.Log;\nimport com.foxdebug.acode.rk.exec.terminal.*;\n\npublic class ProcessUtils {\n    \n    /**\n     * Gets the PID of a process using reflection\n     */\n    public static long getPid(Process process) {\n        try {\n            Field f = process.getClass().getDeclaredField(\"pid\");\n            f.setAccessible(true);\n            return f.getLong(process);\n        } catch (Exception e) {\n            return -1;\n        }\n    }\n    \n    /**\n     * Checks if a process is still alive\n     */\n    public static boolean isAlive(Process process) {\n        try {\n            process.exitValue();\n            return false;\n        } catch(IllegalThreadStateException e) {\n            return true;\n        }\n    }\n    \n    /**\n     * Forcefully kills a process and its children\n     */\n    public static void killProcessTree(Process process) {\n        try {\n            long pid = getPid(process);\n            if (pid > 0) {\n                Runtime.getRuntime().exec(\"kill -9 -\" + pid);\n            }\n        } catch (Exception error) {\n            Log.w(\"ProcessUtils\", \"Failed to kill process tree.\", error);\n        }\n        process.destroy();\n    }\n}\n"
  },
  {
    "path": "src/plugins/terminal/src/android/StreamHandler.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport java.io.*;\nimport com.foxdebug.acode.rk.exec.terminal.*;\npublic class StreamHandler {\n    \n    public interface OutputListener {\n        void onLine(String line);\n    }\n    \n    /**\n     * Streams output from an InputStream to a listener\n     */\n    public static void streamOutput(InputStream inputStream, OutputListener listener) {\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {\n            String line;\n            while ((line = reader.readLine()) != null) {\n                listener.onLine(line);\n            }\n        } catch (IOException ignored) {\n        }\n    }\n    \n    /**\n     * Writes input to an OutputStream\n     */\n    public static void writeToStream(OutputStream outputStream, String input) throws IOException {\n        outputStream.write((input + \"\\n\").getBytes());\n        outputStream.flush();\n    }\n}"
  },
  {
    "path": "src/plugins/terminal/src/android/TerminalService.java",
    "content": "package com.foxdebug.acode.rk.exec.terminal;\n\nimport android.app.Notification;\nimport android.app.NotificationChannel;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.app.Service;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Message;\nimport android.os.Messenger;\nimport android.os.PowerManager;\nimport android.os.RemoteException;\nimport androidx.core.app.NotificationCompat;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.Executors;\nimport com.foxdebug.acode.rk.exec.terminal.*;\n\n\npublic class TerminalService extends Service {\n\n    public static final int MSG_START_PROCESS = 1;\n    public static final int MSG_WRITE_TO_PROCESS = 2;\n    public static final int MSG_STOP_PROCESS = 3;\n    public static final int MSG_IS_RUNNING = 4;\n    public static final int MSG_EXEC = 5;\n\n    public static final String CHANNEL_ID = \"terminal_exec_channel\";\n    \n    public static final String ACTION_EXIT_SERVICE = \"com.foxdebug.acode.ACTION_EXIT_SERVICE\";\n    public static final String MOVE_TO_BACKGROUND = \"com.foxdebug.acode.MOVE_TO_BACKGROUND\";\n    public static final String MOVE_TO_FOREGROUND = \"com.foxdebug.acode.MOVE_TO_FOREGROUND\";\n    public static final String ACTION_TOGGLE_WAKE_LOCK = \"com.foxdebug.acode.ACTION_TOGGLE_WAKE_LOCK\";\n    public static boolean Default_Foreground = true;\n\n    private final Map<String, Process> processes = new ConcurrentHashMap<>();\n    private final Map<String, OutputStream> processInputs = new ConcurrentHashMap<>();\n    private final Map<String, Messenger> clientMessengers = new ConcurrentHashMap<>();\n    private final java.util.concurrent.ExecutorService threadPool = Executors.newCachedThreadPool();\n\n    private final Messenger serviceMessenger = new Messenger(new ServiceHandler());\n    \n    private PowerManager.WakeLock wakeLock;\n    private boolean isWakeLockHeld = false;\n    private ProcessManager processManager;\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        processManager = new ProcessManager(this);\n        if(Default_Foreground){\n            createNotificationChannel();\n            updateNotification();\n        }\n    }\n\n    @Override\n    public IBinder onBind(Intent intent) {\n        return serviceMessenger.getBinder();\n    }\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        if (intent != null) {\n            String action = intent.getAction();\n            if (ACTION_EXIT_SERVICE.equals(action)) {\n                stopForeground(true);\n                stopSelf();\n                return START_NOT_STICKY;\n            } else if (ACTION_TOGGLE_WAKE_LOCK.equals(action)) {\n                toggleWakeLock();\n            } else if(MOVE_TO_BACKGROUND.equals(action)){\n                Default_Foreground = false;\n                stopForeground(true);\n            } else if(MOVE_TO_FOREGROUND.equals(action)){\n                Default_Foreground = true;\n                createNotificationChannel();\n                updateNotification();\n            }\n        }\n        return START_STICKY;\n    }\n\n    private class ServiceHandler extends Handler {\n        @Override\n        public void handleMessage(Message msg) {\n            Bundle bundle = msg.getData();\n            String id = bundle.getString(\"id\");\n            Messenger clientMessenger = msg.replyTo;\n\n            switch (msg.what) {\n                case MSG_START_PROCESS:\n                    String cmd = bundle.getString(\"cmd\");\n                    String alpine = bundle.getString(\"alpine\");\n                    clientMessengers.put(id, clientMessenger);\n                    startProcess(id, cmd, \"true\".equals(alpine));\n                    break;\n                case MSG_WRITE_TO_PROCESS:\n                    String input = bundle.getString(\"input\");\n                    writeToProcess(id, input);\n                    break;\n                case MSG_STOP_PROCESS:\n                    stopProcess(id);\n                    break;\n                case MSG_IS_RUNNING:\n                    isProcessRunning(id, clientMessenger);\n                    break;\n                case MSG_EXEC:\n                    String execCmd = bundle.getString(\"cmd\");\n                    String execAlpine = bundle.getString(\"alpine\");\n                    clientMessengers.put(id, clientMessenger);\n                    exec(id, execCmd, \"true\".equals(execAlpine));\n                    break;\n            }\n        }\n    }\n\n    private void toggleWakeLock() {\n        if (isWakeLockHeld) {\n            releaseWakeLock();\n        } else {\n            acquireWakeLock();\n        }\n        updateNotification();\n    }\n\n    private void acquireWakeLock() {\n        if (wakeLock == null) {\n            PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);\n            wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, \"AcodeTerminal:WakeLock\");\n        }\n        \n        if (!isWakeLockHeld) {\n            wakeLock.acquire();\n            isWakeLockHeld = true;\n        }\n    }\n\n    private void releaseWakeLock() {\n        if (wakeLock != null && isWakeLockHeld) {\n            wakeLock.release();\n            isWakeLockHeld = false;\n        }\n    }\n\n    private void startProcess(String pid, String cmd, boolean useAlpine) {\n        threadPool.execute(() -> {\n            try {\n                ProcessBuilder builder = processManager.createProcessBuilder(cmd, useAlpine);\n                Process process = builder.start();\n                \n                processes.put(pid, process);\n                processInputs.put(pid, process.getOutputStream());\n                \n                // Stream stdout\n                threadPool.execute(() -> \n                    StreamHandler.streamOutput(process.getInputStream(), \n                        line -> sendMessageToClient(pid, \"stdout\", line))\n                );\n                \n                // Stream stderr\n                threadPool.execute(() -> \n                    StreamHandler.streamOutput(process.getErrorStream(), \n                        line -> sendMessageToClient(pid, \"stderr\", line))\n                );\n                \n                // Wait for process completion\n                threadPool.execute(() -> {\n                    try {\n                        int exitCode = process.waitFor();\n                        sendMessageToClient(pid, \"exit\", String.valueOf(exitCode));\n                        cleanup(pid);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                });\n            } catch (IOException e) {\n                e.printStackTrace();\n                sendMessageToClient(pid, \"stderr\", \"Failed to start process: \" + e.getMessage());\n                sendMessageToClient(pid, \"exit\", \"1\");\n                cleanup(pid);\n            }\n        });\n    }\n\n    private void exec(String execId, String cmd, boolean useAlpine) {\n        threadPool.execute(() -> {\n            try {\n                ProcessManager.ExecResult result = processManager.executeCommand(cmd, useAlpine);\n                \n                if (result.isSuccess()) {\n                    sendExecResultToClient(execId, true, result.stdout);\n                } else {\n                    sendExecResultToClient(execId, false, result.getErrorMessage());\n                }\n                \n                cleanup(execId);\n            } catch (Exception e) {\n                sendExecResultToClient(execId, false, \"Exception: \" + e.getMessage());\n                cleanup(execId);\n            }\n        });\n    }\n\n    private void sendMessageToClient(String id, String action, String data) {\n        Messenger clientMessenger = clientMessengers.get(id);\n        if (clientMessenger != null) {\n            try {\n                Message msg = Message.obtain();\n                Bundle bundle = new Bundle();\n                bundle.putString(\"id\", id);\n                bundle.putString(\"action\", action);\n                bundle.putString(\"data\", data);\n                msg.setData(bundle);\n                clientMessenger.send(msg);\n            } catch (RemoteException e) {\n                cleanup(id);\n            }\n        }\n    }\n\n    private void sendExecResultToClient(String id, boolean isSuccess, String data) {\n        Messenger clientMessenger = clientMessengers.get(id);\n        if (clientMessenger != null) {\n            try {\n                Message msg = Message.obtain();\n                Bundle bundle = new Bundle();\n                bundle.putString(\"id\", id);\n                bundle.putString(\"action\", \"exec_result\");\n                bundle.putString(\"data\", data);\n                bundle.putBoolean(\"isSuccess\", isSuccess);\n                msg.setData(bundle);\n                clientMessenger.send(msg);\n            } catch (RemoteException e) {\n                cleanup(id);\n            }\n        }\n    }\n\n    private void writeToProcess(String pid, String input) {\n        try {\n            OutputStream os = processInputs.get(pid);\n            if (os != null) {\n                StreamHandler.writeToStream(os, input);\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void stopProcess(String pid) {\n        Process process = processes.get(pid);\n        if (process != null) {\n            ProcessUtils.killProcessTree(process);\n            cleanup(pid);\n        }\n    }\n\n   private void isProcessRunning(String pid, Messenger clientMessenger) {\n    boolean running =\n        processes.containsKey(pid) &&\n        ProcessUtils.isAlive(processes.get(pid));\n\n    try {\n        Message reply = Message.obtain();\n        Bundle bundle = new Bundle();\n        bundle.putString(\"id\", pid);\n        bundle.putString(\"action\", \"isRunning\");\n        bundle.putString(\"data\", running ? \"running\" : \"stopped\");\n        reply.setData(bundle);\n        clientMessenger.send(reply);\n    } catch (RemoteException e) {\n        // nothing else to do\n    }\n}\n\n\n    private void cleanup(String id) {\n        processes.remove(id);\n        processInputs.remove(id);\n        clientMessengers.remove(id);\n    }\n\n    private void createNotificationChannel() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            NotificationChannel serviceChannel = new NotificationChannel(\n                    CHANNEL_ID,\n                    \"Terminal Executor Channel\",\n                    NotificationManager.IMPORTANCE_LOW\n            );\n            NotificationManager manager = getSystemService(NotificationManager.class);\n            if (manager != null) {\n                manager.createNotificationChannel(serviceChannel);\n            }\n        }\n    }\n\n    private void updateNotification() {\n        Intent exitIntent = new Intent(this, TerminalService.class);\n        exitIntent.setAction(ACTION_EXIT_SERVICE);\n        PendingIntent exitPendingIntent = PendingIntent.getService(this, 0, exitIntent, \n            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\n        Intent wakeLockIntent = new Intent(this, TerminalService.class);\n        wakeLockIntent.setAction(ACTION_TOGGLE_WAKE_LOCK);\n        PendingIntent wakeLockPendingIntent = PendingIntent.getService(this, 1, wakeLockIntent,\n            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);\n\n        String contentText = \"Executor service\" + (isWakeLockHeld ? \" (wakelock held)\" : \"\");\n        String wakeLockButtonText = isWakeLockHeld ? \"Release Wake Lock\" : \"Acquire Wake Lock\";\n\n        int notificationIcon = resolveDrawableId(\"ic_notification\", \"ic_launcher_foreground\", \"ic_launcher\");\n\n        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)\n                .setContentTitle(\"Acode Service\")\n                .setContentText(contentText)\n                .setSmallIcon(notificationIcon)\n                .setOngoing(true)\n                .addAction(notificationIcon, wakeLockButtonText, wakeLockPendingIntent)\n                .addAction(notificationIcon, \"Exit\", exitPendingIntent)\n                .build();\n\n        startForeground(1, notification);\n    }\n\n    @Override\n    public void onDestroy() {\n        stopForeground(true);\n        super.onDestroy();\n        releaseWakeLock();\n        \n        for (Process process : processes.values()) {\n            ProcessUtils.killProcessTree(process);\n        }\n\n        processes.clear();\n        processInputs.clear();\n        clientMessengers.clear();\n        threadPool.shutdown();\n    }\n\n    private int resolveDrawableId(String... names) {\n        for (String name : names) {\n            int id = getResources().getIdentifier(name, \"drawable\", getPackageName());\n            if (id != 0) return id;\n        }\n        return android.R.drawable.sym_def_app_icon;\n    }\n}"
  },
  {
    "path": "src/plugins/terminal/www/Executor.js",
    "content": "/**\n * @class Executor\n * @description\n * This class provides an interface to run shell commands from a Cordova app.\n * It supports real-time process streaming, writing input to running processes,\n * stopping them, and executing one-time commands.\n */\n\nconst exec = require('cordova/exec');\n\nclass Executor {\n  constructor(BackgroundExecutor = false) {\n    this.ExecutorType = BackgroundExecutor ? \"BackgroundExecutor\" : \"Executor\";\n  }\n\n  /**\n   * Spawns a process and exposes it as a raw WebSocket stream.\n   *\n   * @param {string[]} cmd - Command and arguments to execute (e.g. `[\"sh\", \"-c\", \"echo hi\"]`).\n   * @param {(ws: WebSocket) => void} callback - Called with the connected WebSocket once the\n   *   process is ready. Use `ws.send()` to write to stdin and `ws.onmessage` to read stdout.\n   */\n  spawnStream(cmd, callback, onError) {\n    exec((port) => {\n      const ws = new WebSocket(`ws://127.0.0.1:${port}`);\n      ws.binaryType = \"arraybuffer\";\n\n      ws.onopen = () => {\n        callback(ws);\n      };\n\n      ws.onerror = (e) => {\n        if (onError) onError(e);\n      };\n\n    }, (err) => { if (onError) onError(err); }, \"Executor\", \"spawn\", [cmd]);\n  }\n\n\n  /**\n   * Starts a shell process and enables real-time streaming of stdout, stderr, and exit status.\n   *\n   * @param {string} command - The shell command to run (e.g., `\"sh\"`, `\"ls -al\"`).\n   * @param {(type: 'stdout' | 'stderr' | 'exit', data: string) => void} onData - Callback that receives real-time output:\n   *   - `\"stdout\"`: Standard output line.\n   *   - `\"stderr\"`: Standard error line.\n   *   - `\"exit\"`: Exit code of the process.\n   * @param {boolean} [alpine=false] - Whether to run the command inside the Alpine sandbox environment (`true`) or on Android directly (`false`).\n   * @returns {Promise<string>} Resolves with a unique process ID (UUID) used for future references like `write()` or `stop()`.\n   *\n   * @example\n   * const executor = new Executor();\n   * executor.start('sh', (type, data) => {\n   *   //console.log(`[${type}] ${data}`);\n   * }).then(uuid => {\n   *   executor.write(uuid, 'echo Hello World');\n   *   executor.stop(uuid);\n   * });\n   */\n  start(command, onData, alpine = false) {\n    return new Promise((resolve, reject) => {\n      let first = true;\n      exec(\n        async (message) => {\n          //console.log(message);\n          if (first) {\n            first = false;\n            await new Promise(resolve => setTimeout(resolve, 100));\n            // First message is always the process UUID\n            resolve(message);\n          } else {\n            const match = message.match(/^([^:]+):(.*)$/);\n            if (match) {\n              const prefix = match[1];         // e.g. \"stdout\"\n              const content = match[2]; // output\n              onData(prefix, content);\n            } else {\n              onData(\"unknown\", message);\n            }\n          }\n        },\n        reject,\n        this.ExecutorType,\n        \"start\",\n        [command, String(alpine)]\n      );\n    });\n  }\n\n  /**\n   * Sends input to a running process's stdin.\n   *\n   * @param {string} uuid - The process ID returned by {@link Executor#start}.\n   * @param {string} input - Input string to send (e.g., shell commands).\n   * @returns {Promise<string>} Resolves once the input is written.\n   *\n   * @example\n   * executor.write(uuid, 'ls /sdcard');\n   */\n  write(uuid, input) {\n    //console.log(\"write: \" + input + \" to \" + uuid);\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"write\", [uuid, input]);\n    });\n  }\n\n  /**\n   * Moves the executor service to the background (stops foreground notification).\n   *\n   * @returns {Promise<string>} Resolves when the service is moved to background.\n   *\n   * @example\n   * executor.moveToBackground();\n   */\n  moveToBackground() {\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"moveToBackground\", []);\n    });\n  }\n\n  /**\n   * Moves the executor service to the foreground (shows notification).\n   *\n   * @returns {Promise<string>} Resolves when the service is moved to foreground.\n   *\n   * @example\n   * executor.moveToForeground();\n   */\n  moveToForeground() {\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"moveToForeground\", []);\n    });\n  }\n\n  /**\n   * Terminates a running process.\n   *\n   * @param {string} uuid - The process ID returned by {@link Executor#start}.\n   * @returns {Promise<string>} Resolves when the process has been stopped.\n   *\n   * @example\n   * executor.stop(uuid);\n   */\n  stop(uuid) {\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"stop\", [uuid]);\n    });\n  }\n\n  /**\n   * Checks if a process is still running.\n   *\n   * @param {string} uuid - The process ID returned by {@link Executor#start}.\n   * @returns {Promise<boolean>} Resolves `true` if the process is running, `false` otherwise.\n   *\n   * @example\n   * const isAlive = await executor.isRunning(uuid);\n   */\n  isRunning(uuid) {\n    return new Promise((resolve, reject) => {\n      exec(\n        (result) => {\n          resolve(result === \"running\");\n        },\n        reject,\n        this.ExecutorType,\n        \"isRunning\",\n        [uuid]\n      );\n    });\n  }\n\n  /**\n   * Stops the executor service completely.\n   *\n   * @returns {Promise<string>} Resolves when the service has been stopped.\n   *\n   * Note: This does not gurantee that all running processes have been killed, but the service will no longer be active. Use with caution.\n   * \n   * @example\n   * executor.stopService();\n   */\n  stopService() {\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"stopService\", []);\n    });\n  }\n\n  /**\n   * Executes a shell command once and waits for it to finish.\n   * Unlike {@link Executor#start}, this does not stream output.\n   *\n   * @param {string} command - The shell command to execute.\n   * @param {boolean} [alpine=false] - Whether to run the command in the Alpine sandbox (`true`) or Android environment (`false`).\n   * @returns {Promise<string>} Resolves with standard output on success, rejects with an error or standard error on failure.\n   *\n   * @example\n   * executor.execute('ls -l')\n   *   .then(//console.log)\n   *   .catch(console.error);\n   */\n  execute(command, alpine = false) {\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"exec\", [command, String(alpine)]);\n    });\n  }\n\n  /**\n   * Loads a native library from the specified path.\n   *\n   * @param {string} path - The path to the native library to load.\n   * @returns {Promise<string>} Resolves when the library has been loaded.\n   *\n   * @example\n   * executor.loadLibrary('/path/to/library.so');\n   */\n  loadLibrary(path) {\n    return new Promise((resolve, reject) => {\n      exec(resolve, reject, this.ExecutorType, \"loadLibrary\", [path]);\n    });\n  }\n}\n\n//backward compatibility\nconst executorInstance = new Executor();\nexecutorInstance.BackgroundExecutor = new Executor(true);\n\nmodule.exports = executorInstance;\n"
  },
  {
    "path": "src/plugins/terminal/www/Terminal.js",
    "content": "const Executor = require(\"./Executor\");\n\nconst Terminal = {\n    /**\n     * Starts the AXS environment by writing init scripts and executing the sandbox.\n     * @param {boolean} [installing=false] - Whether AXS is being started during installation.\n     * @param {Function} [logger=console.log] - Function to log standard output.\n     * @param {Function} [err_logger=console.error] - Function to log errors.\n     * @returns {Promise<boolean>} - Returns true if installation completes with exit code 0, void if not installing\n     */\n    async startAxs(installing = false, logger = console.log, err_logger = console.error) {\n        const filesDir = await new Promise((resolve, reject) => {\n            system.getFilesDir(resolve, reject);\n        });\n\n        if (installing) {\n            return new Promise((resolve, reject) => {\n                readAsset(\"init-alpine.sh\", async (content) => {\n                    system.writeText(`${filesDir}/init-alpine.sh`, content, logger, err_logger);\n                });\n\n                readAsset(\"rm-wrapper.sh\", async (content) => {\n                    system.deleteFile(`${filesDir}/alpine/bin/rm`, logger, err_logger);\n                    system.writeText(`${filesDir}/alpine/bin/rm`, content, logger, err_logger);\n                    system.setExec(`${filesDir}/alpine/bin/rm`, true, logger, err_logger);\n                });\n\n                readAsset(\"init-sandbox.sh\", (content) => {\n                    system.writeText(`${filesDir}/init-sandbox.sh`, content, logger, err_logger);\n\n                    Executor.start(\"sh\", (type, data) => {\n                        logger(`${type} ${data}`);\n\n                        // Check for exit code during installation\n                        if (type === \"exit\") {\n                            resolve(data === \"0\");\n                        }\n                    }).then(async (uuid) => {\n                        await Executor.write(uuid, `source ${filesDir}/init-sandbox.sh ${installing ? \"--installing\" : \"\"}; exit`);\n                    }).catch((error) => {\n                        err_logger(\"Failed to start AXS:\", error);\n                        resolve(false);\n                    });\n                });\n            });\n        } else {\n            readAsset(\"rm-wrapper.sh\", async (content) => {\n                system.deleteFile(`${filesDir}/alpine/bin/rm`, logger, err_logger);\n                system.writeText(`${filesDir}/alpine/bin/rm`, content, logger, err_logger);\n                system.setExec(`${filesDir}/alpine/bin/rm`, true, logger, err_logger);\n            });\n\n            readAsset(\"init-alpine.sh\", async (content) => {\n                system.writeText(`${filesDir}/init-alpine.sh`, content, logger, err_logger);\n            });\n\n            readAsset(\"init-sandbox.sh\", (content) => {\n                system.writeText(`${filesDir}/init-sandbox.sh`, content, logger, err_logger);\n\n                Executor.start(\"sh\", (type, data) => {\n                    logger(`${type} ${data}`);\n                }).then(async (uuid) => {\n                    await Executor.write(uuid, `source ${filesDir}/init-sandbox.sh ${installing ? \"--installing\" : \"\"}; exit`);\n                });\n            });\n        }\n    },\n\n    /**\n     * Stops the AXS process by forcefully killing it.\n     * @returns {Promise<void>}\n     */\n    async stopAxs() {\n        await Executor.execute(`kill -KILL $(cat $PREFIX/pid)`);\n    },\n\n    /**\n     * Checks if the AXS process is currently running.\n     * @returns {Promise<boolean>} - `true` if AXS is running, `false` otherwise.\n     */\n    async isAxsRunning() {\n        const filesDir = await new Promise((resolve, reject) => {\n            system.getFilesDir(resolve, reject);\n        });\n\n        const pidExists = await new Promise((resolve, reject) => {\n            system.fileExists(`${filesDir}/pid`, false, (result) => {\n                resolve(result == 1);\n            }, reject);\n        });\n\n        if (!pidExists) return false;\n\n        const result = await Executor.BackgroundExecutor.execute(`kill -0 $(cat $PREFIX/pid) 2>/dev/null && echo \"true\" || echo \"false\"`);\n        return String(result).toLowerCase() === \"true\";\n    },\n\n    /**\n     * Installs Alpine by downloading binaries and extracting the root filesystem.\n     * Also sets up additional dependencies for F-Droid variant.\n     * @param {Function} [logger=console.log] - Function to log standard output.\n     * @param {Function} [err_logger=console.error] - Function to log errors.\n     * @returns {Promise<boolean>} - Returns true if installation completes with exit code 0\n     */\n    async install(logger = console.log, err_logger = console.error) {\n        if (!(await this.isSupported())) return false;\n\n        try {\n            //cleanup before insatll\n            await this.uninstall();\n        } catch (e) {\n            //supress error\n        }\n\n        const filesDir = await new Promise((resolve, reject) => {\n            system.getFilesDir(resolve, reject);\n        });\n\n        const arch = await new Promise((resolve, reject) => {\n            system.getArch(resolve, reject);\n        });\n\n        try {\n            let alpineUrl;\n            let axsUrl;\n            let prootUrl;\n            let libTalloc;\n            let libproot = null;\n            let libproot32 = null;\n\n            if (arch === \"arm64-v8a\") {\n                libproot = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm64/libproot.so\";\n                libproot32 = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm64/libproot32.so\";\n                libTalloc = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm64/libtalloc.so\";\n                prootUrl = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm64/libproot-xed.so\";\n                axsUrl = `https://github.com/bajrangCoder/acodex_server/releases/latest/download/axs-musl-android-arm64`;\n                alpineUrl = \"https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/aarch64/alpine-minirootfs-3.21.0-aarch64.tar.gz\";\n            } else if (arch === \"armeabi-v7a\") {\n                libproot = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm32/libproot.so\";\n                libTalloc = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm32/libtalloc.so\";\n                prootUrl = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/arm32/libproot-xed.so\";\n                axsUrl = `https://github.com/bajrangCoder/acodex_server/releases/latest/download/axs-musl-android-armv7`;\n                alpineUrl = \"https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/armhf/alpine-minirootfs-3.21.0-armhf.tar.gz\";\n            } else if (arch === \"x86_64\") {\n                libproot = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/x64/libproot.so\";\n                libproot32 = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/x64/libproot32.so\";\n                libTalloc = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/x64/libtalloc.so\";\n                prootUrl = \"https://raw.githubusercontent.com/Acode-Foundation/Acode/main/src/plugins/proot/libs/x64/libproot-xed.so\";\n                axsUrl = `https://github.com/bajrangCoder/acodex_server/releases/latest/download/axs-musl-android-x86_64`;\n                alpineUrl = \"https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-minirootfs-3.21.0-x86_64.tar.gz\";\n            } else {\n                throw new Error(`Unsupported architecture: ${arch}`);\n            }\n\n\n            logger(\"⬇️  Downloading sandbox filesystem...\");\n            await new Promise((resolve, reject) => {\n                cordova.plugin.http.downloadFile(\n                    alpineUrl, {}, {},\n                    cordova.file.dataDirectory + \"alpine.tar.gz\",\n                    resolve, reject\n                );\n            });\n\n            logger(\"⬇️  Downloading axs...\");\n            await new Promise((resolve, reject) => {\n                cordova.plugin.http.downloadFile(\n                    axsUrl, {}, {},\n                    cordova.file.dataDirectory + \"axs\",\n                    resolve, reject\n                );\n            });\n\n            const isFdroid = await Executor.execute(\"echo $FDROID\");\n            if (isFdroid === \"true\") {\n                logger(\"🐧  F-Droid flavor detected, downloading additional files...\");\n                logger(\"⬇️  Downloading compatibility layer...\");\n                await new Promise((resolve, reject) => {\n                    cordova.plugin.http.downloadFile(\n                        prootUrl, {}, {},\n                        cordova.file.dataDirectory + \"libproot-xed.so\",\n                        resolve, reject\n                    );\n                });\n\n                logger(\"⬇️  Downloading supporting library...\");\n                await new Promise((resolve, reject) => {\n                    cordova.plugin.http.downloadFile(\n                        libTalloc, {}, {},\n                        cordova.file.dataDirectory + \"libtalloc.so.2\",\n                        resolve, reject\n                    );\n                });\n\n                if (libproot != null) {\n                    await new Promise((resolve, reject) => {\n                        cordova.plugin.http.downloadFile(\n                            libproot, {}, {},\n                            cordova.file.dataDirectory + \"libproot.so\",\n                            resolve, reject\n                        );\n                    });\n                }\n\n                if (libproot32 != null) {\n                    await new Promise((resolve, reject) => {\n                        cordova.plugin.http.downloadFile(\n                            libproot32, {}, {},\n                            cordova.file.dataDirectory + \"libproot32.so\",\n                            resolve, reject\n                        );\n                    });\n                }\n\n            }\n\n            logger(\"✅  All downloads completed\");\n\n            logger(\"📁  Setting up directories...\");\n\n            await new Promise((resolve, reject) => {\n                system.mkdirs(`${filesDir}/.downloaded`, resolve, reject);\n            });\n\n            const alpineDir = `${filesDir}/alpine`;\n\n            await new Promise((resolve, reject) => {\n                system.mkdirs(alpineDir, resolve, reject);\n            });\n\n            logger(\"📦  Extracting sandbox filesystem...\");\n            await Executor.execute(`tar --no-same-owner -xf ${filesDir}/alpine.tar.gz -C ${alpineDir}`);\n\n            logger(\"⚙️  Applying basic configuration...\");\n            system.writeText(`${alpineDir}/etc/resolv.conf`, `nameserver 8.8.4.4 \\nnameserver 8.8.8.8`);\n\n            readAsset(\"rm-wrapper.sh\", async (content) => {\n                system.deleteFile(`${alpineDir}/bin/rm`, logger, err_logger);\n                system.writeText(`${alpineDir}/bin/rm`, content, logger, err_logger);\n                system.setExec(`${alpineDir}/bin/rm`, true, logger, err_logger);\n            });\n\n            logger(\"✅  Extraction complete\");\n            await new Promise((resolve, reject) => {\n                system.mkdirs(`${filesDir}/.extracted`, resolve, reject);\n            });\n\n            logger(\"⚙️  Updating sandbox enviroment...\");\n            const installResult = await this.startAxs(true, logger, err_logger);\n            return installResult;\n\n        } catch (e) {\n            err_logger(\"Installation failed:\", e);\n            console.error(\"Installation failed:\", e);\n            return false;\n        }\n    },\n\n    /**\n     * Checks if alpine is already installed.\n     * @returns {Promise<boolean>} - Returns true if all required files and directories exist.\n     */\n    isInstalled() {\n        return new Promise(async (resolve, reject) => {\n            const filesDir = await new Promise((resolve, reject) => {\n                system.getFilesDir(resolve, reject);\n            });\n\n            const alpineExists = await new Promise((resolve, reject) => {\n                system.fileExists(`${filesDir}/alpine`, false, (result) => {\n                    resolve(result == 1);\n                }, reject);\n            });\n\n            const downloaded = alpineExists && await new Promise((resolve, reject) => {\n                system.fileExists(`${filesDir}/.downloaded`, false, (result) => {\n                    resolve(result == 1);\n                }, reject);\n            });\n\n            const extracted = alpineExists && await new Promise((resolve, reject) => {\n                system.fileExists(`${filesDir}/.extracted`, false, (result) => {\n                    resolve(result == 1);\n                }, reject);\n            });\n\n            const configured = alpineExists && await new Promise((resolve, reject) => {\n                system.fileExists(`${filesDir}/.configured`, false, (result) => {\n                    resolve(result == 1);\n                }, reject);\n            });\n\n            resolve(alpineExists && downloaded && extracted && configured);\n        });\n    },\n\n    /**\n     * Checks if the current device architecture is supported.\n     * @returns {Promise<boolean>} - `true` if architecture is supported, otherwise `false`.\n     */\n    isSupported() {\n        return new Promise((resolve, reject) => {\n            system.getArch((arch) => {\n                resolve([\"arm64-v8a\", \"armeabi-v7a\", \"x86_64\"].includes(arch));\n            }, reject);\n        });\n    },\n    /**\n     * Creates a backup of the Alpine Linux installation\n     * @async\n     * @function backup\n     * @description Creates a compressed tar archive of the Alpine installation\n     * @returns {Promise<string>} Promise that resolves to the file URI of the created backup file (aterm_backup.tar)\n     * @throws {string} Rejects with \"Alpine is not installed.\" if Alpine is not currently installed\n     * @throws {string} Rejects with command output if backup creation fails\n     * @example\n     * try {\n     *   const backupPath = await backup();\n     *   console.log(`Backup created at: ${backupPath}`);\n     * } catch (error) {\n     *   console.error(`Backup failed: ${error}`);\n     * }\n     */\n    backup() {\n        return new Promise(async (resolve, reject) => {\n            if (!await this.isInstalled()) {\n                reject(\"Alpine is not installed.\");\n                return;\n            }\n            const cmd = `\n            set -e\n            INCLUDE_FILES=\"alpine .downloaded .extracted .configured axs\"\n            if [ \"$FDROID\" = \"true\" ]; then\n                INCLUDE_FILES=\"$INCLUDE_FILES libtalloc.so.2 libproot-xed.so\"\n            fi\n            EXCLUDE=\"--exclude=alpine/data --exclude=alpine/system --exclude=alpine/vendor --exclude=alpine/sdcard --exclude=alpine/storage --exclude=alpine/public --exclude=alpine/apex --exclude=alpine/odm --exclude=alpine/product --exclude=alpine/system_ext --exclude=alpine/linkerconfig --exclude=alpine/proc --exclude=alpine/sys --exclude=alpine/dev --exclude=alpine/run --exclude=alpine/tmp\"\n            tar -cf \"$PREFIX/aterm_backup.tar\" -C \"$PREFIX\" $EXCLUDE $INCLUDE_FILES\n            echo \"ok\"\n            `;\n            const result = await Executor.execute(cmd);\n            if (result === \"ok\") {\n                resolve(cordova.file.dataDirectory + \"aterm_backup.tar\");\n            } else {\n                reject(result);\n            }\n        });\n    },\n    /**\n     * Restores Alpine Linux installation from a backup file\n     * @async\n     * @function restore\n     * @description Restores the Alpine installation from a previously created backup file (aterm_backup.tar).\n     * This function stops any running Alpine processes, removes existing installation files, and extracts\n     * the backup to restore the previous state. The backup file must exist in the expected location.\n     * @returns {Promise<string>} Promise that resolves to \"ok\" when restoration completes successfully\n     * @throws {string} Rejects with \"Backup File does not exist\" if aterm_backup.tar is not found\n     * @throws {string} Rejects with command output if restoration fails\n     * @example\n     * try {\n     *   await restore();\n     *   console.log(\"Alpine installation restored successfully\");\n     * } catch (error) {\n     *   console.error(`Restore failed: ${error}`);\n     * }\n     */\n    restore() {\n        return new Promise(async (resolve, reject) => {\n            if (await this.isAxsRunning()) {\n                await this.stopAxs();\n            }\n\n            const cmd = `\n            set -e\n\n            INCLUDE_FILES=\"$PREFIX/alpine $PREFIX/.downloaded $PREFIX/.extracted $PREFIX/.configured $PREFIX/axs\"\n\n            if [ \"$FDROID\" = \"true\" ]; then\n                INCLUDE_FILES=\"$INCLUDE_FILES $PREFIX/libtalloc.so.2 $PREFIX/libproot-xed.so\"\n            fi\n\n            for item in $INCLUDE_FILES; do\n                rm -rf -- \"$item\"\n            done\n\n            tar -xf $PREFIX/aterm_backup.* -C \"$PREFIX\"\n            echo \"ok\"\n            `;\n\n            const result = await Executor.execute(cmd);\n            if (result === \"ok\") {\n                resolve(result);\n            } else {\n                reject(result);\n            }\n        });\n    },\n    /**\n     * Uninstalls the Alpine Linux installation\n     * @async\n     * @function uninstall\n     * @description Completely removes the Alpine Linux installation from the device by deleting all\n     * Alpine-related files and directories. This function stops any running Alpine processes before\n     * removal. NOTE: This does not perform cleanup of $PREFIX\n     * @returns {Promise<string>} Promise that resolves to \"ok\" when uninstallation completes successfully\n     * @throws {string} Rejects with command output if uninstallation fails\n     * @example\n     * try {\n     *   await uninstall();\n     *   console.log(\"Alpine installation removed successfully\");\n     * } catch (error) {\n     *   console.error(`Uninstall failed: ${error}`);\n     * }\n     */\n    uninstall() {\n        return new Promise(async (resolve, reject) => {\n            if (await this.isAxsRunning()) {\n                await this.stopAxs();\n            }\n\n            const cmd = `\n            set -e\n\n            INCLUDE_FILES=\"$PREFIX/alpine $PREFIX/.downloaded $PREFIX/.extracted $PREFIX/.configured $PREFIX/axs\"\n\n            if [ \"$FDROID\" = \"true\" ]; then\n                INCLUDE_FILES=\"$INCLUDE_FILES $PREFIX/libtalloc.so.2 $PREFIX/libproot-xed.so\"\n            fi\n\n            for item in $INCLUDE_FILES; do\n                rm -rf -- \"$item\"\n            done\n\n            echo \"ok\"\n            `;\n            const result = await Executor.execute(cmd);\n            if (result === \"ok\") {\n                resolve(result);\n            } else {\n                reject(result);\n            }\n        });\n    }\n};\n\n\nfunction readAsset(assetPath, callback) {\n    const assetUrl = \"file:///android_asset/\" + assetPath;\n\n    window.resolveLocalFileSystemURL(assetUrl, fileEntry => {\n        fileEntry.file(file => {\n            const reader = new FileReader();\n            reader.onloadend = () => callback(reader.result);\n            reader.readAsText(file);\n        }, console.error);\n    }, console.error);\n}\n\nmodule.exports = Terminal;"
  },
  {
    "path": "src/plugins/websocket/README.md",
    "content": "# Cordova Plugin: OkHttp WebSocket\n\nA Cordova plugin that uses [OkHttp](https://square.github.io/okhttp/) to provide WebSocket support in your Cordova app.\nIt aims to mimic the [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) in JavaScript, with additional features.\n\n## Features\n\n* ✅ WebSocket API-like interface\n* ✅ Event support: `onopen`, `onmessage`, `onerror`, `onclose`\n* ✅ `extensions` and `readyState` properties\n* ✅ `listClients()` to list active connections\n* ✅ Support for protocols\n* ✅ Support for Custom Headers.\n* ✅ Compatible with Cordova for Android\n\n---\n\n## Usage\n\n### Import\n\n```javascript\nconst WebSocketPlugin = cordova.websocket;\n```\n\n### Connect to WebSocket\n\n```javascript\nWebSocketPlugin.connect(\"wss://example.com/socket\", [\"protocol1\", \"protocol2\"], headers)\n  .then(ws => {\n    ws.onopen = (e) => console.log(\"Connected!\", e);\n    ws.onmessage = (e) => console.log(\"Message:\", e.data);\n    ws.onerror = (e) => console.error(\"Error:\", e);\n    ws.onclose = (e) => console.log(\"Closed:\", e);\n    \n    ws.send(\"Hello from Cordova!\");\n    ws.close();\n  })\n  .catch(err => console.error(\"WebSocket connection failed:\", err));\n```\n\n---\n\n## API Reference\n\n### Methods\n\n* `WebSocketPlugin.connect(url, protocols, headers, binaryType)`\n    * Connects to a WebSocket server.\n    * `url`: The WebSocket server URL.\n    * `protocols`: (Optional) An array of subprotocol strings.\n    * `headers`: (Optional) Custom headers as key-value pairs.\n    * `binaryType`: (Optional) Initial binary type setting.\n    * **Returns:** A Promise that resolves to a `WebSocketInstance`.\n\n* `WebSocketPlugin.listClients()`\n    * Lists all stored webSocket instance IDs.\n    * **Returns:** `Promise` that resolves to an array of `instanceId` strings.\n\n* `WebSocketPlugin.send(instanceId, message, binary)`\n    * Sends a message to the server using an instance ID.\n    * `instanceId`: The ID of the WebSocket instance.\n    * `message`: The message to send (string or ArrayBuffer/ArrayBufferView).\n    * `binary`: (Optional) Whether to send the message as binary, accepts `boolean`\n    * **Returns:** `Promise` that resolves when the message is sent.\n\n* `WebSocketPlugin.close(instanceId, code, reason)`\n    * same as `WebSocketInstance.close(code, reason)` but needs `instanceId`.\n    * **Returns:** `Promise` that resolves.\n\n### WebSocketInstance Methods\n\n* `WebSocketInstance.send(message, binary)`\n    * Sends a message to the server.\n    * `message`: The message to send (string or ArrayBuffer/ArrayBufferView).\n    * `binary`: (Optional) Whether to send the message as binary. accepts `boolean`\n    * Throws an error if the connection is not open.\n\n* `WebSocketInstance.close(code, reason)`\n    * Closes the connection.\n    * `code`: (Optional) If unspecified, a close code for the connection is automatically set: to 1000 for a normal closure, or otherwise to [another standard value in the range 1001-1015](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1) that indicates the actual reason the connection was closed.\n    * `reason`: A string providing a [custom WebSocket connection close reason](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6) (a concise human-readable prose explanation for the closure). The value must be no longer than 123 bytes (encoded in UTF-8).\n\n---\n\n### Properties of `WebSocketInstance`\n\n* `onopen`: Event listener for connection open.\n* `onmessage`: Event listener for messages received.\n* `onclose`: Event listener for connection close.\n* `onerror`: Event listener for errors.\n* `readyState`: (number) The state of the connection.\n    * 0 (`CONNECTING`): Socket created, not yet open.\n    * 1 (`OPEN`): Connection is open and ready.\n    * 2 (`CLOSING`): Connection is closing.\n    * 3 (`CLOSED`): Connection is closed or couldn't be opened.\n* `extensions`: (string) Extensions negotiated by the server.\n* `binaryType`: (string) Type of binary data to use ('arraybuffer' or '' (binary payload returned as strings.)).\n* `url`: (string) The WebSocket server URL.\n* `instanceId`: (string) Unique identifier for this WebSocket instance.\n\n### Event Handling\n\n`WebSocketInstance` extends `EventTarget`, providing standard event handling methods:\n\n* `addEventListener(type, listener)`: Registers an event listener.\n* `removeEventListener(type, listener)`: Removes an event listener.\n* `dispatchEvent(event)`: Dispatches an event to the object.\n\nExample of using event listeners:\n```javascript\nconst ws = await WebSocketPlugin.connect(\"wss://example.com/socket\");\n\n// Using on* properties\nws.onmessage = (event) => console.log(\"Message:\", event.data);\n\n// Using addEventListener\nws.addEventListener('message', (event) => console.log(\"Message:\", event.data));\n```\n\n### Constants\n\n* `WebSocketInstance.CONNECTING`: 0\n* `WebSocketInstance.OPEN`: 1\n* `WebSocketInstance.CLOSING`: 2\n* `WebSocketInstance.CLOSED`: 3\n\n---\n\n## Notes\n\n* Only supported on Android (via OkHttp).\n* Make sure to handle connection lifecycle properly (close sockets when done).\n* `listClients()` is useful for debugging and management.\n---"
  },
  {
    "path": "src/plugins/websocket/package.json",
    "content": "{\n  \"name\": \"cordova-plugin-websocket\",\n  \"version\": \"0.0.1\",\n  \"description\": \"This cordova plugin is created to use WebSocket (client) in web/js.\",\n  \"cordova\": {\n    \"id\": \"cordova-plugin-websocket\",\n    \"platforms\": [\n      \"android\"\n    ]\n  },\n  \"keywords\": [\n    \"cordova\",\n    \"websocket\",\n    \"cordova-android\",\n    \"ws\"\n  ],\n  \"author\": \"Acode-Foundation (created by UnschooledGamer)\",\n  \"license\": \"Apache-2.0\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  }\n}"
  },
  {
    "path": "src/plugins/websocket/plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plugin id=\"cordova-plugin-websocket\" version=\"0.0.1\" xmlns=\"http://apache.org/cordova/ns/plugins/1.0\">\n    <name>cordova-plugin-websocket</name>\n    <description>Cordova Websocket</description>\n    <license>MIT</license>\n    <keywords>cordova,ws,WebSocket</keywords>\n    <js-module src=\"www/websocket.js\" name=\"WebSocket\">\n        <clobbers target=\"cordova.websocket\" />\n    </js-module>\n\n    <platform name=\"android\">\n        <config-file target=\"res/xml/config.xml\" parent=\"/*\">\n            <feature name=\"WebSocketPlugin\">\n                <param name=\"android-package\" value=\"com.foxdebug.websocket.WebSocketPlugin\" />\n            </feature>\n        </config-file>\n        <source-file src=\"src/android/WebSocketPlugin.java\" target-dir=\"src/com/foxdebug/websocket\" />\n        <source-file src=\"src/android/WebSocketInstance.java\" target-dir=\"src/com/foxdebug/websocket\" />\n        <framework src=\"com.squareup.okhttp3:okhttp:4.12.0\" />\n    </platform>\n</plugin>\n"
  },
  {
    "path": "src/plugins/websocket/src/android/WebSocketInstance.java",
    "content": "package com.foxdebug.websocket;\n\nimport android.util.Base64;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport org.apache.cordova.*;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.Iterator;\nimport java.util.concurrent.TimeUnit;\n\nimport okhttp3.*;\n\nimport okio.ByteString;\n\npublic class WebSocketInstance extends WebSocketListener {\n    private static final String TAG = \"WebSocketInstance\";\n    private static final int DEFAULT_CLOSE_CODE = 1000;\n    private static final String DEFAULT_CLOSE_REASON = \"Normal closure\";\n\n    private WebSocket webSocket;\n    private CallbackContext callbackContext;\n    private final CordovaInterface cordova;\n    private final String instanceId;\n    private String extensions = \"\";\n    private String protocol = \"\";\n    private String binaryType = \"\";\n    private int readyState = 0; // CONNECTING\n\n    // okHttpMainClient parameter is used. To have a single main client(singleton), with per-websocket configuration using newBuilder method.\n    public WebSocketInstance(String url, JSONArray protocols, JSONObject headers, String binaryType, OkHttpClient okHttpMainClient, CordovaInterface cordova, String instanceId) {\n        this.cordova = cordova;\n        this.instanceId = instanceId;\n        this.binaryType = binaryType;\n\n        OkHttpClient client = okHttpMainClient.newBuilder()\n                .connectTimeout(10, TimeUnit.SECONDS)\n                .build();\n\n        Request.Builder requestBuilder = new Request.Builder().url(url);\n\n        // custom headers support.\n        if (headers != null) {\n            Iterator<String> keys = headers.keys();\n            while (keys.hasNext()) {\n                String key = keys.next();\n                String value = headers.optString(key);\n                requestBuilder.addHeader(key, value);\n            }\n        }\n\n        // adds Sec-WebSocket-Protocol header if protocols is present.\n        if (protocols != null) {\n            StringBuilder protocolHeader = new StringBuilder();\n            for (int i = 0; i < protocols.length(); i++) {\n                protocolHeader.append(protocols.optString(i)).append(\",\");\n            }\n            if (protocolHeader.length() > 0) {\n                protocolHeader.setLength(protocolHeader.length() - 1);\n                requestBuilder.addHeader(\"Sec-WebSocket-Protocol\", protocolHeader.toString());\n            }\n        }\n\n        client.newWebSocket(requestBuilder.build(), this);\n    }\n\n    public void setCallback(CallbackContext callbackContext) {\n        this.callbackContext = callbackContext;\n        PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);\n        result.setKeepCallback(true);\n        callbackContext.sendPluginResult(result);\n    }\n\n    public void send(String message, boolean isBinary) {\n        if (this.webSocket != null) {\n            Log.d(TAG, \"websocket instanceId=\" + this.instanceId + \" received send(..., isBinary=\" + isBinary + \") action call, sending message=\" + message);\n            if(isBinary) {\n                this.sendBinary(message);\n                return;\n            }\n            this.webSocket.send(message);\n        } else {\n            Log.d(TAG, \"websocket instanceId=\" + this.instanceId + \" received send(..., isBinary=\" + isBinary + \")  ignoring... as webSocket is null (not present/connected)\");\n        }\n    }\n\n    /**\n     * Sends bytes as the data of a binary (type 0x2) message.\n     * @param base64Data Binary Data received from JS bridge encoded as base64 String\n     */\n    private void sendBinary(String base64Data) {\n        byte[] data = Base64.decode(base64Data, Base64.DEFAULT);\n        this.webSocket.send(ByteString.of(data));\n    }\n\n    public String close(int code, String reason) {\n        if (this.webSocket != null) {\n            this.readyState = 2; // CLOSING\n            try {\n                boolean result = this.webSocket.close(code, reason);\n                Log.d(TAG, \"websocket instanceId=\" + this.instanceId + \" received close() action call, code=\" + code + \" reason=\" + reason + \" close method result: \" + result);\n\n                // if a graceful shutdown was already underway...\n                // or if the web socket is already closed or canceled. do nothing.\n                if(!result) {\n                    return null;\n                }\n            } catch (Exception e) {\n                return e.getMessage();\n            }\n\n            return null;\n        } else {\n            Log.d(TAG, \"websocket instanceId=\" + this.instanceId + \" received close() action call, ignoring... as webSocket is null (not present)\");\n            // TODO: finding a better way of telling it wasn't successful.\n            return \"\";\n        }\n    }\n\n    public String close() {\n        Log.d(TAG, \"WebSocket instanceId=\" + this.instanceId + \" close() called with no arguments. Using defaults.\");\n        // Calls the more specific version with default values\n        return close(DEFAULT_CLOSE_CODE, DEFAULT_CLOSE_REASON);\n    }\n\n    @Override\n    public void onOpen(@NonNull WebSocket webSocket, Response response) {\n        this.webSocket = webSocket;\n        this.readyState = 1; // OPEN\n        this.extensions = response.headers(\"Sec-WebSocket-Extensions\").toString();\n        this.protocol = response.header(\"Sec-WebSocket-Protocol\");\n        Log.i(TAG, \"websocket instanceId=\" + this.instanceId + \" Opened\" + \"received extensions=\" + this.extensions);\n        sendEvent(\"open\", null, false, false);\n    }\n\n    @Override\n    public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {\n        Log.d(TAG, \"websocket instanceId=\" + this.instanceId +  \" Received message: \" + text);\n        sendEvent(\"message\", text, false, false);\n    }\n\n    // This is called when the Websocket server sends a binary(type 0x2) message.\n    @Override\n    public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {\n        Log.d(TAG, \"websocket instanceId=\" + this.instanceId +  \" Received message(bytes/binary payload): \" + bytes.toString());\n\n        try {\n            if (\"arraybuffer\".equals(this.binaryType)) {\n                String base64 = bytes.base64();\n                sendEvent(\"message\", base64, true, false);\n            } else {\n                sendEvent(\"message\", bytes.utf8(), true, true);\n            }\n        } catch (Exception e) {\n            Log.e(TAG, \"Error sending message\", e);\n        }\n\n    }\n\n    @Override\n    public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {\n        this.readyState = 2; // CLOSING\n        Log.i(TAG, \"websocket instanceId=\" + this.instanceId + \" is Closing code: \" + code + \" reason: \" + reason);\n        this.webSocket.close(code, reason);\n    }\n\n    @Override\n    public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {\n        this.readyState = 3; // CLOSED\n        Log.i(TAG, \"websocket instanceId=\" + this.instanceId + \" Closed code: \" + code + \" reason: \" + reason);\n        JSONObject closedEvent = new JSONObject();\n        try {\n            closedEvent.put(\"code\", code);\n            closedEvent.put(\"reason\", reason);\n        } catch (JSONException e) {\n            Log.e(TAG, \"Error creating close event\", e);\n        }\n        sendEvent(\"close\", closedEvent.toString(), false, false);\n        // remove instance after receiving close.\n        WebSocketPlugin.removeInstance(this.instanceId);\n    }\n\n    @Override\n    public void onFailure(@NonNull WebSocket webSocket, Throwable t, Response response) {\n        this.readyState = 3; // CLOSED\n        sendEvent(\"error\", t.getMessage(), false, false);\n        Log.e(TAG, \"websocket instanceId=\" + this.instanceId + \" Error: \" + t.getMessage());\n    }\n\n    public void setBinaryType(String binaryType) {\n        this.binaryType = binaryType;\n    }\n\n    private void sendEvent(String type, String data, boolean isBinary, boolean parseAsText) {\n        if (callbackContext != null) {\n            try {\n                JSONObject event = new JSONObject();\n                event.put(\"type\", type);\n                event.put(\"extensions\", this.extensions);\n                event.put(\"readyState\", this.readyState);\n                event.put(\"isBinary\", isBinary);\n                event.put(\"parseAsText\", parseAsText);\n                if (data != null) event.put(\"data\", data);\n                Log.d(TAG, \"sending event: \" + type + \" eventObj \" + event.toString());\n                PluginResult result = new PluginResult(PluginResult.Status.OK, event);\n                result.setKeepCallback(true);\n                callbackContext.sendPluginResult(result);\n            } catch (Exception e) {\n                Log.e(TAG, \"Error sending event\", e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/plugins/websocket/src/android/WebSocketPlugin.java",
    "content": "package com.foxdebug.websocket;\n\nimport android.util.Log;\n\nimport org.apache.cordova.*;\nimport org.json.*;\n\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport okhttp3.OkHttpClient;\n\n// TODO: plugin init & plugin destroy(closing okhttp clients) lifecycles. (✅)\npublic class WebSocketPlugin extends CordovaPlugin {\n    private static final ConcurrentHashMap<String, WebSocketInstance> instances = new ConcurrentHashMap<>();\n    public OkHttpClient okHttpMainClient = null;\n\n    @Override\n    protected void pluginInitialize() {\n        this.okHttpMainClient = new OkHttpClient();\n    }\n\n    @Override\n    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {\n        cordova.getThreadPool().execute(new Runnable() {\n            @Override\n            public void run() {\n\n                switch (action) {\n                    case \"connect\":\n                        String url = args.optString(0);\n                        JSONArray protocols = args.optJSONArray(1);\n                        JSONObject headers = args.optJSONObject(2);\n                        String binaryType = args.optString(3, null);\n                        String id = UUID.randomUUID().toString();\n                        WebSocketInstance instance = new WebSocketInstance(url, protocols, headers, binaryType, okHttpMainClient, cordova, id);\n                        instances.put(id, instance);\n                        callbackContext.success(id);\n                        return;\n\n                    case \"send\":\n                        String instanceId = args.optString(0);\n                        String message = args.optString(1);\n                        boolean isBinary = args.optBoolean(2, false);\n\n                        WebSocketInstance inst = instances.get(instanceId);\n                        Log.d(\"WebSocketPlugin\", \"send called\");\n                        if (inst != null) {\n                            inst.send(message, isBinary);\n                            callbackContext.success();\n                        } else {\n                            callbackContext.error(\"Invalid instance ID\");\n                        }\n                        return;\n\n                    case \"close\":\n                        instanceId = args.optString(0);\n                        // defaults code to 1000 & reason to \"Normal closure\"\n                        int code = args.optInt(1, 1000);\n                        String reason = args.optString(2, \"Normal closure\");\n                        inst = instances.get(instanceId);\n                        if (inst != null) {\n                            String error = inst.close(code, reason);\n\n                            if(error == null) {\n                                callbackContext.success();\n                                return;\n                            } else if(!error.isEmpty()) {\n                                // if error is empty means the websocket is not ready/open.\n                                callbackContext.error(error);\n                                return;\n                            }\n                        } else {\n                            callbackContext.error(\"Invalid instance ID\");\n                        }\n                        return;\n\n                    case \"registerListener\":\n                        instanceId = args.optString(0);\n                        inst = instances.get(instanceId);\n                        if (inst != null) {\n                            inst.setCallback(callbackContext);\n                        } else {\n                            callbackContext.error(\"Invalid instance ID\");\n                        }\n                        return;\n\n                    case \"setBinaryType\":\n                        instanceId = args.optString(0);\n                        String type = args.optString(1);\n\n                        inst = instances.get(instanceId);\n                        if (inst != null) {\n                            inst.setBinaryType(type);\n                        } else {\n                            Log.d(\"WebSocketPlugin\", \"setBinaryType called for instanceId=\" + instanceId + \" but It's not found. ignoring....\");\n                        }\n                        return;\n\n                    case \"listClients\":\n                        JSONArray clientIds = new JSONArray();\n                        for (String clientId : instances.keySet()) {\n                            clientIds.put(clientId);\n                        }\n                        callbackContext.success(clientIds);\n                        return;\n                    default:\n                        return;\n                }\n            }\n        });\n        return true;\n    }\n\n    @Override\n    public void onDestroy() {\n        // clear all.\n        for (WebSocketInstance instance : instances.values()) {\n            // Closing them gracefully.\n            instance.close();\n        }\n        instances.clear();\n        okHttpMainClient.dispatcher().executorService().shutdown();\n        Log.i(\"WebSocketPlugin\", \"cleaned up... on destroy\");\n    }\n\n    public static void removeInstance(String instanceId) {\n        instances.remove(instanceId);\n    }\n}\n"
  },
  {
    "path": "src/plugins/websocket/www/websocket.js",
    "content": "var exec = require('cordova/exec');\n/**\n * Whether to log debug messages\n */\nlet DEBUG = false;\n\nconst logIfDebug = (...args) => {\n    console.log(\"DEBUG flag -> \", cordova.websocket.DEBUG)\n    if (cordova.websocket.DEBUG) {\n        console.log(...args);\n    }\n};\n\nclass WebSocketInstance extends EventTarget {\n    constructor(url, instanceId, binaryType) {\n        super();\n        this.instanceId = instanceId;\n        this.extensions = '';\n        this.readyState = WebSocketInstance.CONNECTING;\n        this.onopen = null;\n        this.onmessage = null;\n        this.onclose = null;\n        this.onerror = null;\n        this.url = url;\n        // NOTE: blob is not supported currently.\n        this._binaryType = binaryType ? binaryType : ''; // empty as Default is string (Same Plugins might require this behavior)\n\n        exec((event) => {\n            logIfDebug(`[Cordova WebSocket - ID=${this.instanceId}] Event from native:`, event);\n\n            if (event.type === 'open') {\n                this.readyState = WebSocketInstance.OPEN;\n                this.extensions = event.extensions || '';\n                if (this.onopen) this.onopen(event);\n                this.dispatchEvent(new Event('open'));\n            }\n\n            if (event.type === 'message') {\n                let msgData = event.data;\n                // parseAsText solely takes care of the state of binaryType,\n                // sometimes, syncing binaryType to Java side might take longer. it's there to not wrongly pass normal string as base64.\n                if (event.isBinary && this.binaryType === 'arraybuffer' && !event.parseAsText) {\n                    let binary = atob(msgData);\n                    let bytes = new Uint8Array(binary.length);\n                    for (let i = 0; i < binary.length; i++) {\n                        bytes[i] = binary.charCodeAt(i);\n                    }\n                    msgData = bytes.buffer;\n                }\n                logIfDebug(`[Cordova WebSocket - ID=${this.instanceId}] msg Event:`, event, msgData);\n                const msgEvent = new MessageEvent('message', { data: msgData  });\n\n                Object.defineProperty(msgEvent, \"binary\", { enumerable: true, value: event.isBinary })\n\n                if (this.onmessage) this.onmessage(msgEvent);\n                this.dispatchEvent(msgEvent);\n            }\n\n            if (event.type === 'close') {\n                this.readyState = WebSocketInstance.CLOSED;\n                const closeData = event && event.data ? event.data : {};\n                const closeEvent = new CloseEvent('close', {\n                    code: closeData.code,\n                    reason: closeData.reason,\n                });\n                if (this.onclose) this.onclose(closeEvent);\n                this.dispatchEvent(closeEvent);\n            }\n\n            if (event.type === 'error') {\n                const errorMessage = event && event.data ? event.data : undefined;\n                const errorEvent = new Event('error');\n                if (errorMessage !== undefined) {\n                    errorEvent.message = errorMessage;\n                }\n                if (this.onerror) this.onerror(errorEvent);\n                this.dispatchEvent(errorEvent);\n            }\n        }, null, \"WebSocketPlugin\", \"registerListener\", [this.instanceId]);\n    }\n\n    get binaryType() {\n        return this._binaryType || '';\n    }\n    \n    set binaryType(type) {\n        // blob isn't supported but checked as browser compatibility, & it default to empty string\n        if (type === 'blob' || type === 'arraybuffer' || type === '') {\n            this._binaryType = type !== 'blob' ? type : '';\n\n            exec(null, null, \"WebSocketPlugin\", \"setBinaryType\", [this.instanceId, type]);\n        } else {\n            console.warn('Invalid binaryType, expected \"blob\" or \"arraybuffer\"');\n        }\n    }\n\n\n    send(message, binary) {\n        if (this.readyState !== WebSocketInstance.OPEN) {\n            throw new Error(`WebSocket is not open/connected`);\n        }\n\n        let finalMessage = null;\n        if (message instanceof ArrayBuffer || ArrayBuffer.isView(message)) {\n            const uint8Array = message instanceof ArrayBuffer ? new Uint8Array(message) : message;\n            finalMessage = btoa(String.fromCharCode.apply(null, uint8Array));\n\n            // set to true as it's the data of a binary (type 0x2) message.\n            binary = true;\n\n            exec(() => logIfDebug(`[Cordova WebSocket - ID=${this.instanceId}] Sent message(binary payload):`, finalMessage), (err) => console.error(`[Cordova WebSocket - ID=${this.instanceId}] Send error:`, err), \"WebSocketPlugin\", \"send\", [this.instanceId, finalMessage, binary]);\n        } else if (typeof message === 'string') {\n            finalMessage = message;\n            \n            // maybe a String to be sent as Binary (if it's true)\n            if(binary) finalMessage = btoa(message)\n\n            exec(() => logIfDebug(`[Cordova WebSocket - ID=${this.instanceId}] Sent message(binary=${binary}):`, finalMessage), (err) => console.error(`[Cordova WebSocket - ID=${this.instanceId}] Send error:`, err), \"WebSocketPlugin\", \"send\", [this.instanceId, finalMessage, binary]);\n        } else {\n            throw new Error(`Unsupported message type: ${typeof message}`);\n        }\n    }\n\n    /**\n     * Closes the WebSocket connection.\n     *\n     * @param {number} code The status code explaining why the connection is being closed.\n     * @param {string} reason A human-readable string explaining why the connection is being closed.\n     */\n    close(code, reason) {\n        this.readyState = WebSocketInstance.CLOSING;\n        exec(() => logIfDebug(`[Cordova WebSocket - ID=${this.instanceId}] Close requested`, code, reason), (err) => console.error(`[Cordova WebSocket - ID=${this.instanceId}] Close error`, err), \"WebSocketPlugin\", \"close\", [this.instanceId, code, reason]);\n    }\n}\n\nWebSocketInstance.CONNECTING = 0;\nWebSocketInstance.OPEN = 1;\nWebSocketInstance.CLOSING = 2;\nWebSocketInstance.CLOSED = 3;\n\nconst connect = function(url, protocols = null, headers = null, binaryType) {\n    return new Promise((resolve, reject) => {\n        exec(instanceId => resolve(new WebSocketInstance(url, instanceId)), reject, \"WebSocketPlugin\", \"connect\", [url, protocols, binaryType, headers]);\n    });\n};\n\nconst listClients = function() {\n    return new Promise((resolve, reject) => {\n        exec(resolve, reject, \"WebSocketPlugin\", \"listClients\", []);\n    });\n};\n\n/** Utility functions, in-case you lost the websocketInstance returned from the connect function */\n\nconst send = function(instanceId, message, binary) {\n    return new Promise((resolve, reject) => {\n        if (typeof message === 'string') {\n            \n            if(binary) message = btoa(message);\n\n            exec(resolve, reject, \"WebSocketPlugin\", \"send\", [instanceId, message, binary]);\n        } else if (message instanceof ArrayBuffer || ArrayBuffer.isView(message)) {\n            const uint8Array = message instanceof ArrayBuffer ? new Uint8Array(message) : message;\n            const base64Message = btoa(String.fromCharCode.apply(null, uint8Array));\n            \n            exec(resolve, reject, \"WebSocketPlugin\", \"send\", [instanceId, base64Message, true]);\n        } else {\n            reject(`Unsupported message type: ${typeof message}`);\n        }\n    });\n};\n\n/**\n * Closes the WebSocket connection.\n *\n * @param {string} instanceId The ID of the WebSocketInstance to close.\n * @param {number} [code] (optional) The status code explaining why the connection is being closed.\n * @param {string} [reason] (optional) A human-readable string explaining why the connection is being closed.\n *\n * @returns {Promise} A promise that resolves when the close operation has completed.\n */\nconst close = function(instanceId, code, reason) {\n    return new Promise((resolve, reject) => {\n        exec(resolve, reject, \"WebSocketPlugin\", \"close\", [instanceId, code, reason]);\n    });\n};\n\nmodule.exports = { connect, listClients, send, close, DEBUG };\n"
  },
  {
    "path": "src/res/file-icons/style.css",
    "content": "@font-face {\n  font-family: 'file-icons';\n  src: url('file-icons.ttf?ujkkfk') format('truetype');\n  font-weight: normal;\n  font-style: normal;\n  font-display: block;\n}\n\n.file {\n  /* use !important to prevent issues with browser extensions that change fonts */\n  font-family: 'file-icons' !important;\n  speak: never;\n  font-style: normal;\n  font-weight: normal;\n  font-variant: normal;\n  text-transform: none;\n  line-height: 1;\n\n  /* Better Font Rendering =========== */\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.file_type_default:before {\n  content: \"\\e91a\";\n  color: #c5c5c5;\n}\n\n.file_type_text:before {\n  content: \"\\e985\";\n  color: #8fa5c2;\n}\n\n.file_type_txt:before {\n  content: \"\\e985\";\n  color: #8fa5c2;\n}\n\n.file_type_image:before {\n  content: \"\\e934\";\n  color: #96e5cf;\n}\n\n.file_type_video:before {\n  content: \"\\e991\";\n  color: #e75749;\n}\n\n.file_type_astro:before {\n  content: \"\\e9a4\";\n  color: #fff;\n}\n\n.file_type_music:before {\n  content: \"\\e950\";\n}\n\n.file_type_zip:before {\n  content: \"\\e99b\";\n  color: #a08a00;\n}\n\n.file_type_compressed:before {\n  content: \"\\e99b\";\n  color: #a08a00;\n}\n\n.file_type_android:before {\n  content: \"\\e99e\";\n  color: #3ddc84;\n}\n\n.file_type_smali:before {\n  content: \"\\e99e\";\n  color: #3ddc84;\n}\n\n.file_type_rhtml:before {\n  content: \"\\e9a0\";\n  color: #c00;\n}\n\n.file_type_forth:before {\n  content: \"\\e9a1\";\n  color: #35c0ed;\n}\n\n.file_type_frt:before {\n  content: \"\\e9a1\";\n  color: #35c0ed;\n}\n\n.file_type_fs:before {\n  content: \"\\e9a1\";\n  color: #35c0ed;\n}\n\n.file_type_ldr:before {\n  content: \"\\e9a1\";\n  color: #35c0ed;\n}\n\n.file_type_fth:before {\n  content: \"\\e9a1\";\n  color: #35c0ed;\n}\n\n.file_type_4th:before {\n  content: \"\\e9a1\";\n  color: #35c0ed;\n}\n\n.file_type_alda:before {\n  content: \"\\e99f\";\n  color: #35c0ed;\n}\n\n.file_type_abc:before {\n  content: \"\\e9ea\";\n  color: #00aeef;\n}\n\n.file_type_sap:before {\n  content: \"\\e9eb\";\n  color: #00aeef;\n}\n\n.file_type_abap:before {\n  content: \"\\e9eb\";\n  color: #00aeef;\n}\n\n.file_type_apache:before {\n  content: \"\\e900\";\n  color: #d22128;\n}\n\n.file_type_apache_conf:before {\n  content: \"\\e900\";\n  color: #d22128;\n}\n\n.file_type_htaccess:before {\n  content: \"\\e900\";\n  color: #d22128;\n}\n\n.file_type_htgroups:before {\n  content: \"\\e900\";\n  color: #d22128;\n}\n\n.file_type_conf:before {\n  content: \"\\e900\";\n  color: #d22128;\n}\n\n.file_type_htpasswd:before {\n  content: \"\\e900\";\n  color: #d22128;\n}\n\n.file_type_apex:before {\n  content: \"\\e901\";\n  color: #0072a0;\n}\n\n.file_type_cls:before {\n  content: \"\\e901\";\n  color: #0072a0;\n}\n\n.file_type_trigger:before {\n  content: \"\\e901\";\n  color: #0072a0;\n}\n\n.file_type_tgr:before {\n  content: \"\\e901\";\n  color: #0072a0;\n}\n\n.file_type_asciidoc:before {\n  content: \"\\e902\";\n  color: #e40046;\n}\n\n.file_type_adoc:before {\n  content: \"\\e902\";\n  color: #e40046;\n}\n\n.file_type_asl:before {\n  content: \"\\e903\";\n  color: #65bed9;\n}\n\n.file_type_dsl:before {\n  content: \"\\e903\";\n  color: #65bed9;\n}\n\n.file_type_assembly:before {\n  content: \"\\e904\";\n  color: #0000bf;\n}\n\n.file_type_a:before {\n  content: \"\\e904\";\n  color: #0000bf;\n}\n\n.file_type_asm:before {\n  content: \"\\e904\";\n  color: #0000bf;\n}\n\n.file_type_assembly_x86:before {\n  content: \"\\e904\";\n  color: #0000bf;\n}\n\n.file_type_autohotkey:before {\n  content: \"\\e905\";\n  color: #44a915;\n}\n\n.file_type_ahk:before {\n  content: \"\\e905\";\n  color: #44a915;\n}\n\n.file_type_babel:before {\n  content: \"\\e906\";\n  color: #f9dc3e;\n}\n\n.file_type_babelrc:before {\n  content: \"\\e906\";\n  color: #f9dc3e;\n}\n\n.file_type_bibtex:before {\n  content: \"\\e907\";\n}\n\n.file_type_bib:before {\n  content: \"\\e907\";\n}\n\n.file_type_blade:before {\n  content: \"\\e908\";\n  color: #ff2d20;\n}\n\n.file_type_php_laravel_blade:before {\n  content: \"\\e908\";\n  color: #ff2d20;\n}\n\n.file_type_c:before {\n  content: \"\\e909\";\n  color: #005f91;\n}\n\n.file_type_cabal:before {\n  content: \"\\e90a\";\n  color: #6bc9dd;\n}\n\n.file_type_haskell_cabal:before {\n  content: \"\\e90a\";\n  color: #6bc9dd;\n}\n\n.file_type_cheader:before {\n  content: \"\\e90b\";\n  color: #005f91;\n}\n\n.file_type_clojure:before {\n  content: \"\\e90c\";\n  color: #5881d8;\n}\n\n.file_type_clj:before {\n  content: \"\\e90c\";\n  color: #5881d8;\n}\n\n.file_type_clojurescript:before {\n  content: \"\\e90d\";\n  color: #5f7fbf;\n}\n\n.file_type_cljs:before {\n  content: \"\\e90d\";\n  color: #5f7fbf;\n}\n\n.file_type_cmake:before {\n  content: \"\\e90e\";\n  color: #064f8c;\n}\n\n.file_type_cobol:before {\n  content: \"\\e90f\";\n  color: #005ca5;\n}\n\n.file_type_cbl:before {\n  content: \"\\e90f\";\n  color: #005ca5;\n}\n\n.file_type_cob:before {\n  content: \"\\e90f\";\n  color: #005ca5;\n}\n\n.file_type_coffeescript:before {\n  content: \"\\e910\";\n}\n\n.file_type_coffee:before {\n  content: \"\\e910\";\n}\n\n.file_type_cf:before {\n  content: \"\\e910\";\n}\n\n.file_type_cson:before {\n  content: \"\\e910\";\n}\n\n.file_type_cakefile:before {\n  content: \"\\e910\";\n}\n\n.file_type_coldfussion:before {\n  content: \"\\e911\";\n  color: #859aa5;\n}\n\n.file_type_cfm:before {\n  content: \"\\e911\";\n  color: #859aa5;\n}\n\n.file_type_cfc:before {\n  content: \"\\e911\";\n  color: #859aa5;\n}\n\n.file_type_cpp:before {\n  content: \"\\e912\";\n  color: #984c93;\n}\n\n.file_type_cc:before {\n  content: \"\\e912\";\n  color: #984c93;\n}\n\n.file_type_cxx:before {\n  content: \"\\e912\";\n  color: #984c93;\n}\n\n.file_type_ino:before {\n  content: \"\\e912\";\n  color: #984c93;\n}\n\n.file_type_cppheader:before {\n  content: \"\\e913\";\n  color: #984c93;\n}\n\n.file_type_hh:before {\n  content: \"\\e913\";\n  color: #984c93;\n}\n\n.file_type_hpp:before {\n  content: \"\\e913\";\n  color: #984c93;\n}\n\n.file_type_crystal:before {\n  content: \"\\e914\";\n  color: #c8c8c8;\n}\n\n.file_type_cr:before {\n  content: \"\\e914\";\n  color: #c8c8c8;\n}\n\n.file_type_csharp:before {\n  content: \"\\e915\";\n  color: #368832;\n}\n\n.file_type_cs:before {\n  content: \"\\e915\";\n  color: #368832;\n}\n\n.file_type_css:before {\n  content: \"\\e916\";\n  color: #1572b6;\n}\n\n.file_type_cssmap:before {\n  content: \"\\e917\";\n  color: #33a9dc;\n}\n\n.file_type_dartlang:before {\n  content: \"\\e918\";\n  color: #0175c2;\n}\n\n.file_type_dart:before {\n  content: \"\\e918\";\n  color: #0175c2;\n}\n\n.file_type_db:before {\n  content: \"\\e919\";\n  color: #c4c7ce;\n}\n\n.file_type_aql:before {\n  content: \"\\e919\";\n  color: #c4c7ce;\n}\n\n.file_type_diff:before {\n  content: \"\\e91b\";\n  color: #536653;\n}\n\n.file_type_patch:before {\n  content: \"\\e91b\";\n  color: #536653;\n}\n\n.file_type_django:before {\n  content: \"\\e91c\";\n  color: #44b78b;\n}\n\n.file_type_dlang:before {\n  content: \"\\e91d\";\n  color: #b03931;\n}\n\n.file_type_d:before {\n  content: \"\\e91d\";\n  color: #b03931;\n}\n\n.file_type_di:before {\n  content: \"\\e91d\";\n  color: #b03931;\n}\n\n.file_type_docker:before {\n  content: \"\\e91e\";\n  color: #00aada;\n}\n\n.file_type_dockerfile:before {\n  content: \"\\e91e\";\n  color: #00aada;\n}\n\n.file_type_docz:before {\n  content: \"\\e91f\";\n  color: #5f5846;\n}\n\n.file_type_drools:before {\n  content: \"\\e920\";\n  color: #0095dd;\n}\n\n.file_type_drl:before {\n  content: \"\\e920\";\n  color: #0095dd;\n}\n\n.file_type_ejs:before {\n  content: \"\\e921\";\n  color: #90a93a;\n}\n\n.file_type_elixir:before {\n  content: \"\\e922\";\n  color: #7c648f;\n}\n\n.file_type_ex:before {\n  content: \"\\e922\";\n  color: #7c648f;\n}\n\n.file_type_exs:before {\n  content: \"\\e922\";\n  color: #7c648f;\n}\n\n.file_type_html_elixir:before {\n  content: \"\\e922\";\n  color: #7c648f;\n}\n\n.file_type_elm:before {\n  content: \"\\e923\";\n  color: #8cd636;\n}\n\n.file_type_erlang:before {\n  content: \"\\e924\";\n  color: #a2003e;\n}\n\n.file_type_erl:before {\n  content: \"\\e924\";\n  color: #a2003e;\n}\n\n.file_type_hrl:before {\n  content: \"\\e924\";\n  color: #a2003e;\n}\n\n.file_type_eslint:before {\n  content: \"\\e925\";\n  color: #4b32c3;\n}\n\n.file_type_fortran:before {\n  content: \"\\e926\";\n  color: #9e564c;\n}\n\n.file_type_f:before {\n  content: \"\\e926\";\n  color: #9e564c;\n}\n\n.file_type_f90:before {\n  content: \"\\e926\";\n  color: #9e564c;\n}\n\n.file_type_fsharp:before {\n  content: \"\\e927\";\n  color: #378bba;\n}\n\n.file_type_fs1:before {\n  content: \"\\e927\";\n  color: #378bba;\n}\n\n.file_type_fsi:before {\n  content: \"\\e927\";\n  color: #378bba;\n}\n\n.file_type_fsx:before {\n  content: \"\\e927\";\n  color: #378bba;\n}\n\n.file_type_fsscript:before {\n  content: \"\\e927\";\n  color: #378bba;\n}\n\n.file_type_gcode:before {\n  content: \"\\e928\";\n  color: #ba0000;\n}\n\n.file_type_git:before {\n  content: \"\\e929\";\n  color: #f05032;\n}\n\n.file_type_gitignore:before {\n  content: \"\\e929\";\n  color: #f05032;\n}\n\n.file_type_golang:before {\n  content: \"\\e92a\";\n  color: #00add8;\n}\n\n.file_type_go:before {\n  content: \"\\e92a\";\n  color: #00add8;\n}\n\n.file_type_gradle:before {\n  content: \"\\e92b\";\n  color: #136a7d;\n}\n\n.file_type_graphql:before {\n  content: \"\\e92c\";\n  color: #e10098;\n}\n\n.file_type_gql:before {\n  content: \"\\e92c\";\n  color: #e10098;\n}\n\n.file_type_graphqlschema:before {\n  content: \"\\e92c\";\n  color: #e10098;\n}\n\n.file_type_prql:before {\n  content: \"\\e9a3\";\n  color: #dfb13c;\n}\n\n.file_type_groovy:before {\n  content: \"\\e92d\";\n}\n\n.file_type_haml:before {\n  content: \"\\e92e\";\n}\n\n.file_type_handlebars:before {\n  content: \"\\e92f\";\n  color: #c19770;\n}\n\n.file_type_hbs:before {\n  content: \"\\e92f\";\n  color: #c19770;\n}\n\n.file_type_tpl:before {\n  content: \"\\e92f\";\n  color: #c19770;\n}\n\n.file_type_mustache:before {\n  content: \"\\e92f\";\n  color: #c19770;\n}\n\n.file_type_haskell:before {\n  content: \"\\e930\";\n  color: #5d4f85;\n}\n\n.file_type_hs:before {\n  content: \"\\e930\";\n  color: #5d4f85;\n}\n\n.file_type_haxe:before {\n  content: \"\\e931\";\n  color: #ea8220;\n}\n\n.file_type_hx:before {\n  content: \"\\e931\";\n  color: #ea8220;\n}\n\n.file_type_hjson:before {\n  content: \"\\e932\";\n  color: #01ca24;\n}\n\n.file_type_html:before {\n  content: \"\\e933\";\n  color: #e34f26;\n}\n\n.file_type_htm:before {\n  content: \"\\e933\";\n  color: #e34f26;\n}\n\n.file_type_xhtml:before {\n  content: \"\\e933\";\n  color: #e34f26;\n}\n\n.file_type_we:before {\n  content: \"\\e933\";\n  color: #e34f26;\n}\n\n.file_type_wpy:before {\n  content: \"\\e933\";\n  color: #e34f26;\n}\n\n.file_type_ini:before {\n  content: \"\\e935\";\n  color: #99b8c4;\n}\n\n.file_type_conf1:before {\n  content: \"\\e935\";\n  color: #99b8c4;\n}\n\n.file_type_cfg:before {\n  content: \"\\e935\";\n  color: #99b8c4;\n}\n\n.file_type_prefs:before {\n  content: \"\\e935\";\n  color: #99b8c4;\n}\n\n.file_type_io:before {\n  content: \"\\e936\";\n  color: #c2c2c2;\n}\n\n.file_type_java:before {\n  content: \"\\e937\";\n  color: #007396;\n}\n\n.file_type_javascript:before {\n  content: \"\\e938\";\n  color: #f5de19;\n}\n\n.file_type_js:before {\n  content: \"\\e938\";\n  color: #f5de19;\n}\n\n.file_type_jsm:before {\n  content: \"\\e938\";\n  color: #f5de19;\n}\n\n.file_type_jsx:before {\n  content: \"\\e938\";\n  color: #f5de19;\n}\n\n.file_type_mjs:before {\n  content: \"\\e938\";\n  color: #f5de19;\n}\n\n.file_type_cjs:before {\n  content: \"\\e938\";\n  color: #f5de19;\n}\n\n.file_type_jsbeautify:before {\n  content: \"\\e939\";\n  color: #f1662a;\n}\n\n.file_type_jsconfig:before {\n  content: \"\\e93a\";\n  color: #f5de19;\n}\n\n.file_type_jsmap:before {\n  content: \"\\e93b\";\n  color: #f5de19;\n}\n\n.file_type_json:before {\n  content: \"\\e93c\";\n  color: #f5de19;\n}\n\n.file_type_curly:before {\n  content: \"\\e93c\";\n  color: #f5de19;\n}\n\n.file_type_json5:before {\n  content: \"\\e93c\";\n  color: #f5de19;\n}\n\n.file_type_jsp:before {\n  content: \"\\e93d\";\n  color: #e56f14;\n}\n\n.file_type_julia:before {\n  content: \"\\e93e\";\n  color: #aa79c1;\n}\n\n.file_type_jl:before {\n  content: \"\\e93e\";\n  color: #aa79c1;\n}\n\n.file_type_jupyter:before {\n  content: \"\\e93f\";\n  color: #f37626;\n}\n\n.file_type_kotlin:before {\n  content: \"\\e940\";\n  color: #0095d5;\n}\n\n.file_type_kt:before {\n  content: \"\\e940\";\n  color: #0095d5;\n}\n\n.file_type_kts:before {\n  content: \"\\e940\";\n  color: #0095d5;\n}\n\n.file_type_latex:before {\n  content: \"\\e941\";\n  color: #008080;\n}\n\n.file_type_less:before {\n  content: \"\\e942\";\n  color: #294e82;\n}\n\n.file_type_license:before {\n  content: \"\\e943\";\n  color: #aa79c1;\n}\n\n.file_type_odin:before {\n  content: \"\\e9a2\";\n  color: #00f3ff;\n}\n\n.file_type_liquid:before {\n  content: \"\\e944\";\n  color: #004999;\n}\n\n.file_type_lisp:before {\n  content: \"\\e945\";\n  color: #c40804;\n}\n\n.file_type_livescript:before {\n  content: \"\\e946\";\n  color: #317eac;\n}\n\n.file_type_log:before {\n  content: \"\\e947\";\n  color: #00bd02;\n}\n\n.file_type_lsl:before {\n  content: \"\\e948\";\n  color: #7fadb2;\n}\n\n.file_type_lua:before {\n  content: \"\\e949\";\n  color: #2c2d72;\n}\n\n.file_type_luau:before {\n  content: \"\\e949\";\n  color: #2c2d72;\n}\n\n.file_type_lp:before {\n  content: \"\\e949\";\n  color: #2c2d72;\n}\n\n.file_type_luapage:before {\n  content: \"\\e949\";\n  color: #2c2d72;\n}\n\n.file_type_makefile:before {\n  content: \"\\e94a\";\n  color: #a42e2b;\n}\n\n.file_type_gnumakefile:before {\n  content: \"\\e94a\";\n  color: #a42e2b;\n}\n\n.file_type_ocamlmakefile:before {\n  content: \"\\e94a\";\n  color: #a42e2b;\n}\n\n.file_type_make:before {\n  content: \"\\e94a\";\n  color: #a42e2b;\n}\n\n.file_type_map:before {\n  content: \"\\e94b\";\n  color: #45d339;\n}\n\n.file_type_mariadb:before {\n  content: \"\\e94c\";\n  color: #c49a6c;\n}\n\n.file_type_markdown:before {\n  content: \"\\e94d\";\n}\n\n.file_type_md:before {\n  content: \"\\e94d\";\n}\n\n.file_type_matlab:before {\n  content: \"\\e94e\";\n  color: #ff8d10;\n}\n\n.file_type_mediawiki:before {\n  content: \"\\e94f\";\n  color: #ffd800;\n}\n\n.file_type_wiki:before {\n  content: \"\\e94f\";\n  color: #ffd800;\n}\n\n.file_type_mysql:before {\n  content: \"\\e951\";\n  color: #4479a1;\n}\n\n.file_type_mysqlserver:before {\n  content: \"\\e951\";\n  color: #4479a1;\n}\n\n.file_type_nginx:before {\n  content: \"\\e952\";\n  color: #269539;\n}\n\n.file_type_nim:before {\n  content: \"\\e953\";\n  color: #ffe953;\n}\n\n.file_type_npm:before {\n  content: \"\\e954\";\n  color: #cb3837;\n}\n\n.file_type_nunjucks:before {\n  content: \"\\e955\";\n  color: #486411;\n}\n\n.file_type_nunjs:before {\n  content: \"\\e955\";\n  color: #486411;\n}\n\n.file_type_nj:before {\n  content: \"\\e955\";\n  color: #486411;\n}\n\n.file_type_njk:before {\n  content: \"\\e955\";\n  color: #486411;\n}\n\n.file_type_objectivec:before {\n  content: \"\\e956\";\n  color: #c2c2c2;\n}\n\n.file_type_m:before {\n  content: \"\\e956\";\n  color: #c2c2c2;\n}\n\n.file_type_objectivecpp:before {\n  content: \"\\e957\";\n  color: #c2c2c2;\n}\n\n.file_type_mm:before {\n  content: \"\\e957\";\n  color: #c2c2c2;\n}\n\n.file_type_ocaml:before {\n  content: \"\\e958\";\n  color: #ec6813;\n}\n\n.file_type_ml:before {\n  content: \"\\e958\";\n  color: #ec6813;\n}\n\n.file_type_mli:before {\n  content: \"\\e958\";\n  color: #ec6813;\n}\n\n.file_type_opengl:before {\n  content: \"\\e959\";\n  color: #4386b5;\n}\n\n.file_type_glsl:before {\n  content: \"\\e959\";\n  color: #4386b5;\n}\n\n.file_type_frag:before {\n  content: \"\\e959\";\n  color: #4386b5;\n}\n\n.file_type_vert:before {\n  content: \"\\e959\";\n  color: #4386b5;\n}\n\n.file_type_perl:before {\n  content: \"\\e95a\";\n  color: #3a3c5b;\n}\n\n.file_type_pl:before {\n  content: \"\\e95a\";\n  color: #3a3c5b;\n}\n\n.file_type_pm:before {\n  content: \"\\e95a\";\n  color: #3a3c5b;\n}\n\n.file_type_p6:before {\n  content: \"\\e95a\";\n  color: #3a3c5b;\n}\n\n.file_type_pl6:before {\n  content: \"\\e95a\";\n  color: #3a3c5b;\n}\n\n.file_type_pm6:before {\n  content: \"\\e95a\";\n  color: #3a3c5b;\n}\n\n.file_type_pgsql:before {\n  content: \"\\e95b\";\n  color: #336791;\n}\n\n.file_type_php:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_inc:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_phtml:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_shtml:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_php3:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_php4:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_php5:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_phps:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_aw:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_ctp:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_module:before {\n  content: \"\\e95c\";\n  color: #777bb4;\n}\n\n.file_type_plsql:before {\n  content: \"\\e95d\";\n  color: #f00;\n}\n\n.file_type_postcss:before {\n  content: \"\\e95e\";\n  color: #dd3735;\n}\n\n.file_type_postcssconfig:before {\n  content: \"\\e95f\";\n  color: #dd3735;\n}\n\n.file_type_postgresql:before {\n  content: \"\\e960\";\n  color: #336791;\n}\n\n.file_type_powershell:before {\n  content: \"\\e961\";\n  color: #5391fe;\n}\n\n.file_type_ps1:before {\n  content: \"\\e961\";\n  color: #5391fe;\n}\n\n.file_type_batchfile:before {\n  content: \"\\e961\";\n  color: #5391fe;\n}\n\n.file_type_bat:before {\n  content: \"\\e961\";\n  color: #5391fe;\n}\n\n.file_type_cmd:before {\n  content: \"\\e961\";\n  color: #5391fe;\n}\n\n.file_type_prettier:before {\n  content: \"\\e962\";\n  color: #f7b93e;\n}\n\n.file_type_prisma:before {\n  content: \"\\e963\";\n  color: #d2d2d2;\n}\n\n.file_type_prolog:before {\n  content: \"\\e964\";\n  color: #ec1c24;\n}\n\n.file_type_plg:before {\n  content: \"\\e964\";\n  color: #ec1c24;\n}\n\n.file_type_protobuf:before {\n  content: \"\\e965\";\n  color: #ff5c77;\n}\n\n.file_type_proto:before {\n  content: \"\\e965\";\n  color: #ff5c77;\n}\n\n.file_type_puppet:before {\n  content: \"\\e966\";\n  color: #ffae1a;\n}\n\n.file_type_epp:before {\n  content: \"\\e966\";\n  color: #ffae1a;\n}\n\n.file_type_pp:before {\n  content: \"\\e966\";\n  color: #ffae1a;\n}\n\n.file_type_python:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_py:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_pyc:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_pyd:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_pyo:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_pyw:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_pyz:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_gyp:before {\n  content: \"\\e967\";\n  color: #3776ab;\n}\n\n.file_type_q:before {\n  content: \"\\e968\";\n  color: #1e78b3;\n}\n\n.file_type_qml:before {\n  content: \"\\e969\";\n  color: #41cd52;\n}\n\n.file_type_qsharp:before {\n  content: \"\\e96a\";\n  color: #33c;\n}\n\n.file_type_rlang:before {\n  content: \"\\e96b\";\n  color: #276dc3;\n}\n\n.file_type_r:before {\n  content: \"\\e96b\";\n  color: #276dc3;\n}\n\n.file_type_razor:before {\n  content: \"\\e96c\";\n  color: #368832;\n}\n\n.file_type_cshtml:before {\n  content: \"\\e96c\";\n  color: #368832;\n}\n\n.file_type_asp:before {\n  content: \"\\e96c\";\n  color: #368832;\n}\n\n.file_type_red:before {\n  content: \"\\e96d\";\n  color: #b32629;\n}\n\n.file_type_reds:before {\n  content: \"\\e96d\";\n  color: #b32629;\n}\n\n.file_type_rest:before {\n  content: \"\\e96e\";\n  color: #ce3f31;\n}\n\n.file_type_robot:before {\n  content: \"\\e96f\";\n  color: #00b0d8;\n}\n\n.file_type_resource:before {\n  content: \"\\e96f\";\n  color: #00b0d8;\n}\n\n.file_type_rollup:before {\n  content: \"\\e970\";\n  color: #ec4a3f;\n}\n\n.file_type_ruby:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_rakefile:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_guardfile:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_gemfile:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_rb:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_ru:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_gemspec:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_rake:before {\n  content: \"\\e971\";\n  color: #cc342d;\n}\n\n.file_type_rust:before {\n  content: \"\\e972\";\n}\n\n.file_type_rs:before {\n  content: \"\\e972\";\n}\n\n.file_type_sass:before {\n  content: \"\\e973\";\n  color: #cd6799;\n}\n\n.file_type_scala:before {\n  content: \"\\e974\";\n  color: #dc322f;\n}\n\n.file_type_sbt:before {\n  content: \"\\e974\";\n  color: #dc322f;\n}\n\n.file_type_scss:before {\n  content: \"\\e975\";\n  color: #cd6799;\n}\n\n.file_type_shell:before {\n  content: \"\\e976\";\n  color: #d9b400;\n}\n\n.file_type_sh:before {\n  content: \"\\e976\";\n  color: #d9b400;\n}\n\n.file_type_bash:before {\n  content: \"\\e976\";\n  color: #d9b400;\n}\n\n.file_type_bashrc:before {\n  content: \"\\e976\";\n  color: #d9b400;\n}\n\n.file_type_slim:before {\n  content: \"\\e977\";\n  color: #5b4a8f;\n}\n\n.file_type_skim:before {\n  content: \"\\e977\";\n  color: #5b4a8f;\n}\n\n.file_type_smarty:before {\n  content: \"\\e978\";\n  color: #fdf700;\n}\n\n.file_type_tpl1:before {\n  content: \"\\e978\";\n  color: #fdf700;\n}\n\n.file_type_sql:before {\n  content: \"\\e979\";\n  color: #ffda44;\n}\n\n.file_type_sqlserver:before {\n  content: \"\\e979\";\n  color: #ffda44;\n}\n\n.file_type_sqlite:before {\n  content: \"\\e97a\";\n  color: #0f80cc;\n}\n\n.file_type_sqlite3:before {\n  content: \"\\e97a\";\n  color: #0f80cc;\n}\n\n.file_type_stylus:before {\n  content: \"\\e97b\";\n}\n\n.file_type_styl:before {\n  content: \"\\e97b\";\n}\n\n.file_type_svelte:before {\n  content: \"\\e97c\";\n  color: #ff3e00;\n}\n\n.file_type_svg:before {\n  content: \"\\e97d\";\n  color: #ffb13b;\n}\n\n.file_type_swift:before {\n  content: \"\\e97e\";\n  color: #fa7343;\n}\n\n.file_type_tailwind:before {\n  content: \"\\e97f\";\n  color: #44a8b3;\n}\n\n.file_type_tcl:before {\n  content: \"\\e980\";\n  color: #c3b15f;\n}\n\n.file_type_terraform:before {\n  content: \"\\e981\";\n  color: #623ce4;\n}\n\n.file_type_tf:before {\n  content: \"\\e981\";\n  color: #623ce4;\n}\n\n.file_type_tfvars:before {\n  content: \"\\e981\";\n  color: #623ce4;\n}\n\n.file_type_terragrunt:before {\n  content: \"\\e981\";\n  color: #623ce4;\n}\n\n.file_type_testjs:before {\n  content: \"\\e982\";\n  color: #f5de19;\n}\n\n.file_type_testts:before {\n  content: \"\\e983\";\n  color: #007acc;\n}\n\n.file_type_tex:before {\n  content: \"\\e984\";\n  color: #cfcfcf;\n}\n\n.file_type_textile:before {\n  content: \"\\e986\";\n  color: #ffe7ac;\n}\n\n.file_type_toml:before {\n  content: \"\\e987\";\n  color: #aaa;\n}\n\n.file_type_tsconfig:before {\n  content: \"\\e988\";\n  color: #007acc;\n}\n\n.file_type_twig:before {\n  content: \"\\e989\";\n  color: #78dc50;\n}\n\n.file_type_swig:before {\n  content: \"\\e989\";\n  color: #78dc50;\n}\n\n.file_type_typescript:before {\n  content: \"\\e98a\";\n  color: #007acc;\n}\n\n.file_type_ts:before {\n  content: \"\\e98a\";\n  color: #007acc;\n}\n\n.file_type_typescriptdef:before {\n  content: \"\\e98b\";\n  color: #007acc;\n}\n\n.file_type_types:before {\n  content: \"\\e98b\";\n  color: #007acc;\n}\n\n.file_type_typings:before {\n  content: \"\\e98b\";\n  color: #007acc;\n}\n\n.file_type_vala:before {\n  content: \"\\e98c\";\n  color: #403757;\n}\n\n.file_type_vb:before {\n  content: \"\\e98d\";\n  color: #00519a;\n}\n\n.file_type_vbs:before {\n  content: \"\\e98d\";\n  color: #00519a;\n}\n\n.file_type_velocity:before {\n  content: \"\\e98e\";\n  color: #262693;\n}\n\n.file_type_vm:before {\n  content: \"\\e98e\";\n  color: #262693;\n}\n\n.file_type_verilog:before {\n  content: \"\\e98f\";\n  color: #1a348f;\n}\n\n.file_type_v:before {\n  content: \"\\e98f\";\n  color: #1a348f;\n}\n\n.file_type_vh:before {\n  content: \"\\e98f\";\n  color: #1a348f;\n}\n\n.file_type_sv:before {\n  content: \"\\e98f\";\n  color: #1a348f;\n}\n\n.file_type_svh:before {\n  content: \"\\e98f\";\n  color: #1a348f;\n}\n\n.file_type_vhdl:before {\n  content: \"\\e990\";\n  color: #0d9b35;\n}\n\n.file_type_vhd:before {\n  content: \"\\e990\";\n  color: #0d9b35;\n}\n\n.file_type_view:before {\n  content: \"\\e992\";\n  color: #e44f26;\n}\n\n.file_type_vue:before {\n  content: \"\\e993\";\n  color: #41b883;\n}\n\n.file_type_wasm:before {\n  content: \"\\e994\";\n  color: #654ff0;\n}\n\n.file_type_webpack:before {\n  content: \"\\e995\";\n  color: #8dd6f9;\n}\n\n.file_type_xml:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_rdf:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_rss:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_wsdl:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_xslt:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_xamlatom:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_mathml:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_mml:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_xul:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_xbl:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type:before {\n  content: \"\\e996\";\n  color: #f1662a;\n}\n\n.file_type_xquery:before {\n  content: \"\\e997\";\n  color: #f1662a;\n}\n\n.file_type_xsl:before {\n  content: \"\\e998\";\n  color: #33a9dc;\n}\n\n.file_type_yaml:before {\n  content: \"\\e999\";\n  color: #ffe885;\n}\n\n.file_type_yml:before {\n  content: \"\\e999\";\n  color: #ffe885;\n}\n\n.file_type_yarn:before {\n  content: \"\\e99a\";\n  color: #2188b6;\n}\n\n.file_type_actionscript:before {\n  content: \"\\e99c\";\n  color: #c41718;\n}\n\n.file_type_as:before {\n  content: \"\\e99c\";\n  color: #c41718;\n}\n\n.file_type_ada:before {\n  content: \"\\e99d\";\n  color: #0f23c3;\n}\n\n.file_type_adb:before {\n  content: \"\\e99d\";\n  color: #0f23c3;\n}\n\n.file_type_zig:before {\n  content: \"\\e9a5\";\n  color: #f7a41d;\n}\n"
  },
  {
    "path": "src/res/icons/style.css",
    "content": "@font-face {\n  font-family: \"code-editor-icon\";\n  src: url(\"icons.ttf?v2\") format(\"truetype\");\n  font-weight: normal;\n  font-style: normal;\n  font-display: block;\n}\n.icon {\n  /* Use !important to prevent extensions from overriding this font. */\n  font-family: \"code-editor-icon\" !important;\n  font-style: normal;\n  font-weight: normal;\n  font-variant: normal;\n  text-transform: none;\n  line-height: 1;\n\n  /* Better Font Rendering =========== */\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n.icon.text-search:before {\n  content: \"\\f02a\";\n}\n.icon.wand:before {\n  content: \"\\f014\";\n}\n.icon.wand-sparkles:before {\n  content: \"\\f013\";\n}\n.icon.link:before {\n  content: \"\\f012\";\n}\n.icon.brain:before {\n  content: \"\\f011\";\n}\n.icon.paperclip:before {\n  content: \"\\f010\";\n}\n.icon.palette:before {\n  content: \"\\f00f\";\n}\n.icon.loader:before {\n  content: \"\\f00e\";\n}\n.icon.square-terminal:before {\n  content: \"\\f00d\";\n}\n.icon.house:before {\n  content: \"\\f00c\";\n}\n.icon.message-circle:before {\n  content: \"\\f00b\";\n}\n.icon.user-round:before {\n  content: \"\\f00a\";\n}\n.icon.funnel:before {\n  content: \"\\f009\";\n}\n.icon.zap:before {\n  content: \"\\f000\";\n}\n.icon.verified:before {\n  content: \"\\f001\";\n}\n.icon.terminal:before {\n  content: \"\\f002\";\n}\n.icon.tag:before {\n  content: \"\\f003\";\n}\n.icon.scale:before {\n  content: \"\\f004\";\n}\n.icon.cart:before {\n  content: \"\\f005\";\n}\n.icon.lightbulb:before {\n  content: \"\\f006\";\n}\n.icon.pin:before {\n  content: \"\\f007\";\n}\n.icon.pin-off:before {\n  content: \"\\f008\";\n}\n.icon.document-information:before {\n  content: \"\\e900\";\n}\n.icon.document-information-outline:before {\n  content: \"\\e901\";\n}\n.icon.document-forbidden:before {\n  content: \"\\e902\";\n}\n.icon.document-forbidden-outline:before {\n  content: \"\\e903\";\n}\n.icon.document-remove:before {\n  content: \"\\e904\";\n}\n.icon.document-remove-outline:before {\n  content: \"\\e905\";\n}\n.icon.document-checked:before {\n  content: \"\\e906\";\n}\n.icon.document-checked-outline:before {\n  content: \"\\e907\";\n}\n.icon.document-cancel:before {\n  content: \"\\e908\";\n}\n.icon.document-cancel-outline:before {\n  content: \"\\e909\";\n}\n.icon.document-error:before {\n  content: \"\\e90a\";\n}\n.icon.document-error-outline:before {\n  content: \"\\e90b\";\n}\n.icon.document-locked:before {\n  content: \"\\e90c\";\n}\n.icon.document-locked-outline:before {\n  content: \"\\e90d\";\n}\n.icon.document-unlocked:before {\n  content: \"\\e90e\";\n}\n.icon.document-unlocked-outline:before {\n  content: \"\\e90f\";\n}\n.icon.document-search:before {\n  content: \"\\e910\";\n}\n.icon.document-search-outline:before {\n  content: \"\\e911\";\n}\n.icon.document-code:before {\n  content: \"\\e912\";\n}\n.icon.document-code-outline:before {\n  content: \"\\e913\";\n}\n.icon.document-text:before {\n  content: \"\\e914\";\n}\n.icon.document-text-outline:before {\n  content: \"\\e915\";\n}\n.icon.text_format:before {\n  content: \"\\e916\";\n}\n.icon.chat_bubble:before {\n  content: \"\\e917\";\n}\n.icon.movie:before {\n  content: \"\\e918\";\n}\n.icon.videocam:before {\n  content: \"\\e919\";\n}\n.icon.document-add:before {\n  content: \"\\e91a\";\n}\n.icon.document-add-outline:before {\n  content: \"\\e91b\";\n}\n.icon.documents:before {\n  content: \"\\e91c\";\n}\n.icon.documents-outline:before {\n  content: \"\\e91d\";\n}\n.icon.folder-information:before {\n  content: \"\\e91e\";\n}\n.icon.folder-information-outline:before {\n  content: \"\\e91f\";\n}\n.icon.folder-remove:before {\n  content: \"\\e920\";\n}\n.icon.folder-remove-outline:before {\n  content: \"\\e921\";\n}\n.icon.folder-add:before {\n  content: \"\\e922\";\n}\n.icon.folder-add-outline:before {\n  content: \"\\e923\";\n}\n.icon.folder-upload:before {\n  content: \"\\e924\";\n}\n.icon.folder-upload-outline:before {\n  content: \"\\e925\";\n}\n.icon.folder-download:before {\n  content: \"\\e926\";\n}\n.icon.folder-download-outline:before {\n  content: \"\\e927\";\n}\n.icon.folder-search:before {\n  content: \"\\e928\";\n}\n.icon.folder-search-outline:before {\n  content: \"\\e929\";\n}\n.icon.folder:before {\n  content: \"\\e92a\";\n}\n.icon.folder-outline:before {\n  content: \"\\e92b\";\n}\n.icon.folder2:before {\n  content: \"\\e92c\";\n}\n.icon.folder2-outline:before {\n  content: \"\\e92d\";\n}\n.icon.android:before {\n  content: \"\\e92e\";\n  color: #a4c639;\n}\n.icon.angular:before {\n  content: \"\\e92f\";\n  color: #dd0031;\n}\n.icon.css3:before {\n  content: \"\\e930\";\n  color: #1572b6;\n}\n.icon.dev-dot-to:before {\n  content: \"\\e931\";\n}\n.icon.facebook:before {\n  content: \"\\e932\";\n  color: #4172b8;\n}\n.icon.git:before {\n  content: \"\\e933\";\n  color: #f05032;\n}\n.icon.github:before {\n  content: \"\\e934\";\n}\n.icon.gmail:before {\n  content: \"\\e935\";\n  color: #d14836;\n}\n.icon.googlechrome:before {\n  content: \"\\e936\";\n  color: #4285f4;\n}\n.icon.googledrive:before {\n  content: \"\\e937\";\n  color: #4285f4;\n}\n.icon.googleplay:before {\n  content: \"\\e938\";\n  color: #607d8b;\n}\n.icon.html5:before {\n  content: \"\\e939\";\n  color: #e34f26;\n}\n.icon.instagram:before {\n  content: \"\\e93a\";\n  color: #e4405f;\n}\n.icon.ionic:before {\n  content: \"\\e93b\";\n  color: #3880ff;\n}\n.icon.javascript:before {\n  content: \"\\e93c\";\n  color: #f7df1e;\n}\n.icon.jekyll:before {\n  content: \"\\e93d\";\n  color: #c00;\n}\n.icon.linkedin:before {\n  content: \"\\e93e\";\n  color: #0077b5;\n}\n.icon.markdown:before {\n  content: \"\\e93f\";\n}\n.icon.npm:before {\n  content: \"\\e940\";\n  color: #cb3837;\n}\n.icon.python:before {\n  content: \"\\e941\";\n  color: #3776ab;\n}\n.icon.react:before {\n  content: \"\\e942\";\n  color: #61dafb;\n}\n.icon.stackexchange:before {\n  content: \"\\e943\";\n  color: #1e5397;\n}\n.icon.stackoverflow:before {\n  content: \"\\e944\";\n  color: #fe7a16;\n}\n.icon.telegram:before {\n  content: \"\\e945\";\n  color: #2ca5e0;\n}\n.icon.twitter:before {\n  content: \"\\e946\";\n  color: #1da1f2;\n}\n.icon.visualstudiocode:before {\n  content: \"\\e947\";\n  color: #007acc;\n}\n.icon.webpack:before {\n  content: \"\\e948\";\n  color: #8dd6f9;\n}\n.icon.yarn:before {\n  content: \"\\e949\";\n  color: #2c8ebb;\n}\n.icon.youtube:before {\n  content: \"\\e94a\";\n  color: #f00;\n}\n.icon.error:before {\n  content: \"\\e94b\";\n}\n.icon.error_outline:before {\n  content: \"\\e94c\";\n}\n.icon.warningreport_problem:before {\n  content: \"\\e94d\";\n}\n.icon.library_addqueueadd_to_photos:before {\n  content: \"\\e94e\";\n}\n.icon.library_music:before {\n  content: \"\\e94f\";\n}\n.icon.new_releases:before {\n  content: \"\\e950\";\n}\n.icon.not_interesteddo_not_disturb:before {\n  content: \"\\e951\";\n}\n.icon.pause:before {\n  content: \"\\e952\";\n}\n.icon.pause_circle_filled:before {\n  content: \"\\e953\";\n}\n.icon.pause_circle_outline:before {\n  content: \"\\e954\";\n}\n.icon.play_arrow:before {\n  content: \"\\e955\";\n}\n.icon.play_circle_filled:before {\n  content: \"\\e956\";\n}\n.icon.play_circle_outline:before {\n  content: \"\\e957\";\n}\n.icon.repeat:before {\n  content: \"\\e958\";\n}\n.icon.repeat_one:before {\n  content: \"\\e959\";\n}\n.icon.replay:before {\n  content: \"\\e95a\";\n}\n.icon.shuffle:before {\n  content: \"\\e95b\";\n}\n.icon.skip_next:before {\n  content: \"\\e95c\";\n}\n.icon.skip_previous:before {\n  content: \"\\e95d\";\n}\n.icon.emailmailmarkunreadlocal_post_office:before {\n  content: \"\\e95e\";\n}\n.icon.vpn_key:before {\n  content: \"\\e95f\";\n}\n.icon.add:before {\n  content: \"\\e960\";\n}\n.icon.add_box:before {\n  content: \"\\e961\";\n}\n.icon.add_circle:before {\n  content: \"\\e962\";\n}\n.icon.add_circle_outlinecontrol_point:before {\n  content: \"\\e963\";\n}\n.icon.block:before {\n  content: \"\\e964\";\n}\n.icon.clearclose:before {\n  content: \"\\e965\";\n}\n.icon.copy:before {\n  content: \"\\e966\";\n}\n.icon.cut:before {\n  content: \"\\e967\";\n}\n.icon.paste:before {\n  content: \"\\e968\";\n}\n.icon.edit:before {\n  content: \"\\e969\";\n}\n.icon.drafts:before {\n  content: \"\\e96a\";\n}\n.icon.forward:before {\n  content: \"\\e96b\";\n}\n.icon.remove:before {\n  content: \"\\e96c\";\n}\n.icon.remove_circledo_not_disturb_on:before {\n  content: \"\\e96d\";\n}\n.icon.remove_circle_outline:before {\n  content: \"\\e96e\";\n}\n.icon.send:before {\n  content: \"\\e96f\";\n}\n.icon.undo:before {\n  content: \"\\e970\";\n}\n.icon.save_alt:before {\n  content: \"\\e971\";\n}\n.icon.file_copy:before {\n  content: \"\\e972\";\n}\n.icon.sd_storagesd_card:before {\n  content: \"\\e973\";\n}\n.icon.attach_file:before {\n  content: \"\\e974\";\n}\n.icon.attach_money:before {\n  content: \"\\e975\";\n}\n.icon.format_bold:before {\n  content: \"\\e976\";\n}\n.icon.format_color_fill:before {\n  content: \"\\e977\";\n}\n.icon.music_video:before {\n  content: \"\\e978\";\n}\n.icon.format_size:before {\n  content: \"\\e979\";\n}\n.icon.format_underlined:before {\n  content: \"\\e97a\";\n}\n.icon.insert_chartpollassessment:before {\n  content: \"\\e97b\";\n}\n.icon.insert_emoticontag_facesmood:before {\n  content: \"\\e97c\";\n}\n.icon.insert_invitationevent:before {\n  content: \"\\e97d\";\n}\n.icon.image:before {\n  content: \"\\e97e\";\n}\n.icon.publish:before {\n  content: \"\\e97f\";\n}\n.icon.vertical_align_bottom:before {\n  content: \"\\e980\";\n}\n.icon.vertical_align_top:before {\n  content: \"\\e981\";\n}\n.icon.monetization_on:before {\n  content: \"\\e982\";\n}\n.icon.cloud:before {\n  content: \"\\e983\";\n}\n.icon.cloud_done:before {\n  content: \"\\e984\";\n}\n.icon.cloud_download:before {\n  content: \"\\e985\";\n}\n.icon.cloud_uploadbackup:before {\n  content: \"\\e986\";\n}\n.icon.file_downloadget_app:before {\n  content: \"\\e987\";\n}\n.icon.file_upload:before {\n  content: \"\\e988\";\n}\n.icon.audiotrack:before {\n  content: \"\\e989\";\n}\n.icon.music_note:before {\n  content: \"\\e98a\";\n}\n.icon.movie_filter:before {\n  content: \"\\e98b\";\n}\n.icon.local_moviestheaters:before {\n  content: \"\\e98c\";\n}\n.icon.keyboard_arrow_down:before {\n  content: \"\\e98d\";\n}\n.icon.keyboard_arrow_left:before {\n  content: \"\\e98e\";\n}\n.icon.keyboard_arrow_right:before {\n  content: \"\\e98f\";\n}\n.icon.keyboard_arrow_up:before {\n  content: \"\\e990\";\n}\n.icon.keyboard_backspace:before {\n  content: \"\\e991\";\n}\n.icon.keyboard_capslock:before {\n  content: \"\\e992\";\n}\n.icon.keyboard_hide:before {\n  content: \"\\e993\";\n}\n.icon.keyboard_tab:before {\n  content: \"\\e994\";\n}\n.icon.keyboard_voice:before {\n  content: \"\\e995\";\n}\n.icon.laptop_chromebook:before {\n  content: \"\\e996\";\n}\n.icon.laptop_mac:before {\n  content: \"\\e997\";\n}\n.icon.laptop_windows:before {\n  content: \"\\e998\";\n}\n.icon.phone_android:before {\n  content: \"\\e999\";\n}\n.icon.phone_iphone:before {\n  content: \"\\e99a\";\n}\n.icon.color_lenspalette:before {\n  content: \"\\e99b\";\n}\n.icon.colorize:before {\n  content: \"\\e99c\";\n}\n.icon.navigate_beforechevron_left:before {\n  content: \"\\e99d\";\n}\n.icon.navigate_nextchevron_right:before {\n  content: \"\\e99e\";\n}\n.icon.remove_red_eyevisibility:before {\n  content: \"\\e99f\";\n}\n.icon.tune:before {\n  content: \"\\e9a0\";\n}\n.icon.add_photo_alternate:before {\n  content: \"\\e9a1\";\n}\n.icon.image_search:before {\n  content: \"\\e9a2\";\n}\n.icon.beenhere:before {\n  content: \"\\e9a3\";\n}\n.icon.apps:before {\n  content: \"\\e9a4\";\n}\n.icon.arrow_back:before {\n  content: \"\\e9a5\";\n}\n.icon.arrow_drop_down:before {\n  content: \"\\e9a6\";\n}\n.icon.arrow_drop_down_circle:before {\n  content: \"\\e9a7\";\n}\n.icon.arrow_drop_up:before {\n  content: \"\\e9a8\";\n}\n.icon.arrow_forward:before {\n  content: \"\\e9a9\";\n}\n.icon.cancel:before {\n  content: \"\\e9aa\";\n}\n.icon.check:before {\n  content: \"\\e9ab\";\n}\n.icon.expand_less:before {\n  content: \"\\e9ac\";\n}\n.icon.expand_more:before {\n  content: \"\\e9ad\";\n}\n.icon.fullscreen:before {\n  content: \"\\e9ae\";\n}\n.icon.fullscreen_exit:before {\n  content: \"\\e9af\";\n}\n.icon.menu:before {\n  content: \"\\e9b0\";\n}\n.icon.keyboard_control:before {\n  content: \"\\e9b1\";\n}\n.icon.more_vert:before {\n  content: \"\\e9b2\";\n}\n.icon.refresh:before {\n  content: \"\\e9b3\";\n}\n.icon.unfold_less:before {\n  content: \"\\e9b4\";\n}\n.icon.unfold_more:before {\n  content: \"\\e9b5\";\n}\n.icon.arrow_upward:before {\n  content: \"\\e9b6\";\n}\n.icon.subdirectory_arrow_left:before {\n  content: \"\\e9b7\";\n}\n.icon.subdirectory_arrow_right:before {\n  content: \"\\e9b8\";\n}\n.icon.arrow_downward:before {\n  content: \"\\e9b9\";\n}\n.icon.first_page:before {\n  content: \"\\e9ba\";\n}\n.icon.last_page:before {\n  content: \"\\e9bb\";\n}\n.icon.arrow_left:before {\n  content: \"\\e9bc\";\n}\n.icon.arrow_right:before {\n  content: \"\\e9bd\";\n}\n.icon.arrow_back_ios:before {\n  content: \"\\e9be\";\n}\n.icon.arrow_forward_ios:before {\n  content: \"\\e9bf\";\n}\n.icon.folder_special:before {\n  content: \"\\e9c0\";\n}\n.icon.priority_high:before {\n  content: \"\\e9c1\";\n}\n.icon.notifications:before {\n  content: \"\\e9c2\";\n}\n.icon.notifications_none:before {\n  content: \"\\e9c3\";\n}\n.icon.person:before {\n  content: \"\\e9c4\";\n}\n.icon.public:before {\n  content: \"\\e9c5\";\n}\n.icon.share:before {\n  content: \"\\e9c6\";\n}\n.icon.sentiment_dissatisfied:before {\n  content: \"\\e9c7\";\n}\n.icon.sentiment_neutral:before {\n  content: \"\\e9c8\";\n}\n.icon.sentiment_satisfied:before {\n  content: \"\\e9c9\";\n}\n.icon.sentiment_very_dissatisfied:before {\n  content: \"\\e9ca\";\n}\n.icon.sentiment_very_satisfied:before {\n  content: \"\\e9cb\";\n}\n.icon.stargrade:before {\n  content: \"\\e9cc\";\n}\n.icon.star_half:before {\n  content: \"\\e9cd\";\n}\n.icon.star_outline:before {\n  content: \"\\e9ce\";\n}\n.icon.account_box:before {\n  content: \"\\e9cf\";\n}\n.icon.account_circle:before {\n  content: \"\\e9d0\";\n}\n.icon.android-full:before {\n  content: \"\\e9d1\";\n}\n.icon.autorenew:before {\n  content: \"\\e9d2\";\n}\n.icon.cached:before {\n  content: \"\\e9d3\";\n}\n.icon.check_circle:before {\n  content: \"\\e9d4\";\n}\n.icon.code:before {\n  content: \"\\e9d5\";\n}\n.icon.delete:before {\n  content: \"\\e9d6\";\n}\n.icon.exit_to_app:before {\n  content: \"\\e9d7\";\n}\n.icon.extension:before {\n  content: \"\\e9d8\";\n}\n.icon.favorite:before {\n  content: \"\\e9d9\";\n}\n.icon.favorite_outline:before {\n  content: \"\\e9da\";\n}\n.icon.help:before {\n  content: \"\\e9db\";\n}\n.icon.highlight_remove:before {\n  content: \"\\e9dc\";\n}\n.icon.historyrestore:before {\n  content: \"\\e9dd\";\n}\n.icon.home:before {\n  content: \"\\e9de\";\n}\n.icon.httpslock:before {\n  content: \"\\e9df\";\n}\n.icon.info:before {\n  content: \"\\e9e0\";\n}\n.icon.info_outline:before {\n  content: \"\\e9e1\";\n}\n.icon.input:before {\n  content: \"\\e9e2\";\n}\n.icon.label:before {\n  content: \"\\e9e3\";\n}\n.icon.label_outline:before {\n  content: \"\\e9e4\";\n}\n.icon.perm_media:before {\n  content: \"\\e9e5\";\n}\n.icon.power_settings_new:before {\n  content: \"\\e9e6\";\n}\n.icon.search:before {\n  content: \"\\e9e7\";\n}\n.icon.settings:before {\n  content: \"\\e9e8\";\n}\n.icon.settings_applications:before {\n  content: \"\\e9e9\";\n}\n.icon.shop:before {\n  content: \"\\e9ea\";\n}\n.icon.spellcheck:before {\n  content: \"\\e9eb\";\n}\n.icon.stars:before {\n  content: \"\\e9ec\";\n}\n.icon.translate:before {\n  content: \"\\e9ed\";\n}\n.icon.visibility_off:before {\n  content: \"\\e9ee\";\n}\n.icon.update:before {\n  content: \"\\e9ef\";\n}\n.icon.g_translate:before {\n  content: \"\\e9f0\";\n}\n.icon.check_circle_outline:before {\n  content: \"\\e9f1\";\n}\n.icon.delete_outline:before {\n  content: \"\\e9f2\";\n}\n.icon.drive_folder_upload:before {\n  content: \"\\e9f3\";\n}\n.icon.library_add_check:before {\n  content: \"\\e9f4\";\n}\n.icon.replay_circle_filled:before {\n  content: \"\\e9f5\";\n}\n.icon.redo:before {\n  content: \"\\e9f6\";\n}\n.icon.save:before {\n  content: \"\\e9f7\";\n}\n.icon.zip:before {\n  content: \"\\e9f8\";\n}\n.icon.zip-outline:before {\n  content: \"\\e9f9\";\n}\n.icon.logout:before {\n  content: \"\\e9fa\";\n}\n.icon.folder_open:before {\n  content: \"\\e9fb\";\n}\n.icon.launchopen_in_new:before {\n  content: \"\\e9fc\";\n}\n.icon.open_in_browser:before {\n  content: \"\\e9fd\";\n}\n.icon.vue:before {\n  content: \"\\e9fe\";\n  color: #4fc08d;\n}\n.icon.angularuniversal:before {\n  content: \"\\e9ff\";\n  color: #00acc1;\n}\n.icon.linkinsert_link:before {\n  content: \"\\ea00\";\n}\n.icon.document-text2:before {\n  content: \"\\ea01\";\n}\n.icon.document-text2-outline:before {\n  content: \"\\ea02\";\n}\n.icon.document-text4:before {\n  content: \"\\ea03\";\n}\n.icon.document-text5:before {\n  content: \"\\ea04\";\n}\n.icon.folder4:before {\n  content: \"\\ea05\";\n}\n.icon.shift:before {\n  content: \"\\ea06\";\n}\n.icon.replace:before {\n  content: \"\\ea07\";\n}\n.icon.replace_all:before {\n  content: \"\\ea08\";\n}\n.icon.moveline-up:before {\n  content: \"\\ea09\";\n}\n.icon.moveline-down:before {\n  content: \"\\ea0a\";\n}\n.icon.copyline-up:before {\n  content: \"\\ea0b\";\n}\n.icon.copyline-down:before {\n  content: \"\\ea0c\";\n}\n.icon.acode:before {\n  content: \"\\ea0d\";\n  color: #3499fe;\n}\n.icon.patreon:before {\n  content: \"\\ea0f\";\n  color: #f96854;\n}\n.icon.paypal:before {\n  content: \"\\ea10\";\n  color: #00457c;\n}\n.icon.ruby:before {\n  content: \"\\ea11\";\n  color: #cc342d;\n}\n.icon.font_download:before {\n  content: \"\\ea12\";\n}\n.icon.notes:before {\n  content: \"\\ea13\";\n}\n.icon.http:before {\n  content: \"\\ea14\";\n}\n.icon.compare_arrows:before {\n  content: \"\\ea15\";\n}\n.icon.home_filled:before {\n  content: \"\\ea16\";\n}\n.icon.height:before {\n  content: \"\\ea17\";\n}\n.icon.all_inclusive:before {\n  content: \"\\ea18\";\n}\n"
  },
  {
    "path": "src/settings/appSettings.js",
    "content": "import fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport { resetKeyBindings } from \"cm/commandRegistry\";\nimport settingsPage from \"components/settingsPage\";\nimport loader from \"dialogs/loader\";\nimport select from \"dialogs/select\";\nimport actions from \"handlers/quickTools\";\nimport actionStack from \"lib/actionStack\";\nimport constants from \"lib/constants\";\nimport fonts from \"lib/fonts\";\nimport lang from \"lib/lang\";\nimport openFile from \"lib/openFile\";\nimport appSettings from \"lib/settings\";\nimport FontManager from \"pages/fontManager\";\nimport QuickToolsSettings from \"pages/quickTools\";\nimport encodings, { getEncoding } from \"utils/encodings\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\nexport default function otherSettings() {\n\tconst values = appSettings.value;\n\tconst title = strings[\"app settings\"].capitalize();\n\tconst appFontText = strings[\"app font\"] || \"App font\";\n\tconst appFontInfo =\n\t\tstrings[\"settings-info-app-font-family\"] ||\n\t\t\"Choose the font used across the app interface.\";\n\tconst defaultFontLabel = strings.default || \"Default\";\n\tconst categories = {\n\t\tinterface: strings[\"settings-category-interface\"],\n\t\tfonts: strings[\"settings-category-fonts\"],\n\t\tfilesSessions: strings[\"settings-category-files-sessions\"],\n\t\tadvanced: strings[\"settings-category-advanced\"],\n\t};\n\tconst items = [\n\t\t{\n\t\t\tkey: \"lang\",\n\t\t\ttext: strings[\"change language\"],\n\t\t\tvalue: values.lang,\n\t\t\tselect: lang.list,\n\t\t\tvalueText: (value) => lang.getName(value),\n\t\t\tinfo: strings[\"settings-info-app-language\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"animation\",\n\t\t\ttext: strings.animation,\n\t\t\tvalue: values.animation,\n\t\t\tvalueText: (value) => strings[value],\n\t\t\tselect: [\n\t\t\t\t[\"no\", strings.no],\n\t\t\t\t[\"yes\", strings.yes],\n\t\t\t\t[\"system\", strings.system],\n\t\t\t],\n\t\t\tinfo: strings[\"settings-info-app-animation\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"fullscreen\",\n\t\t\ttext: strings.fullscreen.capitalize(),\n\t\t\tcheckbox: values.fullscreen,\n\t\t\tinfo: strings[\"settings-info-app-fullscreen\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"keyboardMode\",\n\t\t\ttext: strings[\"keyboard mode\"],\n\t\t\tvalue: values.keyboardMode,\n\t\t\tvalueText(mode) {\n\t\t\t\treturn strings[mode.replace(/_/g, \" \").toLocaleLowerCase()];\n\t\t\t},\n\t\t\tselect: [\n\t\t\t\t[appSettings.KEYBOARD_MODE_NORMAL, strings.normal],\n\t\t\t\t[appSettings.KEYBOARD_MODE_NO_SUGGESTIONS, strings[\"no suggestions\"]],\n\t\t\t\t[\n\t\t\t\t\tappSettings.KEYBOARD_MODE_NO_SUGGESTIONS_AGGRESSIVE,\n\t\t\t\t\tstrings[\"no suggestions aggressive\"],\n\t\t\t\t],\n\t\t\t],\n\t\t\tinfo: strings[\"settings-info-app-keyboard-mode\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"vibrateOnTap\",\n\t\t\ttext: strings[\"vibrate on tap\"],\n\t\t\tcheckbox: values.vibrateOnTap,\n\t\t\tinfo: strings[\"settings-info-app-vibrate-on-tap\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"floatingButton\",\n\t\t\ttext: strings[\"floating button\"],\n\t\t\tcheckbox: values.floatingButton,\n\t\t\tinfo: strings[\"settings-info-app-floating-button\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"showSideButtons\",\n\t\t\ttext: strings[\"show side buttons\"],\n\t\t\tcheckbox: values.showSideButtons,\n\t\t\tinfo: strings[\"settings-info-app-side-buttons\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"showSponsorSidebarApp\",\n\t\t\ttext: `${strings.sponsor} (${strings.sidebar})`,\n\t\t\tcheckbox: values.showSponsorSidebarApp,\n\t\t\tinfo: strings[\"settings-info-app-sponsor-sidebar\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"openFileListPos\",\n\t\t\ttext: strings[\"active files\"],\n\t\t\tvalue: values.openFileListPos,\n\t\t\tvalueText: (value) => strings[value],\n\t\t\tselect: [\n\t\t\t\t[appSettings.OPEN_FILE_LIST_POS_SIDEBAR, strings.sidebar],\n\t\t\t\t[appSettings.OPEN_FILE_LIST_POS_HEADER, strings.header],\n\t\t\t\t[appSettings.OPEN_FILE_LIST_POS_BOTTOM, strings.bottom],\n\t\t\t],\n\t\t\tinfo: strings[\"settings-info-app-open-file-list-position\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"quickTools\",\n\t\t\ttext: strings[\"quick tools\"],\n\t\t\tcheckbox: !!values.quickTools,\n\t\t\tinfo: strings[\"info-quickTools\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"quickToolsTriggerMode\",\n\t\t\ttext: strings[\"quicktools trigger mode\"],\n\t\t\tvalue: values.quickToolsTriggerMode,\n\t\t\tselect: [\n\t\t\t\t[appSettings.QUICKTOOLS_TRIGGER_MODE_CLICK, \"click\"],\n\t\t\t\t[appSettings.QUICKTOOLS_TRIGGER_MODE_TOUCH, \"touch\"],\n\t\t\t],\n\t\t\tinfo: strings[\"settings-info-app-quick-tools-trigger-mode\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"quickToolsSettings\",\n\t\t\ttext: strings[\"shortcut buttons\"],\n\t\t\tinfo: strings[\"settings-info-app-quick-tools-settings\"],\n\t\t\tcategory: categories.interface,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"touchMoveThreshold\",\n\t\t\ttext: strings[\"touch move threshold\"],\n\t\t\tvalue: values.touchMoveThreshold,\n\t\t\tprompt: strings[\"touch move threshold\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\treturn value >= 0;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-app-touch-move-threshold\"],\n\t\t\tcategory: categories.interface,\n\t\t},\n\t\t{\n\t\t\tkey: \"appFont\",\n\t\t\ttext: appFontText,\n\t\t\tvalue: values.appFont || \"\",\n\t\t\tvalueText: (value) => value || defaultFontLabel,\n\t\t\tget select() {\n\t\t\t\treturn [[\"\", defaultFontLabel], ...fonts.getNames()];\n\t\t\t},\n\t\t\tinfo: appFontInfo,\n\t\t\tcategory: categories.fonts,\n\t\t},\n\t\t{\n\t\t\tkey: \"fontManager\",\n\t\t\ttext: strings[\"fonts\"],\n\t\t\tinfo: strings[\"settings-info-app-font-manager\"],\n\t\t\tcategory: categories.fonts,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"rememberFiles\",\n\t\t\ttext: strings[\"remember opened files\"],\n\t\t\tcheckbox: values.rememberFiles,\n\t\t\tinfo: strings[\"settings-info-app-remember-files\"],\n\t\t\tcategory: categories.filesSessions,\n\t\t},\n\t\t{\n\t\t\tkey: \"rememberFolders\",\n\t\t\ttext: strings[\"remember opened folders\"],\n\t\t\tcheckbox: values.rememberFolders,\n\t\t\tinfo: strings[\"settings-info-app-remember-folders\"],\n\t\t\tcategory: categories.filesSessions,\n\t\t},\n\t\t{\n\t\t\tkey: \"retryRemoteFsAfterFail\",\n\t\t\ttext: strings[\"retry ftp/sftp when fail\"],\n\t\t\tcheckbox: values.retryRemoteFsAfterFail,\n\t\t\tinfo: strings[\"settings-info-app-retry-remote-fs\"],\n\t\t\tcategory: categories.filesSessions,\n\t\t},\n\t\t{\n\t\t\tkey: \"excludeFolders\",\n\t\t\ttext: strings[\"exclude files\"],\n\t\t\tvalue: values.excludeFolders.join(\"\\n\"),\n\t\t\tprompt: strings[\"exclude files\"],\n\t\t\tpromptType: \"textarea\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\treturn value.split(\"\\n\").every((item) => {\n\t\t\t\t\t\treturn item.trim().length > 0;\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-app-exclude-folders\"],\n\t\t\tcategory: categories.filesSessions,\n\t\t},\n\t\t{\n\t\t\tkey: \"defaultFileEncoding\",\n\t\t\ttext: strings[\"default file encoding\"],\n\t\t\tvalue: values.defaultFileEncoding,\n\t\t\tvalueText: (value) =>\n\t\t\t\tvalue === \"auto\" ? strings.auto || \"Auto\" : getEncoding(value).label,\n\t\t\tselect: [\n\t\t\t\t[\"auto\", strings.auto || \"Auto\"],\n\t\t\t\t...Object.keys(encodings).map((id) => {\n\t\t\t\t\tconst encoding = encodings[id];\n\t\t\t\t\treturn [id, encoding.label];\n\t\t\t\t}),\n\t\t\t],\n\t\t\tinfo: strings[\"settings-info-app-default-file-encoding\"],\n\t\t\tcategory: categories.filesSessions,\n\t\t},\n\t\t{\n\t\t\tkey: \"keybindings\",\n\t\t\ttext: strings[\"key bindings\"],\n\t\t\tinfo: strings[\"settings-info-app-keybindings\"],\n\t\t\tcategory: categories.advanced,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"confirmOnExit\",\n\t\t\ttext: strings[\"confirm on exit\"],\n\t\t\tcheckbox: values.confirmOnExit,\n\t\t\tinfo: strings[\"settings-info-app-confirm-on-exit\"],\n\t\t\tcategory: categories.advanced,\n\t\t},\n\t\t{\n\t\t\tkey: \"checkFiles\",\n\t\t\ttext: strings[\"check file changes\"],\n\t\t\tcheckbox: values.checkFiles,\n\t\t\tinfo: strings[\"settings-info-app-check-files\"],\n\t\t\tcategory: categories.advanced,\n\t\t},\n\t\t{\n\t\t\tkey: \"checkForAppUpdates\",\n\t\t\ttext: strings[\"check for app updates\"],\n\t\t\tcheckbox: values.checkForAppUpdates,\n\t\t\tinfo: strings[\"info-checkForAppUpdates\"],\n\t\t\tcategory: categories.advanced,\n\t\t},\n\t\t{\n\t\t\tkey: \"console\",\n\t\t\ttext: strings.console,\n\t\t\tvalue: values.console,\n\t\t\tselect: [appSettings.CONSOLE_LEGACY, appSettings.CONSOLE_ERUDA],\n\t\t\tinfo: strings[\"settings-info-app-console\"],\n\t\t\tcategory: categories.advanced,\n\t\t},\n\t\t{\n\t\t\tkey: \"developerMode\",\n\t\t\ttext: strings[\"developer mode\"],\n\t\t\tcheckbox: values.developerMode,\n\t\t\tinfo: strings[\"info-developermode\"],\n\t\t\tcategory: categories.advanced,\n\t\t},\n\t\t{\n\t\t\tkey: \"cleanInstallState\",\n\t\t\ttext: strings[\"clean install state\"],\n\t\t\tinfo: strings[\"settings-info-app-clean-install-state\"],\n\t\t\tcategory: categories.advanced,\n\t\t\tchevron: true,\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tinfoAsDescription: true,\n\t\tvalueInTail: true,\n\t});\n\n\tasync function callback(key, value) {\n\t\tswitch (key) {\n\t\t\tcase \"keybindings\": {\n\t\t\t\tvalue = await select(strings[\"key bindings\"], [\n\t\t\t\t\t[\"edit\", strings.edit],\n\t\t\t\t\t[\"reset\", strings.reset],\n\t\t\t\t]);\n\t\t\t\tif (!value) return;\n\n\t\t\t\tif (value === \"edit\") {\n\t\t\t\t\tactionStack.pop(2);\n\t\t\t\t\topenFile(KEYBINDING_FILE);\n\t\t\t\t} else {\n\t\t\t\t\tresetKeyBindings();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcase \"quickToolsSettings\":\n\t\t\t\tQuickToolsSettings();\n\t\t\t\treturn;\n\n\t\t\tcase \"fontManager\":\n\t\t\t\tFontManager();\n\t\t\t\treturn;\n\n\t\t\tcase \"appFont\":\n\t\t\t\tawait fonts.setAppFont(value);\n\t\t\t\tbreak;\n\n\t\t\tcase \"console\": {\n\t\t\t\tif (value !== \"eruda\") {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst fs = fsOperation(Url.join(DATA_STORAGE, \"eruda.js\"));\n\t\t\t\tif (await fs.exists()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tloader.create(\n\t\t\t\t\tstrings[\"downloading file\"].replace(\"{file}\", \"eruda.js\"),\n\t\t\t\t\tstrings[\"downloading...\"],\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\tconst erudaScript = await ajax({\n\t\t\t\t\t\turl: constants.ERUDA_CDN,\n\t\t\t\t\t\tresponseType: \"text\",\n\t\t\t\t\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\t\t\t\t});\n\t\t\t\t\tawait fsOperation(DATA_STORAGE).createFile(\"eruda.js\", erudaScript);\n\t\t\t\t\tloader.destroy();\n\t\t\t\t} catch (error) {\n\t\t\t\t\thelpers.error(error);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"developerMode\": {\n\t\t\t\tif (value) {\n\t\t\t\t\tconst devTools = (await import(\"lib/devTools\")).default;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait devTools.init(true);\n\t\t\t\t\t\ttoast(\n\t\t\t\t\t\t\tstrings[\"developer mode enabled\"] ||\n\t\t\t\t\t\t\t\t\"Developer mode enabled. Use command palette to toggle inspector.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\thelpers.error(error);\n\t\t\t\t\t\tvalue = false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst devTools = (await import(\"lib/devTools\")).default;\n\t\t\t\t\tdevTools.destroy();\n\t\t\t\t\ttoast(\n\t\t\t\t\t\tstrings[\"developer mode disabled\"] || \"Developer mode disabled\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"cleanInstallState\": {\n\t\t\t\tconst INSTALL_STATE_STORAGE = Url.join(DATA_STORAGE, \".install-state\");\n\n\t\t\t\tconst fs = fsOperation(INSTALL_STATE_STORAGE);\n\n\t\t\t\tif (!(await fs.exists())) {\n\t\t\t\t\ttoast(strings[\"no such file or directory\"]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tloader.create(\"loading...\");\n\n\t\t\t\ttry {\n\t\t\t\t\tawait fs.delete();\n\t\t\t\t\tloader.destroy();\n\t\t\t\t\ttoast(strings[\"success\"]);\n\t\t\t\t} catch (error) {\n\t\t\t\t\thelpers.error(error);\n\t\t\t\t\tloader.destroy();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase \"rememberFiles\":\n\t\t\t\tif (!value) {\n\t\t\t\t\tdelete localStorage.files;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"rememberFolders\":\n\t\t\t\tif (!value) {\n\t\t\t\t\tdelete localStorage.folders;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"floatingButton\":\n\t\t\t\troot.classList.toggle(\"hide-floating-button\");\n\t\t\t\tbreak;\n\n\t\t\tcase \"keyboardMode\":\n\t\t\t\tsystem.setInputType(value);\n\t\t\t\tbreak;\n\n\t\t\tcase \"fullscreen\":\n\t\t\t\tif (value) acode.exec(\"enable-fullscreen\");\n\t\t\t\telse acode.exec(\"disable-fullscreen\");\n\t\t\t\tbreak;\n\n\t\t\tcase \"quickTools\":\n\t\t\t\tif (value) {\n\t\t\t\t\tvalue = 1;\n\t\t\t\t\tactions(\"set-height\", 1);\n\t\t\t\t} else {\n\t\t\t\t\tvalue = 0;\n\t\t\t\t\tactions(\"set-height\", 0);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"excludeFolders\":\n\t\t\t\tvalue = value\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.map((item) => item.trim())\n\t\t\t\t\t.filter((item) => item.length > 0);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\tappSettings.update({\n\t\t\t[key]: value,\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/settings/backupRestore.js",
    "content": "import fsOperation from \"fileSystem\";\nimport settingsPage from \"components/settingsPage\";\nimport toast from \"components/toast\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport constants from \"lib/constants\";\nimport appSettings from \"lib/settings\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport helpers from \"utils/helpers\";\nimport Uri from \"utils/Uri\";\nimport Url from \"utils/Url\";\n\n// Backup format version for future compatibility\nconst BACKUP_VERSION = 2;\n\n/**\n * CRC32 lookup table for checksum calculation\n */\nconst CRC32_TABLE = (() => {\n\tconst table = new Uint32Array(256);\n\tfor (let i = 0; i < 256; i++) {\n\t\tlet crc = i;\n\t\tfor (let j = 0; j < 8; j++) {\n\t\t\tcrc = crc & 1 ? 0xedb88320 ^ (crc >>> 1) : crc >>> 1;\n\t\t}\n\t\ttable[i] = crc >>> 0;\n\t}\n\treturn table;\n})();\n\n/**\n * Generates a CRC32 checksum for data integrity verification\n * More robust than simple hash for detecting corrupted data\n * @param {string} data\n * @returns {string}\n */\nfunction generateChecksum(data) {\n\tlet crc = 0xffffffff;\n\tfor (let i = 0; i < data.length; i++) {\n\t\tconst byte = data.charCodeAt(i) & 0xff;\n\t\tcrc = CRC32_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);\n\t}\n\treturn ((crc ^ 0xffffffff) >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Validates the structure of a backup object\n * Supports both v1 (legacy) and v2 (new) backup formats\n * @param {object} backup\n * @returns {{valid: boolean, errors: string[], warnings: string[], isLegacy: boolean}}\n */\nfunction validateBackupStructure(backup) {\n\tconst errors = [];\n\tconst warnings = [];\n\n\tif (!backup || typeof backup !== \"object\") {\n\t\terrors.push(strings[\"backup not valid object\"]);\n\t\treturn { valid: false, errors, warnings, isLegacy: false };\n\t}\n\n\t// Determine if this is a legacy (v1) backup\n\tconst isLegacy = !backup.version;\n\n\tif (isLegacy) {\n\t\t// Legacy backup (v1) - just needs settings or installedPlugins to be valid\n\t\tconst hasData =\n\t\t\tbackup.settings || backup.keyBindings || backup.installedPlugins;\n\t\tif (!hasData) {\n\t\t\terrors.push(strings[\"backup no data\"]);\n\t\t}\n\t\twarnings.push(strings[\"backup legacy warning\"]);\n\t} else {\n\t\t// Version 2+ backup\n\t\tif (backup.version >= 2) {\n\t\t\tif (!backup.metadata) {\n\t\t\t\twarnings.push(strings[\"backup missing metadata\"]);\n\t\t\t}\n\n\t\t\t// Verify checksum if present\n\t\t\tif (backup.checksum) {\n\t\t\t\ttry {\n\t\t\t\t\tconst dataToCheck = JSON.stringify({\n\t\t\t\t\t\tsettings: backup.settings,\n\t\t\t\t\t\tkeyBindings: backup.keyBindings,\n\t\t\t\t\t\tinstalledPlugins: backup.installedPlugins,\n\t\t\t\t\t});\n\t\t\t\t\tconst expectedChecksum = generateChecksum(dataToCheck);\n\t\t\t\t\tif (backup.checksum !== expectedChecksum) {\n\t\t\t\t\t\twarnings.push(strings[\"backup checksum mismatch\"]);\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\twarnings.push(strings[\"backup checksum verify failed\"]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Validate settings (both versions)\n\tif (backup.settings !== undefined && typeof backup.settings !== \"object\") {\n\t\terrors.push(strings[\"backup invalid settings\"]);\n\t}\n\n\t// Validate keyBindings (both versions)\n\tif (\n\t\tbackup.keyBindings !== undefined &&\n\t\ttypeof backup.keyBindings !== \"object\"\n\t) {\n\t\terrors.push(strings[\"backup invalid keybindings\"]);\n\t}\n\n\t// Validate installedPlugins (both versions)\n\tif (\n\t\tbackup.installedPlugins !== undefined &&\n\t\t!Array.isArray(backup.installedPlugins)\n\t) {\n\t\terrors.push(strings[\"backup invalid plugins\"]);\n\t}\n\n\treturn { valid: errors.length === 0, errors, warnings, isLegacy };\n}\n\n/**\n * Formats a date for backup filename\n * @param {Date} date\n * @returns {string}\n */\nfunction formatDateForFilename(date) {\n\tconst year = date.getFullYear();\n\tconst month = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getDate()).padStart(2, \"0\");\n\tconst hours = String(date.getHours()).padStart(2, \"0\");\n\tconst minutes = String(date.getMinutes()).padStart(2, \"0\");\n\treturn `${year}${month}${day}_${hours}${minutes}`;\n}\n\nfunction backupRestore() {\n\tconst title =\n\t\tstrings.backup.capitalize() + \"/\" + strings.restore.capitalize();\n\tconst items = [\n\t\t{\n\t\t\tkey: \"backup\",\n\t\t\ttext: strings.backup.capitalize(),\n\t\t\ticon: \"file_downloadget_app\",\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"restore\",\n\t\t\ttext: strings.restore.capitalize(),\n\t\t\ticon: \"historyrestore\",\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tnote: strings[\"backup/restore note\"],\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tgroupByDefault: true,\n\t});\n\n\tfunction callback(key) {\n\t\tswitch (key) {\n\t\t\tcase \"backup\":\n\t\t\t\tbackup();\n\t\t\t\treturn;\n\n\t\t\tcase \"restore\":\n\t\t\t\trestore();\n\t\t\t\treturn;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tasync function backup() {\n\t\tconst loaderDialog = loader.create(\n\t\t\tstrings.backup.capitalize(),\n\t\t\tstrings[\"preparing backup\"],\n\t\t);\n\n\t\ttry {\n\t\t\tloaderDialog.show();\n\t\t\tloaderDialog.setMessage(strings[\"collecting settings\"]);\n\n\t\t\tconst settings = appSettings.value;\n\n\t\t\t// Read keybindings with fallback\n\t\t\tloaderDialog.setMessage(strings[\"collecting key bindings\"]);\n\t\t\tlet keyBindings = null;\n\t\t\ttry {\n\t\t\t\tconst keybindingsFS = fsOperation(KEYBINDING_FILE);\n\t\t\t\tif (await keybindingsFS.exists()) {\n\t\t\t\t\tkeyBindings = await keybindingsFS.readFile(\"json\");\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\"Could not read keybindings:\", error);\n\t\t\t}\n\n\t\t\t// Collect plugin information\n\t\t\tloaderDialog.setMessage(strings[\"collecting plugins\"]);\n\t\t\tconst installedPlugins = [];\n\t\t\tconst pluginDetails = [];\n\n\t\t\ttry {\n\t\t\t\tconst pluginsDir = fsOperation(window.PLUGIN_DIR);\n\t\t\t\tif (await pluginsDir.exists()) {\n\t\t\t\t\tconst plugins = await pluginsDir.lsDir();\n\n\t\t\t\t\tfor (const plugin of plugins) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst pluginJsonPath = Url.join(\n\t\t\t\t\t\t\t\twindow.PLUGIN_DIR,\n\t\t\t\t\t\t\t\tplugin.name,\n\t\t\t\t\t\t\t\t\"plugin.json\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst pluginJson =\n\t\t\t\t\t\t\t\tawait fsOperation(pluginJsonPath).readFile(\"json\");\n\n\t\t\t\t\t\t\t// Store detailed plugin info for better restore\n\t\t\t\t\t\t\tconst pluginInfo = {\n\t\t\t\t\t\t\t\tid: pluginJson.id || plugin.name,\n\t\t\t\t\t\t\t\tname: pluginJson.name || plugin.name,\n\t\t\t\t\t\t\t\tversion: pluginJson.version || \"unknown\",\n\t\t\t\t\t\t\t\tsource: pluginJson.source || null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tpluginDetails.push(pluginInfo);\n\n\t\t\t\t\t\t\tif (pluginJson.source) {\n\t\t\t\t\t\t\t\tinstalledPlugins.push(pluginJson.source);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tinstalledPlugins.push(pluginJson.id || plugin.name);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Fallback to just plugin name\n\t\t\t\t\t\t\tinstalledPlugins.push(plugin.name);\n\t\t\t\t\t\t\tpluginDetails.push({\n\t\t\t\t\t\t\t\tid: plugin.name,\n\t\t\t\t\t\t\t\tname: plugin.name,\n\t\t\t\t\t\t\t\tversion: \"unknown\",\n\t\t\t\t\t\t\t\tsource: null,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\"Could not read plugins directory:\", error);\n\t\t\t}\n\n\t\t\tloaderDialog.hide();\n\n\t\t\t// Get destination folder\n\t\t\tconst { url } = await FileBrowser(\"folder\", strings[\"select folder\"]);\n\n\t\t\tloaderDialog.show();\n\t\t\tloaderDialog.setMessage(\n\t\t\t\tstrings[\"creating backup\"] || \"Creating backup file...\",\n\t\t\t);\n\n\t\t\tconst timestamp = formatDateForFilename(new Date());\n\t\t\tconst backupFilename = `Acode_backup_${timestamp}.backup`;\n\t\t\tconst backupDirname = \"Backup\";\n\t\t\tconst backupDir = Url.join(url, backupDirname);\n\t\t\tconst backupFile = Url.join(backupDir, backupFilename);\n\t\t\tconst backupStorageFS = fsOperation(url);\n\t\t\tconst backupDirFS = fsOperation(backupDir);\n\t\t\tconst backupFileFS = fsOperation(backupFile);\n\n\t\t\t// Create backup directory if needed\n\t\t\tif (!(await backupDirFS.exists())) {\n\t\t\t\tawait backupStorageFS.createDirectory(backupDirname);\n\t\t\t}\n\n\t\t\t// Create backup file\n\t\t\tif (!(await backupFileFS.exists())) {\n\t\t\t\tawait backupDirFS.createFile(backupFilename);\n\t\t\t}\n\n\t\t\t// Prepare backup data with checksum\n\t\t\tconst backupData = {\n\t\t\t\tsettings,\n\t\t\t\tkeyBindings,\n\t\t\t\tinstalledPlugins,\n\t\t\t};\n\n\t\t\tconst checksum = generateChecksum(JSON.stringify(backupData));\n\n\t\t\tconst backupObject = {\n\t\t\t\tversion: BACKUP_VERSION,\n\t\t\t\tmetadata: {\n\t\t\t\t\tcreatedAt: new Date().toISOString(),\n\t\t\t\t\tappVersion: BuildInfo?.version || \"unknown\",\n\t\t\t\t\tpluginCount: installedPlugins.length,\n\t\t\t\t\thasSettings: !!settings,\n\t\t\t\t\thasKeyBindings: !!keyBindings,\n\t\t\t\t},\n\t\t\t\tchecksum,\n\t\t\t\tsettings,\n\t\t\t\tkeyBindings,\n\t\t\t\tinstalledPlugins,\n\t\t\t\tpluginDetails, // Extra detail for better restoration info\n\t\t\t};\n\n\t\t\tconst backupString = JSON.stringify(backupObject, null, 2);\n\t\t\tawait backupFileFS.writeFile(backupString);\n\n\t\t\tloaderDialog.destroy();\n\n\t\t\tconst message = [\n\t\t\t\tstrings[\"backup successful\"],\n\t\t\t\t`<br><small>${Uri.getVirtualAddress(backupFile)}</small><br>`,\n\t\t\t\t`<strong>${strings.settings || \"Settings\"}:</strong> ✓`,\n\t\t\t\t`<strong>${strings[\"key bindings\"] || \"Key Bindings\"}:</strong> ${keyBindings ? \"✓\" : \"-\"}`,\n\t\t\t\t`<strong>${strings.plugins || \"Plugins\"}:</strong> ${installedPlugins.length}`,\n\t\t\t].join(\"<br>\");\n\n\t\t\talert(strings.success.toUpperCase(), message);\n\t\t} catch (error) {\n\t\t\tloaderDialog.destroy();\n\t\t\tconsole.error(\"Backup error:\", error);\n\t\t\talert(\n\t\t\t\tstrings.error.toUpperCase(),\n\t\t\t\t`${strings[\"error details\"] || \"Error\"}: ${error.message || error}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tfunction restore() {\n\t\tsdcard.openDocumentFile(\n\t\t\t(data) => {\n\t\t\t\tbackupRestore.restore(data.uri);\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\tconsole.error(\"File picker error:\", error);\n\t\t\t\ttoast(strings.error || \"Error selecting file\");\n\t\t\t},\n\t\t\t\"application/octet-stream\",\n\t\t);\n\t}\n}\n\nbackupRestore.restore = async function (url) {\n\tconst loaderDialog = loader.create(\n\t\tstrings.restore.capitalize(),\n\t\tstrings[\"please wait...\"],\n\t);\n\n\tconst restoreResults = {\n\t\tsettings: { attempted: false, success: false, error: null },\n\t\tkeyBindings: { attempted: false, success: false, error: null },\n\t\tplugins: { attempted: false, success: [], failed: [], skipped: [] },\n\t};\n\n\ttry {\n\t\tloaderDialog.show();\n\n\t\t// Read and parse backup file\n\t\tconst fs = fsOperation(url);\n\t\tlet backupContent;\n\n\t\ttry {\n\t\t\tbackupContent = await fs.readFile(\"utf8\");\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Could not read backup file: ${error.message}`);\n\t\t}\n\n\t\tlet backup;\n\t\ttry {\n\t\t\tbackup = JSON.parse(backupContent);\n\t\t} catch (error) {\n\t\t\tloaderDialog.destroy();\n\t\t\talert(strings.error.toUpperCase(), strings[\"invalid backup file\"]);\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate backup structure\n\t\tloaderDialog.setMessage(strings[\"validating backup\"]);\n\t\tconst validation = validateBackupStructure(backup);\n\n\t\tif (!validation.valid) {\n\t\t\tloaderDialog.destroy();\n\t\t\tconst errorMessage = [\n\t\t\t\tstrings[\"invalid backup file\"],\n\t\t\t\t\"\",\n\t\t\t\t`${strings[\"issues found\"]}:`,\n\t\t\t\t...validation.errors.map((e) => `• ${e}`),\n\t\t\t].join(\"\\n\");\n\t\t\talert(strings.error.toUpperCase(), errorMessage);\n\t\t\treturn;\n\t\t}\n\n\t\tloaderDialog.hide();\n\n\t\t// Show backup info and ask for confirmation\n\t\tconst backupInfo = backup.metadata || {};\n\t\tconst confirmParts = [\n\t\t\t`<strong>${strings[\"restore will include\"]}</strong><br><br>`,\n\t\t\t`${strings.settings}: ${backup.settings ? strings.yes : strings.no}<br>`,\n\t\t\t`${strings[\"key bindings\"]}: ${backup.keyBindings ? strings.yes : strings.no}<br>`,\n\t\t\t`${strings.plugins}: ${backup.installedPlugins?.length || 0}<br>`,\n\t\t];\n\n\t\tif (backupInfo.createdAt) {\n\t\t\tconfirmParts.push(\n\t\t\t\t`<br><small>${strings[\"last modified\"]}: ${new Date(backupInfo.createdAt).toLocaleString()}</small><br>`,\n\t\t\t);\n\t\t}\n\n\t\t// Show warnings if any (legacy backup, checksum issues, etc.)\n\t\tif (validation.warnings && validation.warnings.length > 0) {\n\t\t\tconfirmParts.push(`<br><strong>${strings.warning}:</strong><br>`);\n\t\t\tfor (const warning of validation.warnings) {\n\t\t\t\tconfirmParts.push(`- ${warning}<br>`);\n\t\t\t}\n\t\t}\n\n\t\tconfirmParts.push(`<br><strong>${strings[\"restore warning\"]}</strong>`);\n\n\t\tconst shouldContinue = await confirm(\n\t\t\tstrings.restore.capitalize(),\n\t\t\tconfirmParts.join(\"\"),\n\t\t\ttrue,\n\t\t);\n\n\t\tif (!shouldContinue) {\n\t\t\treturn;\n\t\t}\n\n\t\t// What to restore - restore everything available\n\t\tconst selectedOptions = [];\n\t\tif (backup.settings) selectedOptions.push(\"settings\");\n\t\tif (backup.keyBindings) selectedOptions.push(\"keyBindings\");\n\t\tif (backup.installedPlugins?.length) selectedOptions.push(\"plugins\");\n\n\t\tloaderDialog.show();\n\n\t\t// Restore key bindings first (before settings, in case of reload)\n\t\tif (selectedOptions.includes(\"keyBindings\") && backup.keyBindings) {\n\t\t\trestoreResults.keyBindings.attempted = true;\n\t\t\tloaderDialog.setMessage(strings[\"restoring key bindings\"]);\n\n\t\t\ttry {\n\t\t\t\tconst keybindingsFS = fsOperation(window.KEYBINDING_FILE);\n\n\t\t\t\t// Ensure file exists\n\t\t\t\tif (!(await keybindingsFS.exists())) {\n\t\t\t\t\tconst parentDir = fsOperation(DATA_STORAGE);\n\t\t\t\t\tawait parentDir.createFile(\".key-bindings.json\");\n\t\t\t\t}\n\n\t\t\t\tconst text = JSON.stringify(backup.keyBindings, undefined, 2);\n\t\t\t\tawait keybindingsFS.writeFile(text);\n\t\t\t\trestoreResults.keyBindings.success = true;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error restoring key bindings:\", error);\n\t\t\t\trestoreResults.keyBindings.error = error.message;\n\t\t\t}\n\t\t}\n\n\t\t// Restore plugins\n\t\tif (\n\t\t\tselectedOptions.includes(\"plugins\") &&\n\t\t\tArray.isArray(backup.installedPlugins) &&\n\t\t\tbackup.installedPlugins.length > 0\n\t\t) {\n\t\t\trestoreResults.plugins.attempted = true;\n\t\t\tconst { default: installPlugin } = await import(\"lib/installPlugin\");\n\n\t\t\tconst totalPlugins = backup.installedPlugins.length;\n\t\t\tlet currentPlugin = 0;\n\n\t\t\tfor (const id of backup.installedPlugins) {\n\t\t\t\tcurrentPlugin++;\n\n\t\t\t\tif (!id) {\n\t\t\t\t\trestoreResults.plugins.skipped.push({\n\t\t\t\t\t\tid: \"(empty)\",\n\t\t\t\t\t\treason: \"Empty plugin ID\",\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst pluginName =\n\t\t\t\t\tbackup.pluginDetails?.find((p) => p.id === id || p.source === id)\n\t\t\t\t\t\t?.name || id;\n\n\t\t\t\tloaderDialog.setMessage(\n\t\t\t\t\t`${strings[\"restoring plugins\"]} (${currentPlugin}/${totalPlugins}): ${pluginName}`,\n\t\t\t\t);\n\n\t\t\t\ttry {\n\t\t\t\t\tif (\n\t\t\t\t\t\tid.startsWith(\"content://\") ||\n\t\t\t\t\t\tid.startsWith(\"file://\") ||\n\t\t\t\t\t\tid.includes(\"/\")\n\t\t\t\t\t) {\n\t\t\t\t\t\t// Local plugin case\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Check if the source file still exists\n\t\t\t\t\t\t\tconst sourceFS = fsOperation(id);\n\t\t\t\t\t\t\tif (!(await sourceFS.exists())) {\n\t\t\t\t\t\t\t\trestoreResults.plugins.skipped.push({\n\t\t\t\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\t\t\t\treason: strings[\"source not found\"],\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tawait installPlugin(id);\n\t\t\t\t\t\t\trestoreResults.plugins.success.push(pluginName);\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\trestoreResults.plugins.failed.push({\n\t\t\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\t\t\treason: error.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Remote plugin case - fetch from API\n\t\t\t\t\t\tconst pluginUrl = Url.join(constants.API_BASE, `plugin/${id}`);\n\t\t\t\t\t\tlet remotePlugin = null;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tremotePlugin = await fsOperation(pluginUrl).readFile(\"json\");\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\trestoreResults.plugins.failed.push({\n\t\t\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\t\t\treason: strings[\"plugin not found\"],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (remotePlugin) {\n\t\t\t\t\t\t\tlet purchaseToken = null;\n\t\t\t\t\t\t\tconst isPaid = Number.parseFloat(remotePlugin.price) > 0;\n\n\t\t\t\t\t\t\tif (isPaid) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst [product] = await helpers.promisify(iap.getProducts, [\n\t\t\t\t\t\t\t\t\t\tremotePlugin.sku,\n\t\t\t\t\t\t\t\t\t]);\n\t\t\t\t\t\t\t\t\tif (product) {\n\t\t\t\t\t\t\t\t\t\tconst purchases = await helpers.promisify(iap.getPurchases);\n\t\t\t\t\t\t\t\t\t\tconst purchase = purchases.find((p) =>\n\t\t\t\t\t\t\t\t\t\t\tp.productIds.includes(product.productId),\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (!purchaseToken) {\n\t\t\t\t\t\t\t\t\t\trestoreResults.plugins.skipped.push({\n\t\t\t\t\t\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\t\t\t\t\t\treason: strings[\"paid plugin skipped\"],\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t\trestoreResults.plugins.skipped.push({\n\t\t\t\t\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\t\t\t\t\treason: `Paid plugin - ${error.message}`,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait installPlugin(id, remotePlugin.name, purchaseToken);\n\t\t\t\t\t\t\t\trestoreResults.plugins.success.push(pluginName);\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\trestoreResults.plugins.failed.push({\n\t\t\t\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\t\t\t\treason: error.message,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Error restoring plugin ${id}:`, error);\n\t\t\t\t\trestoreResults.plugins.failed.push({\n\t\t\t\t\t\tid: pluginName,\n\t\t\t\t\t\treason: error.message,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Restore settings last (may trigger reload)\n\t\tif (selectedOptions.includes(\"settings\") && backup.settings) {\n\t\t\trestoreResults.settings.attempted = true;\n\t\t\tloaderDialog.setMessage(strings[\"restoring settings\"]);\n\n\t\t\ttry {\n\t\t\t\t// Validate and merge settings carefully\n\t\t\t\tconst currentSettings = appSettings.value;\n\t\t\t\tconst restoredSettings = backup.settings;\n\n\t\t\t\t// Only restore known settings keys to prevent issues with outdated backups\n\t\t\t\tconst validSettings = {};\n\t\t\t\tfor (const key of Object.keys(currentSettings)) {\n\t\t\t\t\tif (key in restoredSettings) {\n\t\t\t\t\t\t// Type check before applying\n\t\t\t\t\t\tif (typeof restoredSettings[key] === typeof currentSettings[key]) {\n\t\t\t\t\t\t\tvalidSettings[key] = restoredSettings[key];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tawait appSettings.update(validSettings, false);\n\t\t\t\trestoreResults.settings.success = true;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"Error restoring settings:\", error);\n\t\t\t\trestoreResults.settings.error = error.message;\n\t\t\t}\n\t\t}\n\n\t\tloaderDialog.destroy();\n\n\t\t// Build restore summary\n\t\tconst summaryParts = [\n\t\t\t`<strong>${strings[\"restore completed\"]}</strong><br><br>`,\n\t\t];\n\n\t\tif (restoreResults.settings.attempted) {\n\t\t\tconst status = restoreResults.settings.success\n\t\t\t\t? `✓ ${strings.restored}`\n\t\t\t\t: `✗ ${strings.failed}`;\n\t\t\tsummaryParts.push(`${strings.settings}: ${status}<br>`);\n\t\t}\n\n\t\tif (restoreResults.keyBindings.attempted) {\n\t\t\tconst status = restoreResults.keyBindings.success\n\t\t\t\t? `✓ ${strings.restored}`\n\t\t\t\t: `✗ ${strings.failed}`;\n\t\t\tsummaryParts.push(`${strings[\"key bindings\"]}: ${status}<br>`);\n\t\t}\n\n\t\tif (restoreResults.plugins.attempted) {\n\t\t\tsummaryParts.push(`<br><strong>${strings.plugins}</strong><br>`);\n\n\t\t\tif (restoreResults.plugins.success.length > 0) {\n\t\t\t\tsummaryParts.push(\n\t\t\t\t\t`✓ ${strings.restored}: ${restoreResults.plugins.success.length}<br>`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (restoreResults.plugins.failed.length > 0) {\n\t\t\t\tsummaryParts.push(\n\t\t\t\t\t`✗ ${strings.failed}: ${restoreResults.plugins.failed.length}<br>`,\n\t\t\t\t);\n\t\t\t\tfor (const f of restoreResults.plugins.failed.slice(0, 3)) {\n\t\t\t\t\tsummaryParts.push(`<small>- ${f.id}: ${f.reason}</small><br>`);\n\t\t\t\t}\n\t\t\t\tif (restoreResults.plugins.failed.length > 3) {\n\t\t\t\t\tsummaryParts.push(\n\t\t\t\t\t\t`<small>...${strings.more || \"and\"} ${restoreResults.plugins.failed.length - 3} ${strings.more || \"more\"}</small><br>`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (restoreResults.plugins.skipped.length > 0) {\n\t\t\t\tsummaryParts.push(\n\t\t\t\t\t`${strings.skipped}: ${restoreResults.plugins.skipped.length}<br>`,\n\t\t\t\t);\n\t\t\t\tfor (const s of restoreResults.plugins.skipped.slice(0, 3)) {\n\t\t\t\t\tsummaryParts.push(`<small>- ${s.id}: ${s.reason}</small><br>`);\n\t\t\t\t}\n\t\t\t\tif (restoreResults.plugins.skipped.length > 3) {\n\t\t\t\t\tsummaryParts.push(\n\t\t\t\t\t\t`<small>...${strings.more} ${restoreResults.plugins.skipped.length - 3} ${strings.more}</small><br>`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tsummaryParts.push(`<br>${strings[\"reload to apply\"]}`);\n\n\t\tconst shouldReload = await confirm(\n\t\t\tstrings[\"restore completed\"],\n\t\t\tsummaryParts.join(\"\"),\n\t\t\ttrue,\n\t\t);\n\n\t\t// Reload only if user confirms\n\t\tif (shouldReload) {\n\t\t\tlocation.reload();\n\t\t}\n\t} catch (err) {\n\t\tloaderDialog.destroy();\n\t\tconsole.error(\"Restore error:\", err);\n\t\talert(\n\t\t\tstrings.error.toUpperCase(),\n\t\t\t`${strings[\"error details\"]}: ${err.message || err}`,\n\t\t);\n\t}\n};\n\nexport default backupRestore;\n"
  },
  {
    "path": "src/settings/editorSettings.js",
    "content": "import settingsPage from \"components/settingsPage\";\nimport constants from \"lib/constants\";\nimport fonts from \"lib/fonts\";\nimport appSettings from \"lib/settings\";\nimport scrollSettings from \"./scrollSettings\";\n\nexport default function editorSettings() {\n\tconst title = strings[\"editor settings\"];\n\tconst values = appSettings.value;\n\tconst categories = {\n\t\tscrolling: strings[\"settings-category-scrolling\"],\n\t\ttextLayout: strings[\"settings-category-text-layout\"],\n\t\tediting: strings[\"settings-category-editing\"],\n\t\tassistance: strings[\"settings-category-assistance\"],\n\t\tguidesIndicators: strings[\"settings-category-guides-indicators\"],\n\t\tcursorSelection: strings[\"settings-category-cursor-selection\"],\n\t};\n\tconst items = [\n\t\t{\n\t\t\tkey: \"scroll-settings\",\n\t\t\ttext: strings[\"scroll settings\"],\n\t\t\tinfo: strings[\"settings-info-editor-scroll-settings\"],\n\t\t\tcategory: categories.scrolling,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"editorFont\",\n\t\t\ttext: strings[\"editor font\"],\n\t\t\tvalue: values.editorFont,\n\t\t\tget select() {\n\t\t\t\treturn fonts.getNames();\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-editor-font-family\"],\n\t\t\tcategory: categories.textLayout,\n\t\t},\n\t\t{\n\t\t\tkey: \"fontSize\",\n\t\t\ttext: strings[\"font size\"],\n\t\t\tvalue: values.fontSize,\n\t\t\tprompt: strings[\"font size\"],\n\t\t\tpromptOptions: {\n\t\t\t\trequired: true,\n\t\t\t\tmatch: constants.FONT_SIZE,\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-editor-font-size\"],\n\t\t\tcategory: categories.textLayout,\n\t\t},\n\t\t{\n\t\t\tkey: \"lineHeight\",\n\t\t\ttext: strings[\"line height\"],\n\t\t\tvalue: values.lineHeight,\n\t\t\tprompt: strings[\"line height\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\tvalue = Number.parseFloat(value);\n\t\t\t\t\treturn value >= 1 && value <= 2;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-editor-line-height\"],\n\t\t\tcategory: categories.textLayout,\n\t\t},\n\t\t{\n\t\t\tkey: \"textWrap\",\n\t\t\ttext: strings[\"text wrap\"],\n\t\t\tcheckbox: values.textWrap,\n\t\t\tinfo: strings[\"settings-info-editor-text-wrap\"],\n\t\t\tcategory: categories.textLayout,\n\t\t},\n\t\t{\n\t\t\tkey: \"hardWrap\",\n\t\t\ttext: strings[\"hard wrap\"],\n\t\t\tcheckbox: values.hardWrap,\n\t\t\tinfo: strings[\"settings-info-editor-hard-wrap\"],\n\t\t\tcategory: categories.textLayout,\n\t\t},\n\t\t{\n\t\t\tkey: \"autosave\",\n\t\t\ttext: strings.autosave,\n\t\t\tvalue: values.autosave,\n\t\t\tvalueText: (value) => (value ? value : strings.no),\n\t\t\tprompt: strings.delay + \" (>=1000 || 0)\",\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\tvalue = Number.parseInt(value);\n\t\t\t\t\treturn value >= 1000 || value === 0;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-editor-autosave\"],\n\t\t\tcategory: categories.editing,\n\t\t},\n\t\t{\n\t\t\tkey: \"softTab\",\n\t\t\ttext: strings[\"soft tab\"],\n\t\t\tcheckbox: values.softTab,\n\t\t\tinfo: strings[\"settings-info-editor-soft-tab\"],\n\t\t\tcategory: categories.editing,\n\t\t},\n\t\t{\n\t\t\tkey: \"tabSize\",\n\t\t\ttext: strings[\"tab size\"],\n\t\t\tvalue: values.tabSize,\n\t\t\tprompt: strings[\"tab size\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\tvalue = Number.parseInt(value);\n\t\t\t\t\treturn value >= 1 && value <= 8;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-editor-tab-size\"],\n\t\t\tcategory: categories.editing,\n\t\t},\n\t\t{\n\t\t\tkey: \"formatOnSave\",\n\t\t\ttext: strings[\"format on save\"],\n\t\t\tcheckbox: values.formatOnSave,\n\t\t\tinfo: strings[\"settings-info-editor-format-on-save\"],\n\t\t\tcategory: categories.editing,\n\t\t},\n\t\t{\n\t\t\tkey: \"liveAutoCompletion\",\n\t\t\ttext: strings[\"live autocompletion\"],\n\t\t\tcheckbox: values.liveAutoCompletion,\n\t\t\tinfo: strings[\"settings-info-editor-live-autocomplete\"],\n\t\t\tcategory: categories.assistance,\n\t\t},\n\t\t{\n\t\t\tkey: \"colorPreview\",\n\t\t\ttext: strings[\"color preview\"],\n\t\t\tcheckbox: values.colorPreview,\n\t\t\tinfo: strings[\"settings-info-editor-color-preview\"],\n\t\t\tcategory: categories.assistance,\n\t\t},\n\t\t{\n\t\t\tkey: \"linenumbers\",\n\t\t\ttext: strings[\"show line numbers\"],\n\t\t\tcheckbox: values.linenumbers,\n\t\t\tinfo: strings[\"settings-info-editor-line-numbers\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"relativeLineNumbers\",\n\t\t\ttext: strings[\"relative line numbers\"],\n\t\t\tcheckbox: values.relativeLineNumbers,\n\t\t\tinfo: strings[\"settings-info-editor-relative-line-numbers\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"lintGutter\",\n\t\t\ttext: strings[\"lint gutter\"] || \"Show lint gutter\",\n\t\t\tcheckbox: values.lintGutter ?? true,\n\t\t\tinfo: strings[\"settings-info-editor-lint-gutter\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"showSpaces\",\n\t\t\ttext: strings[\"show spaces\"],\n\t\t\tcheckbox: values.showSpaces,\n\t\t\tinfo: strings[\"settings-info-editor-show-spaces\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"indentGuides\",\n\t\t\ttext: strings[\"indent guides\"] || \"Indent guides\",\n\t\t\tcheckbox: values.indentGuides ?? true,\n\t\t\tinfo: strings[\"settings-info-editor-indent-guides\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"rainbowBrackets\",\n\t\t\ttext: strings[\"rainbow brackets\"] || \"Rainbow brackets\",\n\t\t\tcheckbox: values.rainbowBrackets ?? true,\n\t\t\tinfo: strings[\"settings-info-editor-rainbow-brackets\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"fadeFoldWidgets\",\n\t\t\ttext: strings[\"fade fold widgets\"],\n\t\t\tcheckbox: values.fadeFoldWidgets,\n\t\t\tinfo: strings[\"settings-info-editor-fade-fold-widgets\"],\n\t\t\tcategory: categories.guidesIndicators,\n\t\t},\n\t\t{\n\t\t\tkey: \"shiftClickSelection\",\n\t\t\ttext: strings[\"shift click selection\"],\n\t\t\tcheckbox: values.shiftClickSelection,\n\t\t\tinfo: strings[\"settings-info-editor-shift-click-selection\"],\n\t\t\tcategory: categories.cursorSelection,\n\t\t},\n\t\t{\n\t\t\tkey: \"rtlText\",\n\t\t\ttext: strings[\"line based rtl switching\"],\n\t\t\tcheckbox: values.rtlText,\n\t\t\tinfo: strings[\"settings-info-editor-rtl-text\"],\n\t\t\tcategory: categories.cursorSelection,\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tinfoAsDescription: true,\n\t\tvalueInTail: true,\n\t});\n\n\t/**\n\t * Callback for settings page when an item is clicked\n\t * @param {string} key\n\t * @param {string} value\n\t */\n\tfunction callback(key, value) {\n\t\tswitch (key) {\n\t\t\tcase \"scroll-settings\":\n\t\t\t\tappSettings.uiSettings[key].show();\n\t\t\t\treturn;\n\n\t\t\tcase \"editorFont\":\n\t\t\t\tfonts.setFont(value);\n\n\t\t\tdefault:\n\t\t\t\tappSettings.update({\n\t\t\t\t\t[key]: value,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/settings/filesSettings.js",
    "content": "import settingsPage from \"components/settingsPage\";\nimport appSettings from \"lib/settings\";\n\nexport default function filesSettings() {\n\tconst title = strings.settings;\n\tconst values = appSettings.value.fileBrowser;\n\n\tconst items = [\n\t\t{\n\t\t\tkey: \"sortByName\",\n\t\t\ttext: strings[\"sort by name\"],\n\t\t\tcheckbox: values.sortByName,\n\t\t},\n\t\t{\n\t\t\tkey: \"showHiddenFiles\",\n\t\t\ttext: strings[\"show hidden files\"],\n\t\t\tcheckbox: values.showHiddenFiles,\n\t\t\tinfo: strings[\"info-showHiddenFiles\"],\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tgroupByDefault: true,\n\t});\n\n\tfunction callback(key, value) {\n\t\tappSettings.value.fileBrowser[key] = value;\n\t\tappSettings.update();\n\t}\n}\n"
  },
  {
    "path": "src/settings/formatterSettings.js",
    "content": "import { getModes } from \"cm/modelist\";\nimport settingsPage from \"components/settingsPage\";\nimport appSettings from \"lib/settings\";\nimport helpers from \"utils/helpers\";\n\nexport default function formatterSettings(languageName) {\n\tconst title = strings.formatter;\n\tconst values = appSettings.value;\n\tconst { formatters } = acode;\n\tconst languagesLabel = strings.languages || \"Languages\";\n\n\t// Build items from CodeMirror modelist\n\tconst items = getModes()\n\t\t.slice()\n\t\t.sort((a, b) =>\n\t\t\tString(a.caption || a.name).localeCompare(String(b.caption || b.name)),\n\t\t)\n\t\t.map((mode) => {\n\t\t\tconst { name, caption, extensions } = mode;\n\t\t\tconst formatterID = values.formatter[name] || null;\n\t\t\t// Only pass real extensions (skip anchored filename patterns like ^Dockerfile)\n\t\t\tconst extList = String(extensions)\n\t\t\t\t.split(\"|\")\n\t\t\t\t.filter((e) => e && !e.startsWith(\"^\"));\n\t\t\tconst options = acode.getFormatterFor(extList);\n\t\t\tconst sampleExt = extList[0] || name;\n\n\t\t\treturn {\n\t\t\t\tkey: name,\n\t\t\t\ttext: caption,\n\t\t\t\ticon: helpers.getIconForFile(`sample.${sampleExt}`),\n\t\t\t\tvalue: formatterID,\n\t\t\t\tvalueText: (value) => {\n\t\t\t\t\tconst formatter = formatters.find(({ id }) => id === value);\n\t\t\t\t\tif (formatter) {\n\t\t\t\t\t\treturn formatter.name;\n\t\t\t\t\t}\n\t\t\t\t\treturn strings.none;\n\t\t\t\t},\n\t\t\t\tselect: options,\n\t\t\t\tchevron: true,\n\t\t\t\tcategory: languagesLabel,\n\t\t\t};\n\t\t});\n\n\titems.unshift({\n\t\tnote: strings[\"settings-note-formatter-settings\"],\n\t});\n\n\tconst page = settingsPage(title, items, callback, \"separate\", {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page formatter-settings-page\",\n\t\tlistClassName: \"detail-settings-list formatter-settings-list\",\n\t\tnotePosition: \"top\",\n\t});\n\tpage.show(languageName);\n\n\tfunction callback(key, value) {\n\t\tif (value === null) {\n\t\t\t// Delete the key when \"none\" is selected\n\t\t\tdelete values.formatter[key];\n\t\t} else {\n\t\t\tvalues.formatter[key] = value;\n\t\t}\n\t\tappSettings.update();\n\t}\n}\n"
  },
  {
    "path": "src/settings/helpSettings.js",
    "content": "import settingsPage from \"components/settingsPage\";\nimport constants from \"lib/constants\";\n\nexport default function help() {\n\tconst title = strings.help;\n\tconst items = [\n\t\t{\n\t\t\tkey: \"docs\",\n\t\t\ttext: strings.documentation,\n\t\t\tlink: constants.DOCS_URL,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"help\",\n\t\t\ttext: strings.help,\n\t\t\tlink: constants.TELEGRAM_URL,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"faqs\",\n\t\t\ttext: strings.faqs,\n\t\t\tlink: `${constants.WEBSITE_URL}/faqs`,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"bug_report\",\n\t\t\ttext: strings.bug_report,\n\t\t\tlink: `${constants.GITHUB_URL}/issues`,\n\t\t\tchevron: true,\n\t\t},\n\t];\n\n\tconst page = settingsPage(title, items, () => {}, \"separate\", {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tgroupByDefault: true,\n\t});\n\tpage.show();\n}\n"
  },
  {
    "path": "src/settings/lspConfigUtils.js",
    "content": "import lspApi from \"cm/lsp/api\";\nimport appSettings from \"lib/settings\";\n\nfunction cloneLspSettings() {\n\treturn JSON.parse(JSON.stringify(appSettings.value?.lsp || {}));\n}\n\nexport function normalizeServerId(id) {\n\treturn String(id || \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\nexport function normalizeLanguages(value) {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.map((lang) =>\n\t\t\t\tString(lang || \"\")\n\t\t\t\t\t.trim()\n\t\t\t\t\t.toLowerCase(),\n\t\t\t)\n\t\t\t.filter(Boolean);\n\t}\n\n\treturn String(value || \"\")\n\t\t.split(\",\")\n\t\t.map((lang) => lang.trim().toLowerCase())\n\t\t.filter(Boolean);\n}\n\nexport function getServerOverride(id) {\n\treturn appSettings.value?.lsp?.servers?.[normalizeServerId(id)] || {};\n}\n\nexport function isCustomServer(id) {\n\treturn getServerOverride(id).custom === true;\n}\n\nexport async function updateServerConfig(serverId, partial) {\n\tconst key = normalizeServerId(serverId);\n\tif (!key) {\n\t\tthrow new Error(\"Server id is required\");\n\t}\n\n\tconst current = cloneLspSettings();\n\tcurrent.servers = current.servers || {};\n\tconst nextServer = {\n\t\t...(current.servers[key] || {}),\n\t};\n\n\tObject.entries(partial || {}).forEach(([entryKey, value]) => {\n\t\tif (value === undefined) {\n\t\t\tdelete nextServer[entryKey];\n\t\t\treturn;\n\t\t}\n\t\tnextServer[entryKey] = value;\n\t});\n\n\tif (Object.keys(nextServer).length) {\n\t\tcurrent.servers[key] = nextServer;\n\t} else {\n\t\tdelete current.servers[key];\n\t}\n\n\tawait appSettings.update({ lsp: current }, false);\n}\n\nexport async function upsertCustomServer(serverId, config) {\n\tconst key = normalizeServerId(serverId);\n\tif (!key) {\n\t\tthrow new Error(\"Server id is required\");\n\t}\n\n\tconst existingServer = lspApi.servers.get(key);\n\tif (existingServer && getServerOverride(key).custom !== true) {\n\t\tthrow new Error(\"A built-in server already uses this id\");\n\t}\n\n\tconst languages = normalizeLanguages(config.languages);\n\tif (!languages.length) {\n\t\tthrow new Error(\"At least one language id is required\");\n\t}\n\n\tconst current = cloneLspSettings();\n\tcurrent.servers = current.servers || {};\n\tconst existing = current.servers[key] || {};\n\tconst hasTransport = Object.prototype.hasOwnProperty.call(\n\t\tconfig,\n\t\t\"transport\",\n\t);\n\tconst hasLauncher = Object.prototype.hasOwnProperty.call(config, \"launcher\");\n\tconst nextConfig = {\n\t\t...existing,\n\t\t...config,\n\t\tcustom: true,\n\t\tlabel: config.label || existing.label || key,\n\t\tlanguages,\n\t\ttransport: hasTransport\n\t\t\t? config.transport\n\t\t\t: existing.transport || { kind: \"websocket\" },\n\t\tlauncher: hasLauncher ? config.launcher : existing.launcher,\n\t\tenabled: config.enabled !== false,\n\t};\n\n\tconst installKind = nextConfig.launcher?.install?.kind;\n\tif (installKind && installKind !== \"shell\") {\n\t\tconst providedExecutable =\n\t\t\tnextConfig.launcher.install.binaryPath ||\n\t\t\tnextConfig.launcher.install.executable;\n\t\tif (!providedExecutable) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Managed installers must declare the executable path or command they provide\",\n\t\t\t);\n\t\t}\n\t}\n\n\tcurrent.servers[key] = nextConfig;\n\tawait appSettings.update({ lsp: current }, false);\n\n\tconst definition = {\n\t\tid: key,\n\t\tlabel: nextConfig.label,\n\t\tlanguages,\n\t\ttransport: nextConfig.transport,\n\t\tlauncher: nextConfig.launcher,\n\t\tclientConfig: nextConfig.clientConfig,\n\t\tinitializationOptions: nextConfig.initializationOptions,\n\t\tstartupTimeout: nextConfig.startupTimeout,\n\t\tenabled: nextConfig.enabled !== false,\n\t};\n\n\tlspApi.upsert(definition);\n\treturn key;\n}\n\nexport async function removeCustomServer(serverId) {\n\tconst key = normalizeServerId(serverId);\n\tconst current = cloneLspSettings();\n\tcurrent.servers = current.servers || {};\n\tdelete current.servers[key];\n\tawait appSettings.update({ lsp: current }, false);\n\tlspApi.servers.unregister(key);\n}\n"
  },
  {
    "path": "src/settings/lspServerDetail.js",
    "content": "import lspApi from \"cm/lsp/api\";\nimport {\n\tcheckServerInstallation,\n\tgetInstallCommand,\n\tgetUninstallCommand,\n\tinstallServer,\n\tstopManagedServer,\n\tuninstallServer,\n} from \"cm/lsp/serverLauncher\";\nimport settingsPage from \"components/settingsPage\";\nimport toast from \"components/toast\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport prompt from \"dialogs/prompt\";\nimport appSettings from \"lib/settings\";\nimport {\n\tgetServerOverride,\n\tisCustomServer,\n\tremoveCustomServer,\n\tupdateServerConfig,\n} from \"./lspConfigUtils\";\n\nfunction getFeatureItems() {\n\treturn [\n\t\t[\n\t\t\t\"ext_hover\",\n\t\t\t\"hover\",\n\t\t\tstrings[\"lsp-feature-hover\"],\n\t\t\tstrings[\"lsp-feature-hover-info\"],\n\t\t],\n\t\t[\n\t\t\t\"ext_completion\",\n\t\t\t\"completion\",\n\t\t\tstrings[\"lsp-feature-completion\"],\n\t\t\tstrings[\"lsp-feature-completion-info\"],\n\t\t],\n\t\t[\n\t\t\t\"ext_signature\",\n\t\t\t\"signature\",\n\t\t\tstrings[\"lsp-feature-signature\"],\n\t\t\tstrings[\"lsp-feature-signature-info\"],\n\t\t],\n\t\t[\n\t\t\t\"ext_diagnostics\",\n\t\t\t\"diagnostics\",\n\t\t\tstrings[\"lsp-feature-diagnostics\"],\n\t\t\tstrings[\"lsp-feature-diagnostics-info\"],\n\t\t],\n\t\t[\n\t\t\t\"ext_inlayHints\",\n\t\t\t\"inlayHints\",\n\t\t\tstrings[\"lsp-feature-inlay-hints\"],\n\t\t\tstrings[\"lsp-feature-inlay-hints-info\"],\n\t\t],\n\t\t[\n\t\t\t\"ext_formatting\",\n\t\t\t\"formatting\",\n\t\t\tstrings[\"lsp-feature-formatting\"],\n\t\t\tstrings[\"lsp-feature-formatting-info\"],\n\t\t],\n\t];\n}\n\nfunction fillTemplate(template, replacements) {\n\treturn Object.entries(replacements).reduce(\n\t\t(result, [key, value]) => result.replaceAll(`{${key}}`, String(value)),\n\t\tString(template || \"\"),\n\t);\n}\n\nfunction clone(value) {\n\tif (!value || typeof value !== \"object\") return value;\n\treturn JSON.parse(JSON.stringify(value));\n}\n\nfunction mergeLauncher(base, patch) {\n\tif (!base && !patch) return undefined;\n\treturn {\n\t\t...(base || {}),\n\t\t...(patch || {}),\n\t\tbridge: {\n\t\t\t...(base?.bridge || {}),\n\t\t\t...(patch?.bridge || {}),\n\t\t},\n\t\tinstall: {\n\t\t\t...(base?.install || {}),\n\t\t\t...(patch?.install || {}),\n\t\t},\n\t};\n}\n\nfunction isDirectWebSocketServer(server) {\n\treturn server?.transport?.kind === \"websocket\" && !server?.launcher?.bridge;\n}\n\nfunction getMergedConfig(server) {\n\tconst override = getServerOverride(server.id);\n\treturn {\n\t\t...server,\n\t\tenabled: override.enabled ?? server.enabled,\n\t\tstartupTimeout: override.startupTimeout ?? server.startupTimeout,\n\t\tinitializationOptions: {\n\t\t\t...(server.initializationOptions || {}),\n\t\t\t...(override.initializationOptions || {}),\n\t\t},\n\t\tclientConfig: {\n\t\t\t...(server.clientConfig || {}),\n\t\t\t...(override.clientConfig || {}),\n\t\t\tbuiltinExtensions: {\n\t\t\t\t...(server.clientConfig?.builtinExtensions || {}),\n\t\t\t\t...(override.clientConfig?.builtinExtensions || {}),\n\t\t\t},\n\t\t},\n\t\tlauncher: mergeLauncher(server.launcher, override.launcher),\n\t};\n}\n\nfunction formatInstallStatus(result) {\n\tswitch (result?.status) {\n\t\tcase \"present\":\n\t\t\treturn result.version\n\t\t\t\t? fillTemplate(strings[\"lsp-status-installed-version\"], {\n\t\t\t\t\t\tversion: result.version,\n\t\t\t\t\t})\n\t\t\t\t: strings[\"lsp-status-installed\"];\n\t\tcase \"missing\":\n\t\t\treturn strings[\"lsp-status-not-installed\"];\n\t\tcase \"failed\":\n\t\t\treturn strings[\"lsp-status-check-failed\"];\n\t\tdefault:\n\t\t\treturn strings[\"lsp-status-unknown\"];\n\t}\n}\n\nfunction formatStartupTimeoutValue(timeout) {\n\treturn typeof timeout === \"number\"\n\t\t? fillTemplate(strings[\"lsp-timeout-ms\"], { timeout })\n\t\t: strings[\"lsp-default\"];\n}\n\nfunction sanitizeInstallMessage(message) {\n\tconst lines = String(message || \"\")\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean)\n\t\t.filter(\n\t\t\t(line) =>\n\t\t\t\t!/^proot warning:/i.test(line) &&\n\t\t\t\t!line.includes(`\"/proc/self/fd/0\"`) &&\n\t\t\t\t!line.includes(`\"/proc/self/fd/1\"`) &&\n\t\t\t\t!line.includes(`\"/proc/self/fd/2\"`),\n\t\t);\n\n\treturn lines.join(\" \");\n}\n\nfunction formatInstallInfo(result) {\n\tconst cleanedMessage = sanitizeInstallMessage(result?.message);\n\n\tswitch (result?.status) {\n\t\tcase \"present\":\n\t\t\treturn result.version\n\t\t\t\t? fillTemplate(strings[\"lsp-install-info-version-available\"], {\n\t\t\t\t\t\tversion: result.version,\n\t\t\t\t\t})\n\t\t\t\t: strings[\"lsp-install-info-ready\"];\n\t\tcase \"missing\":\n\t\t\treturn strings[\"lsp-install-info-missing\"];\n\t\tcase \"failed\":\n\t\t\treturn cleanedMessage || strings[\"lsp-install-info-check-failed\"];\n\t\tdefault:\n\t\t\treturn cleanedMessage || strings[\"lsp-install-info-unknown\"];\n\t}\n}\n\nfunction formatValue(value) {\n\tif (value === undefined || value === null || value === \"\") return \"\";\n\tlet text = String(value);\n\tif (text.includes(\"\\n\")) {\n\t\t[text] = text.split(\"\\n\");\n\t}\n\tif (text.length > 47) {\n\t\ttext = `${text.slice(0, 47)}...`;\n\t}\n\treturn text;\n}\n\nfunction escapeHtml(text) {\n\tconst div = document.createElement(\"div\");\n\tdiv.textContent = String(text || \"\");\n\treturn div.innerHTML;\n}\n\nfunction updateItemDisplay($list, itemsByKey, key, value, extras = {}) {\n\tconst item = itemsByKey.get(key);\n\tif (!item) return;\n\n\tif (\"value\" in extras) {\n\t\titem.value = extras.value;\n\t} else if (value !== undefined) {\n\t\titem.value = value;\n\t}\n\n\tif (\"info\" in extras) {\n\t\titem.info = extras.info;\n\t}\n\n\tif (\"checkbox\" in extras) {\n\t\titem.checkbox = extras.checkbox;\n\t}\n\n\tconst $item = $list?.querySelector?.(`[data-key=\"${key}\"]`);\n\tif (!$item) return;\n\n\tconst $subtitle = $item.querySelector(\".value\");\n\tif ($subtitle) {\n\t\t$subtitle.textContent = $subtitle.classList.contains(\"setting-info\")\n\t\t\t? String(item.info || \"\")\n\t\t\t: formatValue(item.value);\n\t}\n\n\tconst $trailingValue = $item.querySelector(\".setting-trailing-value\");\n\tif ($trailingValue) {\n\t\t$trailingValue.textContent = formatValue(item.value);\n\t}\n\n\tconst $checkbox = $item.querySelector(\".input-checkbox\");\n\tif ($checkbox && typeof item.checkbox === \"boolean\") {\n\t\t$checkbox.checked = item.checkbox;\n\t}\n}\n\nasync function buildSnapshot(serverId) {\n\tconst liveServer = lspApi.servers.get(serverId);\n\tif (!liveServer) return null;\n\n\tconst merged = getMergedConfig(liveServer);\n\tconst override = getServerOverride(serverId);\n\tconst directWebSocket = isDirectWebSocketServer(merged);\n\tconst installResult = directWebSocket\n\t\t? {\n\t\t\t\tstatus: \"unknown\",\n\t\t\t\tversion: null,\n\t\t\t\tcanInstall: false,\n\t\t\t\tcanUpdate: false,\n\t\t\t\tmessage: strings[\"lsp-websocket-server-managed-externally\"],\n\t\t\t}\n\t\t: await checkServerInstallation(merged).catch((error) => ({\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tversion: null,\n\t\t\t\tcanInstall: true,\n\t\t\t\tcanUpdate: true,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t}));\n\n\treturn {\n\t\tliveServer,\n\t\tmerged,\n\t\toverride,\n\t\tdirectWebSocket,\n\t\tisCustom: isCustomServer(serverId),\n\t\tinstallResult,\n\t\tbuiltinExts: merged.clientConfig?.builtinExtensions || {},\n\t\tinstallCommand: getInstallCommand(merged, \"install\"),\n\t\tupdateCommand: getInstallCommand(merged, \"update\"),\n\t\tuninstallCommand: getUninstallCommand(merged),\n\t};\n}\n\nfunction createItems(snapshot) {\n\tconst featureItems = getFeatureItems();\n\tconst categories = {\n\t\tgeneral: strings[\"settings-category-general\"],\n\t\tinstallation: strings[\"settings-category-installation\"],\n\t\tadvanced: strings[\"settings-category-advanced\"],\n\t\tfeatures: strings[\"settings-category-features\"],\n\t};\n\tconst items = [\n\t\t{\n\t\t\tkey: \"enabled\",\n\t\t\ttext: strings[\"lsp-enabled\"],\n\t\t\tcheckbox: snapshot.merged.enabled !== false,\n\t\t\tinfo: strings[\"settings-info-lsp-server-enabled\"],\n\t\t\tcategory: categories.general,\n\t\t},\n\t\t...(snapshot.isCustom\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tkey: \"remove_custom_server\",\n\t\t\t\t\t\ttext: strings[\"lsp-remove-custom-server\"],\n\t\t\t\t\t\tinfo: strings[\"settings-info-lsp-remove-custom-server\"],\n\t\t\t\t\t\tcategory: categories.general,\n\t\t\t\t\t\tchevron: true,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: []),\n\t\t{\n\t\t\tkey: \"startup_timeout\",\n\t\t\ttext: strings[\"lsp-startup-timeout\"],\n\t\t\tvalue: formatStartupTimeoutValue(snapshot.merged.startupTimeout),\n\t\t\tinfo: strings[\"settings-info-lsp-startup-timeout\"],\n\t\t\tcategory: categories.advanced,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"edit_init_options\",\n\t\t\ttext: strings[\"lsp-edit-initialization-options\"],\n\t\t\tvalue: Object.keys(snapshot.override.initializationOptions || {}).length\n\t\t\t\t? strings[\"lsp-configured\"]\n\t\t\t\t: strings[\"lsp-empty\"],\n\t\t\tinfo: strings[\"settings-info-lsp-edit-init-options\"],\n\t\t\tcategory: categories.advanced,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"view_init_options\",\n\t\t\ttext: strings[\"lsp-view-initialization-options\"],\n\t\t\tinfo: strings[\"settings-info-lsp-view-init-options\"],\n\t\t\tcategory: categories.advanced,\n\t\t\tchevron: true,\n\t\t},\n\t];\n\n\tif (!snapshot.directWebSocket) {\n\t\titems.splice(\n\t\t\t1,\n\t\t\t0,\n\t\t\t{\n\t\t\t\tkey: \"install_status\",\n\t\t\t\ttext: strings[\"lsp-installed\"],\n\t\t\t\tvalue: formatInstallStatus(snapshot.installResult),\n\t\t\t\tinfo: formatInstallInfo(snapshot.installResult),\n\t\t\t\tcategory: categories.installation,\n\t\t\t\tchevron: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: \"install_server\",\n\t\t\t\ttext: strings[\"lsp-install-repair\"],\n\t\t\t\tinfo: strings[\"settings-info-lsp-install-server\"],\n\t\t\t\tcategory: categories.installation,\n\t\t\t\tchevron: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: \"update_server\",\n\t\t\t\ttext: strings[\"lsp-update-server\"],\n\t\t\t\tinfo: strings[\"settings-info-lsp-update-server\"],\n\t\t\t\tcategory: categories.installation,\n\t\t\t\tchevron: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tkey: \"uninstall_server\",\n\t\t\t\ttext: strings[\"lsp-uninstall-server\"],\n\t\t\t\tinfo: strings[\"settings-info-lsp-uninstall-server\"],\n\t\t\t\tcategory: categories.installation,\n\t\t\t\tchevron: true,\n\t\t\t},\n\t\t);\n\t}\n\n\tfeatureItems.forEach(([key, extKey, text, info]) => {\n\t\titems.push({\n\t\t\tkey,\n\t\t\ttext,\n\t\t\tcheckbox: isBuiltinFeatureEnabled(snapshot.builtinExts, extKey),\n\t\t\tinfo,\n\t\t\tcategory: categories.features,\n\t\t});\n\t});\n\n\treturn items;\n}\n\nasync function refreshVisibleState($list, itemsByKey, serverId) {\n\tif (!$list) return;\n\n\tconst snapshot = await buildSnapshot(serverId);\n\tif (!snapshot) return;\n\n\tupdateItemDisplay($list, itemsByKey, \"enabled\", undefined, {\n\t\tcheckbox: snapshot.merged.enabled !== false,\n\t});\n\tupdateItemDisplay(\n\t\t$list,\n\t\titemsByKey,\n\t\t\"install_status\",\n\t\tformatInstallStatus(snapshot.installResult),\n\t\t{\n\t\t\tinfo: formatInstallInfo(snapshot.installResult),\n\t\t},\n\t);\n\tupdateItemDisplay($list, itemsByKey, \"install_server\", \"\");\n\tupdateItemDisplay($list, itemsByKey, \"update_server\", \"\");\n\tupdateItemDisplay($list, itemsByKey, \"uninstall_server\", \"\");\n\tupdateItemDisplay(\n\t\t$list,\n\t\titemsByKey,\n\t\t\"startup_timeout\",\n\t\tformatStartupTimeoutValue(snapshot.merged.startupTimeout),\n\t);\n\tupdateItemDisplay(\n\t\t$list,\n\t\titemsByKey,\n\t\t\"edit_init_options\",\n\t\tObject.keys(snapshot.override.initializationOptions || {}).length\n\t\t\t? strings[\"lsp-configured\"]\n\t\t\t: strings[\"lsp-empty\"],\n\t);\n\n\tgetFeatureItems().forEach(([key, extKey]) => {\n\t\tupdateItemDisplay($list, itemsByKey, key, undefined, {\n\t\t\tcheckbox: isBuiltinFeatureEnabled(snapshot.builtinExts, extKey),\n\t\t});\n\t});\n}\n\nfunction isBuiltinFeatureEnabled(builtinExts, extKey) {\n\tif (extKey === \"inlayHints\") {\n\t\treturn builtinExts?.[extKey] === true;\n\t}\n\treturn builtinExts?.[extKey] !== false;\n}\n\nasync function persistEnabled(serverId, value) {\n\tawait updateServerConfig(serverId, { enabled: value });\n\tlspApi.servers.update(serverId, (current) => ({\n\t\t...current,\n\t\tenabled: value,\n\t}));\n}\n\nasync function persistClientConfig(serverId, clientConfig) {\n\tawait updateServerConfig(serverId, { clientConfig });\n\tlspApi.servers.update(serverId, (current) => ({\n\t\t...current,\n\t\tclientConfig: {\n\t\t\t...(current.clientConfig || {}),\n\t\t\t...clientConfig,\n\t\t},\n\t}));\n}\n\nasync function persistStartupTimeout(serverId, timeout) {\n\tawait updateServerConfig(serverId, { startupTimeout: timeout });\n\tlspApi.servers.update(serverId, (current) => ({\n\t\t...current,\n\t\tstartupTimeout: timeout,\n\t}));\n}\n\nasync function persistInitOptions(serverId, value) {\n\tawait updateServerConfig(serverId, { initializationOptions: value });\n\tlspApi.servers.update(serverId, (current) => ({\n\t\t...current,\n\t\tinitializationOptions: value,\n\t}));\n}\n\nexport default function lspServerDetail(serverId) {\n\tconst initialServer = lspApi.servers.get(serverId);\n\tif (!initialServer) {\n\t\ttoast(strings[\"lsp-server-not-found\"]);\n\t\treturn null;\n\t}\n\n\tconst initialSnapshot = {\n\t\tliveServer: initialServer,\n\t\tmerged: getMergedConfig(initialServer),\n\t\toverride: getServerOverride(serverId),\n\t\tdirectWebSocket: isDirectWebSocketServer(getMergedConfig(initialServer)),\n\t\tisCustom: isCustomServer(serverId),\n\t\tinstallResult: isDirectWebSocketServer(getMergedConfig(initialServer))\n\t\t\t? {\n\t\t\t\t\tstatus: \"unknown\",\n\t\t\t\t\tversion: null,\n\t\t\t\t\tcanInstall: false,\n\t\t\t\t\tcanUpdate: false,\n\t\t\t\t\tmessage: strings[\"lsp-websocket-server-managed-externally\"],\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tstatus: \"unknown\",\n\t\t\t\t\tversion: null,\n\t\t\t\t\tcanInstall: true,\n\t\t\t\t\tcanUpdate: true,\n\t\t\t\t\tmessage: strings[\"lsp-checking-installation-status\"],\n\t\t\t\t},\n\t\tbuiltinExts:\n\t\t\tgetMergedConfig(initialServer).clientConfig?.builtinExtensions || {},\n\t\tinstallCommand: getInstallCommand(\n\t\t\tgetMergedConfig(initialServer),\n\t\t\t\"install\",\n\t\t),\n\t\tupdateCommand: getInstallCommand(getMergedConfig(initialServer), \"update\"),\n\t\tuninstallCommand: getUninstallCommand(getMergedConfig(initialServer)),\n\t};\n\n\tconst items = createItems(initialSnapshot);\n\tconst itemsByKey = new Map(items.map((item) => [item.key, item]));\n\tconst page = settingsPage(\n\t\tinitialServer.label || initialServer.id,\n\t\titems,\n\t\tcallback,\n\t\tundefined,\n\t\t{\n\t\t\tpreserveOrder: true,\n\t\t\tpageClassName: \"detail-settings-page\",\n\t\t\tlistClassName: \"detail-settings-list\",\n\t\t\tvalueInTail: true,\n\t\t},\n\t);\n\n\tconst baseShow = page.show.bind(page);\n\n\treturn {\n\t\t...page,\n\t\tshow(goTo) {\n\t\t\tbaseShow(goTo);\n\t\t\tconst $list = document.querySelector(\"#settings .main.list\");\n\t\t\trefreshVisibleState($list, itemsByKey, serverId).catch(console.error);\n\t\t},\n\t};\n\n\tasync function callback(key, value) {\n\t\tconst $list = this?.parentElement;\n\t\tconst snapshot = await buildSnapshot(serverId);\n\t\tif (!snapshot) {\n\t\t\ttoast(strings[\"lsp-server-not-found\"]);\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (key) {\n\t\t\tcase \"enabled\":\n\t\t\t\tawait persistEnabled(serverId, value);\n\t\t\t\tif (!value) {\n\t\t\t\t\tstopManagedServer(serverId);\n\t\t\t\t}\n\t\t\t\ttoast(\n\t\t\t\t\tvalue\n\t\t\t\t\t\t? strings[\"lsp-server-enabled-toast\"]\n\t\t\t\t\t\t: strings[\"lsp-server-disabled-toast\"],\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"remove_custom_server\":\n\t\t\t\tif (\n\t\t\t\t\t!(await confirm(\n\t\t\t\t\t\tstrings[\"lsp-remove-custom-server\"],\n\t\t\t\t\t\tfillTemplate(strings[\"lsp-remove-custom-server-confirm\"], {\n\t\t\t\t\t\t\tserver: snapshot.liveServer.label || serverId,\n\t\t\t\t\t\t}),\n\t\t\t\t\t))\n\t\t\t\t) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstopManagedServer(serverId);\n\t\t\t\tawait removeCustomServer(serverId);\n\t\t\t\ttoast(strings[\"lsp-custom-server-removed\"]);\n\t\t\t\tpage.hide();\n\t\t\t\tappSettings.uiSettings[\"lsp-settings\"]?.show();\n\t\t\t\treturn;\n\n\t\t\tcase \"install_status\": {\n\t\t\t\tconst result = await checkServerInstallation(snapshot.merged);\n\t\t\t\tconst lines = [\n\t\t\t\t\tfillTemplate(strings[\"lsp-status-line\"], {\n\t\t\t\t\t\tstatus: formatInstallStatus(result),\n\t\t\t\t\t}),\n\t\t\t\t\tresult.version\n\t\t\t\t\t\t? fillTemplate(strings[\"lsp-version-line\"], {\n\t\t\t\t\t\t\t\tversion: result.version,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t: null,\n\t\t\t\t\tfillTemplate(strings[\"lsp-details-line\"], {\n\t\t\t\t\t\tdetails: formatInstallInfo(result),\n\t\t\t\t\t}),\n\t\t\t\t].filter(Boolean);\n\t\t\t\talert(strings[\"lsp-installation-status\"], lines.join(\"<br>\"));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"install_server\":\n\t\t\t\tif (!snapshot.installCommand) {\n\t\t\t\t\ttoast(strings[\"lsp-install-command-unavailable\"]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait installServer(snapshot.merged, \"install\");\n\t\t\t\tbreak;\n\n\t\t\tcase \"update_server\":\n\t\t\t\tif (!snapshot.updateCommand) {\n\t\t\t\t\ttoast(strings[\"lsp-update-command-unavailable\"]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait installServer(snapshot.merged, \"update\");\n\t\t\t\tbreak;\n\n\t\t\tcase \"uninstall_server\":\n\t\t\t\tif (!snapshot.uninstallCommand) {\n\t\t\t\t\ttoast(strings[\"lsp-uninstall-command-unavailable\"]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\t!(await confirm(\n\t\t\t\t\t\tstrings[\"lsp-uninstall-server\"],\n\t\t\t\t\t\tfillTemplate(strings[\"lsp-remove-installed-files\"], {\n\t\t\t\t\t\t\tserver: snapshot.liveServer.label || serverId,\n\t\t\t\t\t\t}),\n\t\t\t\t\t))\n\t\t\t\t) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tawait uninstallServer(snapshot.merged, { promptConfirm: false });\n\t\t\t\ttoast(strings[\"lsp-server-uninstalled\"]);\n\t\t\t\tbreak;\n\n\t\t\tcase \"startup_timeout\": {\n\t\t\t\tconst currentTimeout =\n\t\t\t\t\tsnapshot.override.startupTimeout ??\n\t\t\t\t\tsnapshot.liveServer.startupTimeout ??\n\t\t\t\t\t5000;\n\t\t\t\tconst result = await prompt(\n\t\t\t\t\tstrings[\"lsp-startup-timeout-ms\"],\n\t\t\t\t\tString(currentTimeout),\n\t\t\t\t\t\"number\",\n\t\t\t\t\t{\n\t\t\t\t\t\ttest: (val) => {\n\t\t\t\t\t\t\tconst timeout = Number.parseInt(String(val), 10);\n\t\t\t\t\t\t\treturn Number.isFinite(timeout) && timeout >= 1000;\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tif (result === null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst timeout = Number.parseInt(String(result), 10);\n\t\t\t\tif (!Number.isFinite(timeout) || timeout < 1000) {\n\t\t\t\t\ttoast(strings[\"lsp-invalid-timeout\"]);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tawait persistStartupTimeout(serverId, timeout);\n\t\t\t\ttoast(\n\t\t\t\t\tfillTemplate(strings[\"lsp-startup-timeout-set\"], {\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"edit_init_options\": {\n\t\t\t\tconst currentJson = JSON.stringify(\n\t\t\t\t\tsnapshot.override.initializationOptions || {},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t);\n\t\t\t\tconst result = await prompt(\n\t\t\t\t\tstrings[\"lsp-initialization-options-json\"],\n\t\t\t\t\tcurrentJson || \"{}\",\n\t\t\t\t\t\"textarea\",\n\t\t\t\t\t{\n\t\t\t\t\t\ttest: (val) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tJSON.parse(val);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tif (result === null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tawait persistInitOptions(serverId, JSON.parse(result));\n\t\t\t\ttoast(strings[\"lsp-initialization-options-updated\"]);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"view_init_options\": {\n\t\t\t\tconst json = JSON.stringify(\n\t\t\t\t\tsnapshot.merged.initializationOptions || {},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t);\n\t\t\t\talert(\n\t\t\t\t\tstrings[\"lsp-initialization-options\"],\n\t\t\t\t\t`<pre style=\"overflow: auto; max-height: 60vh; font-size: 12px;\">${escapeHtml(json)}</pre>`,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"ext_hover\":\n\t\t\tcase \"ext_completion\":\n\t\t\tcase \"ext_signature\":\n\t\t\tcase \"ext_diagnostics\":\n\t\t\tcase \"ext_inlayHints\":\n\t\t\tcase \"ext_formatting\": {\n\t\t\t\tconst extKey = key.replace(\"ext_\", \"\");\n\t\t\t\tconst feature = getFeatureItems().find(\n\t\t\t\t\t([featureKey]) => featureKey === key,\n\t\t\t\t);\n\t\t\t\tconst currentClientConfig = clone(snapshot.override.clientConfig || {});\n\t\t\t\tconst currentBuiltins = currentClientConfig.builtinExtensions || {};\n\n\t\t\t\tawait persistClientConfig(serverId, {\n\t\t\t\t\t...currentClientConfig,\n\t\t\t\t\tbuiltinExtensions: {\n\t\t\t\t\t\t...currentBuiltins,\n\t\t\t\t\t\t[extKey]: value,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\ttoast(\n\t\t\t\t\tfillTemplate(strings[\"lsp-feature-state-toast\"], {\n\t\t\t\t\t\tfeature: feature?.[2] || extKey,\n\t\t\t\t\t\tstate: value\n\t\t\t\t\t\t\t? strings[\"lsp-state-enabled\"]\n\t\t\t\t\t\t\t: strings[\"lsp-state-disabled\"],\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\tawait refreshVisibleState($list, itemsByKey, serverId);\n\t}\n}\n"
  },
  {
    "path": "src/settings/lspSettings.js",
    "content": "import { quoteArg } from \"cm/lsp/installRuntime\";\nimport serverRegistry from \"cm/lsp/serverRegistry\";\nimport settingsPage from \"components/settingsPage\";\nimport toast from \"components/toast\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport {\n\tgetServerOverride,\n\tnormalizeLanguages,\n\tnormalizeServerId,\n\tupsertCustomServer,\n} from \"./lspConfigUtils\";\nimport lspServerDetail from \"./lspServerDetail\";\n\nfunction parseArgsInput(value) {\n\tconst normalized = String(value || \"\").trim();\n\tif (!normalized) return [];\n\n\tconst parsed = JSON.parse(normalized);\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(strings[\"lsp-error-args-must-be-array\"]);\n\t}\n\treturn parsed.map((entry) => String(entry));\n}\n\nfunction normalizePackages(value) {\n\treturn String(value || \"\")\n\t\t.split(\",\")\n\t\t.map((entry) => entry.trim())\n\t\t.filter(Boolean);\n}\n\nfunction getInstallMethods() {\n\treturn [\n\t\t{ value: \"manual\", text: strings[\"lsp-install-method-manual\"] },\n\t\t{ value: \"apk\", text: strings[\"lsp-install-method-apk\"] },\n\t\t{ value: \"npm\", text: strings[\"lsp-install-method-npm\"] },\n\t\t{ value: \"pip\", text: strings[\"lsp-install-method-pip\"] },\n\t\t{ value: \"cargo\", text: strings[\"lsp-install-method-cargo\"] },\n\t\t{ value: \"shell\", text: strings[\"lsp-install-method-shell\"] },\n\t];\n}\n\nfunction getTransportMethods() {\n\treturn [\n\t\t{\n\t\t\tvalue: \"stdio\",\n\t\t\ttext:\n\t\t\t\tstrings[\"lsp-transport-method-stdio\"] ||\n\t\t\t\t\"STDIO (launch a binary command)\",\n\t\t},\n\t\t{\n\t\t\tvalue: \"websocket\",\n\t\t\ttext:\n\t\t\t\tstrings[\"lsp-transport-method-websocket\"] ||\n\t\t\t\t\"WebSocket (connect to a ws/wss URL)\",\n\t\t},\n\t];\n}\n\nfunction parseWebSocketUrl(value) {\n\tconst normalized = String(value || \"\").trim();\n\tif (!normalized) {\n\t\tthrow new Error(\n\t\t\tstrings[\"lsp-error-websocket-url-required\"] ||\n\t\t\t\t\"WebSocket URL is required\",\n\t\t);\n\t}\n\tif (!/^wss?:\\/\\//i.test(normalized)) {\n\t\tthrow new Error(\n\t\t\tstrings[\"lsp-error-websocket-url-invalid\"] ||\n\t\t\t\t\"WebSocket URL must start with ws:// or wss://\",\n\t\t);\n\t}\n\treturn normalized;\n}\n\nfunction buildDefaultCheckCommand(binaryCommand, installer) {\n\tconst executable = String(\n\t\tinstaller?.binaryPath || installer?.executable || binaryCommand || \"\",\n\t).trim();\n\tif (!executable) return \"\";\n\tif (installer?.kind === \"manual\" && installer?.binaryPath) {\n\t\treturn `test -x ${quoteArg(installer.binaryPath)}`;\n\t}\n\tif (executable.includes(\"/\")) {\n\t\treturn `test -x ${quoteArg(executable)}`;\n\t}\n\treturn `which ${quoteArg(executable)}`;\n}\n\nasync function promptInstaller(binaryCommand) {\n\tconst method = await select(\n\t\tstrings[\"lsp-install-method-title\"],\n\t\tgetInstallMethods(),\n\t);\n\tif (!method) return null;\n\n\tswitch (method) {\n\t\tcase \"manual\": {\n\t\t\tconst binaryPath = await prompt(\n\t\t\t\tstrings[\"lsp-binary-path-optional\"],\n\t\t\t\tString(binaryCommand || \"\").includes(\"/\") ? String(binaryCommand) : \"\",\n\t\t\t\t\"text\",\n\t\t\t);\n\t\t\tif (binaryPath === null) return null;\n\t\t\treturn {\n\t\t\t\tkind: \"manual\",\n\t\t\t\tsource: \"manual\",\n\t\t\t\texecutable: String(binaryCommand || \"\").trim() || undefined,\n\t\t\t\tbinaryPath: String(binaryPath || \"\").trim() || undefined,\n\t\t\t};\n\t\t}\n\t\tcase \"apk\":\n\t\tcase \"npm\":\n\t\tcase \"pip\":\n\t\tcase \"cargo\": {\n\t\t\tconst packagesInput = await prompt(\n\t\t\t\tstrings[\"lsp-packages-prompt\"].replace(\n\t\t\t\t\t\"{method}\",\n\t\t\t\t\tmethod.toUpperCase(),\n\t\t\t\t),\n\t\t\t\t\"\",\n\t\t\t\t\"text\",\n\t\t\t);\n\t\t\tif (packagesInput === null) return null;\n\t\t\tconst packages = normalizePackages(packagesInput);\n\t\t\tif (!packages.length) {\n\t\t\t\tthrow new Error(strings[\"lsp-error-package-required\"]);\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: method,\n\t\t\t\tsource: method,\n\t\t\t\texecutable: String(binaryCommand || \"\").trim() || undefined,\n\t\t\t\tpackages,\n\t\t\t};\n\t\t}\n\t\tcase \"shell\": {\n\t\t\tconst installCommand = await prompt(\n\t\t\t\tstrings[\"lsp-install-command\"],\n\t\t\t\t\"\",\n\t\t\t\t\"textarea\",\n\t\t\t);\n\t\t\tif (installCommand === null) return null;\n\t\t\tconst updateCommand = await prompt(\n\t\t\t\tstrings[\"lsp-update-command-optional\"],\n\t\t\t\tString(installCommand || \"\"),\n\t\t\t\t\"textarea\",\n\t\t\t);\n\t\t\tif (updateCommand === null) return null;\n\t\t\treturn {\n\t\t\t\tkind: \"shell\",\n\t\t\t\tsource: \"custom\",\n\t\t\t\texecutable: String(binaryCommand || \"\").trim() || undefined,\n\t\t\t\tcommand: String(installCommand || \"\").trim() || undefined,\n\t\t\t\tupdateCommand: String(updateCommand || \"\").trim() || undefined,\n\t\t\t};\n\t\t}\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\n/**\n * LSP Settings page - shows list of all language servers\n * @returns {object} Settings page interface\n */\nexport default function lspSettings() {\n\tconst title =\n\t\tstrings?.lsp_settings || strings[\"language servers\"] || \"Language Servers\";\n\tconst categories = {\n\t\tcustomServers: strings[\"settings-category-custom-servers\"],\n\t\tservers: strings[\"settings-category-servers\"],\n\t};\n\tlet page = createPage();\n\n\treturn {\n\t\tshow(goTo) {\n\t\t\tpage = createPage();\n\t\t\tpage.show(goTo);\n\t\t},\n\t\thide() {\n\t\t\tpage.hide();\n\t\t},\n\t\tsearch(key) {\n\t\t\tpage = createPage();\n\t\t\treturn page.search(key);\n\t\t},\n\t\trestoreList() {\n\t\t\tpage.restoreList();\n\t\t},\n\t\tsetTitle(nextTitle) {\n\t\t\tpage.setTitle(nextTitle);\n\t\t},\n\t};\n\n\tfunction createPage() {\n\t\tconst servers = serverRegistry.listServers();\n\n\t\tconst sortedServers = servers.sort((a, b) => {\n\t\t\tconst aEnabled = getServerOverride(a.id).enabled ?? a.enabled;\n\t\t\tconst bEnabled = getServerOverride(b.id).enabled ?? b.enabled;\n\n\t\t\tif (aEnabled !== bEnabled) {\n\t\t\t\treturn bEnabled ? 1 : -1;\n\t\t\t}\n\t\t\treturn a.label.localeCompare(b.label);\n\t\t});\n\n\t\tconst items = [\n\t\t\t{\n\t\t\t\tkey: \"add_custom_server\",\n\t\t\t\ttext: strings[\"lsp-add-custom-server\"],\n\t\t\t\tinfo: strings[\"settings-info-lsp-add-custom-server\"],\n\t\t\t\tcategory: categories.customServers,\n\t\t\t\tindex: 0,\n\t\t\t\tchevron: true,\n\t\t\t},\n\t\t];\n\n\t\tfor (const server of sortedServers) {\n\t\t\tconst source = server.launcher?.install?.source\n\t\t\t\t? ` • ${server.launcher.install.source}`\n\t\t\t\t: \"\";\n\t\t\tconst languagesList =\n\t\t\t\tArray.isArray(server.languages) && server.languages.length\n\t\t\t\t\t? `${server.languages.join(\", \")}${source}`\n\t\t\t\t\t: source.slice(3);\n\n\t\t\titems.push({\n\t\t\t\tkey: `server:${server.id}`,\n\t\t\t\ttext: server.label,\n\t\t\t\tinfo: languagesList || undefined,\n\t\t\t\tcategory: categories.servers,\n\t\t\t\tchevron: true,\n\t\t\t});\n\t\t}\n\n\t\titems.push({\n\t\t\tnote: strings[\"settings-note-lsp-settings\"],\n\t\t});\n\n\t\treturn settingsPage(title, items, callback, undefined, {\n\t\t\tpreserveOrder: true,\n\t\t\tpageClassName: \"detail-settings-page\",\n\t\t\tlistClassName: \"detail-settings-list\",\n\t\t\tgroupByDefault: true,\n\t\t});\n\t}\n\n\tfunction refreshVisiblePage() {\n\t\tpage.hide();\n\t\tpage = createPage();\n\t\tpage.show();\n\t}\n\n\tasync function callback(key) {\n\t\tif (key === \"add_custom_server\") {\n\t\t\ttry {\n\t\t\t\tconst idInput = await prompt(strings[\"lsp-server-id\"], \"\", \"text\");\n\t\t\t\tif (idInput === null) return;\n\n\t\t\t\tconst serverId = normalizeServerId(idInput);\n\t\t\t\tif (!serverId) {\n\t\t\t\t\ttoast(strings[\"lsp-error-server-id-required\"]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst label = await prompt(\n\t\t\t\t\tstrings[\"lsp-server-label\"],\n\t\t\t\t\tserverId,\n\t\t\t\t\t\"text\",\n\t\t\t\t);\n\t\t\t\tif (label === null) return;\n\n\t\t\t\tconst languageInput = await prompt(\n\t\t\t\t\tstrings[\"lsp-language-ids\"],\n\t\t\t\t\t\"\",\n\t\t\t\t\t\"text\",\n\t\t\t\t);\n\t\t\t\tif (languageInput === null) return;\n\t\t\t\tconst languages = normalizeLanguages(languageInput);\n\t\t\t\tif (!languages.length) {\n\t\t\t\t\ttoast(strings[\"lsp-error-language-id-required\"]);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst transportKind = await select(\n\t\t\t\t\tstrings.type || \"Type\",\n\t\t\t\t\tgetTransportMethods(),\n\t\t\t\t);\n\t\t\t\tif (!transportKind) return;\n\n\t\t\t\tlet transport;\n\t\t\t\tlet launcher;\n\n\t\t\t\tif (transportKind === \"websocket\") {\n\t\t\t\t\tconst websocketUrlInput = await prompt(\n\t\t\t\t\t\tstrings[\"lsp-websocket-url\"] || \"WebSocket URL\",\n\t\t\t\t\t\t\"ws://127.0.0.1:3000/\",\n\t\t\t\t\t\t\"text\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttest: (value) => {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tparseWebSocketUrl(value);\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tif (websocketUrlInput === null) return;\n\n\t\t\t\t\ttransport = {\n\t\t\t\t\t\tkind: \"websocket\",\n\t\t\t\t\t\turl: parseWebSocketUrl(websocketUrlInput),\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tconst binaryCommand = await prompt(\n\t\t\t\t\t\tstrings[\"lsp-binary-command\"],\n\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\"text\",\n\t\t\t\t\t);\n\t\t\t\t\tif (binaryCommand === null) return;\n\t\t\t\t\tif (!String(binaryCommand).trim()) {\n\t\t\t\t\t\ttoast(strings[\"lsp-error-binary-command-required\"]);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst argsInput = await prompt(\n\t\t\t\t\t\tstrings[\"lsp-binary-args\"],\n\t\t\t\t\t\t\"[]\",\n\t\t\t\t\t\t\"textarea\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttest: (value) => {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tparseArgsInput(value);\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tif (argsInput === null) return;\n\n\t\t\t\t\tconst parsedArgs = parseArgsInput(argsInput);\n\t\t\t\t\tconst installer = await promptInstaller(binaryCommand);\n\t\t\t\t\tif (installer === null) return;\n\t\t\t\t\tconst defaultCheckCommand = buildDefaultCheckCommand(\n\t\t\t\t\t\tbinaryCommand,\n\t\t\t\t\t\tinstaller,\n\t\t\t\t\t);\n\n\t\t\t\t\tconst checkCommand = await prompt(\n\t\t\t\t\t\tstrings[\"lsp-check-command-optional\"],\n\t\t\t\t\t\tdefaultCheckCommand,\n\t\t\t\t\t\t\"text\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tplaceholder: defaultCheckCommand || \"which my-language-server\",\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\tif (checkCommand === null) return;\n\n\t\t\t\t\ttransport = {\n\t\t\t\t\t\tkind: \"stdio\",\n\t\t\t\t\t\tcommand: String(binaryCommand).trim(),\n\t\t\t\t\t\targs: parsedArgs,\n\t\t\t\t\t};\n\t\t\t\t\tlauncher = {\n\t\t\t\t\t\tbridge: {\n\t\t\t\t\t\t\tkind: \"axs\",\n\t\t\t\t\t\t\tcommand: String(binaryCommand).trim(),\n\t\t\t\t\t\t\targs: parsedArgs,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcheckCommand: String(checkCommand || \"\").trim() || undefined,\n\t\t\t\t\t\tinstall: installer,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tawait upsertCustomServer(serverId, {\n\t\t\t\t\tlabel: String(label || \"\").trim() || serverId,\n\t\t\t\t\tlanguages,\n\t\t\t\t\ttransport,\n\t\t\t\t\tlauncher,\n\t\t\t\t\tenabled: true,\n\t\t\t\t});\n\n\t\t\t\ttoast(strings[\"lsp-custom-server-added\"]);\n\t\t\t\trefreshVisiblePage();\n\t\t\t\tconst detailPage = lspServerDetail(serverId);\n\t\t\t\tdetailPage?.show();\n\t\t\t} catch (error) {\n\t\t\t\ttoast(\n\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: strings[\"lsp-error-add-server-failed\"],\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (key.startsWith(\"server:\")) {\n\t\t\tconst id = key.split(\":\")[1];\n\t\t\tconst detailPage = lspServerDetail(id);\n\t\t\tif (detailPage) {\n\t\t\t\tdetailPage.show();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/settings/mainSettings.js",
    "content": "import settingsPage from \"components/settingsPage\";\nimport confirm from \"dialogs/confirm\";\nimport rateBox from \"dialogs/rateBox\";\nimport actionStack from \"lib/actionStack\";\nimport openFile from \"lib/openFile\";\nimport removeAds from \"lib/removeAds\";\nimport appSettings from \"lib/settings\";\nimport settings from \"lib/settings\";\nimport openAdRewardsPage from \"pages/adRewards\";\nimport Changelog from \"pages/changelog/changelog\";\nimport plugins from \"pages/plugins\";\nimport Sponsors from \"pages/sponsors\";\nimport themeSetting from \"pages/themeSetting\";\nimport helpers from \"utils/helpers\";\nimport About from \"../pages/about\";\nimport otherSettings from \"./appSettings\";\nimport backupRestore from \"./backupRestore\";\nimport editorSettings from \"./editorSettings\";\nimport filesSettings from \"./filesSettings\";\nimport formatterSettings from \"./formatterSettings\";\nimport lspSettings from \"./lspSettings\";\nimport previewSettings from \"./previewSettings\";\nimport scrollSettings from \"./scrollSettings\";\nimport searchSettings from \"./searchSettings\";\nimport terminalSettings from \"./terminalSettings\";\n\nexport default function mainSettings() {\n\tconst title = strings.settings.capitalize();\n\tconst categories = {\n\t\tcore: strings[\"settings-category-core\"],\n\t\tcustomizationTools: strings[\"settings-category-customization-tools\"],\n\t\tmaintenance: strings[\"settings-category-maintenance\"],\n\t\taboutAcode: strings[\"settings-category-about-acode\"],\n\t\tsupportAcode: strings[\"settings-category-support-acode\"],\n\t};\n\tconst items = [\n\t\t{\n\t\t\tkey: \"app-settings\",\n\t\t\ttext: strings[\"app settings\"],\n\t\t\ticon: \"tune\",\n\t\t\tinfo: strings[\"settings-info-main-app-settings\"],\n\t\t\tcategory: categories.core,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"editor-settings\",\n\t\t\ttext: strings[\"editor settings\"],\n\t\t\ticon: \"text_format\",\n\t\t\tinfo: strings[\"settings-info-main-editor-settings\"],\n\t\t\tcategory: categories.core,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"terminal-settings\",\n\t\t\ttext: `${strings[\"terminal settings\"]}`,\n\t\t\ticon: \"terminal\",\n\t\t\tinfo: strings[\"settings-info-main-terminal-settings\"],\n\t\t\tcategory: categories.core,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"preview-settings\",\n\t\t\ttext: strings[\"preview settings\"],\n\t\t\ticon: \"public\",\n\t\t\tinfo: strings[\"settings-info-main-preview-settings\"],\n\t\t\tcategory: categories.core,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"formatter\",\n\t\t\ttext: strings.formatter,\n\t\t\ticon: \"spellcheck\",\n\t\t\tinfo: strings[\"settings-info-main-formatter\"],\n\t\t\tcategory: categories.customizationTools,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"theme\",\n\t\t\ttext: strings.theme,\n\t\t\ticon: \"color_lenspalette\",\n\t\t\tinfo: strings[\"settings-info-main-theme\"],\n\t\t\tcategory: categories.customizationTools,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"plugins\",\n\t\t\ttext: strings[\"plugins\"],\n\t\t\ticon: \"extension\",\n\t\t\tinfo: strings[\"settings-info-main-plugins\"],\n\t\t\tcategory: categories.customizationTools,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"lsp-settings\",\n\t\t\ttext:\n\t\t\t\tstrings?.lsp_settings ||\n\t\t\t\tstrings[\"language servers\"] ||\n\t\t\t\t\"Language servers\",\n\t\t\ticon: \"zap\",\n\t\t\tinfo: strings[\"settings-info-main-lsp-settings\"],\n\t\t\tcategory: categories.customizationTools,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"backup-restore\",\n\t\t\ttext: `${strings.backup.capitalize()} & ${strings.restore.capitalize()}`,\n\t\t\ticon: \"cached\",\n\t\t\tinfo: strings[\"settings-info-main-backup-restore\"],\n\t\t\tcategory: categories.maintenance,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"editSettings\",\n\t\t\ttext: `${strings[\"edit\"]} settings.json`,\n\t\t\ticon: \"edit\",\n\t\t\tinfo: strings[\"settings-info-main-edit-settings\"],\n\t\t\tcategory: categories.maintenance,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"reset\",\n\t\t\ttext: strings[\"restore default settings\"],\n\t\t\ticon: \"historyrestore\",\n\t\t\tinfo: strings[\"settings-info-main-reset\"],\n\t\t\tcategory: categories.maintenance,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"about\",\n\t\t\ttext: strings.about,\n\t\t\ticon: \"info\",\n\t\t\tinfo: `Version ${BuildInfo.version}`,\n\t\t\tcategory: categories.aboutAcode,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"sponsors\",\n\t\t\ttext: strings.sponsor,\n\t\t\ticon: \"favorite\",\n\t\t\tinfo: strings[\"settings-info-main-sponsors\"],\n\t\t\tcategory: categories.aboutAcode,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"changeLog\",\n\t\t\ttext: `${strings[\"changelog\"]}`,\n\t\t\ticon: \"update\",\n\t\t\tinfo: strings[\"settings-info-main-changelog\"],\n\t\t\tcategory: categories.aboutAcode,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"rateapp\",\n\t\t\ttext: strings[\"rate acode\"],\n\t\t\ticon: \"star_outline\",\n\t\t\tinfo: strings[\"settings-info-main-rateapp\"],\n\t\t\tcategory: categories.aboutAcode,\n\t\t\tchevron: true,\n\t\t},\n\t];\n\n\tif (IS_FREE_VERSION) {\n\t\titems.push({\n\t\t\tkey: \"adRewards\",\n\t\t\ttext: strings[\"earn ad-free time\"],\n\t\t\ticon: \"play_arrow\",\n\t\t\tinfo: strings[\"settings-info-main-ad-rewards\"],\n\t\t\tcategory: categories.supportAcode,\n\t\t\tchevron: true,\n\t\t});\n\t\titems.push({\n\t\t\tkey: \"removeads\",\n\t\t\ttext: strings[\"remove ads\"],\n\t\t\ticon: \"block\",\n\t\t\tinfo: strings[\"settings-info-main-remove-ads\"],\n\t\t\tcategory: categories.supportAcode,\n\t\t\tchevron: true,\n\t\t});\n\t}\n\n\t/**\n\t * Callback for settings page for handling click event\n\t * @this {HTMLElement}\n\t * @param {string} key\n\t */\n\tasync function callback(key) {\n\t\tswitch (key) {\n\t\t\tcase \"app-settings\":\n\t\t\tcase \"backup-restore\":\n\t\t\tcase \"editor-settings\":\n\t\t\tcase \"preview-settings\":\n\t\t\tcase \"terminal-settings\":\n\t\t\tcase \"lsp-settings\":\n\t\t\t\tappSettings.uiSettings[key].show();\n\t\t\t\tbreak;\n\n\t\t\tcase \"theme\":\n\t\t\t\tthemeSetting();\n\t\t\t\tbreak;\n\n\t\t\tcase \"about\":\n\t\t\t\tAbout();\n\t\t\t\tbreak;\n\n\t\t\tcase \"sponsors\":\n\t\t\t\tSponsors();\n\t\t\t\tbreak;\n\n\t\t\tcase \"rateapp\":\n\t\t\t\trateBox();\n\t\t\t\tbreak;\n\n\t\t\tcase \"plugins\":\n\t\t\t\tplugins();\n\t\t\t\tbreak;\n\n\t\t\tcase \"adRewards\":\n\t\t\t\topenAdRewardsPage();\n\t\t\t\tbreak;\n\n\t\t\tcase \"formatter\":\n\t\t\t\tformatterSettings();\n\t\t\t\tbreak;\n\n\t\t\tcase \"editSettings\": {\n\t\t\t\tactionStack.pop();\n\t\t\t\topenFile(settings.settingsFile);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"reset\":\n\t\t\t\tconst confirmation = await confirm(\n\t\t\t\t\tstrings.warning,\n\t\t\t\t\tstrings[\"restore default settings\"],\n\t\t\t\t);\n\t\t\t\tif (confirmation) {\n\t\t\t\t\tawait appSettings.reset();\n\t\t\t\t\tlocation.reload();\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"removeads\":\n\t\t\t\ttry {\n\t\t\t\t\tawait removeAds();\n\t\t\t\t\tthis.remove();\n\t\t\t\t} catch (error) {\n\t\t\t\t\thelpers.error(error);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"changeLog\":\n\t\t\t\tChangelog();\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst page = settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"main-settings-page\",\n\t\tlistClassName: \"main-settings-list\",\n\t});\n\tpage.show();\n\n\tappSettings.uiSettings[\"main-settings\"] = page;\n\tappSettings.uiSettings[\"app-settings\"] = otherSettings();\n\tappSettings.uiSettings[\"file-settings\"] = filesSettings();\n\tappSettings.uiSettings[\"backup-restore\"] = backupRestore();\n\tappSettings.uiSettings[\"editor-settings\"] = editorSettings();\n\tappSettings.uiSettings[\"scroll-settings\"] = scrollSettings();\n\tappSettings.uiSettings[\"search-settings\"] = searchSettings();\n\tappSettings.uiSettings[\"preview-settings\"] = previewSettings();\n\tappSettings.uiSettings[\"terminal-settings\"] = terminalSettings();\n\tappSettings.uiSettings[\"lsp-settings\"] = lspSettings();\n}\n"
  },
  {
    "path": "src/settings/previewSettings.js",
    "content": "import settingsPage from \"components/settingsPage\";\nimport appSettings from \"lib/settings\";\n\nexport default function previewSettings() {\n\tconst values = appSettings.value;\n\tconst title = strings[\"preview settings\"];\n\tconst categories = {\n\t\tserver: strings[\"settings-category-server\"],\n\t\tpreview: strings[\"settings-category-preview\"],\n\t};\n\tconst PORT_REGEX =\n\t\t/^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;\n\tconst items = [\n\t\t{\n\t\t\tkey: \"previewPort\",\n\t\t\ttext: strings[\"preview port\"],\n\t\t\tvalue: values.previewPort,\n\t\t\tprompt: strings[\"preview port\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\treturn PORT_REGEX.test(value);\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-preview-preview-port\"],\n\t\t\tcategory: categories.server,\n\t\t},\n\t\t{\n\t\t\tkey: \"serverPort\",\n\t\t\ttext: strings[\"server port\"],\n\t\t\tvalue: values.serverPort,\n\t\t\tprompt: strings[\"server port\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\treturn PORT_REGEX.test(value);\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-preview-server-port\"],\n\t\t\tcategory: categories.server,\n\t\t},\n\t\t{\n\t\t\tkey: \"previewMode\",\n\t\t\ttext: strings[\"preview mode\"],\n\t\t\tvalue: values.previewMode,\n\t\t\tselect: [\n\t\t\t\t[appSettings.PREVIEW_MODE_BROWSER, strings.browser],\n\t\t\t\t[appSettings.PREVIEW_MODE_INAPP, strings.inapp],\n\t\t\t],\n\t\t\tinfo: strings[\"settings-info-preview-mode\"],\n\t\t\tcategory: categories.server,\n\t\t},\n\t\t{\n\t\t\tkey: \"host\",\n\t\t\ttext: strings.host,\n\t\t\tvalue: values.host,\n\t\t\tprompt: strings.host,\n\t\t\tpromptType: \"text\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnew URL(`http://${value}:${values.previewPort}`);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"settings-info-preview-host\"],\n\t\t\tcategory: categories.server,\n\t\t},\n\t\t{\n\t\t\tkey: \"disableCache\",\n\t\t\ttext: strings[\"disable in-app-browser caching\"],\n\t\t\tcheckbox: values.disableCache,\n\t\t\tinfo: strings[\"settings-info-preview-disable-cache\"],\n\t\t\tcategory: categories.preview,\n\t\t},\n\t\t{\n\t\t\tkey: \"useCurrentFileForPreview\",\n\t\t\ttext: strings[\"should_use_current_file_for_preview\"],\n\t\t\tcheckbox: !!values.useCurrentFileForPreview,\n\t\t\tinfo: strings[\"settings-info-preview-use-current-file\"],\n\t\t\tcategory: categories.preview,\n\t\t},\n\t\t{\n\t\t\tkey: \"showConsoleToggler\",\n\t\t\ttext: strings[\"show console toggler\"],\n\t\t\tcheckbox: values.showConsoleToggler,\n\t\t\tinfo: strings[\"settings-info-preview-show-console-toggler\"],\n\t\t\tcategory: categories.preview,\n\t\t},\n\t\t{\n\t\t\tnote: strings[\"preview settings note\"],\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tinfoAsDescription: true,\n\t\tvalueInTail: true,\n\t});\n\n\tfunction callback(key, value) {\n\t\tappSettings.update({\n\t\t\t[key]: value,\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/settings/scrollSettings.js",
    "content": "import settingsPage from \"components/settingsPage\";\nimport constants from \"lib/constants\";\nimport appSettings from \"lib/settings\";\n\nexport default function scrollSettings() {\n\tconst values = appSettings.value;\n\tconst title = strings[\"scroll settings\"];\n\n\tconst items = [\n\t\t/*{\n\t\t\tkey: \"scrollSpeed\",\n\t\t\ttext: strings[\"scroll speed\"],\n\t\t\tvalue: values.scrollSpeed,\n\t\t\tvalueText: getScrollSpeedString,\n\t\t\tselect: [\n\t\t\t\t[constants.SCROLL_SPEED_FAST_X2, `${strings.fast} x2`],\n\t\t\t\t[constants.SCROLL_SPEED_FAST, strings.fast],\n\t\t\t\t[constants.SCROLL_SPEED_NORMAL, strings.normal],\n\t\t\t\t[constants.SCROLL_SPEED_SLOW, strings.slow],\n\t\t\t],\n\t\t},*/\n\t\t/*{\n\t\t\tkey: \"reverseScrolling\",\n\t\t\ttext: strings[\"reverse scrolling\"],\n\t\t\tcheckbox: values.reverseScrolling,\n\t\t},*/\n\t\t/*{\n\t\t\tkey: \"diagonalScrolling\",\n\t\t\ttext: strings[\"diagonal scrolling\"],\n\t\t\tcheckbox: values.diagonalScrolling,\n\t\t},*/\n\t\t{\n\t\t\tkey: \"scrollbarSize\",\n\t\t\ttext: strings[\"scrollbar size\"],\n\t\t\tvalue: values.scrollbarSize,\n\t\t\tvalueText: (size) => `${size}px`,\n\t\t\tselect: [5, 10, 15, 20],\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tinfoAsDescription: true,\n\t\tvalueInTail: true,\n\t\tgroupByDefault: true,\n\t});\n\n\tfunction callback(key, value) {\n\t\tappSettings.update({\n\t\t\t[key]: value,\n\t\t});\n\t}\n}\n\nfunction getScrollSpeedString(speed) {\n\tswitch (speed) {\n\t\tcase constants.SCROLL_SPEED_FAST:\n\t\t\treturn strings.fast;\n\t\tcase constants.SCROLL_SPEED_SLOW:\n\t\t\treturn strings.slow;\n\t\tcase constants.SCROLL_SPEED_FAST_X2:\n\t\t\treturn `${strings.fast} x2`;\n\t\tcase constants.SCROLL_SPEED_NORMAL:\n\t\t\treturn strings.normal;\n\t\tdefault:\n\t\t\treturn strings.normal;\n\t}\n}\n"
  },
  {
    "path": "src/settings/searchSettings.js",
    "content": "import settingsPage from \"../components/settingsPage\";\nimport appSettings from \"../lib/settings\";\n\nexport default function searchSettings() {\n\tconst title = strings.search;\n\tconst values = appSettings.value.search;\n\tconst items = [\n\t\t{\n\t\t\tkey: \"caseSensitive\",\n\t\t\ttext: strings[\"case sensitive\"],\n\t\t\tcheckbox: values.caseSensitive,\n\t\t},\n\t\t{\n\t\t\tkey: \"regExp\",\n\t\t\ttext: strings[\"regular expression\"],\n\t\t\tcheckbox: values.regExp,\n\t\t},\n\t\t{\n\t\t\tkey: \"wholeWord\",\n\t\t\ttext: strings[\"whole word\"],\n\t\t\tcheckbox: values.wholeWord,\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tgroupByDefault: true,\n\t});\n\n\tfunction callback(key, value) {\n\t\tvalues[key] = value;\n\t\tappSettings.update();\n\t}\n}\n"
  },
  {
    "path": "src/settings/terminalSettings.js",
    "content": "import fsOperation from \"fileSystem\";\nimport settingsPage from \"components/settingsPage\";\nimport {\n\tDEFAULT_TERMINAL_SETTINGS,\n\tTerminalThemeManager,\n} from \"components/terminal\";\nimport toast from \"components/toast\";\nimport alert from \"dialogs/alert\";\nimport confirm from \"dialogs/confirm\";\nimport loader from \"dialogs/loader\";\nimport fonts from \"lib/fonts\";\nimport appSettings from \"lib/settings\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport helpers from \"utils/helpers\";\n\nexport default function terminalSettings() {\n\tconst title = strings[\"terminal settings\"];\n\tconst values = appSettings.value;\n\tconst categories = {\n\t\tpermissions: strings[\"settings-category-permissions\"],\n\t\tdisplay: strings[\"settings-category-display\"],\n\t\tcursor: strings[\"settings-category-cursor\"],\n\t\tsession: strings[\"settings-category-session\"],\n\t\tmaintenance: strings[\"settings-category-maintenance\"],\n\t};\n\n\t// Initialize terminal settings with defaults if not present\n\tif (!values.terminalSettings) {\n\t\tvalues.terminalSettings = {\n\t\t\t...DEFAULT_TERMINAL_SETTINGS,\n\t\t\tfontFamily:\n\t\t\t\tDEFAULT_TERMINAL_SETTINGS.fontFamily || appSettings.value.fontFamily,\n\t\t};\n\t}\n\n\tconst terminalValues = values.terminalSettings;\n\n\tconst items = [\n\t\t{\n\t\t\tkey: \"all_file_access\",\n\t\t\ttext: strings[\"allFileAccess\"],\n\t\t\tinfo: strings[\"info-all_file_access\"],\n\t\t\tcategory: categories.permissions,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"fontSize\",\n\t\t\ttext: strings[\"font size\"],\n\t\t\tvalue: terminalValues.fontSize,\n\t\t\tprompt: strings[\"font size\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\tvalue = Number.parseInt(value);\n\t\t\t\t\treturn value >= 8 && value <= 32;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"info-fontSize\"],\n\t\t\tcategory: categories.display,\n\t\t},\n\t\t{\n\t\t\tkey: \"fontFamily\",\n\t\t\ttext: strings[\"terminal:font family\"],\n\t\t\tvalue: terminalValues.fontFamily,\n\t\t\tget select() {\n\t\t\t\treturn fonts.getNames();\n\t\t\t},\n\t\t\tinfo: strings[\"info-fontFamily\"],\n\t\t\tcategory: categories.display,\n\t\t},\n\t\t{\n\t\t\tkey: \"theme\",\n\t\t\ttext: strings[\"theme\"],\n\t\t\tvalue: terminalValues.theme,\n\t\t\tinfo: strings[\"info-theme\"],\n\t\t\tget select() {\n\t\t\t\treturn TerminalThemeManager.getThemeNames().map((name) => [\n\t\t\t\t\tname,\n\t\t\t\t\tname.charAt(0).toUpperCase() + name.slice(1),\n\t\t\t\t]);\n\t\t\t},\n\t\t\tvalueText(value) {\n\t\t\t\tconst option = this.select.find(([v]) => v === value);\n\t\t\t\treturn option ? option[1] : value;\n\t\t\t},\n\t\t\tcategory: categories.display,\n\t\t},\n\t\t{\n\t\t\tkey: \"fontWeight\",\n\t\t\ttext: strings[\"terminal:font weight\"],\n\t\t\tvalue: terminalValues.fontWeight,\n\t\t\tselect: [\n\t\t\t\t\"normal\",\n\t\t\t\t\"bold\",\n\t\t\t\t\"100\",\n\t\t\t\t\"200\",\n\t\t\t\t\"300\",\n\t\t\t\t\"400\",\n\t\t\t\t\"500\",\n\t\t\t\t\"600\",\n\t\t\t\t\"700\",\n\t\t\t\t\"800\",\n\t\t\t\t\"900\",\n\t\t\t],\n\t\t\tinfo: strings[\"info-fontWeight\"],\n\t\t\tcategory: categories.display,\n\t\t},\n\t\t{\n\t\t\tkey: \"letterSpacing\",\n\t\t\ttext: strings[\"letter spacing\"],\n\t\t\tvalue: terminalValues.letterSpacing,\n\t\t\tprompt: strings[\"letter spacing\"],\n\t\t\tpromptType: \"number\",\n\t\t\tinfo: strings[\"info-letterSpacing\"],\n\t\t\tcategory: categories.display,\n\t\t},\n\t\t{\n\t\t\tkey: \"fontLigatures\",\n\t\t\ttext: strings[\"font ligatures\"],\n\t\t\tcheckbox: terminalValues.fontLigatures,\n\t\t\tinfo: strings[\"info-fontLigatures\"],\n\t\t\tcategory: categories.display,\n\t\t},\n\t\t{\n\t\t\tkey: \"cursorStyle\",\n\t\t\ttext: strings[\"terminal:cursor style\"],\n\t\t\tvalue: terminalValues.cursorStyle,\n\t\t\tselect: [\"block\", \"underline\", \"bar\"],\n\t\t\tinfo: strings[\"info-cursorStyle\"],\n\t\t\tcategory: categories.cursor,\n\t\t},\n\t\t{\n\t\t\tkey: \"cursorInactiveStyle\",\n\t\t\ttext: strings[\"terminal:cursor inactive style\"],\n\t\t\tvalue: terminalValues.cursorInactiveStyle,\n\t\t\tselect: [\"outline\", \"block\", \"bar\", \"underline\", \"none\"],\n\t\t\tinfo: strings[\"info-cursorInactiveStyle\"],\n\t\t\tcategory: categories.cursor,\n\t\t},\n\t\t{\n\t\t\tkey: \"cursorBlink\",\n\t\t\ttext: strings[\"terminal:cursor blink\"],\n\t\t\tcheckbox: terminalValues.cursorBlink,\n\t\t\tinfo: strings[\"info-cursorBlink\"],\n\t\t\tcategory: categories.cursor,\n\t\t},\n\t\t{\n\t\t\tkey: \"scrollback\",\n\t\t\ttext: strings[\"terminal:scrollback\"],\n\t\t\tvalue: terminalValues.scrollback,\n\t\t\tprompt: strings[\"terminal:scrollback\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\tvalue = Number.parseInt(value);\n\t\t\t\t\treturn value >= 100 && value <= 10000;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"info-scrollback\"],\n\t\t\tcategory: categories.session,\n\t\t},\n\t\t{\n\t\t\tkey: \"tabStopWidth\",\n\t\t\ttext: strings[\"terminal:tab stop width\"],\n\t\t\tvalue: terminalValues.tabStopWidth,\n\t\t\tprompt: strings[\"terminal:tab stop width\"],\n\t\t\tpromptType: \"number\",\n\t\t\tpromptOptions: {\n\t\t\t\ttest(value) {\n\t\t\t\t\tvalue = Number.parseInt(value);\n\t\t\t\t\treturn value >= 1 && value <= 8;\n\t\t\t\t},\n\t\t\t},\n\t\t\tinfo: strings[\"info-tabStopWidth\"],\n\t\t\tcategory: categories.session,\n\t\t},\n\t\t{\n\t\t\tkey: \"convertEol\",\n\t\t\ttext: strings[\"terminal:convert eol\"],\n\t\t\tcheckbox: terminalValues.convertEol,\n\t\t\tinfo: strings[\"settings-info-terminal-convert-eol\"],\n\t\t\tcategory: categories.session,\n\t\t},\n\t\t{\n\t\t\tkey: \"imageSupport\",\n\t\t\ttext: strings[\"terminal:image support\"],\n\t\t\tcheckbox: terminalValues.imageSupport,\n\t\t\tinfo: strings[\"info-imageSupport\"],\n\t\t\tcategory: categories.session,\n\t\t},\n\t\t{\n\t\t\tkey: \"confirmTabClose\",\n\t\t\ttext: strings[\"terminal:confirm tab close\"],\n\t\t\tcheckbox: terminalValues.confirmTabClose !== false,\n\t\t\tinfo: strings[\"info-confirmTabClose\"],\n\t\t\tcategory: categories.session,\n\t\t},\n\t\t{\n\t\t\tkey: \"backup\",\n\t\t\ttext: strings.backup,\n\t\t\tinfo: strings[\"info-backup\"],\n\t\t\tcategory: categories.maintenance,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"restore\",\n\t\t\ttext: strings.restore,\n\t\t\tinfo: strings[\"info-restore\"],\n\t\t\tcategory: categories.maintenance,\n\t\t\tchevron: true,\n\t\t},\n\t\t{\n\t\t\tkey: \"uninstall\",\n\t\t\ttext: strings.uninstall,\n\t\t\tinfo: strings[\"info-uninstall\"],\n\t\t\tcategory: categories.maintenance,\n\t\t\tchevron: true,\n\t\t},\n\t];\n\n\treturn settingsPage(title, items, callback, undefined, {\n\t\tpreserveOrder: true,\n\t\tpageClassName: \"detail-settings-page\",\n\t\tlistClassName: \"detail-settings-list\",\n\t\tinfoAsDescription: true,\n\t\tvalueInTail: true,\n\t});\n\n\t/**\n\t * Callback for settings page when an item is clicked\n\t * @param {string} key\n\t * @param {string} value\n\t */\n\tasync function callback(key, value) {\n\t\tswitch (key) {\n\t\t\tcase \"all_file_access\":\n\t\t\t\tif (ANDROID_SDK_INT >= 30) {\n\t\t\t\t\tsystem.isManageExternalStorageDeclared((boolStr) => {\n\t\t\t\t\t\tif (boolStr === \"true\") {\n\t\t\t\t\t\t\tsystem.requestStorageManager(console.log, console.error);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\talert(strings[\"feature not available\"]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}, alert);\n\t\t\t\t} else {\n\t\t\t\t\talert(strings[\"feature not available\"]);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\tcase \"backup\":\n\t\t\t\tterminalBackup();\n\t\t\t\treturn;\n\n\t\t\tcase \"restore\":\n\t\t\t\tterminalRestore();\n\t\t\t\treturn;\n\n\t\t\tcase \"uninstall\":\n\t\t\t\tconst confirmation = await confirm(\n\t\t\t\t\tstrings.confirm,\n\t\t\t\t\t\"Are you sure you want to uninstall the terminal?\",\n\t\t\t\t);\n\t\t\t\tif (confirmation) {\n\t\t\t\t\tloader.showTitleLoader();\n\t\t\t\t\tTerminal.uninstall()\n\t\t\t\t\t\t.then(() => {\n\t\t\t\t\t\t\tloader.removeTitleLoader();\n\t\t\t\t\t\t\talert(\n\t\t\t\t\t\t\t\tstrings.success.toUpperCase(),\n\t\t\t\t\t\t\t\t\"Terminal uninstalled successfully.\",\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch((error) => {\n\t\t\t\t\t\t\tloader.removeTitleLoader();\n\t\t\t\t\t\t\tconsole.error(\"Terminal uninstall failed:\", error);\n\t\t\t\t\t\t\thelpers.error(error);\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\n\t\t\tdefault:\n\t\t\t\tappSettings.update({\n\t\t\t\t\tterminalSettings: {\n\t\t\t\t\t\t...values.terminalSettings,\n\t\t\t\t\t\t[key]: value,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\t// Update any active terminal instances\n\t\t\t\tupdateActiveTerminals(key, value);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Creates a backup of the terminal installation\n\t */\n\tasync function terminalBackup() {\n\t\ttry {\n\t\t\t// Ask user to select backup location\n\t\t\tconst { url } = await FileBrowser(\"folder\", strings[\"select folder\"]);\n\n\t\t\tloader.showTitleLoader();\n\n\t\t\t// Create backup\n\t\t\tconst backupPath = await Terminal.backup();\n\t\t\tawait system.copyToUri(\n\t\t\t\tbackupPath,\n\t\t\t\turl,\n\t\t\t\t\"aterm_backup.tar\",\n\t\t\t\tconsole.log,\n\t\t\t\tconsole.error,\n\t\t\t);\n\t\t\tloader.removeTitleLoader();\n\t\t\talert(strings.success.toUpperCase(), `${strings[\"backup successful\"]}.`);\n\t\t} catch (error) {\n\t\t\tloader.removeTitleLoader();\n\t\t\tconsole.error(\"Terminal backup failed:\", error);\n\t\t\ttoast(error.toString());\n\t\t}\n\t}\n\n\t/**\n\t * Restores terminal installation\n\t */\n\tasync function terminalRestore() {\n\t\ttry {\n\t\t\tawait Executor.execute(\"rm -rf $PREFIX/aterm_backup.*\");\n\n\t\t\tsdcard.openDocumentFile(\n\t\t\t\tasync (data) => {\n\t\t\t\t\tloader.showTitleLoader();\n\t\t\t\t\t//this will create a file at $PREFIX/atem_backup.tar.tar\n\t\t\t\t\tawait system.copyToUri(\n\t\t\t\t\t\tdata.uri,\n\t\t\t\t\t\tcordova.file.dataDirectory,\n\t\t\t\t\t\t\"aterm_backup.tar\",\n\t\t\t\t\t\tconsole.log,\n\t\t\t\t\t\tconsole.error,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Restore\n\t\t\t\t\tawait Terminal.restore();\n\n\t\t\t\t\t//Cleanup restore file\n\t\t\t\t\tawait Executor.execute(\"rm -rf $PREFIX/aterm_backup.*\");\n\n\t\t\t\t\tloader.removeTitleLoader();\n\t\t\t\t\talert(\n\t\t\t\t\t\tstrings.success.toUpperCase(),\n\t\t\t\t\t\t\"Terminal restored successfully\",\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\ttoast,\n\t\t\t\t\"application/x-tar\",\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tloader.removeTitleLoader();\n\t\t\tconsole.error(\"Terminal restore failed:\", error);\n\t\t\ttoast(error.toString());\n\t\t}\n\t}\n}\n\n/**\n * Update active terminal instances with new settings\n * @param {string} key\n * @param {any} value\n */\nexport async function updateActiveTerminals(key, value) {\n\t// Find all terminal tabs and update their settings\n\tconst terminalTabs = editorManager.files.filter(\n\t\t(file) => file.type === \"terminal\",\n\t);\n\n\tterminalTabs.forEach(async (tab) => {\n\t\tif (tab.terminalComponent) {\n\t\t\tconst terminalOptions = {};\n\n\t\t\tswitch (key) {\n\t\t\t\tcase \"fontSize\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.fontSize = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"fontFamily\":\n\t\t\t\t\t// Load font if it's not already loaded\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait fonts.loadFont(value);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.warn(`Failed to load font ${value}:`, error);\n\t\t\t\t\t}\n\t\t\t\t\ttab.terminalComponent.terminal.options.fontFamily = value;\n\t\t\t\t\ttab.terminalComponent.terminal.refresh(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\ttab.terminalComponent.terminal.rows - 1,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"fontWeight\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.fontWeight = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"cursorBlink\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.cursorBlink = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"cursorStyle\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.cursorStyle = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"cursorInactiveStyle\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.cursorInactiveStyle = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"scrollback\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.scrollback = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tabStopWidth\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.tabStopWidth = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"convertEol\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.convertEol = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"letterSpacing\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.letterSpacing = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"theme\":\n\t\t\t\t\ttab.terminalComponent.terminal.options.theme =\n\t\t\t\t\t\tTerminalThemeManager.getTheme(value);\n\t\t\t\t\t// Update container background to match new theme\n\t\t\t\t\tif (tab.terminalComponent.container) {\n\t\t\t\t\t\ttab.terminalComponent.container.style.background =\n\t\t\t\t\t\t\ttab.terminalComponent.terminal.options.theme.background;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"imageSupport\":\n\t\t\t\t\ttab.terminalComponent.updateImageSupport(value);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"fontLigatures\":\n\t\t\t\t\ttab.terminalComponent.updateFontLigatures(value);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n}\n"
  },
  {
    "path": "src/sidebarApps/extensions/index.js",
    "content": "import \"./style.scss\";\n\nimport fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport collapsableList from \"components/collapsableList\";\nimport Sidebar from \"components/sidebar\";\nimport alert from \"dialogs/alert\";\nimport prompt from \"dialogs/prompt\";\nimport select from \"dialogs/select\";\nimport purchaseListener from \"handlers/purchase\";\nimport constants from \"lib/constants\";\nimport InstallState from \"lib/installState\";\nimport loadPlugin from \"lib/loadPlugin\";\nimport settings from \"lib/settings\";\nimport FileBrowser from \"pages/fileBrowser\";\nimport plugin from \"pages/plugin\";\nimport helpers from \"utils/helpers\";\nimport Url from \"utils/Url\";\n\n/** @type {HTMLElement} */\nlet $installed = null;\n/** @type {HTMLElement} */\nlet $explore = null;\n/** @type {HTMLElement} */\nlet container = null;\n/** @type {HTMLElement} */\nlet $searchResult = null;\n\nconst LIMIT = 50;\nlet currentPage = 1;\nlet hasMore = true;\nlet isLoading = false;\nlet currentFilter = null;\nlet filterHasMore = true;\nlet isFilterLoading = false;\nconst SUPPORTED_EDITOR = \"cm\";\n\nfunction withSupportedEditor(url) {\n\tconst separator = url.includes(\"?\") ? \"&\" : \"?\";\n\treturn `${url}${separator}supported_editor=${SUPPORTED_EDITOR}`;\n}\n\nconst $header = (\n\t<div className=\"header\">\n\t\t<div className=\"title\">\n\t\t\t<span>{strings.plugins}</span>\n\t\t\t<div className=\"actions\">\n\t\t\t\t<button type=\"button\" className=\"icon-button\" onclick={filterPlugins}>\n\t\t\t\t\t<span className=\"icon tune\" />\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName=\"icon-button\"\n\t\t\t\t\tonclick={() => addSource()}\n\t\t\t\t>\n\t\t\t\t\t<span className=\"icon add\" />\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t\t<input\n\t\t\toninput={searchPlugin}\n\t\t\ttype=\"search\"\n\t\t\tname=\"search-ext\"\n\t\t\tplaceholder=\"Search\"\n\t\t/>\n\t</div>\n);\n\nconst $style = <style></style>;\n/** @type {Set<HTMLElement>} */\nconst $scrollableLists = new Set();\n\nlet searchTimeout = null;\nlet installedPlugins = [];\n\nexport default [\n\t\"extension\", // icon\n\t\"extensions\", // id\n\tstrings.plugins, // title\n\tinitApp, // init function\n\tfalse, // prepend\n\tonSelected, // onSelected function\n];\n\n/**\n * On selected handler for files app\n * @param {HTMLElement} el\n */\nfunction onSelected(el) {\n\tconst $scrollableLists = container.getAll(\":scope .scroll[data-scroll-top]\");\n\tfor (const $el of $scrollableLists) {\n\t\t$el.scrollTop = $el.dataset.scrollTop;\n\t}\n}\n\n/**\n * Initialize extension app\n * @param {HTMLElement} el\n */\nfunction initApp(el) {\n\tcontainer = el;\n\tcontainer.classList.add(\"extensions\");\n\tcontainer.content = $header;\n\n\tif (!$searchResult) {\n\t\t$searchResult = <ul className=\"list search-result scroll\" />;\n\t\tcontainer.append($searchResult);\n\t}\n\n\tif (!$explore) {\n\t\t$explore = collapsableList(strings.explore);\n\t\t$explore.ontoggle = loadExplore;\n\t\t$explore.$ul.onscroll = handleScroll;\n\t\tcontainer.append($explore);\n\t}\n\n\tif (!$installed) {\n\t\t$installed = collapsableList(strings.installed);\n\t\t$installed.ontoggle = loadInstalled;\n\t\tcontainer.append($installed);\n\t}\n\n\tSidebar.on(\"show\", onSelected);\n\tdocument.head.append($style);\n}\n\nasync function handleScroll(e) {\n\tif (isLoading || !hasMore) return;\n\n\tconst { scrollTop, scrollHeight, clientHeight } = e.target;\n\n\tif (scrollTop + clientHeight >= scrollHeight - 50) {\n\t\tawait loadMorePlugins();\n\t}\n}\n\nasync function handleFilterScroll(e) {\n\tif (isFilterLoading || !filterHasMore || !currentFilter) return;\n\n\tconst { scrollTop, scrollHeight, clientHeight } = e.target;\n\n\tif (scrollTop + clientHeight >= scrollHeight - 50) {\n\t\tawait loadFilteredPlugins(currentFilter, false);\n\t}\n}\n\nasync function loadMorePlugins() {\n\ttry {\n\t\tisLoading = true;\n\t\tstartLoading($explore);\n\n\t\tconst response = await fetch(\n\t\t\twithSupportedEditor(\n\t\t\t\t`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,\n\t\t\t),\n\t\t);\n\t\tconst newPlugins = await response.json();\n\n\t\tif (newPlugins.length < LIMIT) {\n\t\t\thasMore = false;\n\t\t}\n\n\t\tinstalledPlugins = await listInstalledPlugins();\n\t\tconst pluginElements = newPlugins.map(ListItem);\n\t\t$explore.$ul.append(...pluginElements);\n\n\t\tcurrentPage++;\n\t\tupdateHeight($explore);\n\t} catch (error) {\n\t\twindow.log(\"error\", error);\n\t} finally {\n\t\tisLoading = false;\n\t\tstopLoading($explore);\n\t}\n}\n\nasync function loadFilteredPlugins(filterState, isInitial = false) {\n\tif (isFilterLoading || !filterHasMore || !filterState) return;\n\n\ttry {\n\t\tisFilterLoading = true;\n\n\t\tconst { items, hasMore } = await getFilteredPlugins(filterState);\n\n\t\tif (currentFilter !== filterState) {\n\t\t\treturn;\n\t\t}\n\n\t\tinstalledPlugins = await listInstalledPlugins();\n\t\tconst pluginElements = items.map(ListItem);\n\t\tif (pluginElements.length) {\n\t\t\t$searchResult.append(...pluginElements);\n\t\t} else if (isInitial) {\n\t\t\t$searchResult.append(\n\t\t\t\t<span className=\"error empty\">\n\t\t\t\t\t{strings[\"no plugins found\"] || strings.empty || \"No plugins found\"}\n\t\t\t\t</span>,\n\t\t\t);\n\t\t}\n\n\t\tfilterHasMore = hasMore;\n\t\tif (!filterHasMore) {\n\t\t\t$searchResult.onscroll = null;\n\t\t}\n\n\t\tupdateHeight($searchResult);\n\t} catch (error) {\n\t\twindow.log(\"error\", \"Error loading filtered plugins:\");\n\t\twindow.log(\"error\", error);\n\t} finally {\n\t\tisFilterLoading = false;\n\t}\n}\n\nasync function searchPlugin() {\n\tclearTimeout(searchTimeout);\n\tsearchTimeout = setTimeout(async () => {\n\t\t// Clear filter when searching\n\t\tcurrentFilter = null;\n\t\tfilterHasMore = true;\n\t\tisFilterLoading = false;\n\t\t$searchResult.onscroll = null;\n\n\t\t$searchResult.content = \"\";\n\t\tconst status = await helpers.checkAPIStatus();\n\t\tif (!status) {\n\t\t\t$searchResult.content = (\n\t\t\t\t<span className=\"error\">{strings.api_error}</span>\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst query = this.value;\n\t\tif (!query) return;\n\n\t\ttry {\n\t\t\t$searchResult.classList.add(\"loading\");\n\t\t\tconst plugins = await fsOperation(\n\t\t\t\twithSupportedEditor(\n\t\t\t\t\tUrl.join(constants.API_BASE, `plugins?name=${query}`),\n\t\t\t\t),\n\t\t\t).readFile(\"json\");\n\n\t\t\tinstalledPlugins = await listInstalledPlugins();\n\t\t\t$searchResult.content = plugins.map(ListItem);\n\t\t\tupdateHeight($searchResult);\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", error);\n\t\t\t$searchResult.content = <span className=\"error\">{strings.error}</span>;\n\t\t} finally {\n\t\t\t$searchResult.classList.remove(\"loading\");\n\t\t}\n\t}, 500);\n}\n\nasync function filterPlugins() {\n\tconst verifiedLabel = strings[\"verified publisher\"];\n\tconst authorLabel = strings.author || strings.name;\n\tconst keywordsLabel = strings.keywords;\n\n\tconst filterItems = [\n\t\t{ value: \"orderBy:top_rated\", text: strings.top_rated },\n\t\t{ value: \"orderBy:newest\", text: strings.newly_added },\n\t\t{ value: \"orderBy:downloads\", text: strings.most_downloaded },\n\t\t{ value: \"attribute:verified\", text: verifiedLabel },\n\t\t{ value: \"attribute:author\", text: authorLabel },\n\t\t{ value: \"attribute:keywords\", text: keywordsLabel },\n\t];\n\n\tconst filterConfig = {\n\t\t\"orderBy:top_rated\": {\n\t\t\ttype: \"orderBy\",\n\t\t\tvalue: \"top_rated\",\n\t\t\tbaseLabel: strings.top_rated,\n\t\t},\n\t\t\"orderBy:newest\": {\n\t\t\ttype: \"orderBy\",\n\t\t\tvalue: \"newest\",\n\t\t\tbaseLabel: strings.newly_added,\n\t\t},\n\t\t\"orderBy:downloads\": {\n\t\t\ttype: \"orderBy\",\n\t\t\tvalue: \"downloads\",\n\t\t\tbaseLabel: strings.most_downloaded,\n\t\t},\n\t\t\"attribute:verified\": {\n\t\t\ttype: \"verified\",\n\t\t\tbaseLabel: verifiedLabel,\n\t\t\tvalue: true,\n\t\t},\n\t\t\"attribute:author\": { type: \"author\", baseLabel: authorLabel },\n\t\t\"attribute:keywords\": { type: \"keywords\", baseLabel: keywordsLabel },\n\t};\n\n\tconst selection = await select(\"Filter\", filterItems);\n\tif (!selection) return;\n\n\tconst option = filterConfig[selection];\n\tif (!option) return;\n\n\tconst filterState = {\n\t\t...option,\n\t\tnextPage: 1,\n\t\tbuffer: [],\n\t\thasMoreSource: true,\n\t\tdisplayLabel: option.baseLabel,\n\t};\n\n\tif (option.type === \"author\") {\n\t\tconst authorName = (await prompt(\"Enter author name\", \"\", \"text\"))?.trim();\n\t\tif (!authorName) return;\n\t\tfilterState.value = authorName.toLowerCase();\n\t\tfilterState.originalValue = authorName;\n\t\tfilterState.displayLabel = `${option.baseLabel}: ${authorName}`;\n\t} else if (option.type === \"keywords\") {\n\t\tconst rawKeywords = (await prompt(\"Enter keywords\", \"\", \"text\"))?.trim();\n\t\tif (!rawKeywords) return;\n\t\tconst keywordList = rawKeywords\n\t\t\t.split(\",\")\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean);\n\t\tif (!keywordList.length) return;\n\t\tfilterState.value = keywordList.map((item) => item.toLowerCase());\n\t\tfilterState.originalValue = keywordList.join(\", \");\n\t\tfilterState.displayLabel = `${option.baseLabel}: ${filterState.originalValue}`;\n\t}\n\n\tcurrentFilter = filterState;\n\tfilterHasMore = true;\n\tisFilterLoading = false;\n\t$searchResult.content = \"\";\n\n\ttry {\n\t\t$searchResult.classList.add(\"loading\");\n\t\tconst filterMessage = (\n\t\t\t<div className=\"filter-message\">\n\t\t\t\t<span>\n\t\t\t\t\t{strings[\"filtered by\"]} <strong>{filterState.displayLabel}</strong>\n\t\t\t\t</span>\n\t\t\t\t<span\n\t\t\t\t\tclassName=\"icon clearclose close-button\"\n\t\t\t\t\tdata-action=\"clear-filter\"\n\t\t\t\t\tonclick={() => clearFilter()}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t);\n\t\t$searchResult.content = [filterMessage];\n\t\t$searchResult.onscroll = handleFilterScroll;\n\t\tawait loadFilteredPlugins(currentFilter, true);\n\t\tupdateHeight($searchResult);\n\n\t\tfunction clearFilter() {\n\t\t\tcurrentFilter = null;\n\t\t\tfilterHasMore = true;\n\t\t\tisFilterLoading = false;\n\t\t\t$searchResult.content = \"\";\n\t\t\t$searchResult.onscroll = null;\n\t\t\tupdateHeight($searchResult);\n\t\t}\n\t} catch (error) {\n\t\twindow.log(\"error\", \"Error filtering plugins:\");\n\t\twindow.log(\"error\", error);\n\t\t$searchResult.content = <span className=\"error\">{strings.error}</span>;\n\t} finally {\n\t\t$searchResult.classList.remove(\"loading\");\n\t}\n}\n\nasync function addSource(sourceType, value = \"https://\") {\n\tif (!sourceType) {\n\t\tconst sourceOption = [\n\t\t\t[\"remote\", strings.remote],\n\t\t\t[\"local\", strings.local],\n\t\t];\n\t\tsourceType = await select(\"Select Source\", sourceOption);\n\t}\n\n\tif (!sourceType) return;\n\tlet source;\n\tif (sourceType === \"remote\") {\n\t\tsource = await prompt(\"Enter plugin source\", value, \"url\");\n\t} else {\n\t\tsource = (await FileBrowser(\"file\", \"Select plugin source\")).url;\n\t}\n\n\tif (!source) return;\n\n\ttry {\n\t\tconst { default: installPlugin } = await import(\"lib/installPlugin\");\n\t\tawait installPlugin(source);\n\t\tif (!$explore.collapsed) {\n\t\t\t$explore.ontoggle();\n\t\t}\n\t\tif (!$installed.collapsed) {\n\t\t\t$installed.ontoggle();\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(error);\n\t\twindow.toast(helpers.errorMessage(error));\n\t\taddSource(sourceType, source);\n\t}\n}\n\nasync function loadInstalled() {\n\tif (this.collapsed) return;\n\n\tconst plugins = await listInstalledPlugins();\n\tif (!plugins.length) {\n\t\t$installed.collapse();\n\t}\n\t$installed.$ul.content = plugins.map(ListItem);\n\tupdateHeight($installed);\n}\n\nasync function loadExplore() {\n\tif (this.collapsed) return;\n\n\tconst status = await helpers.checkAPIStatus();\n\tif (!status) {\n\t\t$explore.$ul.content = <span className=\"error\">{strings.api_error}</span>;\n\t\treturn;\n\t}\n\n\ttry {\n\t\tstartLoading($explore);\n\t\tcurrentPage = 1;\n\t\thasMore = true;\n\n\t\tconst response = await fetch(\n\t\t\twithSupportedEditor(\n\t\t\t\t`${constants.API_BASE}/plugins?page=${currentPage}&limit=${LIMIT}`,\n\t\t\t),\n\t\t);\n\t\tconst plugins = await response.json();\n\n\t\tif (plugins.length < LIMIT) {\n\t\t\thasMore = false;\n\t\t}\n\n\t\tinstalledPlugins = await listInstalledPlugins();\n\t\t$explore.$ul.content = plugins.map(ListItem);\n\t\tcurrentPage++;\n\t\tupdateHeight($explore);\n\t} catch (error) {\n\t\tconsole.error(\"Failed to load plugins in sidebar explore:\", error);\n\t\t$explore.$ul.content = <span className=\"error\">{strings.error}</span>;\n\t} finally {\n\t\tstopLoading($explore);\n\t}\n}\n\nasync function listInstalledPlugins() {\n\tconst plugins = await Promise.all(\n\t\t(await fsOperation(PLUGIN_DIR).lsDir()).map(async (item) => {\n\t\t\tconst id = Url.basename(item.url);\n\n\t\t\ttry {\n\t\t\t\tconst url = Url.join(item.url, \"plugin.json\");\n\t\t\t\tconst plugin = await fsOperation(url).readFile(\"json\");\n\n\t\t\t\tif (plugin.icon) {\n\t\t\t\t\tconst iconUrl = getLocalRes(id, plugin.icon);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tplugin.icon = await helpers.toInternalUri(iconUrl);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`Failed to resolve plugin icon for \"${id}\" in sidebar.`,\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tplugin.installed = true;\n\t\t\t\treturn plugin;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Skipping unreadable installed plugin \"${id}\" in sidebar.`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}),\n\t);\n\n\treturn plugins.filter(Boolean);\n}\n\nasync function getFilteredPlugins(filterState) {\n\tif (!filterState) return { items: [], hasMore: false };\n\n\tif (filterState.type === \"orderBy\") {\n\t\tconst page = filterState.nextPage || 1;\n\t\ttry {\n\t\t\tlet response;\n\t\t\tif (filterState.value === \"top_rated\") {\n\t\t\t\tresponse = await fetch(\n\t\t\t\t\twithSupportedEditor(\n\t\t\t\t\t\t`${constants.API_BASE}/plugins?explore=random&page=${page}&limit=${LIMIT}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tresponse = await fetch(\n\t\t\t\t\twithSupportedEditor(\n\t\t\t\t\t\t`${constants.API_BASE}/plugin?orderBy=${filterState.value}&page=${page}&limit=${LIMIT}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst items = await response.json();\n\t\t\tif (!Array.isArray(items)) {\n\t\t\t\treturn { items: [], hasMore: false };\n\t\t\t}\n\t\t\tfilterState.nextPage = page + 1;\n\t\t\tconst hasMore = items.length === LIMIT;\n\t\t\treturn { items, hasMore };\n\t\t} catch (error) {\n\t\t\tconsole.error(`Failed to get Filtered Plugins: `, error);\n\t\t\treturn { items: [], hasMore: false };\n\t\t}\n\t}\n\n\tif (!Array.isArray(filterState.buffer)) {\n\t\tfilterState.buffer = [];\n\t}\n\tif (filterState.hasMoreSource === undefined) {\n\t\tfilterState.hasMoreSource = true;\n\t}\n\tif (!filterState.nextPage) {\n\t\tfilterState.nextPage = 1;\n\t}\n\n\tconst items = [];\n\n\twhile (items.length < LIMIT) {\n\t\tif (filterState.buffer.length) {\n\t\t\titems.push(filterState.buffer.shift());\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (filterState.hasMoreSource === false) break;\n\n\t\ttry {\n\t\t\tconst page = filterState.nextPage;\n\t\t\tconst response = await fetch(\n\t\t\t\twithSupportedEditor(\n\t\t\t\t\t`${constants.API_BASE}/plugins?page=${page}&limit=${LIMIT}`,\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst data = await response.json();\n\t\t\tfilterState.nextPage = page + 1;\n\n\t\t\tif (!Array.isArray(data) || !data.length) {\n\t\t\t\tfilterState.hasMoreSource = false;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (data.length < LIMIT) {\n\t\t\t\tfilterState.hasMoreSource = false;\n\t\t\t}\n\n\t\t\tconst matched = data.filter((plugin) =>\n\t\t\t\tdoesPluginMatchFilter(plugin, filterState),\n\t\t\t);\n\t\t\tfilterState.buffer.push(...matched);\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", \"Failed to fetch filtered plugins:\");\n\t\t\twindow.log(\"error\", error);\n\t\t\tfilterState.hasMoreSource = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\twhile (items.length < LIMIT && filterState.buffer.length) {\n\t\titems.push(filterState.buffer.shift());\n\t}\n\n\tconst hasMore =\n\t\t(filterState.hasMoreSource !== false && filterState.nextPage) ||\n\t\tfilterState.buffer.length > 0;\n\n\treturn { items, hasMore: Boolean(hasMore) };\n}\n\nfunction doesPluginMatchFilter(plugin, filterState) {\n\tif (!plugin) return false;\n\n\tswitch (filterState.type) {\n\t\tcase \"verified\":\n\t\t\treturn Boolean(plugin.author_verified);\n\t\tcase \"author\": {\n\t\t\tconst authorName = getPluginAuthorName(plugin);\n\t\t\tif (!authorName) return false;\n\t\t\treturn authorName.toLowerCase().includes(filterState.value);\n\t\t}\n\t\tcase \"keywords\": {\n\t\t\tconst pluginKeywords = getPluginKeywords(plugin)\n\t\t\t\t.map((keyword) => keyword.toLowerCase())\n\t\t\t\t.filter(Boolean);\n\t\t\tif (!pluginKeywords.length) return false;\n\t\t\treturn filterState.value.some((keyword) =>\n\t\t\t\tpluginKeywords.some((pluginKeyword) => pluginKeyword.includes(keyword)),\n\t\t\t);\n\t\t}\n\t\tdefault:\n\t\t\treturn true;\n\t}\n}\n\nfunction getPluginAuthorName(plugin) {\n\tconst { author } = plugin || {};\n\tif (!author) return \"\";\n\tif (typeof author === \"string\") return author;\n\tif (typeof author === \"object\") {\n\t\treturn author.name || author.username || author.github || \"\";\n\t}\n\treturn \"\";\n}\n\nfunction getPluginKeywords(plugin) {\n\tconst { keywords } = plugin || {};\n\tif (!keywords) return [];\n\tif (Array.isArray(keywords)) return keywords;\n\tif (typeof keywords === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(keywords);\n\t\t\tif (Array.isArray(parsed)) return parsed;\n\t\t} catch (error) {\n\t\t\treturn keywords\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((item) => item.trim())\n\t\t\t\t.filter(Boolean);\n\t\t}\n\t}\n\treturn [];\n}\n\nfunction startLoading($list) {\n\t$list.$title.classList.add(\"loading\");\n}\n\nfunction stopLoading($list) {\n\t$list.$title.classList.remove(\"loading\");\n}\n\n/**\n * Update the height of the element\n * @param {HTMLElement} $el\n */\nfunction updateHeight($el) {\n\tremoveHeight($installed, $el !== $installed);\n\tremoveHeight($explore, $el !== $explore);\n\n\ttry {\n\t\tlet height = $header?.getBoundingClientRect().height;\n\t\tconst tileHeight = $el.get(\":scope>.tile\")?.getBoundingClientRect().height;\n\t\tif ($el === $searchResult) {\n\t\t\theight += 60;\n\t\t} else {\n\t\t\theight += $searchResult?.getBoundingClientRect().height + tileHeight;\n\t\t}\n\n\t\tsetHeight($el, height);\n\t} catch (error) {\n\t\tconsole.error(error);\n\t}\n}\n\n/**\n * Remove height styles from an element\n * @param {HTMLElement} $el\n * @param {Boolean} collapse\n */\nfunction removeHeight($el, collapse = false) {\n\tif (collapse) $el.collapse?.();\n\n\t$scrollableLists.delete($el);\n\tupdateStyle();\n}\n\n/**\n * Change the height of an element\n * @param {HTMLElement} $el\n * @param {Number} height\n */\nfunction setHeight($el, height) {\n\t$scrollableLists.add($el);\n\n\tconst calcHeight = height ? `calc(100% - ${height}px)` : \"100%\";\n\t$el.dataset.height = calcHeight;\n\tif ($el === $searchResult) {\n\t\t$el.style.height = \"fit-content\";\n\t\treturn;\n\t}\n\n\tupdateStyle();\n}\n\nfunction updateStyle() {\n\tlet style = \"\";\n\n\t$scrollableLists.forEach(($el) => {\n\t\tstyle += `\n\t\t\t.list.collapsible[data-id=\"${$el.dataset.id}\"] {\n\t\t\t\tmax-height: ${$el.dataset.height} !important;\n\t\t\t}\n\t\t`;\n\t});\n\n\t$style.innerHTML = style;\n}\n\nfunction getLocalRes(id, name) {\n\treturn Url.join(PLUGIN_DIR, id, name);\n}\n\nfunction ListItem({ icon, name, id, version, downloads, installed, source }) {\n\tif (installed === undefined) {\n\t\tinstalled = !!installedPlugins.find(({ id: _id }) => _id === id);\n\t}\n\tconst disabledMap = settings.value.pluginsDisabled || {};\n\tconst enabled = disabledMap[id] !== true;\n\tconst $el = (\n\t\t<div\n\t\t\tdata-plugin-id={id}\n\t\t\tdata-plugin-enabled={enabled !== false}\n\t\t\tclassName=\"tile\"\n\t\t\tstyle={enabled === false ? { opacity: 0.6 } : {}}\n\t\t>\n\t\t\t<span className=\"icon\" style={{ backgroundImage: `url(${icon})` }} />\n\t\t\t<span\n\t\t\t\tclassName=\"text sub-text\"\n\t\t\t\tdata-subtext={`v${version} • ${installed ? `${strings.installed}` : helpers.formatDownloadCount(downloads)}`}\n\t\t\t>\n\t\t\t\t{name}\n\t\t\t</span>\n\t\t\t{installed ? (\n\t\t\t\t<>\n\t\t\t\t\t{source ? (\n\t\t\t\t\t\t<span className=\"icon replay\" data-action=\"rebuild-plugin\" />\n\t\t\t\t\t) : null}\n\t\t\t\t\t<span className=\"icon more_vert\" data-action=\"more-plugin-action\" />\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName=\"install-btn\"\n\t\t\t\t\tdata-action=\"install-plugin\"\n\t\t\t\t>\n\t\t\t\t\t<span className=\"icon file_downloadget_app\" />\n\t\t\t\t</button>\n\t\t\t)}\n\t\t</div>\n\t);\n\n\t$el.onclick = async (event) => {\n\t\tconst morePluginActionButton = event.target.closest(\n\t\t\t'[data-action=\"more-plugin-action\"]',\n\t\t);\n\t\tconst installPluginBtn = event.target.closest(\n\t\t\t'[data-action=\"install-plugin\"]',\n\t\t);\n\t\tconst rebuildPluginBtn = event.target.closest(\n\t\t\t'[data-action=\"rebuild-plugin\"]',\n\t\t);\n\t\tif (morePluginActionButton) {\n\t\t\tmore_plugin_action(id, name);\n\t\t\treturn;\n\t\t}\n\t\tif (installPluginBtn) {\n\t\t\ttry {\n\t\t\t\tlet purchaseToken;\n\t\t\t\tlet product;\n\t\t\t\tconst pluginUrl = Url.join(constants.API_BASE, `plugin/${id}`);\n\t\t\t\tconst remotePlugin = await fsOperation(pluginUrl)\n\t\t\t\t\t.readFile(\"json\")\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\tthrow new Error(\"Failed to fetch plugin details\");\n\t\t\t\t\t});\n\n\t\t\t\tconst isPaid = remotePlugin.price > 0;\n\t\t\t\tif (isPaid) {\n\t\t\t\t\t[product] = await helpers.promisify(iap.getProducts, [\n\t\t\t\t\t\tremotePlugin.sku,\n\t\t\t\t\t]);\n\t\t\t\t\tif (product) {\n\t\t\t\t\t\tconst purchase = await getPurchase(product.productId);\n\t\t\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isPaid && !purchaseToken) {\n\t\t\t\t\tif (!product) throw new Error(\"Product not found\");\n\t\t\t\t\tconst apiStatus = await helpers.checkAPIStatus();\n\n\t\t\t\t\tif (!apiStatus) {\n\t\t\t\t\t\talert(strings.error, strings.api_error);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tiap.setPurchaseUpdatedListener(\n\t\t\t\t\t\t...purchaseListener(onpurchase, onerror),\n\t\t\t\t\t);\n\t\t\t\t\tawait helpers.promisify(iap.purchase, product.productId);\n\n\t\t\t\t\tasync function onpurchase(e) {\n\t\t\t\t\t\tconst purchase = await getPurchase(product.productId);\n\t\t\t\t\t\tawait ajax.post(Url.join(constants.API_BASE, \"plugin/order\"), {\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tid: remotePlugin.id,\n\t\t\t\t\t\t\t\ttoken: purchase?.purchaseToken,\n\t\t\t\t\t\t\t\tpackage: BuildInfo.packageName,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t\tpurchaseToken = purchase?.purchaseToken;\n\t\t\t\t\t}\n\n\t\t\t\t\tasync function onerror(error) {\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst { default: installPlugin } = await import(\"lib/installPlugin\");\n\t\t\t\tawait installPlugin(\n\t\t\t\t\tid,\n\t\t\t\t\tremotePlugin.name,\n\t\t\t\t\tpurchaseToken ? purchaseToken : undefined,\n\t\t\t\t);\n\n\t\t\t\tconst searchInput = container.querySelector('input[name=\"search-ext\"]');\n\t\t\t\tif (searchInput) {\n\t\t\t\t\tsearchInput.value = \"\";\n\t\t\t\t\t$searchResult.content = \"\";\n\t\t\t\t\t// Reset filter state when clearing search results\n\t\t\t\t\tcurrentFilter = null;\n\t\t\t\t\tfilterHasMore = true;\n\t\t\t\t\tisFilterLoading = false;\n\t\t\t\t\t$searchResult.onscroll = null;\n\t\t\t\t\tupdateHeight($searchResult);\n\t\t\t\t\t$installed.expand();\n\t\t\t\t}\n\n\t\t\t\twindow.toast(strings.success, 3000);\n\t\t\t\tif (!$explore.collapsed) {\n\t\t\t\t\t$explore.ontoggle();\n\t\t\t\t}\n\t\t\t\tif (!$installed.collapsed) {\n\t\t\t\t\t$installed.ontoggle();\n\t\t\t\t}\n\n\t\t\t\tasync function getPurchase(sku) {\n\t\t\t\t\tconst purchases = await helpers.promisify(iap.getPurchases);\n\t\t\t\t\tconst purchase = purchases.find((p) => p.productIds.includes(sku));\n\t\t\t\t\treturn purchase;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(err);\n\t\t\t\twindow.toast(helpers.errorMessage(err), 3000);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (rebuildPluginBtn) {\n\t\t\ttry {\n\t\t\t\tconst { default: installPlugin } = await import(\"lib/installPlugin\");\n\t\t\t\tawait installPlugin(source);\n\t\t\t\twindow.toast(strings.success, 3000);\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(err);\n\t\t\t\twindow.toast(helpers.errorMessage(err), 3000);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tplugin(\n\t\t\t{ id, installed },\n\t\t\t() => {\n\t\t\t\tif (!$explore.collapsed) {\n\t\t\t\t\t$explore.ontoggle();\n\t\t\t\t}\n\t\t\t\tif (!$installed.collapsed) {\n\t\t\t\t\t$installed.ontoggle();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tif (!$explore.collapsed) {\n\t\t\t\t\t$explore.ontoggle();\n\t\t\t\t}\n\t\t\t\tif (!$installed.collapsed) {\n\t\t\t\t\t$installed.ontoggle();\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\t};\n\n\treturn $el;\n}\n\nasync function loadAd(el) {\n\tif (!helpers.canShowAds()) return;\n\ttry {\n\t\tif (!(await window.iad?.isLoaded())) {\n\t\t\tconst oldText = el.textContent;\n\t\t\tel.textContent = strings[\"loading...\"];\n\t\t\tawait window.iad.load();\n\t\t\tel.textContent = oldText;\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(error);\n\t}\n}\n\nasync function uninstall(id) {\n\ttry {\n\t\tconst pluginDir = Url.join(PLUGIN_DIR, id);\n\t\tconst state = await InstallState.new(id);\n\t\tawait Promise.all([\n\t\t\tloadAd(this),\n\t\t\tfsOperation(pluginDir).delete(),\n\t\t\tstate.delete(state.storeUrl),\n\t\t]);\n\t\tconst pluginMainScript = document.getElementById(`${id}-mainScript`);\n\t\tif (pluginMainScript) document.head.removeChild(pluginMainScript);\n\t\tacode.unmountPlugin(id);\n\n\t\tconst searchInput = container.querySelector('input[name=\"search-ext\"]');\n\t\tif (searchInput) {\n\t\t\tsearchInput.value = \"\";\n\t\t\t$searchResult.content = \"\";\n\t\t\t// Reset filter state when clearing search results\n\t\t\tcurrentFilter = null;\n\t\t\tfilterHasMore = true;\n\t\t\tisFilterLoading = false;\n\t\t\t$searchResult.onscroll = null;\n\t\t\tupdateHeight($searchResult);\n\t\t\tif ($installed.collapsed) {\n\t\t\t\t$installed.expand();\n\t\t\t}\n\t\t}\n\n\t\t// Show Ad If Its Free Version, interstitial Ad(iad) is loaded.\n\t\tawait helpers.showInterstitialIfReady();\n\t} catch (err) {\n\t\thelpers.error(err);\n\t}\n}\n\nasync function more_plugin_action(id, pluginName) {\n\tconst disabledMap = settings.value.pluginsDisabled || {};\n\tconst enabled = disabledMap[id] !== true;\n\tlet actions = [];\n\tconst pluginSettings = settings.uiSettings[`plugin-${id}`];\n\n\tif (pluginSettings) {\n\t\tactions.push(strings.settings);\n\t}\n\n\tactions.push(\n\t\tenabled ? strings.disable || \"Disable\" : strings.enable || \"Enable\",\n\t);\n\n\tactions.push(strings.uninstall);\n\tconst action = await select(\"Action\", actions);\n\tif (!action) return;\n\tswitch (action) {\n\t\tcase strings.settings:\n\t\t\tpluginSettings.setTitle(pluginName);\n\t\t\tpluginSettings.show();\n\t\t\tbreak;\n\t\tcase strings.uninstall:\n\t\t\tawait uninstall(id);\n\t\t\tif (!$explore.collapsed) {\n\t\t\t\t$explore.ontoggle();\n\t\t\t}\n\t\t\tif (!$installed.collapsed) {\n\t\t\t\t$installed.ontoggle();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase strings.disable || \"Disable\":\n\t\t// fallthrough\n\t\tcase strings.enable || \"Enable\":\n\t\t\tif (enabled) {\n\t\t\t\tdisabledMap[id] = true; // Disabling\n\t\t\t} else {\n\t\t\t\tdelete disabledMap[id]; // Enabling\n\t\t\t}\n\n\t\t\tsettings.update({ pluginsDisabled: disabledMap }, false);\n\n\t\t\t// INFO: I don't know how to get all loaded plugins(not installed).\n\t\t\tconst choice = await select(\n\t\t\t\tstrings.info,\n\t\t\t\t[\n\t\t\t\t\t// { value: \"reload_plugins\", text: strings[\"reload_plugins\"] || \"Reload Plugins\" },\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"restart_app\",\n\t\t\t\t\t\ttext: strings[\"restart_app\"] || \"Restart App\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"single\",\n\t\t\t\t\t\ttext: enabled\n\t\t\t\t\t\t\t? strings[\"disable_plugin\"] || \"Disable this Plugin\"\n\t\t\t\t\t\t\t: strings[\"enable_plugin\"] || \"Enable this Plugin\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\t{\n\t\t\t\t\tdefault: \"single\",\n\t\t\t\t},\n\t\t\t);\n\n\t\t\t// if (choice === \"reload_plugins\") {\n\t\t\t// \t// Unmount all currently loaded plugins before reloading\n\t\t\t// \tif (window.acode && typeof window.acode.getLoadedPluginIds === \"function\") {\n\t\t\t// \t\tfor (const pluginId of window.acode.getLoadedPluginIds()) {\n\t\t\t// \t\t\twindow.acode.unmountPlugin(pluginId);\n\t\t\t// \t\t}\n\t\t\t// \t}\n\t\t\t// \tawait window.loadPlugins?.();\n\t\t\t// \twindow.toast(strings.success);\n\t\t\t// }\n\t\t\tif (choice === \"restart_app\") {\n\t\t\t\tlocation.reload();\n\t\t\t} else if (choice === \"single\") {\n\t\t\t\tif (enabled) {\n\t\t\t\t\twindow.acode.unmountPlugin(id);\n\t\t\t\t\twindow.toast(strings[\"plugin_disabled\"] || \"Plugin Disabled\");\n\t\t\t\t} else {\n\t\t\t\t\tawait loadPlugin(id);\n\t\t\t\t\twindow.toast(strings[\"plugin_enabled\"] || \"Plugin enabled\");\n\t\t\t\t}\n\t\t\t\tif (!$explore.collapsed) {\n\t\t\t\t\t$explore.ontoggle();\n\t\t\t\t}\n\t\t\t\tif (!$installed.collapsed) {\n\t\t\t\t\t$installed.ontoggle();\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n"
  },
  {
    "path": "src/sidebarApps/extensions/style.scss",
    "content": "@use \"../../styles/mixins.scss\";\n\n.container.extensions {\n  .header {\n    padding: 10px;\n    display: flex;\n    flex-direction: column;\n    border-bottom: 1px solid var(--border-color);\n    box-sizing: border-box;\n\n    .title {\n      font-weight: 600;\n      display: flex;\n      justify-content: space-between !important;\n      align-items: center;\n      width: 100%;\n      color: var(--primary-text-color);\n      margin-bottom: 8px;\n\n      .actions {\n        display: flex;\n        align-items: center;\n      }\n\n      .icon-button {\n        background: none;\n        border: none;\n        cursor: pointer;\n        color: var(--primary-text-color);\n        margin-left: 0.4rem;\n        border-radius: 6px;\n        transition: all 0.2s ease;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        min-width: 32px;\n        min-height: 32px;\n        font-size: 1.1em;\n\n        &:hover {\n          background-color: var(--primary-color);\n        }\n      }\n    }\n\n    input[type=\"search\"] {\n      width: 100% !important;\n      padding: 0.5rem !important;\n      border: 1px solid var(--border-color);\n      border-radius: 6px;\n      background: var(--secondary-color);\n      color: var(--primary-text-color);\n      font-size: 0.875rem;\n      transition: all 0.2s ease;\n      margin: 0 !important;\n\n      &:focus {\n        outline: none;\n        border-color: var(--active-color);\n        box-shadow: 0 0 0 2px rgba(51, 153, 255, 0.1);\n      }\n\n      &::placeholder {\n        color: var(--secondary-text-color);\n      }\n    }\n  }\n\n  .list.search-result {\n    min-height: 0;\n\n    &.loading {\n      @include mixins.loader(20px);\n    }\n\n    .filter-message {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      background: var(--primary-color);\n      padding: 0.5rem 0.75rem;\n      border-bottom: 1px solid var(--border-color);\n      font-size: 0.875rem;\n      color: var(--primary-text-color);\n\n      strong {\n        color: var(--active-color);\n        margin-left: 0.25rem;\n      }\n\n      .close-button {\n        background: none;\n        border: none;\n        cursor: pointer;\n        padding: 0.25rem;\n        border-radius: 4px;\n        transition: all 0.2s ease;\n        color: var(--secondary-text-color);\n\n        &:hover {\n          background-color: var(--error-text-color);\n          color: white;\n        }\n      }\n    }\n  }\n\n  .tile {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    min-height: 30px;\n    padding: 6px 8px;\n\n    .text {\n      &.sub-text {\n        font-size: 0.8rem;\n\n        &::after {\n          font-size: 0.7rem;\n        }\n      }\n    }\n\n    .more_vert {\n      display: flex;\n      align-items: center;\n      cursor: pointer;\n      justify-content: center;\n      transition: background-color 0.3s ease;\n      margin-left: 10px;\n\n      &:hover {\n        background-color: var(--active-icon-color);\n      }\n    }\n\n    .replay {\n      display: flex;\n      align-items: center;\n      cursor: pointer;\n      justify-content: center;\n      transition: background-color 0.3s ease;\n\n      &:hover {\n        background-color: var(--active-icon-color);\n      }\n    }\n\n    .install-btn {\n      background: none;\n      border: none;\n      margin-left: 10px;\n      border-radius: 50%;\n      cursor: pointer;\n      color: var(--primary-text-color);\n      transition: all 0.3s ease;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n\n      &:hover {\n        background-color: var(--active-icon-color);\n      }\n\n      &:active {\n        transform: scale(0.95);\n      }\n    }\n  }\n\n  .icon {\n    background-size: 24px !important;\n    background-position: center !important;\n  }\n}"
  },
  {
    "path": "src/sidebarApps/files/index.js",
    "content": "import \"./style.scss\";\nimport Sidebar from \"components/sidebar\";\nimport settings from \"lib/settings\";\n\n/**@type {HTMLElement} */\nlet container;\n\nexport default [\n\t\"documents\", // icon\n\t\"files\", // id\n\tstrings[\"files\"], // title\n\tinitApp, // init function\n\tfalse, // prepend\n\tonSelected, // onSelected function\n];\n\n/**\n * Initialize files app\n * @param {HTMLElement} el\n */\nfunction initApp(el) {\n\tcontainer = el;\n\tcontainer.classList.add(\"files\");\n\tcontainer.setAttribute(\"data-msg\", strings[\"open folder\"]);\n\tcontainer.style.overflowX = \"auto\";\n\tcontainer.addEventListener(\"click\", clickHandler);\n\teditorManager.on(\n\t\t[\"new-file\", \"int-open-file-list\", \"remove-file\"],\n\t\t(position) => {\n\t\t\tif (\n\t\t\t\ttypeof position === \"string\" &&\n\t\t\t\tposition !== settings.OPEN_FILE_LIST_POS_SIDEBAR\n\t\t\t)\n\t\t\t\treturn;\n\t\t\tconst fileList = container.get(\":scope > div.file-list\");\n\t\t\tif (fileList) fixHeight(fileList);\n\t\t},\n\t);\n\teditorManager.on(\"add-folder\", fixHeight);\n\tSidebar.on(\"show\", onSelected);\n}\n\n/**\n * On selected handler for files app\n * @param {HTMLElement} el\n */\nfunction onSelected(el) {\n\tconst $scrollableLists = container.getAll(\":scope .scroll[data-scroll-top]\");\n\t$scrollableLists.forEach(($el) => {\n\t\t$el.scrollTop = $el.dataset.scrollTop;\n\t});\n}\n\n/**\n * Click handler for files app\n * @param {MouseEvent} e\n * @returns\n */\nfunction clickHandler(e) {\n\tif (!container.children.length) {\n\t\tacode.exec(\"open-folder\");\n\t\treturn;\n\t}\n\n\tconst { target } = e;\n\tif (target.matches(\".files>.list>.tile\")) {\n\t\tfixHeight(target.parentElement);\n\t}\n}\n\n/**\n * Update list height\n * @param {HTMLElement} target Target element\n */\nexport function fixHeight(target) {\n\tconst lists = Array.from(container.getAll(\":scope > div\"));\n\tconst ITEM_HEIGHT = 30;\n\n\tlet height = (lists.length - 1) * ITEM_HEIGHT;\n\tlet activeFileList;\n\n\tif (settings.value.openFileListPos === settings.OPEN_FILE_LIST_POS_SIDEBAR) {\n\t\tconst [firstList] = lists;\n\t\tif (firstList.classList.contains(\"file-list\")) {\n\t\t\tactiveFileList = firstList;\n\t\t\tif (firstList.unclasped) {\n\t\t\t\tconst heightOffset = height - ITEM_HEIGHT;\n\t\t\t\tconst totalHeight =\n\t\t\t\t\tITEM_HEIGHT * activeFileList.$ul.children.length + ITEM_HEIGHT;\n\t\t\t\tconst maxHeight =\n\t\t\t\t\tlists.length === 1 || !lists.slice(1).find((list) => list.unclasped)\n\t\t\t\t\t\t? window.innerHeight\n\t\t\t\t\t\t: window.innerHeight / 2;\n\t\t\t\tconst minHeight = Math.min(totalHeight, maxHeight - heightOffset);\n\n\t\t\t\tactiveFileList.style.maxHeight = `${minHeight}px`;\n\t\t\t\tactiveFileList.style.height = `${minHeight}px`;\n\t\t\t\theight += minHeight - ITEM_HEIGHT;\n\t\t\t}\n\t\t}\n\t}\n\n\tlists.forEach((list) => {\n\t\tif (list === activeFileList) return;\n\n\t\tif (target === activeFileList) {\n\t\t\tif (list.collapsed) {\n\t\t\t\tlist.style.removeProperty(\"max-height\");\n\t\t\t\tlist.style.removeProperty(\"height\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttarget = list;\n\t\t}\n\n\t\tif (list === target && target.unclasped) {\n\t\t\tlist.style.maxHeight = `calc(100% - ${height}px)`;\n\t\t\tlist.style.height = `calc(100% - ${height}px)`;\n\t\t\treturn;\n\t\t}\n\n\t\tif (list.collapsed) {\n\t\t\tlist.style.removeProperty(\"max-height\");\n\t\t\tlist.style.removeProperty(\"height\");\n\t\t\treturn;\n\t\t}\n\n\t\tlist.collapse();\n\t\tlist.style.removeProperty(\"max-height\");\n\t\tlist.style.removeProperty(\"height\");\n\t\treturn;\n\t});\n}\n"
  },
  {
    "path": "src/sidebarApps/files/style.scss",
    "content": ".container.files {\n  &:empty {\n    height: 100%;\n    width: 100%;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n\n    &::after {\n      content: attr(data-msg);\n      font-weight: 600;\n    }\n  }\n\n  /* Make the container horizontally scrollable */\n  overflow-x: auto !important;\n  max-width: 100%;\n\n  &::-webkit-scrollbar {\n    height: 5px;\n    width: 5px;\n  }\n\n  &::-webkit-scrollbar-thumb {\n    background-color: rgba(0, 0, 0, 0.3);\n    border-radius: 3px;\n  }\n\n  &::-webkit-scrollbar-corner {\n    background: transparent;\n  }\n\n  scrollbar-width: thin;\n  scrollbar-color: rgba(0, 0, 0, 0.3) transparent;\n\n  .list {\n    min-width: 100%;\n    width: max-content;\n    max-width: none;\n  }\n\n  ul {\n    min-width: 100%;\n    width: max-content;\n    overflow-x: visible !important;\n    max-width: none;\n    margin-left: 0;\n    &::-webkit-scrollbar-corner {\n      background: transparent;\n    }\n  }\n\n  li {\n    min-width: 100%;\n    width: max-content;\n  }\n\n  .tile {\n    > .text {\n      white-space: nowrap !important;\n      overflow: visible !important;\n      width: max-content !important;\n      text-overflow: clip !important;\n    }\n  }\n\n  /* Add indent guides for folders (excluding first level) */\n  .list.collapsible > ul > .collapsible > ul {\n    position: relative;\n    padding-left: 24px;\n\n    &::before {\n      content: \"\";\n      position: absolute;\n      left: 14px;\n      top: 0;\n      height: 100%;\n      width: 1px;\n      background: var(--border-color);\n      z-index: 0;\n    }\n\n    /* Add guides for deeper nesting */\n    .collapsible > ul {\n      padding-left: 24px;\n\n      &::before {\n        content: \"\";\n        position: absolute;\n        left: 14px;\n        top: 0;\n        height: 100%;\n        width: 1px;\n        background: var(--border-color);\n        z-index: 0;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/sidebarApps/index.js",
    "content": "import appSettings from \"lib/settings\";\nimport Sponsors from \"pages/sponsors\";\nimport SidebarApp from \"./sidebarApp\";\n\nconst SIDEBAR_APPS_LAST_SECTION = \"sidebarAppsLastSection\";\n\n/**@type {HTMLElement} */\nlet $apps;\n/**@type {HTMLElement} */\nlet $sidebar;\n/**@type {string} */\nlet currentSection = localStorage.getItem(SIDEBAR_APPS_LAST_SECTION);\n/**@type {SidebarApp[]} */\nconst apps = [];\n/**@type {HTMLSpanElement | null} */\nlet $sponsorIcon = null;\n\n/**\n * @param {string} icon icon of the app\n * @param {string} id id of the app\n * @param {HTMLElement} el element to show in sidebar\n * @param {string} title title of the app\n * @param {(container:HTMLElement)=>void} initFunction\n * @param {boolean} prepend weather to show this app at the top of the sidebar or not\n * @param {(container:HTMLElement)=>void} onSelected\n * @returns {void}\n */\nfunction add(\n\ticon,\n\tid,\n\ttitle,\n\tinitFunction,\n\tprepend = false,\n\tonSelected = () => {},\n) {\n\tcurrentSection ??= id;\n\n\tconst app = new SidebarApp(icon, id, title, initFunction, onSelected);\n\tapps.push(app);\n\tapp.install(prepend);\n\n\tif (currentSection === id) {\n\t\tsetActiveApp(id);\n\t}\n}\n\n/**\n * Removes a sidebar app with the given ID.\n * @param {string} id - The ID of the sidebar app to remove.\n * @returns {void}\n */\nfunction remove(id) {\n\tconst app = apps.find((app) => app.id === id);\n\tif (!app) return;\n\tconst wasActive = app.active;\n\tapp.remove();\n\tapps.splice(apps.indexOf(app), 1);\n\tif (wasActive && apps.length > 0) {\n\t\tconst preferredApp = apps.find((app) => app.id === currentSection);\n\t\tsetActiveApp(preferredApp?.id || apps[0].id);\n\t\treturn;\n\t}\n\n\tif (!apps.length) {\n\t\tcurrentSection = null;\n\t\tlocalStorage.removeItem(SIDEBAR_APPS_LAST_SECTION);\n\t}\n}\n\n/**\n * Initialize sidebar apps\n * @param {HTMLElement} $el\n */\nfunction init($el) {\n\t$sidebar = $el;\n\t$apps = $sidebar.get(\".app-icons-container\");\n\t$apps.addEventListener(\"click\", onclick);\n\tSidebarApp.init($el, $apps);\n\tappSettings.on(\n\t\t\"update:showSponsorSidebarApp\",\n\t\tsetSponsorSidebarAppVisibility,\n\t);\n}\n\n/**\n * Loads all sidebar apps.\n */\nasync function loadApps() {\n\tadd(...(await import(\"./files\")).default);\n\tadd(...(await import(\"./searchInFiles\")).default);\n\tadd(...(await import(\"./extensions\")).default);\n\tadd(...(await import(\"./notification\")).default);\n\tsetSponsorSidebarAppVisibility(appSettings.value.showSponsorSidebarApp);\n}\n\n/**\n * Adds or removes the sponsor icon in sidebar based on settings.\n * @param {boolean} visible\n */\nfunction setSponsorSidebarAppVisibility(visible) {\n\tif (!$apps) return;\n\n\tif (visible) {\n\t\tif ($sponsorIcon?.isConnected) return;\n\t\t$sponsorIcon = (\n\t\t\t<span\n\t\t\t\tclassName=\"icon favorite\"\n\t\t\t\ttitle={strings.sponsor}\n\t\t\t\tonclick={Sponsors}\n\t\t\t/>\n\t\t);\n\t\t$apps.append($sponsorIcon);\n\t\treturn;\n\t}\n\n\tif ($sponsorIcon) {\n\t\t$sponsorIcon.remove();\n\t\t$sponsorIcon = null;\n\t}\n}\n\n/**\n * Ensures that at least one app is active.\n * Call this AFTER all plugins have been loaded to handle cases where\n * the stored section was from an uninstalled plugin.\n * @returns {void}\n */\nfunction ensureActiveApp() {\n\tconst activeApps = apps.filter((app) => app.active);\n\tif (activeApps.length === 1) return;\n\n\tif (activeApps.length > 1) {\n\t\tconst preferredActiveApp = activeApps.find(\n\t\t\t(app) => app.id === currentSection,\n\t\t);\n\t\tsetActiveApp(preferredActiveApp?.id || activeApps[0].id);\n\t\treturn;\n\t}\n\n\tif (apps.length > 0) {\n\t\tconst preferredApp = apps.find((app) => app.id === currentSection);\n\t\tsetActiveApp(preferredApp?.id || apps[0].id);\n\t}\n}\n\n/**\n * Gets the container of the app with the given ID.\n * @param {string} id\n * @returns\n */\nfunction get(id) {\n\tconst app = apps.find((app) => app.id === id);\n\treturn app.container;\n}\n\n/**\n * Handles click on sidebar apps\n * @param {MouseEvent} e\n */\nfunction onclick(e) {\n\tconst target = e.target;\n\tconst { action, id } = target.dataset;\n\n\tif (action !== \"sidebar-app\") return;\n\n\tsetActiveApp(id);\n}\n\n/**\n * Activates the given sidebar app and deactivates all others.\n * @param {string} id\n * @returns {void}\n */\nfunction setActiveApp(id) {\n\tconst app = apps.find((app) => app.id === id);\n\tif (!app) return;\n\n\tcurrentSection = id;\n\tlocalStorage.setItem(SIDEBAR_APPS_LAST_SECTION, id);\n\n\tfor (const currentApp of apps) {\n\t\tcurrentApp.active = currentApp.id === id;\n\t}\n}\n\nexport default {\n\tinit,\n\tadd,\n\tget,\n\tremove,\n\tloadApps,\n\tensureActiveApp,\n};\n"
  },
  {
    "path": "src/sidebarApps/notification/index.js",
    "content": "import NotificationManager from \"lib/notificationManager\";\nimport \"./style.scss\";\nimport Sidebar from \"components/sidebar\";\n\n/**@type {HTMLElement} */\nlet container;\n/** @type {HTMLElement} */\nlet $notificationContainer = null;\n\nlet notificationManager;\n\nexport default [\n\t\"notifications\", // icon\n\t\"notification\", // id\n\tstrings[\"notifications\"], // title\n\tinitApp, // init function\n\tfalse, // prepend\n\tonSelected, // onSelected function\n];\n\nconst $header = (\n\t<div className=\"header\">\n\t\t<div className=\"title\">\n\t\t\t{strings[\"notifications\"]}\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"icon-button\"\n\t\t\t\tonclick={() => notificationManager.clearAll()}\n\t\t\t>\n\t\t\t\t<span data-action=\"clear\" className=\"icon clearclose\"></span>\n\t\t\t</button>\n\t\t</div>\n\t</div>\n);\n\n/**\n * Initialize files app\n * @param {HTMLElement} el\n */\nfunction initApp(el) {\n\tcontainer = el;\n\tcontainer.classList.add(\"notifications\");\n\tcontainer.content = $header;\n\t$notificationContainer = (\n\t\t<div className=\"notifications-container scroll\"></div>\n\t);\n\tcontainer.append($notificationContainer);\n\n\tnotificationManager = new NotificationManager();\n\n\tSidebar.on(\"show\", onSelected);\n}\n\n/**\n * On selected handler for files app\n * @param {HTMLElement} el\n */\nfunction onSelected(el) {\n\tconst $scrollableLists = container.getAll(\":scope .scroll[data-scroll-top]\");\n\t$scrollableLists.forEach(($el) => {\n\t\t$el.scrollTop = $el.dataset.scrollTop;\n\t});\n}\n"
  },
  {
    "path": "src/sidebarApps/notification/style.scss",
    "content": ".container.notifications {\n    display: flex;\n    flex-direction: column;\n\n    .header {\n        padding: 10px;\n        display: flex;\n        justify-content: space-between;\n        align-items: center;\n        border-bottom: 1px solid var(--border-color);\n\n        .title {\n            gap: 4px;\n            display: flex;\n            font-weight: 600;\n            align-items: center;\n            color: var(--primary-text-color);\n            justify-content: space-between !important;\n\n            .icon-button {\n                border: none;\n                background: none;\n                cursor: pointer;\n                color: var(--primary-text-color);\n                margin-left: 0.4rem;\n                border-radius: 6px;\n                transition: all 0.2s ease;\n                display: flex;\n                align-items: center;\n                justify-content: center;\n                min-width: 32px;\n                min-height: 32px;\n                font-size: 1.1em;\n\n                &:hover {\n                    background-color: var(--primary-color);\n                }\n            }\n        }\n    }\n\n    .notifications-container {\n        flex: 1;\n        overflow-y: auto;\n        padding: 12px;\n        display: flex;\n        flex-direction: column;\n        gap: 8px;\n\n        .empty-state {\n            text-align: center;\n            color: var(--secondary-text-color);\n            padding: 20px;\n            font-size: 14px;\n        }\n\n        .notification-item {\n            padding: 10px 12px;\n            border-radius: 6px;\n            background: var(--popup-background-color);\n            display: flex;\n            gap: 10px;\n            align-items: flex-start;\n            animation: slideIn 0.3s ease-out;\n            border: 1px solid var(--popup-border-color);\n            transition: all 0.2s ease;\n\n            &:hover {\n                background: rgba($color: #000000, $alpha: 0.2);\n                border-color: var(--popup-border-color);\n            }\n\n            .notification-icon {\n                width: 20px;\n                height: 20px;\n                display: flex;\n                align-items: center;\n                justify-content: center;\n                flex-shrink: 0;\n                color: var(--primary-text-color);\n            }\n\n            .notification-content {\n                flex: 1;\n                min-width: 0;\n\n                .notification-title {\n                    font-size: 13px;\n                    font-weight: 500;\n                    margin-bottom: 4px;\n                    white-space: nowrap;\n                    overflow: hidden;\n                    word-wrap: break-word;\n                    color: var(--primary-text-color);\n                    display: flex;\n                    justify-content: space-between;\n                    align-items: center;\n\n                    .notification-time {\n                        font-size: 11px;\n                        color: var(--secondary-text-color);\n                    }\n                }\n\n                .notification-message {\n                    font-size: 12px;\n                    color: var(--secondary-text-color);\n                    line-height: 1.4;\n                    overflow: hidden;\n                    display: -webkit-box;\n                    -webkit-box-orient: vertical;\n                    word-break: break-word;\n                }\n\n                .notification-actions {\n                    margin-top: 8px;\n                    display: flex;\n                    justify-content: flex-end;\n\n                    .action-button {\n                        font-size: 11px;\n                        padding: 6px 12px;\n                        border-radius: 4px;\n                        background: transparent;\n                        color: var(--secondary-text-color);\n                        cursor: pointer;\n                        transition: all 0.2s ease;\n                        border: 1px solid var(--border-color);\n                        display: flex;\n                        align-items: center;\n                        gap: 4px;\n\n                        &:hover {\n                            background: rgba($color: var(--border-color), $alpha: 0.6);\n                            color: var(--primary-text-color);\n                            border-color: var(--border-color);\n                        }\n\n                        &:active {\n                            transform: translateY(1px);\n                        }\n\n                        &::before {\n                            content: '×';\n                            font-size: 14px;\n                            line-height: 1;\n                            opacity: 0.8;\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\n\n@keyframes slideIn {\n    from {\n        transform: translateX(100%);\n        opacity: 0;\n    }\n\n    to {\n        transform: translateX(0);\n        opacity: 1;\n    }\n}\n"
  },
  {
    "path": "src/sidebarApps/searchInFiles/cmResultView.js",
    "content": "import { EditorState, StateEffect, StateField } from \"@codemirror/state\";\nimport {\n\tDecoration,\n\tEditorView,\n\tViewPlugin,\n\tWidgetType,\n} from \"@codemirror/view\";\nimport appSettings from \"lib/settings\";\nimport helpers from \"utils/helpers\";\n\n/**\n * CodeMirror view to render search results\n *\n * @param {HTMLElement} container\n * @param {object} opts\n * @param {(lineIndex:number)=>void} opts.onLineClick\n * @param {()=>string[]} opts.getWords - returns list of words to highlight\n * @param {()=>string[]} opts.getFileNames - returns list of filenames (used to style header lines)\n */\nexport function createSearchResultView(\n\tcontainer,\n\t{ onLineClick, getWords, getFileNames, getRegex },\n) {\n\tlet view;\n\n\t// Effect and field to maintain collapsed headers (by line number)\n\tconst toggleFold = StateEffect.define();\n\tconst foldState = StateField.define({\n\t\tcreate() {\n\t\t\treturn new Set();\n\t\t},\n\t\tupdate(value, tr) {\n\t\t\tlet next = value;\n\t\t\tfor (const e of tr.effects) {\n\t\t\t\tif (e.is(toggleFold)) {\n\t\t\t\t\tif (next === value) next = new Set(value);\n\t\t\t\t\tconst ln = e.value;\n\t\t\t\t\tif (next.has(ln)) next.delete(ln);\n\t\t\t\t\telse next.add(ln);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Reset folds on full document reset\n\t\t\tif (tr.docChanged && tr.startState.doc.length === 0) return new Set();\n\t\t\treturn next;\n\t\t},\n\t});\n\n\tfunction eachGroup(doc, fn) {\n\t\t// Groups start at lines not beginning with a tab\n\t\tconst total = doc.lines;\n\t\tlet start = 1;\n\t\twhile (start <= total) {\n\t\t\tconst header = doc.line(start);\n\t\t\t// If header starts with tab, advance until a non-tab header\n\t\t\tif (header.text.startsWith(\"\\t\")) {\n\t\t\t\tstart++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet end = start;\n\t\t\tfor (let i = start + 1; i <= total; i++) {\n\t\t\t\tconst line = doc.line(i);\n\t\t\t\tif (!line.text.startsWith(\"\\t\")) break;\n\t\t\t\tend = i;\n\t\t\t}\n\t\t\tfn({ start, end });\n\t\t\tstart = end + 1;\n\t\t}\n\t}\n\n\tclass ChevronWidget extends WidgetType {\n\t\tconstructor(collapsed) {\n\t\t\tsuper();\n\t\t\tthis.collapsed = collapsed;\n\t\t}\n\t\teq(other) {\n\t\t\treturn other.collapsed === this.collapsed;\n\t\t}\n\t\ttoDOM() {\n\t\t\tconst span = document.createElement(\"span\");\n\t\t\tspan.className = `cm-foldChevron icon keyboard_arrow_${this.collapsed ? \"right\" : \"down\"}`;\n\t\t\treturn span;\n\t\t}\n\t\tignoreEvent() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tclass SummaryWidget extends WidgetType {\n\t\tconstructor(text) {\n\t\t\tsuper();\n\t\t\tthis.text = text;\n\t\t}\n\t\teq(other) {\n\t\t\treturn other.text === this.text;\n\t\t}\n\t\ttoDOM() {\n\t\t\tconst div = document.createElement(\"div\");\n\t\t\tdiv.className = \"cm-collapsedSummary\";\n\t\t\tdiv.textContent = this.text;\n\t\t\treturn div;\n\t\t}\n\t\tignoreEvent() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tclass CountWidget extends WidgetType {\n\t\tconstructor(count) {\n\t\t\tsuper();\n\t\t\tthis.count = count;\n\t\t}\n\t\teq(other) {\n\t\t\treturn other.count === this.count;\n\t\t}\n\t\ttoDOM() {\n\t\t\tconst span = document.createElement(\"span\");\n\t\t\tspan.className = \"cm-fileCount\";\n\t\t\tspan.textContent = `(${this.count})`;\n\t\t\treturn span;\n\t\t}\n\t\tignoreEvent() {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tclass FileIconWidget extends WidgetType {\n\t\tconstructor(className) {\n\t\t\tsuper();\n\t\t\tthis.className = className;\n\t\t}\n\t\teq(other) {\n\t\t\treturn other.className === this.className;\n\t\t}\n\t\ttoDOM() {\n\t\t\tconst span = document.createElement(\"span\");\n\t\t\tspan.className = `${this.className} cm-fileIcon`;\n\t\t\treturn span;\n\t\t}\n\t\tignoreEvent() {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction buildGroupDecos(state) {\n\t\tconst doc = state.doc;\n\t\tconst folded = state.field(foldState, false) || new Set();\n\t\t// No removed groups\n\t\tconst fns =\n\t\t\t(typeof getFileNames === \"function\" ? getFileNames() : []) || [];\n\t\tif (!fns.length || doc.length === 0 || doc.lines === 0)\n\t\t\treturn Decoration.none;\n\n\t\tconst builder = [];\n\t\t// Build header chevrons and collapses per group\n\t\tlet groupIndex = 0;\n\t\teachGroup(doc, ({ start, end }) => {\n\t\t\tconst header = doc.line(start);\n\t\t\tconst key = start - 1;\n\t\t\tconst collapsed = folded.has(key); // zero-based line index\n\t\t\t// Header line class and chevron widget\n\t\t\tbuilder.push(\n\t\t\t\tDecoration.line({ class: \"cm-fileName\" }).range(header.from),\n\t\t\t);\n\t\t\tbuilder.push(\n\t\t\t\tDecoration.widget({\n\t\t\t\t\twidget: new ChevronWidget(collapsed),\n\t\t\t\t\tside: -1,\n\t\t\t\t}).range(header.from),\n\t\t\t);\n\t\t\t// File icon\n\t\t\tconst fileNames =\n\t\t\t\t(typeof getFileNames === \"function\" ? getFileNames() : []) || [];\n\t\t\tconst fname = fileNames[groupIndex] || \"\";\n\t\t\tconst iconClass = helpers.getIconForFile(fname);\n\t\t\tbuilder.push(\n\t\t\t\tDecoration.widget({\n\t\t\t\t\twidget: new FileIconWidget(iconClass),\n\t\t\t\t\tside: -1,\n\t\t\t\t}).range(header.from),\n\t\t\t);\n\t\t\t// Count badge on right\n\t\t\tconst count = Math.max(0, end - start);\n\t\t\tbuilder.push(\n\t\t\t\tDecoration.widget({ widget: new CountWidget(count), side: 1 }).range(\n\t\t\t\t\theader.to,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tif (collapsed && end > start) {\n\t\t\t\t// Hide content lines and show a summary placeholder\n\t\t\t\tconst first = doc.line(start + 1);\n\t\t\t\tconst last = doc.line(end);\n\t\t\t\tbuilder.push(\n\t\t\t\t\tDecoration.replace({ block: true }).range(first.from, last.to),\n\t\t\t\t);\n\t\t\t\tconst count2 = end - start;\n\t\t\t\tbuilder.push(\n\t\t\t\t\tDecoration.widget({\n\t\t\t\t\t\twidget: new SummaryWidget(\n\t\t\t\t\t\t\t`${count2} result${count2 > 1 ? \"s\" : \"\"}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tside: 1,\n\t\t\t\t\t\tblock: true,\n\t\t\t\t\t}).range(first.from),\n\t\t\t\t);\n\t\t\t}\n\t\t\tgroupIndex++;\n\t\t});\n\n\t\treturn Decoration.set(builder, true);\n\t}\n\n\tconst groupDecoField = StateField.define({\n\t\tcreate(state) {\n\t\t\treturn buildGroupDecos(state);\n\t\t},\n\t\tupdate(decos, tr) {\n\t\t\tif (\n\t\t\t\ttr.docChanged ||\n\t\t\t\ttr.startState.field(foldState, false) !==\n\t\t\t\t\ttr.state.field(foldState, false)\n\t\t\t) {\n\t\t\t\treturn buildGroupDecos(tr.state);\n\t\t\t}\n\t\t\treturn decos.map(tr.changes);\n\t\t},\n\t\tprovide: (f) => EditorView.decorations.from(f),\n\t});\n\n\tconst decorationsPlugin = ViewPlugin.fromClass(\n\t\tclass {\n\t\t\tconstructor(view) {\n\t\t\t\tthis.decorations = this.buildDecos(view);\n\t\t\t}\n\n\t\t\tupdate(update) {\n\t\t\t\tif (\n\t\t\t\t\tupdate.docChanged ||\n\t\t\t\t\tupdate.viewportChanged ||\n\t\t\t\t\tupdate.startState.field(foldState) !== update.state.field(foldState)\n\t\t\t\t) {\n\t\t\t\t\tthis.decorations = this.buildDecos(update.view);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbuildDecos(view) {\n\t\t\t\tconst builder = [];\n\t\t\t\tlet searchRegex = null;\n\t\t\t\tif (typeof getRegex === \"function\") {\n\t\t\t\t\tconst r = getRegex();\n\t\t\t\t\tif (r && r.source) {\n\t\t\t\t\t\tconst flags = (r.ignoreCase ? \"i\" : \"\") + \"g\";\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tsearchRegex = new RegExp(r.source, flags);\n\t\t\t\t\t\t} catch {}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst words = searchRegex ? [] : (getWords?.() || []).filter(Boolean);\n\n\t\t\t\tlet wordRegex = null;\n\t\t\t\tif (!searchRegex && words.length) {\n\t\t\t\t\tconst escaped = words\n\t\t\t\t\t\t.map((w) => w.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n\t\t\t\t\t\t.join(\"|\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\twordRegex = new RegExp(escaped, \"g\");\n\t\t\t\t\t} catch {}\n\t\t\t\t}\n\t\t\t\t// Add match highlights only on visible lines to keep it fast\n\t\t\t\tconst matcher = searchRegex || wordRegex;\n\t\t\t\tif (matcher) {\n\t\t\t\t\tfor (const { from, to } of view.visibleRanges) {\n\t\t\t\t\t\tlet pos = from;\n\t\t\t\t\t\twhile (pos <= to) {\n\t\t\t\t\t\t\tconst line = view.state.doc.lineAt(pos);\n\t\t\t\t\t\t\tconst text = line.text;\n\t\t\t\t\t\t\tif (text && text.charCodeAt(0) === 9) {\n\t\t\t\t\t\t\t\tmatcher.lastIndex = 0;\n\t\t\t\t\t\t\t\tlet m;\n\t\t\t\t\t\t\t\twhile ((m = matcher.exec(text))) {\n\t\t\t\t\t\t\t\t\tconst fromPos = line.from + m.index;\n\t\t\t\t\t\t\t\t\tconst toPos = fromPos + m[0].length;\n\t\t\t\t\t\t\t\t\tbuilder.push(\n\t\t\t\t\t\t\t\t\t\tDecoration.mark({ class: \"cm-match\" }).range(\n\t\t\t\t\t\t\t\t\t\t\tfromPos,\n\t\t\t\t\t\t\t\t\t\t\ttoPos,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tif (m.index === matcher.lastIndex) matcher.lastIndex++;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (line.to >= to) break;\n\t\t\t\t\t\t\tpos = line.to + 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn Decoration.set(builder, true);\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tdecorations: (v) => v.decorations,\n\t\t\teventHandlers: {\n\t\t\t\tmousedown(event, view) {\n\t\t\t\t\t// Map click to line number and notify (use client coords)\n\t\t\t\t\tconst pos = view.posAtCoords({ x: event.clientX, y: event.clientY });\n\t\t\t\t\tif (pos == null) return;\n\t\t\t\t\t// Only react when clicking on a line element, not empty space\n\t\t\t\t\tconst lineEl =\n\t\t\t\t\t\tevent.target && event.target.closest\n\t\t\t\t\t\t\t? event.target.closest(\".cm-line\")\n\t\t\t\t\t\t\t: null;\n\t\t\t\t\tif (!lineEl) return;\n\t\t\t\t\tconst ln = view.state.doc.lineAt(pos).number - 1; // zero-based\n\t\t\t\t\tconst lineText = view.state.doc.line(ln + 1).text;\n\t\t\t\t\tconst isHeader = lineText.length > 0 && lineText.charCodeAt(0) !== 9;\n\t\t\t\t\tif (isHeader) {\n\t\t\t\t\t\t// Toggle collapse on header click\n\t\t\t\t\t\tview.dispatch({ effects: toggleFold.of(ln) });\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\t// Only trigger navigation for match lines (start with tab)\n\t\t\t\t\tif (!(lineText && lineText.charCodeAt(0) === 9)) return;\n\t\t\t\t\tonLineClick?.(ln);\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t);\n\n\tconst readOnly = EditorState.readOnly.of(true);\n\tconst lineWrap = EditorView.lineWrapping;\n\tconst noCursor = EditorView.editable.of(false);\n\n\tfunction getEditorFontFamily() {\n\t\tconst font = appSettings?.value?.editorFont || \"Roboto Mono\";\n\t\treturn `${font}, Noto Mono, Monaco, monospace`;\n\t}\n\n\tconst theme = EditorView.theme({\n\t\t\"&\": {\n\t\t\tfontSize: String(appSettings?.value?.fontSize || \"12px\"),\n\t\t\tlineHeight: String(appSettings?.value?.lineHeight || 1.5),\n\t\t},\n\t\t\".cm-content\": {\n\t\t\tpadding: 0,\n\t\t\tfontFamily: getEditorFontFamily(),\n\t\t},\n\t\t\".cm-line\": {\n\t\t\tcolor: \"var(--primary-text-color)\",\n\t\t},\n\t});\n\n\tconst state = EditorState.create({\n\t\tdoc: \"\",\n\t\textensions: [\n\t\t\tEditorState.tabSize.of(1),\n\t\t\treadOnly,\n\t\t\tnoCursor,\n\t\t\tlineWrap,\n\t\t\ttheme,\n\t\t\tfoldState,\n\t\t\tgroupDecoField,\n\t\t\tdecorationsPlugin,\n\t\t],\n\t});\n\n\tview = new EditorView({ state, parent: container });\n\n\treturn {\n\t\tsetValue(text) {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: text || \"\" },\n\t\t\t});\n\t\t},\n\t\tinsert(text) {\n\t\t\tif (!text) return;\n\t\t\tview.dispatch({ changes: { from: view.state.doc.length, insert: text } });\n\t\t},\n\t\tsetGhostText(text) {\n\t\t\tthis.setValue(text || \"\");\n\t\t},\n\t\tremoveGhostText() {\n\t\t\tthis.setValue(\"\");\n\t\t},\n\t\tget view() {\n\t\t\treturn view;\n\t\t},\n\t};\n}\n"
  },
  {
    "path": "src/sidebarApps/searchInFiles/index.js",
    "content": "import \"./styles.scss\";\nimport fsOperation from \"fileSystem\";\nimport { EditorView } from \"@codemirror/view\";\nimport autosize from \"autosize\";\nimport Checkbox from \"components/checkbox\";\nimport Sidebar, { preventSlide } from \"components/sidebar\";\nimport escapeStringRegexp from \"escape-string-regexp\";\nimport Reactive from \"html-tag-js/reactive\";\nimport Ref from \"html-tag-js/ref\";\nimport files, { Tree } from \"lib/fileList\";\nimport openFile from \"lib/openFile\";\nimport settings from \"lib/settings\";\nimport helpers from \"utils/helpers\";\nimport { createSearchResultView } from \"./cmResultView\";\n\n// Local highlight sources\nconst words = [];\nconst fileNames = [];\nconst MAX_HL_WORDS = 400; // cap to avoid massive regex in result view\n\nconst workers = [];\nconst results = [];\nconst filesSearched = [];\nconst filesReplaced = [];\n\nconst $container = Ref();\nconst $regExp = Ref();\nconst $search = Ref();\nconst $replace = Ref();\nconst $exclude = Ref();\nconst $include = Ref();\nconst $wholeWord = Ref();\nconst $caseSensitive = Ref();\nconst $btnReplaceAll = Ref();\nconst $resultOverview = Ref();\nconst $error = Reactive();\nconst $progress = Reactive();\n\nconst resultOverview = {\n\tfilesCount: 0,\n\tmatchesCount: 0,\n\treset() {\n\t\tthis.filesCount = 0;\n\t\tthis.matchesCount = 0;\n\t\t$resultOverview.innerHTML = searchResultText(0, 0);\n\t\t$resultOverview.classList.remove(\"error\");\n\t},\n};\n\nconst CASE_SENSITIVE = \"search-in-files-case-sensitive\";\nconst WHOLE_WORD = \"search-in-files-whole-word\";\nconst REG_EXP = \"search-in-files-reg-exp\";\nconst EXCLUDE = \"search-in-files-exclude\";\nconst INCLUDE = \"search-in-files-include\";\n\nconst store = {\n\tget caseSensitive() {\n\t\treturn localStorage.getItem(CASE_SENSITIVE) === \"true\";\n\t},\n\tset caseSensitive(value) {\n\t\tlocalStorage.setItem(CASE_SENSITIVE, value);\n\t},\n\tget wholeWord() {\n\t\treturn localStorage.getItem(WHOLE_WORD) === \"true\";\n\t},\n\tset wholeWord(value) {\n\t\treturn localStorage.setItem(WHOLE_WORD, value);\n\t},\n\tget regExp() {\n\t\treturn localStorage.getItem(REG_EXP) === \"true\";\n\t},\n\tset regExp(value) {\n\t\treturn localStorage.setItem(REG_EXP, value);\n\t},\n\tget exclude() {\n\t\treturn localStorage.getItem(EXCLUDE);\n\t},\n\tset exclude(value) {\n\t\treturn localStorage.setItem(EXCLUDE, value);\n\t},\n\tget include() {\n\t\treturn localStorage.getItem(INCLUDE);\n\t},\n\tset include(value) {\n\t\treturn localStorage.setItem(INCLUDE, value);\n\t},\n};\n\nconst debounceSearch = helpers.debounce(searchAll, 500);\n\nlet useIncludeAndExclude = false;\nlet searchResult = null; // CM6 wrapper from createSearchResultView\nlet currentSearchRegex = null;\nlet replacing = false;\nlet newFiles = 0;\nlet searching = false;\n\naddEventListener($regExp, \"change\", onInput);\naddEventListener($wholeWord, \"change\", onInput);\naddEventListener($caseSensitive, \"change\", onInput);\naddEventListener($search, \"input\", onInput);\naddEventListener($include, \"input\", onInput);\naddEventListener($exclude, \"input\", onInput);\naddEventListener($btnReplaceAll, \"click\", replaceAll);\n\nfiles.on(\"push-file\", () => {\n\tif (!searching) return;\n\t$error.value = strings[\"missed files\"].replace(\"{count}\", ++newFiles);\n});\n\n$container.onref = ($el) => {\n\tsearchResult = createSearchResultView($el, {\n\t\tonLineClick: onCursorChange,\n\t\tgetWords: () => words,\n\t\tgetFileNames: () => fileNames,\n\t\tgetRegex: () => currentSearchRegex,\n\t});\n\t$container.style.lineHeight = \"1.5\";\n};\n\npreventSlide((target) => {\n\treturn $container.el?.contains(target);\n});\n\nexport default [\n\t\"search\",\n\t\"searchInFiles\",\n\tstrings[\"search in files\"],\n\t(/**@type {HTMLElement} */ el) => {\n\t\tel.classList.add(\"search-in-files\");\n\n\t\tel.content = (\n\t\t\t<>\n\t\t\t\t<div className=\"header\">\n\t\t\t\t\t<div className=\"options\">\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={store.caseSensitive}\n\t\t\t\t\t\t\tsize=\"10px\"\n\t\t\t\t\t\t\ttext=\"aA\"\n\t\t\t\t\t\t\tref={$caseSensitive}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={store.wholeWord}\n\t\t\t\t\t\t\tsize=\"10px\"\n\t\t\t\t\t\t\ttext=\"a-z\"\n\t\t\t\t\t\t\tref={$wholeWord}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tchecked={store.regExp}\n\t\t\t\t\t\t\tsize=\"10px\"\n\t\t\t\t\t\t\ttext=\".*\"\n\t\t\t\t\t\t\tref={$regExp}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Details>\n\t\t\t\t\t\t<Summary>\n\t\t\t\t\t\t\t<Textarea\n\t\t\t\t\t\t\t\tref={$search}\n\t\t\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\t\t\tname=\"search\"\n\t\t\t\t\t\t\t\tplaceholder={strings[\"search\"]}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</Summary>\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tref={$btnReplaceAll}\n\t\t\t\t\t\t\t\tclassName=\"icon replace_all\"\n\t\t\t\t\t\t\t></button>\n\t\t\t\t\t\t\t<Textarea\n\t\t\t\t\t\t\t\tref={$replace}\n\t\t\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\t\t\tname=\"replace\"\n\t\t\t\t\t\t\t\tplaceholder={strings[\"replace\"]}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Details>\n\t\t\t\t\t<Details\n\t\t\t\t\t\tonexpand={(expanded) => {\n\t\t\t\t\t\t\tuseIncludeAndExclude = expanded;\n\t\t\t\t\t\t\tif ($exclude.value || $include.value) {\n\t\t\t\t\t\t\t\tonInput();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Summary marker={false} className=\"extras\">\n\t\t\t\t\t\t\t...\n\t\t\t\t\t\t</Summary>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tvalue={store.exclude}\n\t\t\t\t\t\t\tref={$exclude}\n\t\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\t\tname=\"exclude\"\n\t\t\t\t\t\t\tplaceholder={strings[\"exclude files\"]}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tvalue={store.include}\n\t\t\t\t\t\t\tref={$include}\n\t\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\t\tname=\"include\"\n\t\t\t\t\t\t\tplaceholder={strings[\"include files\"]}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</Details>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"search-result\">\n\t\t\t\t\t<span ref={$resultOverview} innerHTML={searchResultText(0, 0)}></span>{\" \"}\n\t\t\t\t\t({$progress}%)\n\t\t\t\t</div>\n\t\t\t\t<div className=\"error\">{$error}</div>\n\t\t\t\t<div\n\t\t\t\t\tref={$container}\n\t\t\t\t\tclassName=\"search-in-file-editor editor-container\"\n\t\t\t\t></div>\n\t\t\t</>\n\t\t);\n\t},\n\tfalse, // show as first item\n\t() => {},\n];\n\n/**\n * Worker message handler\n * @param {Event} e\n */\nasync function onWorkerMessage(e) {\n\tconst { action, error, data, id } = e.data;\n\tif (error) {\n\t\twindow.log(\"error\", error);\n\t\tconsole.error(error);\n\t\treturn;\n\t}\n\n\tswitch (action) {\n\t\tcase \"get-file\": {\n\t\t\tlet content;\n\t\t\tlet readError;\n\n\t\t\tconst editorFile = editorManager.getFile(data, \"uri\");\n\t\t\tif (editorFile?.session?.doc) {\n\t\t\t\ttry {\n\t\t\t\t\tcontent = editorFile.session.doc.toString() || \"\";\n\t\t\t\t} catch (_) {\n\t\t\t\t\tcontent = \"\";\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tcontent = await fsOperation(data).readFile(\n\t\t\t\t\t\tsettings.value.defaultFileEncoding,\n\t\t\t\t\t);\n\t\t\t\t} catch (er) {\n\t\t\t\t\treadError = er;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\te.target.postMessage({\n\t\t\t\tid,\n\t\t\t\taction: \"get-file\",\n\t\t\t\tdata: content,\n\t\t\t\terror: readError,\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"search-result\": {\n\t\t\tconst { file, matches, text } = data;\n\n\t\t\tif (!matches.length) return;\n\t\t\tif (filesSearched.includes(file)) return;\n\n\t\t\tfilesSearched.push(Tree.fromJSON(file));\n\t\t\t// Clear any ghost text on first result\n\t\t\tif (filesSearched.length === 1) {\n\t\t\t\tsearchResult.setValue(\"\");\n\t\t\t}\n\t\t\tresultOverview.filesCount += 1;\n\t\t\tresultOverview.matchesCount += matches.length;\n\t\t\t$resultOverview.innerHTML = searchResultText(\n\t\t\t\tresultOverview.filesCount,\n\t\t\t\tresultOverview.matchesCount,\n\t\t\t);\n\n\t\t\tconst index = filesSearched.length - 1;\n\t\t\tresults.push({\n\t\t\t\tfile: index,\n\t\t\t\tmatch: null,\n\t\t\t\tposition: null,\n\t\t\t});\n\n\t\t\tfileNames.push(file.name);\n\t\t\tfor (const result of matches) {\n\t\t\t\tresult.file = index;\n\t\t\t\tresults.push(result);\n\t\t\t\tif (words.length < MAX_HL_WORDS) {\n\t\t\t\t\tconst token = escapeStringRegexp(result.renderText);\n\t\t\t\t\tif (!words.includes(token)) words.push(token);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (fileNames.length > 1) {\n\t\t\t\tsearchResult.insert(`\\n${text}`);\n\t\t\t} else {\n\t\t\t\tsearchResult.insert(text);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"replace-result\": {\n\t\t\tconst { file, text } = data;\n\t\t\tfilesReplaced.push(file);\n\t\t\topenFile(file.url, {\n\t\t\t\trender: filesSearched.length === filesReplaced.length,\n\t\t\t\ttext,\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"done-replacing\": {\n\t\t\te.target.doneReplacing = true;\n\n\t\t\tif (workers.find((worker) => worker.started && !worker.doneReplacing)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tawait helpers.showInterstitialIfReady();\n\n\t\t\tterminateWorker(false);\n\t\t\treplacing = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"done-searching\": {\n\t\t\te.target.doneSearching = true;\n\n\t\t\tif (workers.find((worker) => worker.started && !worker.doneSearching)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst showAd = results.length > 100;\n\t\t\tif (showAd) {\n\t\t\t\tawait helpers.showInterstitialIfReady();\n\t\t\t}\n\n\t\t\tif (!results.length) {\n\t\t\t\tsearchResult.setGhostText(strings[\"no result\"], { row: 0, column: 0 });\n\t\t\t}\n\n\t\t\tsearching = false;\n\t\t\tterminateWorker(false);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"progress\": {\n\t\t\te.target.progress = data;\n\t\t\tconst startedWorkers = workers.filter((worker) => worker.started);\n\t\t\tconst progress = Math.round(\n\t\t\t\tstartedWorkers.reduce((acc, { progress = 0 }) => acc + progress, 0) /\n\t\t\t\t\tstartedWorkers.length,\n\t\t\t);\n\t\t\t$progress.value = progress;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n}\n\n/**\n * On input event handler\n * @param {InputEvent} e\n */\n\nfunction onInput(e) {\n\tif (!searchResult || replacing) return;\n\n\tconst { target } = e || {};\n\n\tif (target === $caseSensitive.el) {\n\t\tstore.caseSensitive = $caseSensitive.el.checked;\n\t}\n\n\tif (target === $wholeWord.el) {\n\t\tstore.wholeWord = $wholeWord.el.checked;\n\t}\n\n\tif (target === $regExp.el) {\n\t\tstore.regExp = $regExp.el.checked;\n\t}\n\n\tif (target === $exclude.el) {\n\t\tstore.exclude = $exclude.el.value;\n\t}\n\n\tif (target === $include.el) {\n\t\tstore.include = $include.el.value;\n\t}\n\n\tterminateWorker();\n\tsearching = false;\n\tnewFiles = 0;\n\t$error.value = \"\";\n\tresults.length = 0;\n\t$progress.value = 0;\n\tfilesSearched.length = 0;\n\tresultOverview.reset();\n\tsearchResult.setValue(\"\");\n\tsearchResult.setGhostText(strings[\"searching...\"], { row: 0, column: 0 });\n\tremoveEvents();\n\tdebounceSearch();\n}\n\nasync function searchAll() {\n\tconst search = $search.value;\n\tif (!search) {\n\t\tsearchResult.removeGhostText();\n\t\treturn;\n\t}\n\n\tconst options = getOptions();\n\tconst regex = toRegex(search, options);\n\tif (!regex) {\n\t\tsearchResult.removeGhostText();\n\t\treturn;\n\t}\n\n\taddEvents();\n\n\tconst allFiles = files();\n\teditorManager.files.forEach((file) => {\n\t\tconst exists = allFiles.find((f) => f.url === file.uri);\n\t\tif (exists) return;\n\n\t\tallFiles.push(new Tree(file.name, file.uri, false));\n\t});\n\n\tif (!allFiles.length) {\n\t\tsearchResult.removeGhostText();\n\t\t$progress.value = 100;\n\t\treturn;\n\t}\n\n\tsearching = true;\n\twords.length = 0;\n\tfileNames.length = 0;\n\tcurrentSearchRegex = regex;\n\tsearchResult.setGhostText(strings[\"searching...\"], { row: 0, column: 0 });\n\tsendMessage(\"search-files\", allFiles, regex, options);\n}\n\n/**\n * Replaces all occurrences of the search query with the replacement text in the files.\n * Sends a message to the worker threads to perform the replacement.\n */\nasync function replaceAll() {\n\tterminateWorker();\n\tfilesReplaced.length = 0;\n\n\tconst search = $search.value;\n\tconst replace = $replace.value;\n\tconst options = getOptions();\n\tif (!search || !replace) return;\n\tconst regex = toRegex(search, options);\n\tif (!regex) return;\n\n\treplacing = true;\n\tsendMessage(\"replace-files\", filesSearched, regex, options, replace);\n}\n\n/**\n * Sends a message to the worker threads to perform a specific action on a subset of files.\n *\n * @param {string} action - The action to be performed by the worker threads.\n * @param {Array<Tree>} files - The files to be processed.\n * @param {string} search - The search query.\n * @param {object} options - The search options.\n * @param {string} replace - The replacement text (if applicable).\n */\nfunction sendMessage(action, files, search, options, replace) {\n\tconst len = workers.length;\n\tconst limit = Math.ceil(files.length / len);\n\tfor (let i = 0; i < len; i++) {\n\t\tconst worker = workers[i];\n\t\tconst offset = i * limit;\n\t\tconst filesForThisWorker = files\n\t\t\t.slice(offset, offset + limit)\n\t\t\t.map((file) => file.toJSON());\n\t\tif (!filesForThisWorker.length) break;\n\t\tworker.started = true;\n\t\tworker.postMessage({\n\t\t\taction: action,\n\t\t\tdata: {\n\t\t\t\tfiles: filesForThisWorker,\n\t\t\t\tsearch,\n\t\t\t\treplace,\n\t\t\t\toptions,\n\t\t\t},\n\t\t});\n\t}\n}\n\n/**\n * Worker error handler\n * @param {Error} e\n */\nfunction onErrorMessage(e) {\n\tconsole.error(e);\n}\n\n/**\n * Terminates the existing Web Workers, if any, and then initializes new ones.\n * Also sets the onmessage and onerror handlers for these workers.\n * @param {boolean} [initializeNewWorkers=true] - Whether to initialize new workers after terminating the existing ones.\n */\nfunction terminateWorker(initializeNewWorkers = true) {\n\tworkers.forEach((worker) => worker.terminate());\n\tworkers.length = 0;\n\n\tif (!initializeNewWorkers) return;\n\n\tconst len = navigator.hardwareConcurrency - 1 || 2;\n\n\tfor (let i = 0; i < len; i++) {\n\t\tconst worker = getWorker();\n\t\tworker.onmessage = onWorkerMessage;\n\t\tworker.onerror = onErrorMessage;\n\t\tworkers.push(worker);\n\t}\n}\n\n/**\n * Creates and returns a new Web Worker that executes the code in 'searchInFilesWorker.build.js'.\n *\n * @returns {Worker} A new Worker object that runs the code in 'searchInFilesWorker.build.js'.\n */\nfunction getWorker() {\n\treturn new Worker(new URL(\"./worker.js\", import.meta.url));\n}\n\n/**\n * @typedef {object} Options\n * @property {boolean} caseSensitive\n * @property {boolean} wholeWord\n * @property {boolean} regExp\n * @property {string} exclude\n * @property {string} include\n */\n\n/**\n * Retrieves the search options currently set in the user interface. This includes\n * search parameters such as 'case sensitive', 'whole word', 'regular expressions',\n * 'exclude' and 'include' depending on whether they are checked or filled in the UI.\n *\n * Note that the 'exclude' and 'include' options are only retrieved when\n * the corresponding UI section is expanded (i.e., `useIncludeAndExclude` is true).\n *\n * @returns {Options}\n */\nfunction getOptions() {\n\tconst exclude = useIncludeAndExclude ? $exclude.el.value.trim() : \"\";\n\tconst include = useIncludeAndExclude ? $include.el.value.trim() : \"\";\n\tconst caseSensitive = $caseSensitive.el.checked;\n\tconst wholeWord = $wholeWord.el.checked;\n\tconst regExp = $regExp.el.checked;\n\n\treturn {\n\t\tcaseSensitive,\n\t\twholeWord,\n\t\tregExp,\n\t\texclude,\n\t\tinclude,\n\t};\n}\n\n/**\n * Binds an event listener to the 'onref' method of the specified element reference.\n *\n * @param {Ref} $ref - The element reference containing the 'onref' method.\n * @param {string} type - The event type to listen for (e.g., 'input', 'change').\n * @param {Function} handler - The event handler function to be executed when the event occurs.\n * @returns {void}\n *\n * @example\n * // Add an input event listener to $search element reference\n * addEventListener($search, 'input', debounceInput);\n */\nfunction addEventListener($ref, type, handler) {\n\t$ref.onref = ($el) => {\n\t\t$el.addEventListener(type, handler);\n\t};\n}\n\n/**\n * Generates a search result text based on the number of files and matches.\n *\n * @param {number} files - The number of files searched.\n * @param {number} matches - The number of matches found.\n * @returns {string} - The search result text.\n */\nfunction searchResultText(files, matches) {\n\treturn strings[\"search result\"]\n\t\t.replace(\"{files}\", `<strong>${files}</strong>`)\n\t\t.replace(\"{matches}\", `<strong>${matches}</strong>`);\n}\n\n/**\n * A function component that returns a div element with the \"details\" attribute.\n *\n * @param {Object} props - The properties object for the component.\n * @param {Function} props.onexpand - Callback function to be executed when the div expands.\n * @param {Array} children - An array of child elements to be inserted into the div.\n *\n * @returns {HTMLDivElement} A div element with the \"details\" attribute, and any child elements.\n */\nfunction Details({ onexpand }, children) {\n\tif (onexpand) onexpand(false);\n\treturn (\n\t\t<div onexpand={onexpand} attr-is=\"details\">\n\t\t\t{children}\n\t\t</div>\n\t);\n}\n\n/**\n * A function component that returns a div element that functions as a summary.\n *\n * @param {Object} props - The properties object for the component.\n * @param {boolean} props.marker - Indicator whether a marker should be included in the div.\n * @param {string} props.className - CSS class name to be applied to the div.\n * @param {Array} children - An array of child elements to be inserted into the div.\n *\n * @returns {HTMLDivElement} A div element with a 'summary' attribute, a marker (if specified), and any child elements.\n */\nfunction Summary({ marker = true, className }, children) {\n\treturn (\n\t\t<div onclick={toggle} attr-is=\"summary\" className={className}>\n\t\t\t{marker && <span className=\"marker\"></span>}\n\t\t\t{children}\n\t\t</div>\n\t);\n\n\t/**\n\t * A function that toggles the 'open' attribute on the parent element of the div\n\t * and calls the onexpand function of the parent element if it exists.\n\t *\n\t * @this {HTMLElement} The div element that the function is bound to.\n\t * @param {MouseEvent} e - The event object from the click event.\n\t */\n\tfunction toggle(e) {\n\t\tif (\n\t\t\te.target instanceof HTMLInputElement ||\n\t\t\te.target instanceof HTMLTextAreaElement ||\n\t\t\te.target instanceof HTMLSelectElement ||\n\t\t\te.target.contentEditable === \"true\"\n\t\t)\n\t\t\treturn;\n\n\t\tconst $details = this.parentElement;\n\n\t\t$details.toggleAttribute(\"open\");\n\t\tif ($details.hasAttribute(\"open\")) {\n\t\t\t$details.onexpand?.(true);\n\t\t} else {\n\t\t\t$details.onexpand?.(false);\n\t\t}\n\t}\n}\n\n/**\n * Create a textarea element with autosize\n * @param {object} param0\n * @param {string} param0.name\n * @param {string} param0.placeholder\n * @param {Ref} param0.ref\n * @returns {HTMLTextAreaElement}\n */\nfunction Textarea({ name, placeholder, ref }) {\n\treturn autosize(\n\t\t<textarea ref={ref} name={name} placeholder={placeholder}></textarea>,\n\t);\n}\n\n/**\n * Converts a search string and options into a regular expression.\n *\n * @param {string} search - The search string.\n * @param {object} options - The search options.\n * @param {boolean} [options.caseSensitive=false] - Whether the search is case-sensitive.\n * @param {boolean} [options.wholeWord=false] - Whether to match whole words only.\n * @param {boolean} [options.regExp=false] - Whether the search string is a regular expression.\n * @returns {RegExp} - The regular expression created from the search string and options.\n */\nfunction toRegex(search, options) {\n\tconst { caseSensitive = false, wholeWord = false, regExp = false } = options;\n\n\tlet flags = caseSensitive ? \"gm\" : \"gim\";\n\tlet regexString = regExp ? search : escapeStringRegexp(search);\n\n\tif (wholeWord) {\n\t\tconst wordBoundary = \"\\\\b\";\n\t\tregexString = `${wordBoundary}${regexString}${wordBoundary}`;\n\t}\n\n\ttry {\n\t\treturn new RegExp(regexString, flags);\n\t} catch (error) {\n\t\tconst [, message] = error.message.split(/:(.*)/);\n\t\t$resultOverview.classList.add(\"error\");\n\t\t$resultOverview.textContent = strings[\"invalid regex\"].replace(\n\t\t\t\"{message}\",\n\t\t\tmessage || error.message,\n\t\t);\n\t\treturn null;\n\t}\n}\n\n/**\n * On cursor change event handler\n */\nasync function onCursorChange(line) {\n\tconst result = results[line];\n\tif (!result) return;\n\tconst { file, position } = result;\n\tif (!position) {\n\t\t// header line clicked; CM view folding not implemented yet\n\t\treturn;\n\t}\n\n\tSidebar.hide();\n\tconst { url } = filesSearched[file];\n\tawait openFile(url, { render: true });\n\tconst { editor } = editorManager;\n\ttry {\n\t\t// Compute offsets from row/column (rows from worker are 0-based)\n\t\tconst doc = editor.state.doc;\n\t\tconst startLine = doc.line(position.start.row + 1);\n\t\tconst endLine = doc.line(position.end.row + 1);\n\t\tconst from = Math.min(startLine.from + position.start.column, startLine.to);\n\t\tconst to = Math.min(endLine.from + position.end.column, endLine.to);\n\t\teditor.dispatch({\n\t\t\tselection: { anchor: from, head: to },\n\t\t\teffects: EditorView.scrollIntoView(from, { y: \"center\" }),\n\t\t});\n\t} catch (error) {\n\t\tconsole.warn(`Failed to focus search result at line ${line}.`, error);\n\t}\n}\n\n/**\n * When a file is added or removed from the file list\n * @param {import('lib/fileList').Tree} tree\n */\nfunction onFileUpdate(tree) {\n\tif (!tree || tree?.children) return;\n\tonInput();\n}\n\n/**\n * Add event listeners to file changes\n */\nfunction addEvents() {\n\tfiles.on(\"add-file\", onFileUpdate);\n\tfiles.on(\"remove-file\", onFileUpdate);\n\tfiles.on(\"add-folder\", onInput);\n\tfiles.on(\"remove-folder\", onInput);\n\tfiles.on(\"refresh\", onInput);\n\teditorManager.on(\"rename-file\", onInput);\n\teditorManager.on(\"file-content-changed\", onInput);\n}\n\n/**\n * Remove event listeners to file changes\n */\nfunction removeEvents() {\n\tfiles.off(\"add-file\", onFileUpdate);\n\tfiles.off(\"remove-file\", onFileUpdate);\n\tfiles.off(\"add-folder\", onInput);\n\tfiles.off(\"remove-folder\", onInput);\n\tfiles.off(\"refresh\", onInput);\n\teditorManager.off(\"rename-file\", onInput);\n\teditorManager.off(\"file-content-changed\", onInput);\n}\n"
  },
  {
    "path": "src/sidebarApps/searchInFiles/styles.scss",
    "content": ".search-in-files {\n  width: calc(100% - 40px - 20px) !important;\n  margin: 0 auto;\n  overflow: visible !important;\n\n  [is='details'] {\n    width: 100%;\n\n    >[is='summary'] {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      margin-bottom: 10px;\n\n      textarea,\n      input {\n        margin: 0 !important;\n        white-space: nowrap;\n        overflow-x: auto !important;\n      }\n\n      .marker::after {\n        width: 10px;\n        font-size: 1rem;\n        content: '\\25B6';\n      }\n    }\n\n    >[is='summary']~* {\n      display: none;\n      margin: 0 0 5px auto !important;\n    }\n\n    &[open] {\n      >[is='summary'] {\n        .marker::after {\n          content: '\\25BC';\n        }\n      }\n\n      >[is='summary']~* {\n        display: block;\n      }\n\n      >[is='summary']~div {\n        display: flex !important;\n        justify-content: space-between;\n        align-items: center;\n\n        >* {\n          margin: 0 !important;\n        }\n      }\n    }\n  }\n\n  .cm-editor {\n    height: 100%;\n    width: 100%;\n    background-color: var(--primary-color);\n    color: var(--primary-text-color);\n\n\n    .cm-content {\n      background-color: inherit;\n      color: inherit;\n      padding: 0 !important;\n\n      .cm-line {\n        padding: 0 !important;\n      }\n\n      .cm-line:nth-child(odd) {\n        background-color: rgba(0, 0, 0, 0.2) !important;\n      }\n    }\n\n    .cm-scroller {\n      overflow: auto;\n    }\n\n    .cm-line.cm-fileName .cm-foldChevron {\n      display: inline-flex;\n      align-items: center;\n      width: auto;\n      margin-right: 2px;\n      opacity: 0.9;\n      vertical-align: middle;\n    }\n\n\n    .cm-line.cm-fileName {\n      font-weight: 600;\n    }\n\n    .cm-line.cm-fileName .cm-fileIcon {\n      display: inline-flex;\n      align-items: center;\n      vertical-align: middle;\n      margin-right: 2px;\n      font-size: 1em;\n      line-height: 1;\n    }\n\n    .cm-line.cm-fileName .cm-fileCount {\n      display: inline-block;\n      margin-left: 8px;\n      font-size: 0.9em;\n      color: var(--secondary-text-color);\n    }\n\n    .cm-collapsedSummary {\n      padding: 4px 8px;\n      margin: 2px 0 6px 18px;\n      font-size: 0.9em;\n      color: var(--secondary-text-color);\n      background: color-mix(in srgb, var(--secondary-color) 60%, transparent);\n      border: 1px solid var(--border-color);\n      border-radius: 4px;\n    }\n\n    /* Match highlight */\n    .cm-match {\n      background: color-mix(in srgb, var(--active-color) 60%, transparent);\n      color: var(--secondary-text-color);\n      outline: 1px solid var(--border-color);\n      border-radius: 2px;\n      font-weight: 500;\n    }\n  }\n\n  .cm-editor {\n    width: 100% !important;\n    margin-left: 0 !important;\n  }\n\n  .search-in-file-editor {\n    height: 100%;\n    min-height: 0;\n    overflow: hidden;\n    display: block;\n  }\n\n  textarea {\n    height: auto;\n    resize: none;\n    overflow-y: hidden;\n  }\n\n  .extras {\n    display: block !important;\n    line-height: 0px !important;\n    margin-bottom: 10px !important;\n    text-align: right !important;\n  }\n\n  .options {\n    width: 100%;\n    margin-bottom: 10px;\n    text-align: right;\n  }\n\n  button {\n    display: inline-block;\n    background-color: transparent;\n    border: none;\n    color: rgb(37, 37, 37);\n    color: var(--secondary-text-color);\n  }\n\n  .error {\n    color: orangered;\n  }\n\n  .search-result {\n    position: relative;\n    padding-bottom: 5px;\n  }\n}"
  },
  {
    "path": "src/sidebarApps/searchInFiles/worker.js",
    "content": "import \"core-js/stable\";\nimport picomatch from \"picomatch/posix\";\n\nconst resolvers = {};\n\nself.onmessage = (ev) => {\n\tconst { action, data, error, id } = ev.data;\n\tswitch (action) {\n\t\tcase \"search-files\":\n\t\t\tprocessFiles(data, \"search\");\n\t\t\tbreak;\n\n\t\tcase \"replace-files\":\n\t\t\tprocessFiles(data, \"replace\");\n\t\t\tbreak;\n\n\t\tcase \"get-file\": {\n\t\t\tif (!resolvers[id]) return;\n\t\t\tconst cb = resolvers[id];\n\t\t\tcb(data, error);\n\t\t\tdelete resolvers[id];\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t\treturn false;\n\t}\n};\n\n/**\n * Process files for search or replace operations.\n *\n * @param {object} data - The data containing files, search, replace, and options.\n * @param {'search' | 'replace'} [mode='search'] - The mode of operation (search or replace).\n */\nfunction processFiles(data, mode = \"search\") {\n\tconst process = mode === \"search\" ? searchInFile : replaceInFile;\n\tconst { files, search, replace, options } = data;\n\tconst { test: skip } = Skip(options);\n\tconst total = files.length;\n\tlet count = 0;\n\n\tfiles.forEach(processFile);\n\n\t/**\n\t * Process a file for search or replace operation.\n\t *\n\t * @param {object} file - The file object to process.\n\t * @param {string} file.url - The URL of the file.\n\t */\n\tfunction processFile(file) {\n\t\tif (skip(file)) {\n\t\t\tdone(++count / total, mode);\n\t\t\treturn;\n\t\t}\n\n\t\tgetFile(file.url, (res, err) => {\n\t\t\tif (err) {\n\t\t\t\tdone(++count / total, mode);\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tprocess({ file, content: res, search, replace, options });\n\t\t\tdone(++count / total, mode);\n\t\t});\n\t}\n}\n\n/**\n * Search for a string in the content of a file.\n * @param {object} arg - The content of the file to search.\n * @param {import('lib/fileList').Tree} arg.file - The file.\n * @param {string} arg.content - The file content.\n * @param {RegExp} arg.search - The string to search for.\n */\nfunction searchInFile({ file, content, search }) {\n\tconst matches = [];\n\n\tlet text = `${file.name}`;\n\tlet match;\n\n\tif (text.length > 30) {\n\t\ttext = `...${text.slice(-30)}`;\n\t}\n\n\twhile ((match = search.exec(content))) {\n\t\tconst [word] = match;\n\t\tconst start = match.index;\n\t\tconst end = start + word.length;\n\t\tconst position = {\n\t\t\tstart: getLineColumn(content, start),\n\t\t\tend: getLineColumn(content, end),\n\t\t};\n\t\tconst [line, renderText] = getSurrounding(content, word, start, end);\n\t\ttext += `\\n\\t${line.trim()}`;\n\t\tmatches.push({ match: word, position, renderText });\n\t}\n\n\tself.postMessage({\n\t\taction: \"search-result\",\n\t\tdata: {\n\t\t\tfile,\n\t\t\tmatches,\n\t\t\ttext,\n\t\t},\n\t});\n}\n\n/**\n * Replace a string in the content of a file.\n * @param {object} arg - The content of the file to search.\n * @param {import('lib/fileList').Tree} arg.file - The content of the file to search.\n * @param {string} content - The content of the file to search.\n * @param {RegExp} arg.search - The string to search for.\n * @param {string} arg.replace - The string to replace with.\n */\nfunction replaceInFile({ file, content, search, replace }) {\n\tconst text = content.replace(search, replace);\n\n\tself.postMessage({\n\t\taction: \"replace-result\",\n\t\tdata: { file, text },\n\t});\n}\n\n/**\n * Gets surrounding text of a match.\n * @param {string} content\n * @param {string} word\n * @param {number} start\n * @param {number} end\n */\nfunction getSurrounding(content, word, start, end) {\n\tconst max = 50;\n\tconst remaining = max - (end - start);\n\tlet result = [];\n\n\tif (remaining <= 0) {\n\t\tword = word.slice(-max);\n\t\tresult = [`...${word}`, word];\n\t} else {\n\t\tlet left = Math.floor(remaining / 2);\n\t\tlet right = left;\n\n\t\tlet leftText = content.substring(start - left, start);\n\t\tlet rightText = content.substring(end, end + right);\n\n\t\tresult = [`${leftText}${word}${rightText}`, word];\n\t}\n\n\treturn result.map((text) => text.replace(/[\\r\\n]+/g, \" ⏎ \"));\n}\n\n/**\n * Determines the line and column numbers for a given position in the file.\n *\n * @param {string} file - The file content as a string.\n * @param {number} position - The position in the file for which line and column\n * numbers are to be determined.\n *\n * @returns {Object} An object with 'line' and 'column' properties, representing\n * the line and column numbers respectively for the given position.\n *\n * @example\n *\n * const file = 'Hello, this is a test.\\nAnother test is here.';\n * const position = 15;\n * const lineColumn = getLineColumn(file, position);\n *\n * // lineColumn: { line: 1, column: 16 }\n */\nfunction getLineColumn(file, position) {\n\tconst lines = file.substring(0, position).split(\"\\n\");\n\tconst lineNumber = lines.length - 1;\n\tconst columnNumber = lines[lineNumber].length;\n\treturn { row: lineNumber, column: columnNumber };\n}\n\n/**\n * Retrieves the contents of a file from the main thread.\n * @param {string} url\n * @param {function} cb\n */\nfunction getFile(url, cb) {\n\tconst id = Number.parseInt(Date.now() + Math.random() * 1000000);\n\tresolvers[id] = cb;\n\tself.postMessage({\n\t\taction: \"get-file\",\n\t\tdata: url,\n\t\tid,\n\t});\n}\n\n/**\n * Sends a message to the main thread to indicate that the worker is done searching\n * or replacing.\n * @param {boolean} ratio\n * @param {'search'|'replace'} mode\n */\nfunction done(ratio, mode) {\n\tif (ratio === 1) {\n\t\tself.postMessage({\n\t\t\taction: \"progress\",\n\t\t\tdata: 100,\n\t\t});\n\t\tself.postMessage({\n\t\t\taction: `done-${mode === \"search\" ? \"searching\" : \"replacing\"}`,\n\t\t});\n\t} else {\n\t\tself.postMessage({\n\t\t\taction: \"progress\",\n\t\t\tdata: Math.floor(ratio * 100),\n\t\t});\n\t}\n}\n\n/**\n * Creates a skip function that filters files based on exclusion and inclusion patterns.\n *\n * @param {object} arg - The exclusion patterns separated by commas.\n * @param {string} arg.exclude - The exclusion patterns separated by commas.\n * @param {string} arg.include - The inclusion patterns separated by commas.\n */\nfunction Skip({ exclude, include }) {\n\t// Default exclude patterns for binary/media/archives/fonts/etc.\n\tconst defaultExcludes = [\n\t\t\"**/*.png\",\n\t\t\"**/*.jpg\",\n\t\t\"**/*.jpeg\",\n\t\t\"**/*.gif\",\n\t\t\"**/*.bmp\",\n\t\t\"**/*.webp\",\n\t\t\"**/*.avif\",\n\t\t\"**/*.ico\",\n\t\t\"**/*.svgz\",\n\t\t\"**/*.mp3\",\n\t\t\"**/*.wav\",\n\t\t\"**/*.ogg\",\n\t\t\"**/*.flac\",\n\t\t\"**/*.m4a\",\n\t\t\"**/*.aac\",\n\t\t\"**/*.mp4\",\n\t\t\"**/*.mkv\",\n\t\t\"**/*.webm\",\n\t\t\"**/*.mov\",\n\t\t\"**/*.avi\",\n\t\t\"**/*.zip\",\n\t\t\"**/*.gz\",\n\t\t\"**/*.bz2\",\n\t\t\"**/*.xz\",\n\t\t\"**/*.7z\",\n\t\t\"**/*.rar\",\n\t\t\"**/*.tar\",\n\t\t\"**/*.exe\",\n\t\t\"**/*.dll\",\n\t\t\"**/*.so\",\n\t\t\"**/*.bin\",\n\t\t\"**/*.class\",\n\t\t\"**/*.ttf\",\n\t\t\"**/*.otf\",\n\t\t\"**/*.woff\",\n\t\t\"**/*.woff2\",\n\t\t\"**/*.pdf\",\n\t\t\"**/*.psd\",\n\t\t\"**/*.ai\",\n\t\t\"**/*.sketch\",\n\t];\n\tconst userExcludes = (exclude ? exclude.split(\",\") : [])\n\t\t.map((p) => p.trim())\n\t\t.filter(Boolean);\n\tconst excludeFiles = [...defaultExcludes, ...userExcludes];\n\tconst includeFiles = (include ? include.split(\",\") : [\"**\"]).map((p) =>\n\t\tp.trim(),\n\t);\n\n\t/**\n\t * Tests whether a file should be skipped based on exclusion and inclusion patterns.\n\t *\n\t * @param {object} file - The file to be tested.\n\t * @param {string} file.path - The relative URL of the file.\n\t * @returns {boolean} - Returns true if the file should be skipped, false otherwise.\n\t */\n\tfunction test(file) {\n\t\tif (!file.path) return false;\n\t\tconst match = (pattern) =>\n\t\t\tpicomatch.isMatch(file.path, pattern, { matchBase: true });\n\t\treturn excludeFiles.some(match) || !includeFiles.some(match);\n\t}\n\n\treturn {\n\t\ttest,\n\t};\n}\n\n/**\n * @typedef {Object} Match\n * @property {string} line - The line of the file where the match was found.\n * @property {string} text - Match result converted to a string.\n * @property {Object} position - An object representing the start and end positions of the match.\n * @property {Object} position.start - An object with properties line and column representing the start position.\n * @property {number} position.start.line - The line number of the start position.\n * @property {number} position.start.column - The column number of the start position.\n * @property {Object} position.end - An object with properties line and column representing the end position.\n * @property {number} position.end.line - The line number of the end position.\n * @property {number} position.end.column - The column number of the end position.\n */\n"
  },
  {
    "path": "src/sidebarApps/sidebarApp.js",
    "content": "/**@type {HTMLElement} */\nlet $apps;\n/**@type {HTMLElement} */\nlet $sidebar;\n/**@type {HTMLElement} */\nlet $contaienr;\n\nexport default class SidebarApp {\n\t/**@type {HTMLSpanElement} */\n\t#icon;\n\t/**@type {string} */\n\t#id;\n\t/**@type {string} */\n\t#init;\n\t/**@type {string} */\n\t#title;\n\t/**@type {boolean} */\n\t#active;\n\t/**@type {(el:HTMLElement)=>void} */\n\t#onselect;\n\t/**@type {HTMLElement} */\n\t#container;\n\n\t/**\n\t * Creates a new sidebar app.\n\t * @param {string} icon\n\t * @param {string} id\n\t * @param {string} title\n\t * @param {(el:HTMLElement)=>void} init\n\t * @param {(el:HTMLElement)=>void} onselect\n\t */\n\tconstructor(icon, id, title, init, onselect) {\n\t\tconst emptyFunc = () => {};\n\t\tthis.#container = <div className=\"container\"></div>;\n\t\tthis.#icon = <Icon icon={icon} id={id} title={title} />;\n\t\tthis.#id = id;\n\t\tthis.#title = title;\n\t\tthis.#init = init || emptyFunc;\n\t\tthis.#onselect = onselect || emptyFunc;\n\t\tthis.#init(this.#container);\n\t}\n\n\t/**\n\t * Installs the app in the sidebar.\n\t * @param {boolean} prepend\n\t * @returns {void}\n\t */\n\tinstall(prepend = false) {\n\t\tif (prepend) {\n\t\t\t$apps.prepend(this.#icon);\n\t\t\treturn;\n\t\t}\n\n\t\t$apps.append(this.#icon);\n\t}\n\n\t/**\n\t * Initialize the sidebar element.\n\t * @param {HTMLElement} $el  sidebar element\n\t * @param {HTMLElement} $el2 apps element\n\t */\n\tstatic init($el, $el2) {\n\t\t$sidebar = $el;\n\t\t$apps = $el2;\n\t}\n\n\t/**@type {HTMLSpanElement} */\n\tget icon() {\n\t\treturn this.#icon;\n\t}\n\n\t/**@type {string} */\n\tget id() {\n\t\treturn this.#id;\n\t}\n\n\t/**@type {string} */\n\tget title() {\n\t\treturn this.#title;\n\t}\n\n\t/**@type {boolean} */\n\tget active() {\n\t\treturn !!this.#active;\n\t}\n\n\t/**@param {boolean} value */\n\tset active(value) {\n\t\tconst nextValue = !!value;\n\t\tif (this.#active === nextValue) return;\n\n\t\tthis.#active = nextValue;\n\t\tthis.#icon.classList.toggle(\"active\", this.#active);\n\t\tif (this.#active) {\n\t\t\tconst oldContainer = getContainer(this.#container);\n\t\t\t// Try to replace the old container, or append if it's not in the DOM\n\t\t\ttry {\n\t\t\t\tif (oldContainer && oldContainer.parentNode === $sidebar) {\n\t\t\t\t\t$sidebar.replaceChild($contaienr, oldContainer);\n\t\t\t\t} else {\n\t\t\t\t\t// Old container not in sidebar, just append the new one\n\t\t\t\t\tconst existingContainer = $sidebar.get(\".container\");\n\t\t\t\t\tif (existingContainer) {\n\t\t\t\t\t\t$sidebar.replaceChild($contaienr, existingContainer);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$sidebar.appendChild($contaienr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Fallback: append the new container\n\t\t\t\tconsole.warn(\"Error switching sidebar container:\", error);\n\t\t\t\tconst existingContainer = $sidebar.get(\".container\");\n\t\t\t\tif (existingContainer) {\n\t\t\t\t\texistingContainer.remove();\n\t\t\t\t}\n\t\t\t\t$sidebar.appendChild($contaienr);\n\t\t\t}\n\t\t\tthis.#onselect(this.#container);\n\t\t}\n\t}\n\n\t/**@type {HTMLElement} */\n\tget container() {\n\t\treturn this.#container;\n\t}\n\n\t/**@type {(el:HTMLElement)=>void} */\n\tget init() {\n\t\treturn this.#init;\n\t}\n\n\t/**@type {(el:HTMLElement)=>void} */\n\tget onselect() {\n\t\treturn this.#onselect;\n\t}\n\n\tremove() {\n\t\tif (this.#icon) {\n\t\t\tthis.#icon.remove();\n\t\t\tthis.#icon = null;\n\t\t}\n\t\tif (this.#container) {\n\t\t\tthis.#container.remove();\n\t\t\tthis.#container = null;\n\t\t}\n\t}\n}\n\n/**\n * Creates a icon element for a sidebar app.\n * @param {object} param0\n * @param {string} param0.icon\n * @param {string} param0.id\n * @returns {HTMLElement}\n */\nfunction Icon({ icon, id, title }) {\n\tconst className = `icon ${icon}`;\n\treturn (\n\t\t<span\n\t\t\tdata-action=\"sidebar-app\"\n\t\t\tdata-id={id}\n\t\t\ttitle={title}\n\t\t\tclassName={className}\n\t\t></span>\n\t);\n}\n\n/**\n * Gets the container or sets it if it's not set.\n * @param {HTMLElement} $el\n * @returns {HTMLElement}\n */\nfunction getContainer($el) {\n\tconst res = $contaienr;\n\n\tif ($el) {\n\t\t$contaienr = $el;\n\t}\n\n\treturn res || $sidebar.get(\".container\");\n}\n"
  },
  {
    "path": "src/styles/codemirror.scss",
    "content": ".editor-container {\n  position: relative;\n}\n\n.editor-container > .cursor-menu {\n  z-index: 600;\n}\n\n.cm-tooltip {\n  box-sizing: border-box;\n  max-width: min(32rem, calc(100vw - 1.25rem));\n  width: max-content;\n  padding: 0.4rem 0.45rem;\n  border-radius: 0;\n  overscroll-behavior: contain;\n  overflow-y: auto;\n  max-height: min(70vh, 22rem);\n\n  .cm-tooltip-section + .cm-tooltip-section {\n    margin-top: 0.5rem;\n  }\n}\n\n.cm-tooltip.cm-tooltip-hover {\n  font-size: 0.9rem;\n  line-height: 1.45;\n  word-break: break-word;\n  max-height: min(65vh, 20rem);\n}\n\n.cm-tooltip.cm-tooltip-autocomplete {\n  display: flex;\n  flex-wrap: nowrap;\n  align-items: stretch;\n  gap: 0.4rem;\n  width: auto;\n  min-width: min(15rem, calc(100vw - 1.75rem));\n  max-width: min(32rem, calc(100vw - 1.25rem));\n  max-height: min(60vh, 20rem);\n  padding: 0.25rem;\n  overflow: visible;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete > ul {\n  flex: 1 1 auto;\n  max-height: inherit;\n  overflow: auto;\n  padding: 0.25rem;\n  margin: 0;\n  scrollbar-gutter: stable;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete > ul > li {\n  display: flex;\n  align-items: center;\n  gap: 0.12rem;\n  padding: 0.3rem 0.36rem;\n  border-radius: 0.2rem;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete .cm-completionIcon {\n  flex: 0 0 auto;\n  min-width: 1rem;\n  text-align: center;\n  line-height: 1;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete .cm-completionLabel {\n  flex: 1 1 auto;\n  font-size: 0.95em;\n  line-height: 1.4;\n  overflow-wrap: anywhere;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete .cm-completionMatchedText {\n  font-weight: 600;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete .cm-completionDetail {\n  margin-left: auto;\n  font-size: 0.85em;\n}\n\n.cm-tooltip.cm-tooltip-autocomplete .cm-completionInfo {\n  flex: 1 1 45%;\n  min-width: min(12rem, calc(100vw - 3rem));\n  max-width: min(18rem, calc(100vw - 2.5rem));\n  max-height: inherit;\n  padding: 0.3rem 0.35rem;\n  font-size: 0.85rem;\n  line-height: 1.35;\n  overflow: auto;\n}\n\n@media (max-width: 480px) {\n  .cm-tooltip {\n    font-size: 0.9rem;\n    max-width: calc(100vw - 1.25rem);\n    max-height: min(70vh, 20rem);\n  }\n\n  .cm-tooltip.cm-tooltip-autocomplete {\n    flex-direction: column;\n    min-width: min(13.5rem, calc(100vw - 1.5rem));\n    max-width: calc(100vw - 1.35rem);\n    max-height: min(65vh, 18rem);\n  }\n\n  .cm-tooltip.cm-tooltip-autocomplete > ul > li {\n    padding: 0.32rem 0.4rem;\n  }\n\n  .cm-tooltip.cm-tooltip-autocomplete .cm-completionInfo {\n    min-width: auto;\n    max-width: 100%;\n    max-height: 12rem;\n    padding: 0.35rem 0.4rem 0.2rem;\n  }\n}\n"
  },
  {
    "path": "src/styles/console.m.scss",
    "content": "c-toggler {\n  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAQAAABKfvVzAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAE9JREFUOMtjYBh0gBHO+k+cSkYilcPVMpHqpIHRwIgUFERp+I8SekQ5CY8WXH7AqYWJyGglqIERV3QykaYcV7DiSSwsODw8yJIGdTPQIAQAg9gKJl7UINwAAAAASUVORK5CYII=);\n  background-position: center;\n  background-repeat: no-repeat;\n  background-size: 24px;\n  position: fixed;\n  top: 0;\n  left: 0;\n  height: 30px;\n  width: 30px;\n  background-color: #fff;\n  transform-origin: center;\n  border-radius: 50%;\n  box-shadow: -2px 2px 8px rgba(0, 0, 0, .4);\n  z-index: 99999;\n  opacity: 0.5;\n}\n\nc-object {\n  color: #9999ff;\n  text-decoration: underline;\n}\n\nc-toggler:active {\n  box-shadow: -1px 1px 4px rgba(0, 0, 0, .4)\n}\n\nc-line {\n  display: block;\n}\n\nc-console {\n  box-sizing: border-box;\n  overflow-y: auto;\n  position: fixed;\n  top: 0;\n  left: 0;\n  height: 100vh;\n  width: 100vw;\n  background-color: #313131;\n  z-index: 99998;\n  color: #eeeeee;\n  font-family: var(--app-font-family);\n}\n\nc-console[title] {\n  padding-top: 65px;\n  animation: --page-transition .1s ease 1;\n}\n\nc-console br:last-of-type {\n  display: none;\n}\n\nc-console textarea {\n  color: white;\n  caret-color: currentColor !important;\n  background-color: inherit;\n}\n\nc-input {\n  display: flex;\n  width: 100%;\n  height: fit-content;\n}\n\nc-input::before {\n  content: '>>';\n  margin: 0 5px;\n  height: 100%;\n}\n\n#__c-input {\n  width: 100%;\n  border: none;\n  resize: none;\n  height: 200px;\n  position: relative;\n  background-color: transparent;\n  overflow: visible;\n}\n\n#__c-input:focus {\n  outline: none;\n}\n\nc-console[title]::before {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  background-color: inherit;\n  z-index: 999999;\n  content: attr(title);\n  display: flex;\n  height: 44px;\n  align-items: center;\n  justify-content: center;\n  font-family: Verdana, Geneva, Tahoma, sans-serif;\n  font-weight: 900;\n  box-shadow: 0 2px 4px rgba(0, 0, 0, .2);\n  margin-bottom: 10px;\n  color: white;\n  font-size: medium;\n}\n\nc-message {\n  position: relative;\n  display: flex;\n  border-bottom: solid 1px rgba(204, 204, 204, 0.4);\n  margin-bottom: 35px;\n  font-size: .9rem;\n  flex-wrap: wrap;\n}\n\nc-code {\n  position: relative;\n  color: rgb(214, 211, 211);\n  font-size: 1em;\n  font-family: 'Courier New', Courier, monospace;\n  overflow-x: auto;\n  white-space: pre;\n  margin-bottom: 0px;\n  border: 'none';\n}\n\nc-code::after {\n  content: 'use';\n  background-color: #666;\n  color: inherit;\n  border-radius: 4px;\n  padding: 0 0.4rem;\n  font-size: 0.6rem;\n}\n\nc-code::before {\n  content: '>>';\n  padding: 0 5px;\n  font-style: italic;\n}\n\nc-key {\n  font-size: 0.9rem;\n  color: #cc66ff;\n}\n\nc-message[log-level=error] {\n  border-bottom: solid 1px rgba(255, 255, 255, 0.4);\n  background-color: #422;\n  color: inherit;\n}\n\nc-message[log-level=error]::after {\n  background-color: #cc4343;\n  color: inherit\n}\n\nc-message[log-level=warn] {\n  border-bottom: solid 1px rgba(255, 255, 255, 0.4);\n  background-color: #633;\n  color: inherit;\n}\n\nc-message[log-level=warn]::after {\n  background-color: #cc6969;\n  color: inherit\n}\n\nc-stack:not(:empty) {\n  content: attr(data-stack);\n  font-family: Verdana, Geneva, Tahoma, sans-serif;\n  position: absolute;\n  top: 100%;\n  right: 0;\n  display: flex;\n  height: 20px;\n  align-items: center;\n  justify-content: space-between;\n  width: 100vw;\n  background-color: inherit;\n  padding: 0 5px;\n  box-sizing: border-box;\n  font-size: .8rem;\n  color: inherit;\n}\n\nc-text {\n  padding: 2px;\n  white-space: pre;\n  font-family: Verdana, Geneva, Tahoma, sans-serif;\n  overflow: auto;\n  box-sizing: border-box;\n  max-width: 100vw;\n  font-size: 0.9rem;\n  width: 100%;\n  padding-left: 10px;\n  white-space: break-spaces;\n}\n\nc-text.__c-boolean {\n  color: rgb(130, 80, 177);\n}\n\nc-text.__c-number {\n  color: rgb(97, 88, 221);\n}\n\nc-text.__c-symbol {\n  color: rgb(111, 89, 172);\n}\n\nc-text.__c-function {\n  color: rgb(145, 136, 168);\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 0.9rem;\n}\n\nc-text.__c-function::before {\n  content: 'ƒ';\n  margin: 0 2px;\n  font-style: italic;\n  color: #9999ff;\n}\n\nc-text.__c-object,\nc-text.__c-undefined {\n  color: rgb(118, 163, 118);\n}\n\nc-text.__c-string {\n  color: rgb(59, 161, 59);\n}\n\nc-text.__c-string:not(.no-quotes)::before {\n  content: '\"';\n  margin-right: 2px;\n}\n\nc-text.__c-string:not(.no-quotes)::after {\n  content: '\"';\n  margin-left: 2px;\n}\n\nc-message.error c-text {\n  overflow: unset;\n  white-space: pre-wrap;\n  word-break: break-word;\n  color: white;\n}\n\nc-group {\n  display: none;\n  margin-left: 14px;\n}\n\nc-type[type=\"body-toggler\"].__show-data+c-group {\n  display: block;\n}\n\nc-type[type=\"body-toggler\"]::before {\n  display: inline-block;\n  content: '▸';\n  margin-right: 2.5px;\n}\n\nc-type[type=\"body-toggler\"]::after {\n  content: '{...}';\n}\n\nc-type[type=\"body-toggler\"].__show-data::before {\n  content: '▾';\n}\n\nc-type[type=\"body-toggler\"].__show-data::after {\n  display: none;\n}\n\nc-table {\n  display: table;\n  width: 100%;\n  border-collapse: collapse;\n  border-spacing: 0;\n  font-size: 0.9rem;\n  color: rgb(214, 211, 211);\n  border: solid 1px rgba(204, 204, 204, 0.4);\n}\n\nc-table c-row {\n  display: table-row;\n  border-bottom: solid 1px rgba(204, 204, 204, 0.4);\n}\n\nc-table c-row:last-child {\n  border-bottom: none;\n}\n\nc-table c-row:first-child {\n  font-weight: bold;\n}\n\nc-table c-cell {\n  display: table-cell;\n  padding: 5px;\n  border-bottom: solid 1px rgba(204, 204, 204, 0.4);\n}\n\nc-table c-cell:not(:last-child) {\n  border-left: solid 1px rgba(204, 204, 204, 0.4);\n}\n\n@keyframes --page-transition {\n  0% {\n    opacity: 0;\n    transform: translate3d(0, 50%, 0)\n  }\n\n  100% {\n    opacity: 1;\n    transform: translate3d(0, 0, 0)\n  }\n}\n"
  },
  {
    "path": "src/styles/fileInfo.scss",
    "content": "#file-info {\n  overflow-x: hidden;\n  padding: 0.8rem;\n\n  .file-header {\n    margin-bottom: 1.5rem;\n\n    .file-name {\n      font-size: 1.25rem;\n      font-weight: 500;\n      color: var(--popup-text-color);\n      display: flex;\n      align-items: center;\n      gap: 0.5rem;\n      flex-wrap: wrap;\n\n      .name-part {\n        max-width: 100%;\n        overflow: hidden;\n        text-overflow: ellipsis;\n        white-space: nowrap;\n        position: relative;\n\n        &:hover {\n          overflow: visible;\n          white-space: normal;\n          word-break: break-all;\n          z-index: 1;\n          border-radius: 4px;\n          background: var(--popup-background-color);\n        }\n      }\n      .file-extension {\n        background: color-mix(\n          in srgb,\n          var(--button-background-color) 40%,\n          transparent\n        );\n        padding: 0.25rem 0.5rem;\n        border-radius: 4px;\n        font-size: 0.875rem;\n        color: var(--active-color);\n        font-weight: 600;\n        letter-spacing: 0.5px;\n        text-transform: uppercase;\n        flex-shrink: 0;\n      }\n    }\n  }\n\n  .info-grid {\n    display: grid;\n    grid-template-columns: repeat(2, 1fr);\n    gap: 1.5rem;\n  }\n\n  .info-item {\n    display: flex;\n    flex-direction: column;\n    gap: 0.5rem;\n\n    .info-label {\n      font-size: 0.875rem;\n      color: color-mix(in srgb, var(--popup-text-color) 60%, transparent);\n      text-transform: uppercase;\n      letter-spacing: 0.5px;\n    }\n\n    .info-value {\n      font-size: 1rem;\n      color: var(--popup-text-color);\n      font-weight: 500;\n    }\n  }\n\n  .path-section {\n    margin-top: 1.5rem;\n    padding-top: 1.5rem;\n    border-top: 1px solid var(--border-color);\n\n    .path-value {\n      word-break: break-all;\n    }\n  }\n\n  @media (max-width: 480px) {\n    .file-card {\n      padding: 1.5rem;\n    }\n\n    .info-grid {\n      grid-template-columns: 1fr;\n      gap: 1rem;\n    }\n  }\n}\n"
  },
  {
    "path": "src/styles/fonts.scss",
    "content": "@font-face {\n  font-family: 'Fira Code';\n  src: url('../res/fonts/FiraCode.ttf') format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}\n\n@font-face {\n  font-family: 'Roboto Mono';\n  font-style: normal;\n  font-weight: 400;\n  font-display: swap;\n  src: url('../res/fonts/RobotoMono.ttf') format('truetype');\n  unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;\n}\n\n@font-face {\n  font-family: 'Source Code';\n  src: url('../res/fonts/SourceCodePro.ttf') format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}\n\n@font-face {\n  font-family: 'Cascadia Code';\n  src: url('../res/fonts/CascadiaCode.ttf') format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}\n\n@font-face {\n  font-family: 'Proggy Clean';\n  src: url('../res/fonts/ProggyClean.ttf') format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}\n\n@font-face {\n  font-family: 'JetBrains Mono Bold';\n  src: url('../res/fonts/JetBrainsMono-Bold.ttf') format('truetype');\n  font-weight: 300 700;\n}\n\n@font-face {\n  font-family: 'JetBrains Mono Regular';\n  src: url('../res/fonts/JetBrainsMono-Regular.ttf') format('truetype');\n  font-weight: 300 700;\n  font-style: normal;\n}\n\n@font-face {\n  font-display: swap;\n  font-family: Poppins;\n  font-style: normal;\n  font-weight: 400;\n  src: local('Poppins-Regular'), url('../res/fonts/Poppins-Regular.woff2') format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n  font-display: swap;\n  font-family: Poppins;\n  font-style: normal;\n  font-weight: 500;\n  src: local('Poppins-Medium'), url('../res/fonts/Poppins-Medium.woff2') format('woff2');\n  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;\n}\n\n@font-face {\n  font-family: Righteous;\n  font-style: normal;\n  font-weight: 400;\n  src: url('../res/fonts/righteous-latin-regular.woff') format('woff');\n}\n\n@font-face {\n  font-display: swap;\n  font-family: NotoMono;\n  src: url('../res/fonts/NotoMono-Regular.woff') format(\"woff\");\n  font-weight: 400;\n  font-style: normal;\n}\n\n@font-face {\n  font-display: swap;\n  font-family: NotoMono;\n  src: url('../res/fonts/CourierNew-Regular.woff2') format(\"woff2\");\n  font-weight: 400;\n  font-style: normal;\n  unicode-range: U+0590-06FF;\n}"
  },
  {
    "path": "src/styles/keyframes.scss",
    "content": "@keyframes circular-loader-animation {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@keyframes linear-loader-animation {\n  0% {\n    background-color: rgb(0, 0, 0);\n    transform: scale3d(0, 1, 1);\n  }\n\n  50% {\n    background-color: rgb(255, 255, 255);\n    transform: scale3d(4, 1, 1);\n  }\n\n  100% {\n    background-color: rgb(0, 0, 0);\n    transform: scale3d(0, 1, 1);\n  }\n}\n\n@keyframes show-searchbar {\n  from {\n    opacity: 0;\n    transform: translate3d(0, -100%, 0);\n  }\n\n  to {\n    opacity: 1;\n    transform: translate3d(0, 0, 0);\n  }\n}\n\n@keyframes show-sidebar {\n  0% {\n    transform: translate(-100%, 0);\n  }\n\n  100% {\n    transform: translate(0, 0);\n  }\n}\n\n@keyframes menu-grow {\n  0% {\n    transform: scale(0) translateZ(0);\n    opacity: 0;\n  }\n\n  80% {\n    opacity: 1;\n  }\n\n  100% {\n    transform: scale(1) translateZ(0);\n  }\n}\n\n@keyframes scrollbar-show {\n  0% {\n    opacity: 0;\n  }\n\n  100% {\n    opacity: 1;\n  }\n}\n\n@keyframes slide-up {\n  0% {\n    transform: translateY(100%);\n  }\n\n  100% {\n    transform: translateY(0);\n  }\n}\n\n@keyframes hide-loader {\n  from {\n    transform: translateX(-50%) translateY(0) scale3d(1, 1, 1);\n    opacity: 1;\n  }\n\n  to {\n    transform: translateX(-50%) translateY(-100%) scale3d(0.5, 0.5, 1);\n    opacity: 0;\n  }\n}\n\n@keyframes appear {\n  from {\n    transform: translateX(-50%) translateY(-100%) scale3d(0.5, 0.5, 1);\n    opacity: 0.5;\n  }\n\n  to {\n    transform: translateX(-50%) translateY(0) scale3d(1, 1, 1);\n    opacity: 1;\n  }\n}\n\n@keyframes sake {\n  90% {\n    transform: translate3d(0, 0, 0);\n  }\n\n  93% {\n    transform: translate3d(-10px, 0, 0);\n  }\n\n  97% {\n    transform: translate3d(10px, 0, 0);\n  }\n\n  100% {\n    transform: translate3d(0, 0, 0);\n  }\n}\n\n@keyframes move-around {\n  0% {\n    transform: scaleX(1) translate3d(-100px, 0, 0);\n    background-color: rgb(211, 106, 106);\n  }\n\n  25% {\n    background-color: rgb(157, 211, 106);\n  }\n\n  50% {\n    transform: scaleX(1) translate3d(100px, 0, 0);\n    background-color: rgb(211, 106, 197);\n  }\n\n  75% {\n    background-color: rgb(130, 106, 211);\n  }\n\n  100% {\n    transform: scaleX(1) translate3d(-100px, 0, 0);\n    background-color: rgb(211, 106, 106);\n  }\n}\n\n@keyframes slow-appear {\n  0% {\n    opacity: 0;\n  }\n\n  100% {\n    opacity: 1;\n  }\n}\n\n@keyframes strech {\n  from {\n    opacity: 0;\n    transform: scale(0, 1);\n  }\n\n  to {\n    opacity: 1;\n    transform: scale(1, 1);\n  }\n}\n\n@keyframes page-transition {\n  0% {\n    opacity: 0;\n    transform: translate3d(0, 50%, 0);\n  }\n\n  100% {\n    opacity: 1;\n    transform: translate3d(0, 0, 0);\n  }\n}\n\n@keyframes page-transition-opacity {\n  0% {\n    opacity: 0;\n  }\n\n  100% {\n    opacity: 1;\n  }\n}\n\n@keyframes float-appear {\n  0% {\n    opacity: 0;\n    box-shadow: none;\n    transform: scale(0.6) rotate(360deg) translateZ(0);\n  }\n\n  100% {\n    opacity: 1;\n    box-shadow: none;\n    transform: scale(1) rotate(0deg) translateZ(0);\n  }\n}"
  },
  {
    "path": "src/styles/list.scss",
    "content": "@use \"./mixins.scss\";\n\n.list {\n  width: 100%;\n\n  &:not(.collapsible) {\n    overflow-y: auto;\n  }\n\n  &.collapsible {\n    &.hidden {\n      ul {\n        display: none;\n      }\n\n      >.tile {\n        >.folder::before {\n          content: \"\\e92c\" !important;\n        }\n\n        >.indicator::before {\n          content: \"\\e9bd\" !important;\n        }\n      }\n    }\n\n    >.tile {\n      position: relative;\n      height: 36px;\n      font-size: 1em;\n\n      background-color: rgba($color: #000000, $alpha: 0.1);\n\n      &.loading {\n        @include mixins.linear-loader(30%, 2px);\n      }\n\n      >.folder::before {\n        content: \"\\e92d\";\n      }\n\n      >.indicator::before {\n        content: \"\\e9a6\";\n      }\n\n      .icon {\n        height: 36px;\n        min-width: 36px;\n        font-size: 1.15em;\n      }\n    }\n\n    ul {\n      list-style: none;\n      padding-left: 10px;\n      box-sizing: border-box;\n\n      >.tile {\n        height: 34px;\n\n        .icon {\n          height: 34px;\n          min-width: 34px;\n          font-size: 1.1em;\n        }\n      }\n\n      .collapsible {\n        >.tile {\n          background-color: transparent;\n        }\n      }\n    }\n\n    .icon.lang {\n      padding-right: 5px;\n      font-family: var(--app-font-family);\n      font-weight: bolder;\n      color: rgb(37, 37, 37);\n      color: var(--secondary-text-color);\n      font-weight: 900;\n      text-transform: uppercase;\n    }\n  }\n\n  >.list-item {\n    display: flex;\n    min-height: 60px;\n    text-decoration: none;\n    margin: auto;\n    box-sizing: border-box;\n\n    &.disabled {\n      pointer-events: none;\n      opacity: 0.8;\n    }\n\n    &.no-transform {\n      .value {\n        text-transform: none !important;\n      }\n    }\n\n    &:not(:last-of-type) {\n      border-bottom: solid 1px rgba(122, 122, 122, 0.227);\n      border-bottom: solid 1px var(--border-color);\n    }\n\n    &:first-child {\n      .container .value {\n        text-transform: none;\n      }\n    }\n\n    .container {\n      flex: 1;\n      display: flex;\n      flex-direction: column;\n      overflow: hidden;\n\n      .text {\n        flex: 1.2;\n        display: flex;\n        align-items: center;\n\n        .info-button {\n          opacity: 0.5;\n          width: fit-content;\n          height: fit-content;\n          margin-left: 10px;\n          font-size: 0.8rem;\n        }\n\n        button {\n          background-color: rgb(51, 153, 255);\n          background-color: var(--button-background-color);\n          color: rgb(255, 255, 255);\n          color: var(--button-text-color);\n          border: none;\n          border-radius: 4px;\n          margin: 0 10px;\n          font-size: 0.6rem;\n          padding: 5px;\n          box-sizing: border-box;\n        }\n\n        .text {\n          span {\n            width: 100%;\n            overflow: hidden;\n            text-overflow: ellipsis;\n          }\n        }\n      }\n\n      .value {\n        flex: 0.8;\n        display: flex;\n        align-items: center;\n        opacity: 0.6;\n\n        &.nc {\n          text-transform: none;\n        }\n      }\n    }\n\n    .icon {\n      height: 60px;\n      width: 60px;\n\n      &.no-icon {\n        width: 20px;\n      }\n    }\n\n    .icon {\n      font-size: 1.4em;\n    }\n\n    * {\n      pointer-events: none;\n    }\n\n    [data-action],\n    [action],\n    a {\n      pointer-events: all !important;\n    }\n  }\n}\n\nul {\n  list-style: none;\n\n  &.list {\n    overflow-x: hidden;\n    overflow-y: auto;\n\n    li {\n      // max-width: 600px;\n      margin: auto;\n      box-sizing: border-box;\n\n      &.tile {\n        .icon {\n          &.file {\n            background-position: center;\n            background-size: 34px;\n          }\n\n          &.folder {\n            color: rgb(206, 206, 53);\n          }\n        }\n\n        .text {\n          span {\n            width: 100%;\n            overflow: hidden;\n            text-overflow: ellipsis;\n          }\n        }\n      }\n\n      * {\n        pointer-events: none;\n        overflow: hidden;\n      }\n\n      [data-action],\n      [action],\n      a {\n        pointer-events: all !important;\n        overflow: auto !important;\n      }\n\n      &.tile:active {\n        transition: all 300ms ease;\n        background-color: rgba($color: #000000, $alpha: 0.2);\n      }\n\n      &:last-child {\n        border-bottom: solid 4px rgba(122, 122, 122, 0.227);\n        border-bottom: solid 4px var(--border-color);\n      }\n    }\n\n    li:last-child {\n      border-bottom: none;\n    }\n  }\n}\n\n.list {\n\n  &:empty,\n  &.empty {\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    min-height: 40px;\n\n    &::after {\n      content: attr(empty-msg);\n      color: rgb(37, 37, 37);\n      color: var(--secondary-text-color);\n      text-align: center;\n      font-weight: 900;\n      background: transparent;\n    }\n  }\n}\n\n.list-item,\n.tile {\n  &:focus {\n    background-color: rgba($color: #000000, $alpha: 0.2);\n  }\n}\n"
  },
  {
    "path": "src/styles/markdown.scss",
    "content": ":root {\n  --color-note: #2f81f7;\n  --color-tip: #3fb950;\n  --color-warning: #d29922;\n  --color-severe: #db6d28;\n  --color-caution: #f85149;\n  --color-important: #a371f7;\n}\n\n.md {\n  a {\n    color: #0645ad;\n    text-decoration: none;\n  }\n\n  a:visited {\n    color: #0b0080;\n  }\n\n  a:hover {\n    color: #06e;\n  }\n\n  a:active {\n    color: #faa700;\n  }\n\n  a:focus {\n    outline: thin dotted;\n  }\n\n  a:hover,\n  a:active {\n    outline: 0;\n  }\n\n  ::-moz-selection {\n    background: rgba(255, 255, 0, 0.3);\n    color: #000;\n  }\n\n  ::selection {\n    background: rgba(255, 255, 0, 0.3);\n    color: #000;\n  }\n\n  a::-moz-selection {\n    background: rgba(255, 255, 0, 0.3);\n    color: #0645ad;\n  }\n\n  a::selection {\n    background: rgba(255, 255, 0, 0.3);\n    color: #0645ad;\n  }\n\n  p {\n    margin: 1em 0;\n  }\n\n  img {\n    max-width: 100%;\n  }\n\n  h1 {\n    margin: 0.67em 0;\n    font-weight: 600;\n    padding-bottom: 0.3em;\n    font-size: 2em;\n    border-bottom: 1px solid #bb800926;\n  }\n\n  h2 {\n    font-weight: 600;\n    padding-bottom: 0.3em;\n    font-size: 1.5em;\n    border-bottom: 1px solid #3d444db3;\n  }\n\n  h1,\n  h2,\n  h3,\n  h4,\n  h5,\n  h6 {\n    margin-top: 1.5rem;\n    margin-bottom: 1rem;\n    font-weight: 600;\n    line-height: 1.25;\n  }\n\n  h3 {\n    font-weight: 600;\n    font-size: 1.25em;\n  }\n\n  h4 {\n    font-weight: 600;\n    font-size: 1em;\n  }\n\n  h5 {\n    font-weight: 600;\n    font-size: 0.875em;\n  }\n\n  h6 {\n    font-weight: 600;\n    font-size: 0.85em;\n    color: #9198a1;\n  }\n\n  blockquote {\n    margin: 0;\n    padding: 0 1em;\n    color: #9198a1;\n    border-left: 0.25em solid #3d444d;\n  }\n\n  hr {\n    display: block;\n    height: 0;\n    border: 0;\n    border-top: 1px solid var(--border-color, #3d444d);\n    margin: 1.5em 0;\n    padding: 0;\n  }\n\n  pre,\n  code,\n  samp {\n    color: rgb(107, 107, 107);\n    font-family: monospace, monospace;\n    _font-family: \"courier new\", monospace;\n    font-size: 0.98em;\n    padding: 0.25rem;\n  }\n\n  kbd {\n    display: inline-block;\n    padding: 0.25rem;\n    font:\n      11px ui-monospace,\n      SFMono-Regular,\n      SF Mono,\n      Menlo,\n      Consolas,\n      Liberation Mono,\n      monospace;\n    line-height: 10px;\n    color: #f0f6fc;\n    vertical-align: middle;\n    background-color: #151b23;\n    border: solid 1px #3d444db3;\n    border-bottom-color: #3d444db3;\n    border-radius: 6px;\n    box-shadow: inset 0 -1px 0 #3d444db3;\n  }\n\n  pre {\n    white-space: pre;\n    white-space: pre-wrap;\n    word-wrap: break-word;\n    position: relative !important;\n  }\n\n  pre:hover .copy-button {\n    opacity: 1;\n  }\n\n  .copy-button {\n    position: absolute;\n    top: 8px;\n    right: 5px;\n    padding: 4px 8px;\n    background: rgba(0, 0, 0, 0.8);\n    border: none;\n    border-radius: 4px;\n    cursor: pointer;\n    opacity: 0;\n    transition: opacity 0.2s;\n    font-size: 12px;\n    color: inherit;\n\n    &:hover {\n      background: rgba(0, 0, 0, 0.9);\n    }\n  }\n\n  b,\n  strong {\n    font-weight: bold;\n  }\n\n  dfn {\n    font-style: italic;\n  }\n\n  ins {\n    background: #ff9;\n    color: #000;\n    text-decoration: none;\n  }\n\n  mark {\n    background: #ff0;\n    color: #000;\n    font-style: italic;\n    font-weight: bold;\n  }\n\n  sub,\n  sup {\n    font-size: 75%;\n    line-height: 0;\n    position: relative;\n    vertical-align: baseline;\n  }\n\n  sup {\n    top: -0.5em;\n  }\n\n  sub {\n    bottom: -0.25em;\n  }\n\n  ul,\n  ol {\n    margin: 1em 0;\n    padding: 0 0 0 2em;\n  }\n\n  ul {\n    list-style-type: disc;\n  }\n\n  li p:last-child {\n    margin: 0;\n  }\n\n  dd {\n    margin: 0 0 0 2em;\n  }\n\n  img {\n    border: 0;\n    -ms-interpolation-mode: bicubic;\n    vertical-align: middle;\n  }\n\n  table {\n    border-collapse: collapse;\n    border-spacing: 0;\n    width: 100%;\n    margin: 1em 0;\n    display: block;\n    overflow-x: auto;\n\n    th,\n    td {\n      padding: 8px 12px;\n      border: 1px solid var(--border-color, #3d444d);\n      text-align: left;\n    }\n\n    th {\n      background-color: rgba(255, 255, 255, 0.05);\n      font-weight: 600;\n    }\n\n    tr:nth-child(even) {\n      background-color: rgba(255, 255, 255, 0.02);\n    }\n\n    tr:hover {\n      background-color: rgba(255, 255, 255, 0.05);\n    }\n  }\n\n  td {\n    vertical-align: top;\n  }\n\n  @media only screen and (min-width: 480px) {\n    body {\n      font-size: 14px;\n    }\n  }\n\n  @media only screen and (min-width: 768px) {\n    body {\n      font-size: 16px;\n    }\n  }\n\n  @media print {\n    * {\n      background: transparent !important;\n      color: black !important;\n      filter: none !important;\n      -ms-filter: none !important;\n    }\n\n    body {\n      font-size: 12pt;\n      max-width: 100%;\n    }\n\n    a,\n    a:visited {\n      text-decoration: underline;\n    }\n\n    hr {\n      height: 1px;\n      border: 0;\n      border-bottom: 1px solid black;\n    }\n\n    a[href]:after {\n      content: \" (\" attr(href) \")\";\n    }\n\n    abbr[title]:after {\n      content: \" (\" attr(title) \")\";\n    }\n\n    .ir a:after,\n    a[href^=\"javascript:\"]:after,\n    a[href^=\"#\"]:after {\n      content: \"\";\n    }\n\n    blockquote {\n      border: 1px solid #999;\n      padding-right: 1em;\n      page-break-inside: avoid;\n    }\n\n    tr,\n    img {\n      page-break-inside: avoid;\n    }\n\n    img {\n      max-width: 100% !important;\n    }\n\n    @page :left {\n      margin: 15mm 20mm 15mm 10mm;\n    }\n\n    @page :right {\n      margin: 15mm 10mm 15mm 20mm;\n    }\n\n    p,\n    h2,\n    h3 {\n      orphans: 3;\n      widows: 3;\n    }\n\n    h2,\n    h3 {\n      page-break-after: avoid;\n    }\n  }\n\n  .markdown-alert {\n    padding: 0.5rem 1rem;\n    margin-bottom: 16px;\n    color: inherit;\n    border-left: 0.25em solid #888;\n  }\n\n  .markdown-alert> :first-child {\n    margin-top: 0;\n  }\n\n  .markdown-alert> :last-child {\n    margin-bottom: 0;\n  }\n\n  .markdown-alert .markdown-alert-title {\n    display: flex;\n    font-weight: 500;\n    align-items: center;\n    line-height: 1;\n  }\n\n  .markdown-alert .markdown-alert-title .octicon {\n    margin-right: 0.5rem;\n    display: inline-block;\n    overflow: visible !important;\n    vertical-align: text-bottom;\n    fill: currentColor;\n  }\n\n  .markdown-alert.markdown-alert-note {\n    border-left-color: var(--color-note);\n  }\n\n  .markdown-alert.markdown-alert-note .markdown-alert-title {\n    color: var(--color-note);\n  }\n\n  .markdown-alert.markdown-alert-important {\n    border-left-color: var(--color-important);\n  }\n\n  .markdown-alert.markdown-alert-important .markdown-alert-title {\n    color: var(--color-important);\n  }\n\n  .markdown-alert.markdown-alert-warning {\n    border-left-color: var(--color-warning);\n  }\n\n  .markdown-alert.markdown-alert-warning .markdown-alert-title {\n    color: var(--color-warning);\n  }\n\n  .markdown-alert.markdown-alert-tip {\n    border-left-color: var(--color-tip);\n  }\n\n  .markdown-alert.markdown-alert-tip .markdown-alert-title {\n    color: var(--color-tip);\n  }\n\n  .markdown-alert.markdown-alert-caution {\n    border-left-color: var(--color-caution);\n  }\n\n  .markdown-alert.markdown-alert-caution .markdown-alert-title {\n    color: var(--color-caution);\n  }\n\n  .task-list-item {\n    list-style-type: none;\n\n    label {\n      font-weight: 400;\n    }\n\n    &.enabled label {\n      cursor: pointer;\n    }\n\n    &+.task-list-item {\n      margin-top: 0.25rem;\n    }\n\n    .handle {\n      display: none;\n    }\n\n    &-checkbox {\n      margin: 0 0.2em 0.25em -1.4em;\n      vertical-align: middle;\n      border-radius: 4px;\n      cursor: pointer;\n    }\n  }\n\n  ul:dir(rtl) .task-list-item-checkbox,\n  ol:dir(rtl) .task-list-item-checkbox {\n    margin: 0 -1.6em 0.25em 0.2em;\n  }\n\n  .contains-task-list {\n\n    &:hover .task-list-item-convert-container,\n    &:focus-within .task-list-item-convert-container {\n      display: block;\n      width: auto;\n      height: 24px;\n      overflow: visible;\n      clip: auto;\n    }\n  }\n}"
  },
  {
    "path": "src/styles/mixins.scss",
    "content": "@mixin circular-loader($size) {\n  display: block;\n  width: $size;\n  height: $size;\n  border-radius: 50%;\n  border: 3px solid rgb(153, 153, 255);\n  border: 3px solid var(--popup-icon-color);\n  border-top-color: transparent;\n  animation: circular-loader-animation 1s linear infinite;\n  box-sizing: border-box;\n}\n\n@mixin loader($size) {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n\n  &::after {\n    content: '';\n    @include circular-loader($size);\n  }\n}\n\n@mixin linear-loader($width, $height) {\n  &::after {\n    content: '';\n    display: block;\n    position: absolute;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    margin: 0 auto;\n    width: 30%;\n    height: 2px;\n    background-color: rgb(0, 0, 0);\n    animation: linear-loader-animation ease 1s infinite;\n    border-radius: 1px;\n  }\n}\n\n@mixin active-icon() {\n  background-color: inherit !important;\n  color: rgba(0, 0, 0, 0.2);\n  color: var(--active-color);\n  text-shadow: 0 0 0.5rem var(--box-shadow-color);\n}\n\n@mixin icon-badge() {\n  position: relative;\n\n  &::after {\n    content: '•';\n    position: absolute;\n    top: 5px;\n    right: 5px;\n    color: #ffda0c;\n    font-size: 1.4em;\n    height: fit-content;\n    line-height: 4px;\n    text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);\n  }\n}"
  },
  {
    "path": "src/styles/overrideAceStyle.scss",
    "content": ".ace_mobile-menu,\n.ace_tooltip.ace_doc-tooltip {\n  display: none !important;\n}\n\n.ace_editor {\n  &[data-font=\"Fira Code\"] {\n    font-feature-settings:\n      \"liga\" on,\n      \"calt\" on;\n    -webkit-font-feature-settings:\n      \"liga\" on,\n      \"calt\" on;\n    -webkit-font-smoothing: antialiased;\n    text-rendering: optimizeLegibility;\n    unicode-bidi: isolate;\n  }\n}\n\n.ace_tooltip {\n  background-color: rgb(255, 255, 255);\n  background-color: var(--secondary-color);\n  color: rgb(37, 37, 37);\n  color: var(--secondary-text-color);\n  max-width: 68%;\n  white-space: pre-wrap;\n}\n\nmain .ace_editor {\n  textarea {\n    user-select: none !important;\n    pointer-events: none !important;\n    transform: translate(-100000px, -10000px) !important;\n  }\n}\n\n.ace-container {\n  height: 100%;\n}\n\n.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\n  background-color: rgba(from var(--active-color) r g b / 0.3);\n}\n\n.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight {\n  color: var(--popup-active-color);\n}\n\n.ace_dark.ace_editor.ace_autocomplete {\n  border: 1px solid var(--popup-border-color);\n  box-shadow: 2px 3px 5px var(--box-shadow-color);\n  line-height: 1.4;\n  background: var(--primary-color);\n  color: var(--primary-text-color);\n}\n\n.ace_hidden-cursors .ace_cursor {\n  opacity: 0.8 !important;\n}\n"
  },
  {
    "path": "src/styles/page.scss",
    "content": "body {\n  &.no-animation {\n    footer {\n      border-top: solid 1px rgba(0, 0, 0, 0.2);\n    }\n\n    .header,\n    header {\n      &.tile {\n        border-bottom: solid 1px rgba(0, 0, 0, 0.2);\n        box-sizing: border-box;\n      }\n    }\n  }\n\n  &.fullscreen-mode {\n    .open-file-list {\n      height: 30px;\n      left: 40px;\n    }\n\n    wc-page {\n      &#root {\n        &[open-file-list-pos=\"bottom\"] {\n          &[footer-height=\"1\"] {\n            header {\n              bottom: 40px;\n            }\n\n            #sidebar-toggler {\n              bottom: 40px;\n            }\n          }\n\n          &[footer-height=\"2\"] {\n            header {\n              bottom: 80px;\n            }\n\n            #sidebar-toggler {\n              bottom: 80px;\n            }\n          }\n\n          &[footer-height=\"3\"] {\n            header {\n              bottom: 120px;\n            }\n\n            #sidebar-toggler {\n              bottom: 120px;\n            }\n          }\n\n          header {\n            bottom: 0;\n            top: auto !important;\n            z-index: 99;\n          }\n\n          #sidebar-toggler {\n            bottom: 0;\n            top: auto !important;\n            z-index: 99;\n            transition: none !important;\n          }\n        }\n\n        &:not(.top-bar) {\n          &.show-header {\n            #header-toggler {\n              opacity: 1;\n              transform: translate3d(-110px, 0, 0);\n            }\n\n            >header {\n              top: 10px;\n              right: 10px;\n              height: 40px;\n              display: flex;\n              border-radius: 20px;\n              border: solid 1px rgb(255, 255, 255);\n              border: solid 1px var(--primary-text-color);\n            }\n          }\n\n          #header-toggler {\n            display: flex;\n          }\n        }\n\n        &.top-bar {\n          #sidebar-toggler {\n            top: 0;\n            left: 0;\n            height: 30px !important;\n            width: 40px !important;\n            opacity: 1;\n            border-radius: 0;\n            border: none;\n            box-shadow: none !important;\n          }\n\n          .open-file-list {\n            top: 0;\n            width: calc(100% - 140px);\n          }\n\n          >header {\n            display: flex;\n            justify-content: flex-end;\n\n            .icon {\n              margin: 0 5px;\n            }\n          }\n        }\n\n        &.primary {\n          &.top-bar {\n            >main {\n              height: calc(100vh - 30px);\n              min-height: calc(100vh - 30px);\n            }\n          }\n\n          >main {\n            height: 100vh;\n            min-height: 100vh;\n          }\n\n          &[footer-height=\"1\"] {\n            &.top-bar {\n              >main {\n                height: calc(100vh - 70px);\n                min-height: calc(100vh - 70px);\n              }\n            }\n\n            >main {\n              height: calc(100vh - 40px);\n              min-height: calc(100vh - 40px);\n            }\n          }\n\n          &[footer-height=\"2\"] {\n            &.top-bar {\n              main {\n                height: calc(100vh - 110px);\n                min-height: calc(100vh - 110px);\n              }\n            }\n\n            main {\n              height: calc(100vh - 80px);\n              min-height: calc(100vh - 80px);\n            }\n          }\n\n          &[footer-height=\"3\"] {\n            &.top-bar {\n              main {\n                height: calc(100vh - 150px);\n                min-height: calc(100vh - 150px);\n              }\n            }\n\n            main {\n              height: calc(100vh - 120px);\n              min-height: calc(100vh - 120px);\n            }\n          }\n        }\n\n        header {\n          display: none;\n          height: 30px;\n          position: fixed;\n          top: 0;\n          right: 0;\n          left: auto;\n          width: 100px;\n          margin: 0;\n          box-shadow: none;\n\n          .text {\n            display: none;\n          }\n\n          .icon {\n            height: 28px;\n            width: 28px;\n            margin: auto;\n\n            &.menu {\n              display: none;\n            }\n          }\n        }\n\n        #sidebar-toggler {\n          display: flex;\n        }\n      }\n    }\n  }\n\n  header {\n    .icon {\n      font-size: 1.5em !important;\n    }\n  }\n}\n\nwc-page {\n  position: fixed !important;\n  top: 0;\n  left: 0;\n  transform: rotate(0) translate3d(0, 0, 0);\n  overflow-x: hidden;\n  overflow-y: auto;\n  background-color: rgb(255, 255, 255);\n  background-color: var(--secondary-color);\n  z-index: 108;\n  height: 100%;\n  width: 100%;\n  box-sizing: border-box;\n\n  &[open-file-list-pos=\"bottom\"] {\n    &#root {\n      footer {\n        box-shadow: 0 -32px 4px rgba(0, 0, 0, 0.2);\n        box-shadow: 0 -32px 4px var(--box-shadow-color);\n      }\n\n      .open-file-list {\n        top: auto !important;\n        bottom: 0;\n        z-index: 99;\n        position: absolute;\n      }\n    }\n\n    #quicktools-toggler {\n      margin-bottom: 30px;\n    }\n  }\n\n  .main {\n    >.options {\n      display: flex;\n      height: 40px;\n      min-height: 40px;\n      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);\n      box-shadow: 0 1px 4px var(--box-shadow-color);\n\n      >* {\n        position: relative;\n        display: flex;\n        flex: 1;\n        align-items: center;\n        justify-content: center;\n        box-sizing: border-box;\n\n        &.active::after {\n          content: \"\";\n          position: absolute;\n          bottom: 0;\n          left: 0;\n          height: 2px;\n          width: 100%;\n          animation: strech 300ms ease 1;\n          background-color: rgb(51, 153, 255);\n          background-color: var(--active-color);\n        }\n      }\n    }\n  }\n\n  &.hide-floating-button {\n    #quicktools-toggler {\n      display: none;\n    }\n  }\n\n  &[footer-height] {\n    #quicktools-toggler {\n      opacity: 1;\n    }\n  }\n\n  &[footer-height=\"1\"] {\n    &[open-file-list-pos=\"bottom\"] {\n      &#root {\n        .open-file-list {\n          bottom: 40px;\n        }\n      }\n    }\n\n    #quicktools-toggler {\n      transform: translate3d(0, -40px, 0);\n    }\n\n    &.top-bar {\n\n      main,\n      .main {\n        height: calc(100vh - 115px);\n        min-height: calc(100vh - 115px);\n      }\n    }\n\n    main,\n    .main {\n      height: calc(100vh - 85px);\n      min-height: calc(100vh - 85px);\n    }\n  }\n\n  &[footer-height=\"2\"] {\n    &[open-file-list-pos=\"bottom\"] {\n      &#root {\n        .open-file-list {\n          bottom: 80px;\n        }\n      }\n    }\n\n    #quicktools-toggler {\n      transform: translate3d(0, -80px, 0);\n    }\n\n    &.top-bar {\n\n      .main,\n      main {\n        height: calc(100vh - 155px);\n        min-height: calc(100vh - 155px);\n      }\n    }\n\n    .main,\n    main {\n      height: calc(100vh - 125px);\n      min-height: calc(100vh - 125px);\n    }\n  }\n\n  &[footer-height=\"3\"] {\n    &[open-file-list-pos=\"bottom\"] {\n      &#root {\n        .open-file-list {\n          bottom: 120px;\n        }\n      }\n    }\n\n    #quicktools-toggler {\n      transform: translate3d(0, -140px, 0);\n    }\n\n    &.top-bar {\n\n      .main,\n      main {\n        height: calc(100vh - 195px);\n        min-height: calc(100vh - 195px);\n      }\n    }\n\n    .main,\n    main {\n      height: calc(100vh - 165px);\n      min-height: calc(100vh - 165px);\n    }\n  }\n\n  header {\n    position: sticky;\n    top: 0;\n    left: 0;\n    width: 100%;\n  }\n\n  &.top-bar {\n    main {\n      height: calc(100vh - 75px);\n      min-height: calc(100vh - 75px);\n    }\n  }\n\n  main {\n    position: relative;\n\n    &:empty {\n      position: fixed;\n      top: 45px;\n      height: calc(100% - 45px);\n      width: 100%;\n      display: flex;\n\n      &::after {\n        content: attr(data-empty-msg);\n        font-weight: 900;\n        color: rgb(153, 153, 153);\n        height: fit-content;\n        max-width: 60vw;\n        margin: auto;\n        text-align: center;\n        font-size: 1.6em;\n      }\n    }\n  }\n\n  footer {\n    position: fixed;\n    bottom: 0;\n    left: 0;\n    width: 100%;\n    z-index: 98;\n    background-color: rgb(153, 153, 255);\n    background-color: var(--primary-color);\n    padding: 0 1px;\n    box-sizing: border-box;\n    box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 -2px 4px var(--box-shadow-color);\n\n    .icon {\n      border-radius: 0;\n    }\n  }\n\n  &.primary {\n    z-index: auto;\n  }\n\n  &:not(.primary):not(.no-transition) {\n    animation: page-transition 200ms ease 1;\n  }\n\n  &.no-transition {\n    animation: page-transition-opacity 200ms ease 1;\n  }\n\n  &:not(#root) {\n    z-index: 110;\n  }\n\n  &.hide {\n    transition: 300ms ease;\n    opacity: 0;\n    transform: translate3d(0, 50%, 0);\n  }\n\n  main,\n  .main {\n    height: calc(100vh - 45px);\n    min-height: calc(100vh - 45px);\n    margin: auto;\n    z-index: 1;\n\n    .navigation {\n      display: flex;\n      height: 30px;\n      border-bottom: solid 1px rgba(122, 122, 122, 0.227);\n      border-bottom: solid 1px var(--border-color);\n      box-sizing: border-box;\n      overflow-x: auto;\n\n      &::after {\n        content: \"\";\n        display: block;\n        min-width: 10px;\n      }\n\n      .nav {\n        display: flex;\n        height: 25px;\n        margin: auto 1.5px;\n        font-size: 0.9em;\n        box-sizing: border-box;\n        align-items: center;\n        color: color-mix(in srgb, var(--primary-text-color) 60%, transparent);\n        transition: color 0.2s;\n\n        &::after {\n          content: attr(text);\n          padding: 2.5px;\n          box-sizing: border-box;\n          white-space: nowrap;\n          word-break: keep-all;\n          cursor: pointer;\n        }\n\n        &:first-of-type {\n          margin-left: 10px;\n        }\n\n        &:last-of-type::after {\n          color: var(--active-color);\n          font-weight: 500;\n        }\n\n        &:not(:first-of-type)::before {\n          font-family: \"code-editor-icon\";\n          content: \"\\E99E\";\n          margin: auto 1.5px;\n        }\n      }\n    }\n  }\n\n  .editor-container {\n    height: 100%;\n  }\n}"
  },
  {
    "path": "src/styles/wideScreen.scss",
    "content": "@media screen and (min-width: 1024px) {\n\n  html,\n  body {\n    font-size: 15px;\n  }\n\n  .header,\n  header {\n    height: 60px;\n\n    >.text {\n      font-size: 1.4em;\n    }\n  }\n\n  .section,\n  .button-container {\n    min-height: 45px;\n\n    .icon {\n      font-size: 1.4em;\n    }\n  }\n\n  input {\n    height: 45px;\n    font-size: 1.1em;\n    text-indent: 12px;\n  }\n\n  .floating.icon {\n    height: 65px;\n    width: 65px;\n    font-size: 2rem;\n    top: 15px;\n    right: 15px;\n    box-shadow: 0 0 2px 4px rgba(0, 0, 0, 0.2);\n  }\n\n  .context-menu {\n    min-width: 260px;\n    max-width: 400px;\n    min-height: 48px;\n\n    li {\n      height: 55px;\n      font-size: 1.05em;\n\n      &.notice::after {\n        font-size: 1.3em;\n      }\n\n      .text {\n        .value {\n          font-size: 0.85em;\n        }\n      }\n\n      .icon {\n        font-size: 1.1em;\n      }\n    }\n  }\n\n  #search-bar {\n    height: 50px;\n\n    input {\n      margin: 8px;\n      border-radius: 6px;\n      font-size: 1.1em;\n      padding: 0 12px;\n    }\n\n    .icon {\n      height: 50px;\n      width: 50px;\n      font-size: 1.3em;\n    }\n  }\n\n  .tile {\n    .icon {\n      height: 60px;\n      width: 60px;\n      font-size: 2.5em;\n      background-size: 2em;\n    }\n  }\n\n  .prompt {\n    max-width: 400px;\n\n    &.select {\n      min-width: 280px;\n      max-width: 420px;\n\n      ul {\n        padding: 15px;\n\n        li {\n          height: 48px;\n          font-size: 1.1em;\n          margin: 2px 0;\n          padding: 0 8px;\n\n          .icon {\n            font-size: 1.1em;\n            margin-right: 8px;\n          }\n        }\n      }\n    }\n\n    ul li {\n      height: 48px;\n      font-size: 1.1em;\n    }\n\n    .title {\n      font-size: 1.35em;\n\n      &:not(:empty) {\n        min-height: 48px;\n        margin: 8px 15px 0 15px;\n      }\n    }\n\n    .message {\n\n      .message,\n      &:not(.loader) {\n        padding: 15px;\n        min-height: 48px;\n        font-size: 1.3em;\n      }\n    }\n\n    .input-group {\n      min-height: 48px;\n      margin: 4px auto;\n      max-width: 360px;\n\n      .hero {\n        height: 48px;\n        font-size: 1.3em;\n      }\n\n      .input-checkbox {\n        height: 48px;\n      }\n    }\n\n    .input {\n      max-width: 360px;\n      font-size: 1.1em;\n      margin: 4px auto;\n      padding: 4px 0;\n    }\n  }\n\n  #plugin {\n    .list-item {\n      margin: 0 1rem 0.75rem 1rem;\n      padding: 1.25rem;\n      border-radius: 16px;\n\n      .plugin-header {\n        gap: 1.25rem;\n        min-height: 56px;\n\n        .plugin-icon {\n          width: 56px;\n          height: 56px;\n          border-radius: 14px;\n        }\n\n        .plugin-info {\n          gap: 1.25rem;\n\n          .plugin-main {\n            .plugin-title {\n              gap: 1rem;\n              margin-bottom: 0.5rem;\n\n              .plugin-name {\n                font-size: 1.1rem;\n                font-weight: 700;\n              }\n\n              .plugin-version {\n                font-size: 0.8rem;\n                padding: 0.3rem 0.6rem;\n              }\n            }\n\n            .plugin-meta {\n              font-size: 0.95rem;\n              gap: 0.75rem;\n\n              .plugin-stats {\n                font-size: 0.95rem;\n                gap: 0.5rem;\n\n                .icon {\n                  width: 16px;\n                  height: 16px;\n                  font-size: 16px;\n                }\n              }\n            }\n          }\n\n          .plugin-price {\n            padding: 0.5rem 1rem;\n            font-size: 0.95rem;\n            border-radius: 10px;\n          }\n        }\n      }\n\n      .plugin-toggle-switch {\n        min-width: auto;\n        padding: 0;\n\n        .plugin-toggle-track {\n          width: 2.8rem;\n          height: 1.65rem;\n          border-radius: 999px;\n        }\n\n        .plugin-toggle-thumb {\n          width: 1.25rem;\n          height: 1.25rem;\n          left: 0;\n          top: 0;\n        }\n\n        &[data-enabled=\"true\"] .plugin-toggle-thumb {\n          transform: translateX(1.12rem);\n        }\n      }\n    }\n  }\n\n  #sidebar {\n    .container {\n      .header {\n        height: 100px;\n      }\n\n      >.list[class] {\n        &.hidden[class] {\n          max-height: 40px;\n          min-height: 40px;\n        }\n\n        >ul {\n          max-height: calc(100% - 40px);\n          height: calc(100% - 40px);\n        }\n      }\n\n      &.extensions {\n        .header {\n          padding: 12px;\n\n          .title {\n            min-height: 40px;\n            font-size: 1.25rem;\n\n            .icon-button {\n              padding: 0.5rem;\n              margin-left: 0.5rem;\n              border-radius: 8px;\n              min-width: 36px;\n              min-height: 36px;\n              font-size: 1.1em;\n            }\n          }\n\n          input[type=\"search\"] {\n            padding: 0.6rem;\n            font-size: 0.95rem;\n          }\n        }\n\n        .tile {\n          min-height: 40px;\n          padding: 5px 12px;\n\n          .text {\n            &.sub-text {\n              font-size: 0.85rem;\n\n              &::after {\n                font-size: 0.75rem;\n              }\n            }\n          }\n        }\n\n        .icon {\n          background-size: 26px !important;\n        }\n      }\n\n      &.notifications {\n        .header {\n          height: unset;\n        }\n      }\n\n      &.search-in-files {\n        .header {\n          min-height: 100px;\n          height: fit-content;\n        }\n      }\n    }\n  }\n\n  .list {\n    &.collapsible {\n      >.tile {\n        height: 35px;\n        font-size: 1.1em;\n\n        .icon {\n          height: 35px;\n          min-width: 35px;\n        }\n      }\n    }\n\n    ul {\n      >.tile {\n        height: 35px;\n\n        .icon {\n          height: 32px;\n          min-width: 32px;\n          font-size: 1.1em;\n        }\n      }\n    }\n\n    >.list-item {\n      min-height: 65px;\n      font-size: 1.1em;\n\n      .icon {\n        height: 65px;\n        width: 65px;\n        font-size: 1.6em;\n\n        &.no-icon {\n          width: 25px;\n        }\n      }\n    }\n  }\n\n  w-page {\n    .main {\n      >.options {\n        height: 45px;\n        min-height: 45px;\n        font-size: 1.1em;\n\n        >* {\n          &.active::after {\n            height: 3px;\n          }\n        }\n      }\n    }\n\n    &[footer-height=\"1\"] {\n      #quicktools-toggler {\n        transform: translate3d(0, -45px, 0);\n      }\n    }\n\n    &[footer-height=\"2\"] {\n      #quicktools-toggler {\n        transform: translate3d(0, -100px, 0);\n      }\n    }\n\n    &[footer-height=\"3\"] {\n      #quicktools-toggler {\n        transform: translate3d(0, -150px, 0);\n      }\n    }\n\n    main,\n    .main {\n\n      .navigation {\n        height: 35px;\n\n        .nav {\n          height: 30px;\n          font-size: 1em;\n          margin: auto 2px;\n\n          &::after {\n            padding: 3px;\n          }\n\n          &:first-of-type {\n            margin-left: 15px;\n          }\n\n          &:not(:first-of-type)::before {\n            margin: auto 2px;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/ace.test.js",
    "content": "import { TestRunner } from \"./tester\";\n\n/**\n * Ace Editor API Compatibility Tests\n *\n * These tests validate that the CodeMirror-based editor (from editorManager)\n * properly implements the Ace Editor API compatibility layer.\n */\nexport async function runAceCompatibilityTests(writeOutput) {\n\tconst runner = new TestRunner(\"Ace API Compatibility\");\n\n\tfunction getEditor() {\n\t\treturn editorManager?.editor;\n\t}\n\n\tasync function createTestFile(text = \"\") {\n\t\tconst EditorFile = acode.require(\"editorFile\");\n\t\tconst file = new EditorFile(\"__ace_test__.txt\", {\n\t\t\ttext,\n\t\t\trender: true,\n\t\t});\n\t\tawait new Promise((r) => setTimeout(r, 100));\n\t\treturn file;\n\t}\n\n\trunner.test(\"editorManager.editor exists\", (test) => {\n\t\ttest.assert(\n\t\t\ttypeof editorManager !== \"undefined\",\n\t\t\t\"editorManager should exist\",\n\t\t);\n\t\ttest.assert(\n\t\t\teditorManager.editor != null,\n\t\t\t\"editorManager.editor should exist\",\n\t\t);\n\t});\n\n\trunner.test(\"editorManager isCodeMirror flag\", (test) => {\n\t\ttest.assertEqual(editorManager.isCodeMirror, true);\n\t});\n\n\trunner.test(\"editor.getValue()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.getValue === \"function\",\n\t\t\t\"getValue should be a function\",\n\t\t);\n\t\tconst value = editor.getValue();\n\t\ttest.assert(typeof value === \"string\", \"getValue should return string\");\n\t});\n\n\trunner.test(\"editor.insert()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.insert === \"function\",\n\t\t\t\"insert should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.getCursorPosition()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.getCursorPosition === \"function\",\n\t\t\t\"getCursorPosition should exist\",\n\t\t);\n\t\tconst pos = editor.getCursorPosition();\n\t\ttest.assert(typeof pos.row === \"number\", \"row should be number\");\n\t\ttest.assert(typeof pos.column === \"number\", \"column should be number\");\n\t});\n\n\trunner.test(\"editor.gotoLine()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.gotoLine === \"function\",\n\t\t\t\"gotoLine should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.moveCursorToPosition()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.moveCursorToPosition === \"function\",\n\t\t\t\"moveCursorToPosition should exist\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.selection object\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(editor.selection != null, \"selection should exist\");\n\t});\n\n\trunner.test(\"editor.selection.getRange()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.selection.getRange === \"function\",\n\t\t\t\"getRange should be a function\",\n\t\t);\n\t\tconst range = editor.selection.getRange();\n\t\ttest.assert(range.start != null, \"range should have start\");\n\t\ttest.assert(range.end != null, \"range should have end\");\n\t});\n\n\trunner.test(\"editor.getSelectionRange()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.getSelectionRange === \"function\",\n\t\t\t\"getSelectionRange should be a function\",\n\t\t);\n\t\tconst range = editor.getSelectionRange();\n\t\ttest.assert(range.start != null, \"range should have start\");\n\t\ttest.assert(range.end != null, \"range should have end\");\n\t});\n\n\trunner.test(\"editor.scrollToRow()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.scrollToRow === \"function\",\n\t\t\t\"scrollToRow should be a function\",\n\t\t);\n\t\tconst ok = editor.scrollToRow(0);\n\t\ttest.assert(ok === true || ok === undefined, \"scrollToRow should not fail\");\n\t});\n\n\trunner.test(\"editor.selection.getCursor()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.selection.getCursor === \"function\",\n\t\t\t\"getCursor should be a function\",\n\t\t);\n\t\tconst pos = editor.selection.getCursor();\n\t\ttest.assert(typeof pos.row === \"number\", \"row should be number\");\n\t\ttest.assert(typeof pos.column === \"number\", \"column should be number\");\n\t});\n\n\trunner.test(\"editor.getCopyText()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.getCopyText === \"function\",\n\t\t\t\"getCopyText should exist\",\n\t\t);\n\t\tconst text = editor.getCopyText();\n\t\ttest.assert(typeof text === \"string\", \"should return string\");\n\t});\n\n\trunner.test(\"editor.session exists\", async (test) => {\n\t\tconst testFile = await createTestFile(\"test\");\n\t\tconst editor = getEditor();\n\t\ttest.assert(editor.session != null, \"session should exist\");\n\t\ttestFile.remove(false);\n\t});\n\n\trunner.test(\"editor.setTheme()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.setTheme === \"function\",\n\t\t\t\"setTheme should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.commands object\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(editor.commands != null, \"commands should exist\");\n\t});\n\n\trunner.test(\"editor.commands.addCommand()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.commands.addCommand === \"function\",\n\t\t\t\"addCommand should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.commands.removeCommand()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.commands.removeCommand === \"function\",\n\t\t\t\"removeCommand should exist\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.commands.commands getter\", (test) => {\n\t\tconst editor = getEditor();\n\t\tconst cmds = editor.commands.commands;\n\t\ttest.assert(\n\t\t\ttypeof cmds === \"object\" && cmds !== null,\n\t\t\t\"commands should return object\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.execCommand()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.execCommand === \"function\",\n\t\t\t\"execCommand should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.focus()\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.focus === \"function\",\n\t\t\t\"focus should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.state (CodeMirror)\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(editor.state != null, \"state should exist\");\n\t});\n\n\trunner.test(\"editor.dispatch (CodeMirror)\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.dispatch === \"function\",\n\t\t\t\"dispatch should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"editor.contentDOM (CodeMirror)\", (test) => {\n\t\tconst editor = getEditor();\n\t\ttest.assert(editor.contentDOM != null, \"contentDOM should exist\");\n\t});\n\n\trunner.test(\"ace.require('ace/ext/modelist')\", (test) => {\n\t\ttest.assert(window.ace != null, \"window.ace should exist\");\n\t\ttest.assert(\n\t\t\ttypeof window.ace.require === \"function\",\n\t\t\t\"ace.require should be a function\",\n\t\t);\n\t\tconst modelist = window.ace.require(\"ace/ext/modelist\");\n\t\ttest.assert(modelist != null, \"modelist should be available\");\n\t\ttest.assert(\n\t\t\ttypeof modelist.getModeForPath === \"function\",\n\t\t\t\"modelist.getModeForPath should be a function\",\n\t\t);\n\t});\n\n\t// Session API tests\n\n\trunner.test(\"session.getValue()\", async (test) => {\n\t\tconst testFile = await createTestFile(\"test content\");\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.session.getValue === \"function\",\n\t\t\t\"getValue should exist\",\n\t\t);\n\t\tconst value = editor.session.getValue();\n\t\ttest.assert(typeof value === \"string\", \"should return string\");\n\t\ttest.assertEqual(value, \"test content\");\n\t\ttestFile.remove(false);\n\t});\n\n\trunner.test(\"session.setValue()\", async (test) => {\n\t\tconst testFile = await createTestFile(\"original\");\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.session.setValue === \"function\",\n\t\t\t\"setValue should exist\",\n\t\t);\n\t\teditor.session.setValue(\"modified\");\n\t\ttest.assertEqual(editor.session.getValue(), \"modified\");\n\t\ttestFile.remove(false);\n\t});\n\n\trunner.test(\"session.getLength()\", async (test) => {\n\t\tconst testFile = await createTestFile(\"line1\\nline2\\nline3\");\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.session.getLength === \"function\",\n\t\t\t\"getLength should exist\",\n\t\t);\n\t\tconst len = editor.session.getLength();\n\t\ttest.assert(typeof len === \"number\", \"should return number\");\n\t\ttest.assertEqual(len, 3);\n\t\ttestFile.remove(false);\n\t});\n\n\trunner.test(\"session.getLine()\", async (test) => {\n\t\tconst testFile = await createTestFile(\"first\\nsecond\\nthird\");\n\t\tconst editor = getEditor();\n\t\ttest.assert(\n\t\t\ttypeof editor.session.getLine === \"function\",\n\t\t\t\"getLine should exist\",\n\t\t);\n\t\ttest.assertEqual(editor.session.getLine(0), \"first\");\n\t\ttest.assertEqual(editor.session.getLine(1), \"second\");\n\t\ttest.assertEqual(editor.session.getLine(2), \"third\");\n\t\ttestFile.remove(false);\n\t});\n\n\treturn await runner.run(writeOutput);\n}\n"
  },
  {
    "path": "src/test/editor.tests.js",
    "content": "import { history, isolateHistory, redo, undo } from \"@codemirror/commands\";\nimport {\n\tbracketMatching,\n\tdefaultHighlightStyle,\n\tfoldGutter,\n\tsyntaxHighlighting,\n} from \"@codemirror/language\";\nimport { highlightSelectionMatches, searchKeymap } from \"@codemirror/search\";\nimport { EditorSelection, EditorState } from \"@codemirror/state\";\nimport { EditorView } from \"@codemirror/view\";\nimport createBaseExtensions from \"cm/baseExtensions\";\nimport indentGuides from \"cm/indentGuides\";\nimport { getEdgeScrollDirections } from \"cm/touchSelectionMenu\";\nimport { TestRunner } from \"./tester\";\n\nexport async function runCodeMirrorTests(writeOutput) {\n\tconst runner = new TestRunner(\"CodeMirror 6 Editor Tests\");\n\n\tfunction createEditor(doc = \"\", extensions = []) {\n\t\tconst container = document.createElement(\"div\");\n\t\tcontainer.style.width = \"500px\";\n\t\tcontainer.style.height = \"300px\";\n\t\tcontainer.style.backgroundColor = \"#1e1e1e\";\n\t\tdocument.body.appendChild(container);\n\n\t\tconst state = EditorState.create({\n\t\t\tdoc,\n\t\t\textensions: [...createBaseExtensions(), ...extensions],\n\t\t});\n\n\t\tconst view = new EditorView({ state, parent: container });\n\t\treturn { view, container };\n\t}\n\n\tasync function withEditor(test, fn, initialDoc = \"\", extensions = []) {\n\t\tlet view, container;\n\n\t\ttry {\n\t\t\t({ view, container } = createEditor(initialDoc, extensions));\n\t\t\ttest.assert(view != null, \"EditorView instance should be created\");\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\t\t\tawait fn(view);\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 200));\n\t\t} finally {\n\t\t\tif (view) view.destroy();\n\t\t\tif (container) container.remove();\n\t\t}\n\t}\n\n\t// =========================================\n\t// BASIC EDITOR TESTS\n\t// =========================================\n\n\trunner.test(\"CodeMirror imports available\", async (test) => {\n\t\ttest.assert(\n\t\t\ttypeof EditorView !== \"undefined\",\n\t\t\t\"EditorView should be defined\",\n\t\t);\n\t\ttest.assert(\n\t\t\ttypeof EditorState !== \"undefined\",\n\t\t\t\"EditorState should be defined\",\n\t\t);\n\t\ttest.assert(\n\t\t\ttypeof EditorState.create === \"function\",\n\t\t\t\"EditorState.create should be a function\",\n\t\t);\n\t});\n\n\trunner.test(\"Acode exposes shared CodeMirror modules\", async (test) => {\n\t\tconst codemirror = acode.require(\"codemirror\");\n\t\tconst language = acode.require(\"@codemirror/language\");\n\t\tconst lezer = acode.require(\"@lezer/highlight\");\n\t\tconst state = acode.require(\"@codemirror/state\");\n\t\tconst view = acode.require(\"@codemirror/view\");\n\n\t\ttest.assert(codemirror != null, \"codemirror namespace should exist\");\n\t\ttest.assert(language != null, \"@codemirror/language should exist\");\n\t\ttest.assert(lezer != null, \"@lezer/highlight should exist\");\n\t\ttest.assert(state != null, \"@codemirror/state should exist\");\n\t\ttest.assert(view != null, \"@codemirror/view should exist\");\n\t\ttest.assert(\n\t\t\tlanguage.StreamLanguage != null,\n\t\t\t\"@codemirror/language should export StreamLanguage\",\n\t\t);\n\t\ttest.assert(lezer.tags != null, \"@lezer/highlight should export tags\");\n\t\ttest.assert(\n\t\t\tstate.EditorState != null,\n\t\t\t\"@codemirror/state should export EditorState\",\n\t\t);\n\t\ttest.assert(\n\t\t\tview.EditorView != null,\n\t\t\t\"@codemirror/view should export EditorView\",\n\t\t);\n\t\ttest.assertEqual(\n\t\t\tlanguage.StreamLanguage,\n\t\t\tcodemirror.language.StreamLanguage,\n\t\t\t\"language exports should share the same singleton instance\",\n\t\t);\n\t\ttest.assertEqual(\n\t\t\tlezer.tags,\n\t\t\tcodemirror.lezer.tags,\n\t\t\t\"lezer exports should share the same singleton instance\",\n\t\t);\n\t\ttest.assertEqual(\n\t\t\tstate.EditorState,\n\t\t\tcodemirror.state.EditorState,\n\t\t\t\"state exports should share the same singleton instance\",\n\t\t);\n\t\ttest.assertEqual(\n\t\t\tview.EditorView,\n\t\t\tcodemirror.view.EditorView,\n\t\t\t\"view exports should share the same singleton instance\",\n\t\t);\n\t});\n\n\trunner.test(\"Editor creation\", async (test) => {\n\t\tconst { view, container } = createEditor();\n\t\ttest.assert(view != null, \"EditorView instance should be created\");\n\t\ttest.assert(view.dom instanceof HTMLElement, \"Editor should have DOM\");\n\t\ttest.assert(view.state instanceof EditorState, \"Editor should have state\");\n\t\tview.destroy();\n\t\tcontainer.remove();\n\t});\n\n\trunner.test(\"State access\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tconst state = view.state;\n\t\t\ttest.assert(state != null, \"Editor state should exist\");\n\t\t\ttest.assert(typeof state.doc !== \"undefined\", \"State should have doc\");\n\t\t\ttest.assert(\n\t\t\t\ttypeof state.doc.toString === \"function\",\n\t\t\t\t\"Doc should have toString\",\n\t\t\t);\n\t\t});\n\t});\n\n\trunner.test(\"Set and get document content\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tconst text = \"Hello CodeMirror 6\";\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: text },\n\t\t\t});\n\t\t\ttest.assertEqual(view.state.doc.toString(), text);\n\t\t});\n\t});\n\n\t// =========================================\n\t// CURSOR AND SELECTION TESTS\n\t// =========================================\n\n\trunner.test(\"Cursor movement\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tconst doc = \"line1\\nline2\\nline3\";\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: doc },\n\t\t\t});\n\n\t\t\tconst line2 = view.state.doc.line(2);\n\t\t\tconst targetPos = line2.from + 2;\n\t\t\tview.dispatch({\n\t\t\t\tselection: { anchor: targetPos, head: targetPos },\n\t\t\t});\n\n\t\t\tconst pos = view.state.selection.main.head;\n\t\t\tconst lineInfo = view.state.doc.lineAt(pos);\n\t\t\ttest.assertEqual(lineInfo.number, 2);\n\t\t\ttest.assertEqual(pos - lineInfo.from, 2);\n\t\t});\n\t});\n\n\trunner.test(\"Selection handling\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: \"abc\\ndef\" },\n\t\t\t});\n\t\t\tview.dispatch({\n\t\t\t\tselection: { anchor: 0, head: view.state.doc.length },\n\t\t\t});\n\n\t\t\tconst { from, to } = view.state.selection.main;\n\t\t\tconst selectedText = view.state.doc.sliceString(from, to);\n\t\t\ttest.assert(selectedText.length > 0, \"Should have selected text\");\n\t\t\ttest.assertEqual(selectedText, \"abc\\ndef\");\n\t\t});\n\t});\n\n\trunner.test(\"Multiple selections\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: \"foo bar foo\" },\n\t\t\t});\n\n\t\t\tview.dispatch({\n\t\t\t\tselection: EditorSelection.create([\n\t\t\t\t\tEditorSelection.range(0, 3),\n\t\t\t\t\tEditorSelection.range(8, 11),\n\t\t\t\t]),\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.selection.ranges.length, 2);\n\t\t\ttest.assertEqual(view.state.doc.sliceString(0, 3), \"foo\");\n\t\t\ttest.assertEqual(view.state.doc.sliceString(8, 11), \"foo\");\n\t\t});\n\t});\n\n\trunner.test(\"Selection with cursor (empty range)\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\tview.dispatch({\n\t\t\t\tselection: EditorSelection.cursor(5),\n\t\t\t});\n\n\t\t\tconst main = view.state.selection.main;\n\t\t\ttest.assertEqual(main.from, 5);\n\t\t\ttest.assertEqual(main.to, 5);\n\t\t\ttest.assert(main.empty, \"Cursor selection should be empty\");\n\t\t});\n\t});\n\n\t// =========================================\n\t// HISTORY (UNDO/REDO) TESTS\n\t// =========================================\n\n\trunner.test(\"Undo works\", async (test) => {\n\t\tconst { view, container } = createEditor(\"one\");\n\n\t\ttry {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 3, insert: \"\\ntwo\" },\n\t\t\t});\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"one\\ntwo\");\n\n\t\t\tundo(view);\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"one\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Redo works\", async (test) => {\n\t\tconst { view, container } = createEditor(\"one\");\n\n\t\ttry {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 3, insert: \"\\ntwo\" },\n\t\t\t});\n\n\t\t\tundo(view);\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"one\");\n\n\t\t\tredo(view);\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"one\\ntwo\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Multiple undo steps\", async (test) => {\n\t\tconst { view, container } = createEditor(\"\");\n\n\t\ttry {\n\t\t\t// Use isolateHistory to force each change into separate history entries\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"a\" },\n\t\t\t\tannotations: isolateHistory.of(\"full\"),\n\t\t\t});\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 1, insert: \"b\" },\n\t\t\t\tannotations: isolateHistory.of(\"full\"),\n\t\t\t});\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 2, insert: \"c\" },\n\t\t\t\tannotations: isolateHistory.of(\"full\"),\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"abc\");\n\n\t\t\tundo(view);\n\t\t\tundo(view);\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"a\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\t// =========================================\n\t// DOCUMENT MANIPULATION TESTS\n\t// =========================================\n\n\trunner.test(\"Line count\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: \"a\\nb\\nc\\nd\" },\n\t\t\t});\n\t\t\ttest.assertEqual(view.state.doc.lines, 4);\n\t\t});\n\t});\n\n\trunner.test(\"Insert text at position\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 5, to: 5, insert: \" there\" },\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"hello there world\");\n\t\t});\n\t});\n\n\trunner.test(\"Replace text range\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 6, to: 11, insert: \"cm6\" },\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"hello cm6\");\n\t\t});\n\t});\n\n\trunner.test(\"Delete text\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 5, to: 11, insert: \"\" },\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"hello\");\n\t\t});\n\t});\n\n\trunner.test(\"Batch changes\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"aaa bbb ccc\" },\n\t\t\t});\n\n\t\t\tview.dispatch({\n\t\t\t\tchanges: [\n\t\t\t\t\t{ from: 0, to: 3, insert: \"xxx\" },\n\t\t\t\t\t{ from: 4, to: 7, insert: \"yyy\" },\n\t\t\t\t\t{ from: 8, to: 11, insert: \"zzz\" },\n\t\t\t\t],\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"xxx yyy zzz\");\n\t\t});\n\t});\n\n\trunner.test(\"Line information\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: {\n\t\t\t\t\tfrom: 0,\n\t\t\t\t\tto: view.state.doc.length,\n\t\t\t\t\tinsert: \"line one\\nline two\\nline three\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst line2 = view.state.doc.line(2);\n\t\t\ttest.assertEqual(line2.number, 2);\n\t\t\ttest.assertEqual(line2.text, \"line two\");\n\t\t\ttest.assert(line2.from > 0, \"Line 2 should have positive from\");\n\t\t});\n\t});\n\n\trunner.test(\"Position conversions\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: {\n\t\t\t\t\tfrom: 0,\n\t\t\t\t\tto: view.state.doc.length,\n\t\t\t\t\tinsert: \"abc\\ndefgh\\nij\",\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst pos = 7; // 'g' in \"defgh\"\n\t\t\tconst lineInfo = view.state.doc.lineAt(pos);\n\n\t\t\ttest.assertEqual(lineInfo.number, 2);\n\t\t\ttest.assertEqual(lineInfo.text, \"defgh\");\n\t\t\ttest.assertEqual(pos - lineInfo.from, 3);\n\t\t});\n\t});\n\n\trunner.test(\"Empty document handling\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\ttest.assertEqual(view.state.doc.length, 0);\n\t\t\ttest.assertEqual(view.state.doc.lines, 1);\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"\");\n\t\t});\n\t});\n\n\t// =========================================\n\t// DOM AND VIEW TESTS\n\t// =========================================\n\n\trunner.test(\"DOM elements exist\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\ttest.assert(view.dom != null, \"view.dom should exist\");\n\t\t\ttest.assert(view.scrollDOM != null, \"view.scrollDOM should exist\");\n\t\t\ttest.assert(view.contentDOM != null, \"view.contentDOM should exist\");\n\t\t});\n\t});\n\n\trunner.test(\"Indent guides render as indentation spans\", async (test) => {\n\t\tconst doc = \"function x() {\\n  if (true) {\\n    return 1;\\n  }\\n}\";\n\t\tawait withEditor(\n\t\t\ttest,\n\t\t\tasync (view) => {\n\t\t\t\tconst guideLine = view.dom.querySelector(\".cm-indent-guides\");\n\t\t\t\tconst legacyWidget = view.dom.querySelector(\n\t\t\t\t\t\".cm-indent-guides-wrapper\",\n\t\t\t\t);\n\t\t\t\ttest.assert(guideLine != null, \"Indent guide span should exist\");\n\t\t\t\ttest.assert(\n\t\t\t\t\tlegacyWidget == null,\n\t\t\t\t\t\"Indent guides should not create widget wrapper DOM\",\n\t\t\t\t);\n\t\t\t},\n\t\t\tdoc,\n\t\t\t[indentGuides()],\n\t\t);\n\t});\n\n\trunner.test(\"Focus and blur\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.focus();\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\t\t\ttest.assert(view.hasFocus, \"Editor should have focus\");\n\n\t\t\tview.contentDOM.blur();\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 50));\n\t\t\ttest.assert(!view.hasFocus, \"Editor should not have focus after blur\");\n\t\t});\n\t});\n\n\trunner.test(\"Scroll API\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tconst longDoc = Array(100).fill(\"line\").join(\"\\n\");\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: view.state.doc.length, insert: longDoc },\n\t\t\t});\n\n\t\t\tconst line50 = view.state.doc.line(50);\n\t\t\tview.dispatch({\n\t\t\t\teffects: EditorView.scrollIntoView(line50.from, { y: \"center\" }),\n\t\t\t});\n\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 100));\n\t\t\ttest.assert(\n\t\t\t\tview.scrollDOM.scrollTop >= 0,\n\t\t\t\t\"scrollTop should be accessible\",\n\t\t\t);\n\t\t});\n\t});\n\n\trunner.test(\"Viewport info\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tconst longDoc = Array(200).fill(\"some text content\").join(\"\\n\");\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: longDoc },\n\t\t\t});\n\n\t\t\tconst viewport = view.viewport;\n\t\t\ttest.assert(typeof viewport.from === \"number\", \"viewport.from exists\");\n\t\t\ttest.assert(typeof viewport.to === \"number\", \"viewport.to exists\");\n\t\t\ttest.assert(viewport.to > viewport.from, \"viewport has range\");\n\t\t});\n\t});\n\n\t// =========================================\n\t// CODEMIRROR-SPECIFIC FEATURES\n\t// =========================================\n\n\trunner.test(\"EditorState facets\", async (test) => {\n\t\tconst { view, container } = createEditor(\"test\");\n\n\t\ttry {\n\t\t\tconst readOnly = view.state.facet(EditorState.readOnly);\n\t\t\ttest.assert(typeof readOnly === \"boolean\", \"readOnly facet exists\");\n\t\t\ttest.assertEqual(readOnly, false);\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Read-only facet value\", async (test) => {\n\t\tconst container = document.createElement(\"div\");\n\t\tcontainer.style.width = \"500px\";\n\t\tcontainer.style.height = \"300px\";\n\t\tdocument.body.appendChild(container);\n\n\t\tconst state = EditorState.create({\n\t\t\tdoc: \"read only content\",\n\t\t\textensions: [EditorState.readOnly.of(true)],\n\t\t});\n\n\t\tconst view = new EditorView({ state, parent: container });\n\n\t\ttry {\n\t\t\tconst isReadOnly = view.state.facet(EditorState.readOnly);\n\t\t\ttest.assertEqual(isReadOnly, true, \"Should report as read-only\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Transaction filtering\", async (test) => {\n\t\tlet filterCalled = false;\n\n\t\tconst container = document.createElement(\"div\");\n\t\tcontainer.style.width = \"500px\";\n\t\tcontainer.style.height = \"300px\";\n\t\tdocument.body.appendChild(container);\n\n\t\tconst state = EditorState.create({\n\t\t\tdoc: \"original\",\n\t\t\textensions: [\n\t\t\t\tEditorState.transactionFilter.of((tr) => {\n\t\t\t\t\tif (tr.docChanged) filterCalled = true;\n\t\t\t\t\treturn tr;\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\n\t\tconst view = new EditorView({ state, parent: container });\n\n\t\ttry {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, to: 8, insert: \"modified\" },\n\t\t\t});\n\n\t\t\ttest.assert(filterCalled, \"Transaction filter should be called\");\n\t\t\ttest.assertEqual(view.state.doc.toString(), \"modified\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Update listener\", async (test) => {\n\t\tlet updateCount = 0;\n\t\tlet docChanged = false;\n\n\t\tconst container = document.createElement(\"div\");\n\t\tcontainer.style.width = \"500px\";\n\t\tcontainer.style.height = \"300px\";\n\t\tdocument.body.appendChild(container);\n\n\t\tconst state = EditorState.create({\n\t\t\tdoc: \"\",\n\t\t\textensions: [\n\t\t\t\tEditorView.updateListener.of((update) => {\n\t\t\t\t\tupdateCount++;\n\t\t\t\t\tif (update.docChanged) docChanged = true;\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\n\t\tconst view = new EditorView({ state, parent: container });\n\n\t\ttry {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"hello\" },\n\t\t\t});\n\n\t\t\ttest.assert(updateCount > 0, \"Update listener should fire\");\n\t\t\ttest.assert(docChanged, \"docChanged should be true\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"State effects\", async (test) => {\n\t\tconst { StateEffect } = await import(\"@codemirror/state\");\n\t\tconst myEffect = StateEffect.define();\n\n\t\tlet effectReceived = false;\n\n\t\tconst container = document.createElement(\"div\");\n\t\tcontainer.style.width = \"500px\";\n\t\tcontainer.style.height = \"300px\";\n\t\tdocument.body.appendChild(container);\n\n\t\tconst state = EditorState.create({\n\t\t\tdoc: \"\",\n\t\t\textensions: [\n\t\t\t\tEditorView.updateListener.of((update) => {\n\t\t\t\t\tfor (const tr of update.transactions) {\n\t\t\t\t\t\tfor (const effect of tr.effects) {\n\t\t\t\t\t\t\tif (effect.is(myEffect)) {\n\t\t\t\t\t\t\t\teffectReceived = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t],\n\t\t});\n\n\t\tconst view = new EditorView({ state, parent: container });\n\n\t\ttry {\n\t\t\tview.dispatch({\n\t\t\t\teffects: myEffect.of(\"test-value\"),\n\t\t\t});\n\n\t\t\ttest.assert(effectReceived, \"Custom state effect should be received\");\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Compartments for dynamic config\", async (test) => {\n\t\tconst { Compartment } = await import(\"@codemirror/state\");\n\n\t\tconst readOnlyComp = new Compartment();\n\n\t\tconst container = document.createElement(\"div\");\n\t\tcontainer.style.width = \"500px\";\n\t\tcontainer.style.height = \"300px\";\n\t\tdocument.body.appendChild(container);\n\n\t\tconst state = EditorState.create({\n\t\t\tdoc: \"test\",\n\t\t\textensions: [readOnlyComp.of(EditorState.readOnly.of(false))],\n\t\t});\n\n\t\tconst view = new EditorView({ state, parent: container });\n\n\t\ttry {\n\t\t\ttest.assertEqual(view.state.facet(EditorState.readOnly), false);\n\n\t\t\tview.dispatch({\n\t\t\t\teffects: readOnlyComp.reconfigure(EditorState.readOnly.of(true)),\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.facet(EditorState.readOnly), true);\n\t\t} finally {\n\t\t\tview.destroy();\n\t\t\tcontainer.remove();\n\t\t}\n\t});\n\n\trunner.test(\"Document iteration\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"line1\\nline2\\nline3\" },\n\t\t\t});\n\n\t\t\tconst lines = [];\n\t\t\tfor (let i = 1; i <= view.state.doc.lines; i++) {\n\t\t\t\tlines.push(view.state.doc.line(i).text);\n\t\t\t}\n\n\t\t\ttest.assertEqual(lines.length, 3);\n\t\t\ttest.assertEqual(lines[0], \"line1\");\n\t\t\ttest.assertEqual(lines[1], \"line2\");\n\t\t\ttest.assertEqual(lines[2], \"line3\");\n\t\t});\n\t});\n\n\trunner.test(\"Text iterator\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\tconst iter = view.state.doc.iter();\n\t\t\tlet text = \"\";\n\t\t\twhile (!iter.done) {\n\t\t\t\ttext += iter.value;\n\t\t\t\titer.next();\n\t\t\t}\n\n\t\t\ttest.assertEqual(text, \"hello world\");\n\t\t});\n\t});\n\n\trunner.test(\"Slice string\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\ttest.assertEqual(view.state.doc.sliceString(0, 5), \"hello\");\n\t\t\ttest.assertEqual(view.state.doc.sliceString(6, 11), \"world\");\n\t\t\ttest.assertEqual(view.state.doc.sliceString(6), \"world\");\n\t\t});\n\t});\n\n\trunner.test(\"Line at position\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"aaa\\nbbb\\nccc\" },\n\t\t\t});\n\n\t\t\tconst lineAtStart = view.state.doc.lineAt(0);\n\t\t\ttest.assertEqual(lineAtStart.number, 1);\n\n\t\t\tconst lineAtMiddle = view.state.doc.lineAt(5);\n\t\t\ttest.assertEqual(lineAtMiddle.number, 2);\n\n\t\t\tconst lineAtEnd = view.state.doc.lineAt(10);\n\t\t\ttest.assertEqual(lineAtEnd.number, 3);\n\t\t});\n\t});\n\n\trunner.test(\"Visible ranges\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tconst longDoc = Array(100).fill(\"content\").join(\"\\n\");\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: longDoc },\n\t\t\t});\n\n\t\t\tconst visibleRanges = view.visibleRanges;\n\t\t\ttest.assert(Array.isArray(visibleRanges), \"visibleRanges is an array\");\n\t\t\ttest.assert(visibleRanges.length > 0, \"Should have visible ranges\");\n\n\t\t\tfor (const range of visibleRanges) {\n\t\t\t\ttest.assert(typeof range.from === \"number\", \"range.from exists\");\n\t\t\t\ttest.assert(typeof range.to === \"number\", \"range.to exists\");\n\t\t\t}\n\t\t});\n\t});\n\n\trunner.test(\"coordsAtPos\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"hello\" },\n\t\t\t});\n\n\t\t\tconst coords = view.coordsAtPos(0);\n\t\t\ttest.assert(coords != null, \"coords should exist\");\n\t\t\ttest.assert(typeof coords.left === \"number\", \"coords.left exists\");\n\t\t\ttest.assert(typeof coords.top === \"number\", \"coords.top exists\");\n\t\t});\n\t});\n\n\trunner.test(\"posAtCoords\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"hello world\" },\n\t\t\t});\n\n\t\t\tconst rect = view.contentDOM.getBoundingClientRect();\n\t\t\tconst pos = view.posAtCoords({ x: rect.left + 10, y: rect.top + 10 });\n\n\t\t\ttest.assert(pos != null || pos === null, \"posAtCoords should return\");\n\t\t});\n\t});\n\n\trunner.test(\"Edge scroll direction helper\", async (test) => {\n\t\tconst rect = {\n\t\t\tleft: 100,\n\t\t\tright: 300,\n\t\t\ttop: 200,\n\t\t\tbottom: 400,\n\t\t};\n\n\t\tconst leftTop = getEdgeScrollDirections({\n\t\t\tx: 110,\n\t\t\ty: 210,\n\t\t\trect,\n\t\t\tallowHorizontal: true,\n\t\t});\n\t\ttest.assertEqual(leftTop.horizontal, -1);\n\t\ttest.assertEqual(leftTop.vertical, -1);\n\n\t\tconst rightBottom = getEdgeScrollDirections({\n\t\t\tx: 295,\n\t\t\ty: 395,\n\t\t\trect,\n\t\t\tallowHorizontal: true,\n\t\t});\n\t\ttest.assertEqual(rightBottom.horizontal, 1);\n\t\ttest.assertEqual(rightBottom.vertical, 1);\n\n\t\tconst noHorizontal = getEdgeScrollDirections({\n\t\t\tx: 110,\n\t\t\ty: 395,\n\t\t\trect,\n\t\t\tallowHorizontal: false,\n\t\t});\n\t\ttest.assertEqual(noHorizontal.horizontal, 0);\n\t\ttest.assertEqual(noHorizontal.vertical, 1);\n\t});\n\n\trunner.test(\"lineBlockAt\", async (test) => {\n\t\tawait withEditor(test, async (view) => {\n\t\t\tview.dispatch({\n\t\t\t\tchanges: { from: 0, insert: \"line1\\nline2\\nline3\" },\n\t\t\t});\n\n\t\t\tconst line2Start = view.state.doc.line(2).from;\n\t\t\tconst block = view.lineBlockAt(line2Start);\n\n\t\t\ttest.assert(block != null, \"lineBlockAt should return block\");\n\t\t\ttest.assert(typeof block.from === \"number\", \"block.from exists\");\n\t\t\ttest.assert(typeof block.to === \"number\", \"block.to exists\");\n\t\t\ttest.assert(typeof block.height === \"number\", \"block.height exists\");\n\t\t});\n\t});\n\n\treturn await runner.run(writeOutput);\n}\n\nexport { runCodeMirrorTests as runAceEditorTests };\n"
  },
  {
    "path": "src/test/exec.tests.js",
    "content": "import { TestRunner } from \"./tester\";\n\nexport async function runExecutorTests(writeOutput) {\n\tconst runner = new TestRunner(\"Executor API Tests\");\n\n\trunner.test(\"Executor available\", async (test) => {\n\t\ttest.assert(\n\t\t\ttypeof Executor !== \"undefined\",\n\t\t\t\"Executor should be available globally\",\n\t\t);\n\t});\n\n\trunner.test(\"Background Executor available\", async (test) => {\n\t\ttest.assert(\n\t\t\ttypeof Executor.BackgroundExecutor !== \"undefined\",\n\t\t\t\"Background Executor should be available globally\",\n\t\t);\n\t});\n\n\trunner.test(\"execute()\", async (test) => {\n\t\ttest.assert(\n\t\t\t(await Executor.execute(\"echo test123\")).includes(\"test123\"),\n\t\t\t\"Command output should match\",\n\t\t);\n\t});\n\n\trunner.test(\"execute() (BackgroundExecutor)\", async (test) => {\n\t\ttest.assert(\n\t\t\t(await Executor.BackgroundExecutor.execute(\"echo test123\")).includes(\n\t\t\t\t\"test123\",\n\t\t\t),\n\t\t\t\"Command output should match\",\n\t\t);\n\t});\n\n\trunner.test(\"start()\", async (test) => {\n\t\tlet stdout = \"\";\n\n\t\tconst uuid = await Executor.start(\"sh\", (type, data) => {\n\t\t\tif (type === \"stdout\") stdout += data;\n\t\t});\n\n\t\tawait Executor.write(uuid, \"echo hello\\n\");\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\t\tawait Executor.stop(uuid);\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\ttest.assert(stdout.includes(\"hello\"), \"Shell should echo output\");\n\t});\n\n\trunner.test(\"start() (BackgroundExecutor)\", async (test) => {\n\t\tlet stdout = \"\";\n\n\t\tconst uuid = await Executor.BackgroundExecutor.start(\"sh\", (type, data) => {\n\t\t\tif (type === \"stdout\") stdout += data;\n\t\t});\n\n\t\tawait Executor.BackgroundExecutor.write(uuid, \"echo hello\\n\");\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\t\tawait Executor.BackgroundExecutor.stop(uuid);\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\ttest.assert(stdout.includes(\"hello\"), \"Shell should echo output\");\n\t});\n\n\trunner.test(\"start/stop() (BackgroundExecutor)\", async (test) => {\n\t\tlet stdout = \"\";\n\n\t\tconst uuid = await Executor.BackgroundExecutor.start(\n\t\t\t\"sh\",\n\t\t\t(type, data) => {},\n\t\t);\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\tconst isRunning = await Executor.BackgroundExecutor.isRunning(uuid);\n\n\t\ttest.assert(isRunning === true, \"Executor must be running\");\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\tawait Executor.BackgroundExecutor.stop(uuid);\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\ttest.assert(\n\t\t\tisRunning !== (await Executor.BackgroundExecutor.isRunning(uuid)),\n\t\t\t\"Executor must be stopped\",\n\t\t);\n\t\ttest.assert(\n\t\t\t(await Executor.BackgroundExecutor.isRunning(uuid)) === false,\n\t\t\t\"Executor must be stopped\",\n\t\t);\n\t});\n\n\trunner.test(\"start/stop()\", async (test) => {\n\t\tlet stdout = \"\";\n\n\t\tconst uuid = await Executor.start(\"sh\", (type, data) => {});\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\tconst isRunning = await Executor.isRunning(uuid);\n\n\t\ttest.assert(isRunning === true, \"Executor must be running\");\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\tawait Executor.stop(uuid);\n\n\t\tawait new Promise((r) => setTimeout(r, 200));\n\n\t\ttest.assert(\n\t\t\t(await Executor.isRunning(uuid)) === false,\n\t\t\t\"Executor must be stopped\",\n\t\t);\n\t});\n\n\trunner.test(\"FDROID env variable\", async (test) => {\n\t\tconst result = await Executor.execute(\"echo $FDROID\");\n\n\t\tconst isSet = result.trim().length > 0;\n\n\t\ttest.assert(isSet, \"FDROID env variable should be set\");\n\t});\n\n\treturn await runner.run(writeOutput);\n}\n"
  },
  {
    "path": "src/test/sanity.tests.js",
    "content": "import { TestRunner } from \"./tester\";\n\nexport async function runSanityTests(writeOutput) {\n\tconst runner = new TestRunner(\"JS (WebView) Sanity Tests\");\n\n\t// Test 1: String operations\n\trunner.test(\"String concatenation\", (test) => {\n\t\tconst result = \"Hello\" + \" \" + \"World\";\n\t\ttest.assertEqual(result, \"Hello World\", \"String concatenation should work\");\n\t});\n\n\t// Test 2: Number operations\n\trunner.test(\"Basic arithmetic\", (test) => {\n\t\tconst sum = 5 + 3;\n\t\ttest.assertEqual(sum, 8, \"Addition should work correctly\");\n\t});\n\n\t// Test 3: Array operations\n\trunner.test(\"Array operations\", (test) => {\n\t\tconst arr = [1, 2, 3];\n\t\ttest.assertEqual(arr.length, 3, \"Array length should be correct\");\n\t\ttest.assert(arr.includes(2), \"Array should include 2\");\n\t});\n\n\t// Test 4: Object operations\n\trunner.test(\"Object operations\", (test) => {\n\t\tconst obj = { name: \"Test\", value: 42 };\n\t\ttest.assertEqual(obj.name, \"Test\", \"Object property should be accessible\");\n\t\ttest.assertEqual(obj.value, 42, \"Object value should be correct\");\n\t});\n\n\t// Test 5: Function execution\n\trunner.test(\"Function execution\", (test) => {\n\t\tconst add = (a, b) => a + b;\n\t\tconst result = add(10, 20);\n\t\ttest.assertEqual(result, 30, \"Function should return correct value\");\n\t});\n\n\t// Test 6: Async function\n\trunner.test(\"Async function handling\", async (test) => {\n\t\tconst asyncFunc = async () => {\n\t\t\treturn new Promise((resolve) => {\n\t\t\t\tsetTimeout(() => resolve(\"done\"), 10);\n\t\t\t});\n\t\t};\n\n\t\tconst result = await asyncFunc();\n\t\ttest.assertEqual(result, \"done\", \"Async function should work correctly\");\n\t});\n\n\t// Test 7: Error handling\n\trunner.test(\"Error handling\", (test) => {\n\t\ttry {\n\t\t\tthrow new Error(\"Test error\");\n\t\t} catch (e) {\n\t\t\ttest.assert(e instanceof Error, \"Should catch Error instances\");\n\t\t}\n\t});\n\n\t// Test 8: Conditional logic\n\trunner.test(\"Conditional logic\", (test) => {\n\t\tconst value = 10;\n\t\ttest.assert(value > 5, \"Condition should be true\");\n\t\ttest.assert(!(value < 5), \"Negation should work\");\n\t});\n\n\t// Run all tests\n\treturn await runner.run(writeOutput);\n}\n"
  },
  {
    "path": "src/test/tester.js",
    "content": "import { runAceCompatibilityTests } from \"./ace.test\";\nimport { runCodeMirrorTests } from \"./editor.tests\";\nimport { runExecutorTests } from \"./exec.tests\";\nimport { runSanityTests } from \"./sanity.tests\";\nimport { runUrlTests } from \"./url.tests\";\n\nexport async function runAllTests() {\n\tconst terminal = acode.require(\"terminal\");\n\tconst local = await terminal.createLocal({ name: \"TestCode\" });\n\tfunction write(data) {\n\t\tterminal.write(local.id, data);\n\t}\n\n\t// Run tests at runtime\n\twrite(\"\\x1b[36m\\x1b[1m🚀 TestCode Plugin Loaded\\x1b[0m\\n\");\n\twrite(\"\\x1b[36m\\x1b[1mStarting test execution...\\x1b[0m\\n\");\n\n\ttry {\n\t\t// Run unit tests\n\t\tawait runSanityTests(write);\n\t\tawait runCodeMirrorTests(write);\n\t\tawait runAceCompatibilityTests(write);\n\t\tawait runExecutorTests(write);\n\t\tawait runUrlTests(write);\n\n\t\twrite(\"\\x1b[36m\\x1b[1mTests completed!\\x1b[0m\\n\");\n\t} catch (error) {\n\t\twrite(`\\x1b[31m⚠️ Test execution error: ${error.message}\\x1b[0m\\n`);\n\t}\n}\n\n// ANSI color codes for terminal output\nconst COLORS = {\n\tRESET: \"\\x1b[0m\",\n\tBRIGHT: \"\\x1b[1m\",\n\tDIM: \"\\x1b[2m\",\n\tITALIC: \"\\x1b[3m\",\n\n\t// Foreground colors\n\tRED: \"\\x1b[31m\",\n\tGREEN: \"\\x1b[32m\",\n\tYELLOW: \"\\x1b[33m\",\n\tBLUE: \"\\x1b[34m\",\n\tMAGENTA: \"\\x1b[35m\",\n\tCYAN: \"\\x1b[36m\",\n\tWHITE: \"\\x1b[37m\",\n\tGRAY: \"\\x1b[90m\",\n\n\t// Background colors\n\tBG_RED: \"\\x1b[41m\",\n\tBG_GREEN: \"\\x1b[42m\",\n\tBG_YELLOW: \"\\x1b[43m\",\n\tBG_BLUE: \"\\x1b[44m\",\n};\n\nfunction delay(ms) {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction startSpinner(writeOutput, text) {\n\tlet index = 0;\n\tlet active = true;\n\n\tconst timer = setInterval(() => {\n\t\tif (!active) return;\n\t\tconst frame = SPINNER_FRAMES[index++ % SPINNER_FRAMES.length];\n\t\t// \\r moves cursor to start, \\x1b[K clears the line to the right\n\t\twriteOutput(`\\r  ${COLORS.CYAN}${frame}${COLORS.RESET} ${text}`);\n\t}, 80);\n\n\treturn () => {\n\t\tactive = false;\n\t\tclearInterval(timer);\n\t\t// Clear the line so the \"Success/Fail\" message can take its place\n\t\twriteOutput(\"\\r\\x1b[K\");\n\t};\n}\n\n// Spinner frames\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\n\nclass TestRunner {\n\tconstructor(name = \"Test Suite\") {\n\t\tthis.name = name;\n\t\tthis.tests = [];\n\t\tthis.passed = 0;\n\t\tthis.failed = 0;\n\t\tthis.results = [];\n\t\tthis.skipped = 0;\n\t}\n\n\t/**\n\t * Register a test\n\t */\n\ttest(testName, testFn) {\n\t\tthis.tests.push({ name: testName, fn: testFn });\n\t}\n\n\t/**\n\t * Assertions\n\t */\n\tassert(condition, message) {\n\t\tif (!condition) {\n\t\t\tthrow new Error(message || \"Assertion failed\");\n\t\t}\n\t}\n\n\tassertEqual(actual, expected, message) {\n\t\tif (actual !== expected) {\n\t\t\tthrow new Error(message || `Expected ${expected}, got ${actual}`);\n\t\t}\n\t}\n\n\tskip(reason = \"Skipped\") {\n\t\tthrow new SkipTest(reason);\n\t}\n\n\tasync _runWithTimeout(fn, ctx, timeoutMs) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tlet finished = false;\n\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tif (finished) return;\n\t\t\t\tfinished = true;\n\t\t\t\treject(new Error(`Test timed out after ${timeoutMs}ms`));\n\t\t\t}, timeoutMs);\n\n\t\t\tPromise.resolve()\n\t\t\t\t.then(() => fn(ctx))\n\t\t\t\t.then((result) => {\n\t\t\t\t\tif (finished) return;\n\t\t\t\t\tfinished = true;\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(result);\n\t\t\t\t})\n\t\t\t\t.catch((err) => {\n\t\t\t\t\tif (finished) return;\n\t\t\t\t\tfinished = true;\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Run all tests\n\t */\n\tasync run(writeOutput) {\n\t\tconst line = (text = \"\", color = \"\") => {\n\t\t\twriteOutput(`${color}${text}${COLORS.RESET}\\n`);\n\t\t};\n\n\t\t// Header\n\t\tline();\n\t\tline(\n\t\t\t\"╔════════════════════════════════════════════╗\",\n\t\t\tCOLORS.CYAN + COLORS.BRIGHT,\n\t\t);\n\t\tline(\n\t\t\t`║ 🧪  ${this._padCenter(this.name, 35)} │`,\n\t\t\tCOLORS.CYAN + COLORS.BRIGHT,\n\t\t);\n\t\tline(\n\t\t\t\"╚════════════════════════════════════════════╝\",\n\t\t\tCOLORS.CYAN + COLORS.BRIGHT,\n\t\t);\n\t\tline();\n\n\t\t// Run tests with spinner\n\t\tfor (const test of this.tests) {\n\t\t\tconst stopSpinner = startSpinner(writeOutput, `Running ${test.name}...`);\n\n\t\t\ttry {\n\t\t\t\tawait delay(200);\n\t\t\t\tawait this._runWithTimeout(test.fn, this, 10000);\n\n\t\t\t\tstopSpinner();\n\n\t\t\t\tthis.passed++;\n\t\t\t\tthis.results.push({ name: test.name, status: \"PASS\" });\n\t\t\t\tline(`  ${COLORS.GREEN}✓${COLORS.RESET} ${test.name}`, COLORS.GREEN);\n\t\t\t} catch (error) {\n\t\t\t\tstopSpinner();\n\n\t\t\t\tif (error instanceof SkipTest) {\n\t\t\t\t\tthis.skipped++;\n\t\t\t\t\tthis.results.push({\n\t\t\t\t\t\tname: test.name,\n\t\t\t\t\t\tstatus: \"SKIP\",\n\t\t\t\t\t\treason: error.message,\n\t\t\t\t\t});\n\n\t\t\t\t\tline(\n\t\t\t\t\t\t`  ${COLORS.YELLOW}?${COLORS.RESET} ${test.name}`,\n\t\t\t\t\t\tCOLORS.YELLOW + COLORS.BRIGHT,\n\t\t\t\t\t);\n\t\t\t\t\tline(\n\t\t\t\t\t\t`     ${COLORS.DIM}└─ ${error.message}${COLORS.RESET}`,\n\t\t\t\t\t\tCOLORS.YELLOW + COLORS.DIM,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tthis.failed++;\n\t\t\t\t\tthis.results.push({\n\t\t\t\t\t\tname: test.name,\n\t\t\t\t\t\tstatus: \"FAIL\",\n\t\t\t\t\t\terror: error.message,\n\t\t\t\t\t});\n\n\t\t\t\t\tline(\n\t\t\t\t\t\t`  ${COLORS.RED}✗${COLORS.RESET} ${test.name}`,\n\t\t\t\t\t\tCOLORS.RED + COLORS.BRIGHT,\n\t\t\t\t\t);\n\t\t\t\t\tline(\n\t\t\t\t\t\t`     ${COLORS.DIM}└─ ${error.message}${COLORS.RESET}`,\n\t\t\t\t\t\tCOLORS.RED + COLORS.DIM,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Summary\n\t\tline();\n\t\tline(\"─────────────────────────────────────────────\", COLORS.GRAY);\n\n\t\tconst total = this.tests.length;\n\t\tconst effectiveTotal = total - this.skipped;\n\n\t\tconst percentage = effectiveTotal\n\t\t\t? ((this.passed / effectiveTotal) * 100).toFixed(1)\n\t\t\t: \"0.0\";\n\n\t\tconst statusColor = this.failed === 0 ? COLORS.GREEN : COLORS.YELLOW;\n\n\t\tline(\n\t\t\t`  Tests: ${COLORS.BRIGHT}${total}${COLORS.RESET} | ` +\n\t\t\t\t`${COLORS.GREEN}Passed: ${this.passed}${COLORS.RESET} | ` +\n\t\t\t\t`${COLORS.YELLOW}Skipped: ${this.skipped}${COLORS.RESET} | ` +\n\t\t\t\t`${COLORS.RED}Failed: ${this.failed}${COLORS.RESET}`,\n\t\t);\n\n\t\tline(\n\t\t\t`  Success Rate: ${statusColor}${percentage}%${COLORS.RESET}`,\n\t\t\tstatusColor,\n\t\t);\n\t\tline(\"─────────────────────────────────────────────\", COLORS.GRAY);\n\t\tline();\n\n\t\treturn this.results;\n\t}\n\n\t/**\n\t * Center text helper\n\t */\n\t_padCenter(text, width) {\n\t\tconst pad = Math.max(0, width - text.length);\n\t\treturn (\n\t\t\t\" \".repeat(Math.floor(pad / 2)) + text + \" \".repeat(Math.ceil(pad / 2))\n\t\t);\n\t}\n}\n\nclass SkipTest extends Error {\n\tconstructor(message = \"Skipped\") {\n\t\tsuper(message);\n\t\tthis.name = \"SkipTest\";\n\t}\n}\n\nexport { TestRunner };\n"
  },
  {
    "path": "src/test/url.tests.js",
    "content": "import Url from \"../utils/Url\";\nimport { TestRunner } from \"./tester\";\n\nconst JOIN_CASES = [\n\t{\n\t\tname: \"Android SAF join\",\n\t\tfolderUrl:\n\t\t\t\"content://com.android.externalstorage.documents/tree/primary%3ATesthtml\",\n\t\tactiveLocation:\n\t\t\t\"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/\",\n\t\texpectedJoined:\n\t\t\t\"content://com.android.externalstorage.documents/tree/primary%3ATesthtml::primary:Testhtml/Styles/index.html\",\n\t},\n\t{\n\t\tname: \"Termux SAF join\",\n\t\tfolderUrl:\n\t\t\t\"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui\",\n\t\tactiveLocation:\n\t\t\t\"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/\",\n\t\texpectedJoined:\n\t\t\t\"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui::/data/data/com.termux/files/home/acode-site-ui/index.html\",\n\t},\n\t{\n\t\tname: \"Acode SAF join\",\n\t\tfolderUrl:\n\t\t\t\"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic\",\n\t\tactiveLocation:\n\t\t\t\"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/\",\n\t\texpectedJoined:\n\t\t\t\"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic::/data/user/0/com.foxdebug.acode/files/public/index.html\",\n\t},\n];\n\nconst TRAILING_SLASH_CASES = [\n\t{\n\t\tname: \"Android SAF trailing slash\",\n\t\ta: \"content://com.android.externalstorage.documents/tree/primary%3ATesthtml/\",\n\t\tb: \"content://com.android.externalstorage.documents/tree/primary%3ATesthtml\",\n\t},\n\t{\n\t\tname: \"Termux SAF trailing slash\",\n\t\ta: \"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui/\",\n\t\tb: \"content://com.termux.documents/tree/%2Fdata%2Fdata%2Fcom.termux%2Ffiles%2Fhome%2Facode-site-ui\",\n\t},\n\t{\n\t\tname: \"Acode SAF trailing slash\",\n\t\ta: \"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic/\",\n\t\tb: \"content://com.foxdebug.acode.documents/tree/%2Fdata%2Fuser%2F0%2Fcom.foxdebug.acode%2Ffiles%2Fpublic\",\n\t},\n];\n\nfunction assertJoinCase(\n\ttest,\n\t{ folderUrl, activeLocation, expectedJoined, segment },\n) {\n\tconst joined = Url.join(activeLocation, segment || \"index.html\");\n\n\ttest.assert(joined !== null, \"Joining the SAF URL should return a value\");\n\ttest.assertEqual(\n\t\tjoined,\n\t\texpectedJoined,\n\t\t\"Joined URL should match the expected SAF file URI\",\n\t);\n\ttest.assert(\n\t\t!Url.areSame(folderUrl, joined),\n\t\t\"Folder URL and joined file URL should not be considered the same\",\n\t);\n}\n\nexport async function runUrlTests(writeOutput) {\n\tconst runner = new TestRunner(\"URL / SAF URIs\");\n\n\tfor (const joinCase of JOIN_CASES) {\n\t\trunner.test(joinCase.name, (test) => {\n\t\t\tassertJoinCase(test, joinCase);\n\t\t});\n\t}\n\n\tfor (const trailingSlashCase of TRAILING_SLASH_CASES) {\n\t\trunner.test(trailingSlashCase.name, (test) => {\n\t\t\ttest.assert(\n\t\t\t\tUrl.areSame(trailingSlashCase.a, trailingSlashCase.b),\n\t\t\t\t\"Folder URLs differing only by a trailing slash should be same\",\n\t\t\t);\n\t\t});\n\t}\n\n\trunner.test(\"Android SAF leading slash\", (test) => {\n\t\tassertJoinCase(test, {\n\t\t\t...JOIN_CASES[0],\n\t\t\tsegment: \"/index.html\",\n\t\t});\n\t});\n\n\treturn await runner.run(writeOutput);\n}\n"
  },
  {
    "path": "src/theme/builder.js",
    "content": "import Color from \"utils/color\";\n\nexport default class ThemeBuilder {\n\t#theme = {\n\t\t\"--popup-border-radius\": \"4px\",\n\t\t\"--active-color\": \"rgb(51, 153, 255)\",\n\t\t\"--active-text-color\": \"rgb(255, 215, 0)\",\n\t\t\"--active-icon-color\": \"rgba(0, 0, 0, 0.2)\",\n\t\t\"--border-color\": \"rgba(122, 122, 122, 0.2)\",\n\t\t\"--box-shadow-color\": \"rgba(0, 0, 0, 0.2)\",\n\t\t\"--button-active-color\": \"rgb(44, 142, 240)\",\n\t\t\"--button-background-color\": \"rgb(51, 153, 255)\",\n\t\t\"--button-text-color\": \"rgb(255, 255, 255)\",\n\t\t\"--error-text-color\": \"rgb(255, 185, 92)\",\n\t\t\"--primary-color\": \"rgb(153, 153, 255)\",\n\t\t\"--primary-text-color\": \"rgb(255, 255, 255)\",\n\t\t\"--secondary-color\": \"rgb(255, 255, 255)\",\n\t\t\"--secondary-text-color\": \"rgb(37, 37, 37)\",\n\t\t\"--link-text-color\": \"rgb(97, 94, 253)\",\n\t\t\"--scrollbar-color\": \"rgba(0, 0, 0, 0.3)\",\n\t\t\"--popup-border-color\": \"rgba(0, 0, 0, 0)\",\n\t\t\"--popup-icon-color\": \"rgb(153, 153, 255)\",\n\t\t\"--popup-background-color\": \"rgb(255, 255, 255)\",\n\t\t\"--popup-text-color\": \"rgb(37, 37, 37)\",\n\t\t\"--popup-active-color\": \"rgb(169, 0, 0)\",\n\t\t\"--danger-color\": \"rgb(160, 51, 0)\",\n\t\t\"--danger-text-color\": \"rgb(255, 255, 255)\",\n\t\t\"--file-tab-width\": \"120px\",\n\t};\n\n\tversion = \"free\";\n\tname = \"Default\";\n\ttype = \"light\";\n\t/** Whether Auto update darkened primary color when primary color is updated. */\n\tautoDarkened = true;\n\tpreferredEditorTheme = null;\n\tpreferredFont = null;\n\n\t/**\n\t * Creates a theme\n\t * @param {string} [name] name of the theme\n\t * @param {'dark'|'light'} [type] type of the theme\n\t * @param {'free'|'paid'} [version]  version of the theme\n\t */\n\tconstructor(name = \"\", type = \"dark\", version = \"free\") {\n\t\tthis.name = name;\n\t\tthis.type = type;\n\t\tthis.version = version;\n\t}\n\n\tget id() {\n\t\treturn this.name.toLowerCase();\n\t}\n\n\tget popupBorderRadius() {\n\t\treturn this.#theme[\"--popup-border-radius\"];\n\t}\n\n\tset popupBorderRadius(value) {\n\t\tthis.#theme[\"--popup-border-radius\"] = value;\n\t}\n\n\tget activeColor() {\n\t\treturn this.#theme[\"--active-color\"];\n\t}\n\n\tset activeColor(value) {\n\t\tthis.#theme[\"--active-color\"] = value;\n\t}\n\n\tget activeIconColor() {\n\t\treturn this.#theme[\"--active-icon-color\"];\n\t}\n\n\tset activeIconColor(value) {\n\t\tthis.#theme[\"--active-icon-color\"] = value;\n\t}\n\n\tget borderColor() {\n\t\treturn this.#theme[\"--border-color\"];\n\t}\n\n\tset borderColor(value) {\n\t\tthis.#theme[\"--border-color\"] = value;\n\t}\n\n\tget boxShadowColor() {\n\t\treturn this.#theme[\"--box-shadow-color\"];\n\t}\n\n\tset boxShadowColor(value) {\n\t\tthis.#theme[\"--box-shadow-color\"] = value;\n\t}\n\n\tget buttonActiveColor() {\n\t\treturn this.#theme[\"--button-active-color\"];\n\t}\n\n\tset buttonActiveColor(value) {\n\t\tthis.#theme[\"--button-active-color\"] = value;\n\t}\n\n\tget buttonBackgroundColor() {\n\t\treturn this.#theme[\"--button-background-color\"];\n\t}\n\n\tset buttonBackgroundColor(value) {\n\t\tthis.#theme[\"--button-background-color\"] = value;\n\t}\n\n\tget buttonTextColor() {\n\t\treturn this.#theme[\"--button-text-color\"];\n\t}\n\n\tset buttonTextColor(value) {\n\t\tthis.#theme[\"--button-text-color\"] = value;\n\t}\n\n\tget errorTextColor() {\n\t\treturn this.#theme[\"--error-text-color\"];\n\t}\n\n\tset errorTextColor(value) {\n\t\tthis.#theme[\"--error-text-color\"] = value;\n\t}\n\n\tget primaryColor() {\n\t\treturn this.#theme[\"--primary-color\"];\n\t}\n\n\tset primaryColor(value) {\n\t\tif (this.autoDarkened) {\n\t\t\tthis.darkenedPrimaryColor = Color(value).darken(0.4).hex.toString();\n\t\t}\n\t\tthis.#theme[\"--primary-color\"] = value;\n\t}\n\n\tget primaryTextColor() {\n\t\treturn this.#theme[\"--primary-text-color\"];\n\t}\n\n\tset primaryTextColor(value) {\n\t\tthis.#theme[\"--primary-text-color\"] = value;\n\t}\n\n\tget secondaryColor() {\n\t\treturn this.#theme[\"--secondary-color\"];\n\t}\n\n\tset secondaryColor(value) {\n\t\tthis.#theme[\"--secondary-color\"] = value;\n\t}\n\n\tget secondaryTextColor() {\n\t\treturn this.#theme[\"--secondary-text-color\"];\n\t}\n\n\tset secondaryTextColor(value) {\n\t\tthis.#theme[\"--secondary-text-color\"] = value;\n\t}\n\n\tget linkTextColor() {\n\t\treturn this.#theme[\"--link-text-color\"];\n\t}\n\n\tset linkTextColor(value) {\n\t\tthis.#theme[\"--link-text-color\"] = value;\n\t}\n\n\tget scrollbarColor() {\n\t\treturn this.#theme[\"--scrollbar-color\"];\n\t}\n\n\tset scrollbarColor(value) {\n\t\tthis.#theme[\"--scrollbar-color\"] = value;\n\t}\n\n\tget popupBorderColor() {\n\t\treturn this.#theme[\"--popup-border-color\"];\n\t}\n\n\tset popupBorderColor(value) {\n\t\tthis.#theme[\"--popup-border-color\"] = value;\n\t}\n\n\tget popupIconColor() {\n\t\treturn this.#theme[\"--popup-icon-color\"];\n\t}\n\n\tset popupIconColor(value) {\n\t\tthis.#theme[\"--popup-icon-color\"] = value;\n\t}\n\n\tget popupBackgroundColor() {\n\t\treturn this.#theme[\"--popup-background-color\"];\n\t}\n\n\tset popupBackgroundColor(value) {\n\t\tthis.#theme[\"--popup-background-color\"] = value;\n\t}\n\n\tget popupTextColor() {\n\t\treturn this.#theme[\"--popup-text-color\"];\n\t}\n\n\tset popupTextColor(value) {\n\t\tthis.#theme[\"--popup-text-color\"] = value;\n\t}\n\n\tget popupActiveColor() {\n\t\treturn this.#theme[\"--popup-active-color\"];\n\t}\n\n\tset popupActiveColor(value) {\n\t\tthis.#theme[\"--popup-active-color\"] = value;\n\t}\n\n\tget dangerColor() {\n\t\treturn this.#theme[\"--danger-color\"];\n\t}\n\n\tset dangerColor(value) {\n\t\tthis.#theme[\"--danger-color\"] = value;\n\t}\n\n\tget fileTabWidth() {\n\t\treturn this.#theme[\"--file-tab-width\"];\n\t}\n\n\tset fileTabWidth(value) {\n\t\tthis.#theme[\"--file-tab-width\"] = value;\n\t}\n\n\tget activeTextColor() {\n\t\treturn this.#theme[\"--active-text-color\"];\n\t}\n\n\tset activeTextColor(value) {\n\t\tthis.#theme[\"--active-text-color\"] = value;\n\t}\n\n\tget css() {\n\t\tlet css = \"\";\n\t\tObject.keys(this.#theme).forEach((key) => {\n\t\t\tcss += `${key}: ${this.#theme[key]};`;\n\t\t});\n\t\treturn `:root {${css}}`;\n\t}\n\n\t/**\n\t * Gets the theme as an object\n\t * @param {'rgba'|'hex' | 'none'} colorType\n\t * @returns {Object<string, string>}\n\t */\n\ttoJSON(colorType = \"none\") {\n\t\tconst res = {\n\t\t\tname: this.name,\n\t\t\ttype: this.type,\n\t\t\tversion: this.version,\n\t\t};\n\t\tObject.keys(this.#theme).forEach((key) => {\n\t\t\tconst color =\n\t\t\t\tcolorType === \"hex\"\n\t\t\t\t\t? Color(this.#theme[key]).hex.toString()\n\t\t\t\t\t: colorType === \"rgba\"\n\t\t\t\t\t\t? Color(this.#theme[key]).rgba.toString()\n\t\t\t\t\t\t: this.#theme[key];\n\t\t\tres[ThemeBuilder.#toPascal(key)] = color;\n\t\t});\n\t\treturn res;\n\t}\n\n\ttoString() {\n\t\treturn JSON.stringify(this.toJSON());\n\t}\n\n\t/**\n\t * This method is used to set a darkened primary color.\n\t */\n\tdarkenPrimaryColor() {\n\t\tthis.darkenedPrimaryColor = Color(this.primaryColor)\n\t\t\t.darken(0.4)\n\t\t\t.hex.toString();\n\t}\n\n\tmatches(id) {\n\t\treturn this.id.toLowerCase() === id.toLowerCase();\n\t}\n\n\t/**\n\t * Creates a theme from a CSS string\n\t * @param {string} name\n\t * @param {string} css\n\t * @returns {ThemeBuilder}\n\t */\n\tstatic fromCSS(name, css) {\n\t\tconst themeBuilder = new ThemeBuilder(name);\n\n\t\t// get rules using regex\n\t\tconst rules = css.match(/:root\\s*{([^}]*)}/);\n\t\tif (!rules) throw new Error(\"Invalid CSS string\");\n\n\t\t// get variables using regex\n\t\tconst variables = rules[1].match(/--[\\w-]+:\\s*[^;]+/g);\n\t\tif (!variables) throw new Error(\"Invalid CSS string\");\n\n\t\t// set variables\n\t\tvariables.forEach((variable) => {\n\t\t\tconst [key, value] = variable.split(\":\");\n\t\t\tthemeBuilder(ThemeBuilder.#toPascal(key.trim()), value.trim());\n\t\t});\n\n\t\treturn themeBuilder;\n\t}\n\n\tstatic fromJSON(theme) {\n\t\tif (!theme) throw new Error(\"Theme is required\");\n\t\tif (typeof theme !== \"object\") throw new Error(\"Theme must be an object\");\n\t\tif (!theme.name) throw new Error(\"Theme name is required\");\n\t\tif (!theme.type) throw new Error(\"Theme type is required\");\n\t\tif (!theme.version) throw new Error(\"Theme version is required\");\n\t\tconst themeBuilder = new ThemeBuilder(\n\t\t\ttheme.name,\n\t\t\ttheme.type,\n\t\t\ttheme.version,\n\t\t);\n\n\t\tObject.keys(theme).forEach((key) => {\n\t\t\tif (!Object.getOwnPropertyDescriptor(ThemeBuilder.prototype, key)) return;\n\t\t\tthemeBuilder[key] = theme[key];\n\t\t});\n\n\t\treturn themeBuilder;\n\t}\n\n\t/**\n\t *\n\t * @param {string} str\n\t */\n\tstatic #toPascal(str) {\n\t\t// e.g. '--primary-color' => 'PrimaryColor'\n\t\treturn str\n\t\t\t.replace(/^--/, \"\")\n\t\t\t.replace(/-([a-z])/g, (g) => g[1].toUpperCase());\n\t}\n}\n\nexport function createBuiltInTheme(name, type, version = \"paid\") {\n\tconst theme = new ThemeBuilder(name, type, version);\n\ttheme.autoDarkened = false;\n\treturn theme;\n}\n"
  },
  {
    "path": "src/theme/list.js",
    "content": "import fsOperation from \"fileSystem\";\nimport { isDeviceDarkTheme } from \"lib/systemConfiguration\";\nimport color from \"utils/color\";\nimport Url from \"utils/Url\";\nimport fonts from \"../lib/fonts\";\nimport settings from \"../lib/settings\";\nimport ThemeBuilder from \"./builder\";\nimport themes, { updateSystemTheme } from \"./preInstalled\";\n\n/** @type {Map<string, ThemeBuilder>} */\nconst appThemes = new Map();\nlet themeApplied = false;\nlet firstTime = true;\n\nfunction init() {\n\tthemes.forEach((theme) => add(theme));\n}\n\n/**\n * @typedef {object} Theme\n * @property {string} id\n * @property {string} name\n * @property {string} type\n * @property {string} version\n * @property {string} primaryColor\n */\n\n/**\n * Returns a list of all themes\n * @returns {Theme[]}\n */\nfunction list() {\n\treturn Array.from(appThemes.keys()).map((name) => {\n\t\tconst { id, type, primaryColor, version } = appThemes.get(name);\n\t\treturn {\n\t\t\tid,\n\t\t\ttype,\n\t\t\tversion,\n\t\t\tprimaryColor,\n\t\t\tname: name.capitalize(),\n\t\t};\n\t});\n}\n\n/**\n *\n * @param {string} name\n * @returns {ThemeBuilder}\n */\nfunction get(name) {\n\treturn appThemes.get(name.toLowerCase());\n}\n\n/**\n *\n * @param {ThemeBuilder} theme\n * @returns\n */\nfunction add(theme) {\n\tif (!(theme instanceof ThemeBuilder)) return;\n\tif (appThemes.has(theme.id)) return;\n\n\tappThemes.set(theme.id, theme);\n\n\tconst { appTheme } = settings.value;\n\n\tif (theme.matches(appTheme)) {\n\t\tif (appTheme !== \"system\") {\n\t\t\tapply(appTheme);\n\t\t} else {\n\t\t\tupdateSystemTheme(isDeviceDarkTheme());\n\t\t\tthemeApplied = true;\n\t\t}\n\t}\n}\n\n/**\n * Apply a theme\n * @param {string} id The name of the theme to apply\n * @param {boolean} init Whether or not this is the first time the theme is being applied\n */\nexport async function apply(id, init) {\n\tif (!DOES_SUPPORT_THEME) {\n\t\tid = \"default\";\n\t}\n\n\tthemeApplied = true;\n\tconst loaderFile = Url.join(ASSETS_DIRECTORY, \"res/tail-spin.svg\");\n\tconst svgName = \"__tail-spin__.svg\";\n\tconst img = Url.join(DATA_STORAGE, svgName);\n\tconst theme = get(id);\n\tconst $style = document.head.get(\"style#app-theme\") ?? (\n\t\t<style id=\"app-theme\"></style>\n\t);\n\tconst update = {\n\t\tappTheme: id,\n\t};\n\n\tif (id === \"custom\") {\n\t\tupdate.customTheme = theme.toJSON();\n\t}\n\n\tif (init && theme.preferredEditorTheme) {\n\t\tupdate.editorTheme = theme.preferredEditorTheme;\n\t\tif (editorManager != null && editorManager.editor != null) {\n\t\t\teditorManager.editor.setTheme(theme.preferredEditorTheme);\n\t\t}\n\t}\n\n\tif (init && theme.preferredFont) {\n\t\tupdate.editorFont = theme.preferredFont;\n\t\tfonts.setFont(theme.preferredFont);\n\t}\n\n\tsettings.update(update, false);\n\tlocalStorage.__primary_color = theme.primaryColor;\n\tdocument.body.setAttribute(\"theme-type\", theme.type);\n\t$style.textContent = theme.css;\n\tdocument.head.append($style);\n\n\tconst primaryColor = color(theme.primaryColor).hex.toString();\n\tconst scheme = theme.toJSON(\"hex\");\n\t// Set status bar and navigation bar color\n\tsystem.setUiTheme(primaryColor, scheme);\n\n\tif (firstTime) {\n\t\t// To make sure system bars are updated\n\t\tsetTimeout(() => {\n\t\t\tsystem.setUiTheme(primaryColor, scheme);\n\t\t}, 1000);\n\t\tfirstTime = false;\n\t}\n\n\ttry {\n\t\tlet fs = fsOperation(loaderFile);\n\t\tconst svg = await fs.readFile(\"utf8\");\n\n\t\tfs = fsOperation(img);\n\t\tif (!(await fs.exists())) {\n\t\t\tawait fsOperation(DATA_STORAGE).createFile(svgName);\n\t\t}\n\t\tawait fs.writeFile(svg.replace(/#fff/g, theme.primaryColor));\n\t} catch (error) {\n\t\twindow.log(\"error\", error);\n\t}\n}\n\n/**\n * Update a theme\n * @param {ThemeBuilder} theme\n */\nexport function update(theme) {\n\tif (!(theme instanceof ThemeBuilder)) return;\n\tconst oldTheme = get(theme.id);\n\tif (!oldTheme) {\n\t\tadd(theme);\n\t\treturn;\n\t}\n\tconst json = theme.toJSON();\n\tObject.keys(json).forEach((key) => {\n\t\toldTheme[key] = json[key];\n\t});\n}\n\nexport default {\n\tget applied() {\n\t\treturn themeApplied;\n\t},\n\tinit,\n\tlist,\n\tget,\n\tadd,\n\tapply,\n\tupdate,\n};\n"
  },
  {
    "path": "src/theme/preInstalled.js",
    "content": "import appSettings from \"lib/settings\";\nimport { createBuiltInTheme } from \"./builder\";\nimport { apply } from \"./list\";\n\nconst WHITE = \"rgb(255, 255, 255)\";\n\nconst dark = createBuiltInTheme(\"Dark\", \"dark\", \"free\");\ndark.primaryColor = \"rgb(35, 39, 42)\";\ndark.primaryTextColor = \"rgb(245, 245, 245)\";\ndark.secondaryColor = \"rgb(45, 49, 52)\";\ndark.secondaryTextColor = \"rgb(228, 228, 228)\";\ndark.activeColor = \"rgb(66, 133, 244)\";\ndark.linkTextColor = \"rgb(138, 180, 248)\";\ndark.borderColor = \"rgba(188, 188, 188, 0.15)\";\ndark.popupIconColor = \"rgb(245, 245, 245)\";\ndark.popupBackgroundColor = \"rgb(35, 39, 42)\";\ndark.popupTextColor = \"rgb(245, 245, 245)\";\ndark.popupActiveColor = \"rgb(66, 133, 244)\";\ndark.activeTextColor = \"rgb(255, 255, 255)\";\ndark.errorTextColor = \"rgb(255, 185, 92)\";\ndark.dangerColor = \"rgb(220, 38, 38)\";\ndark.scrollbarColor = \"rgba(255, 255, 255, 0.2)\";\ndark.preferredEditorTheme = getSystemEditorTheme(true);\n\nconst oled = createBuiltInTheme(\"OLED\");\noled.primaryColor = \"rgb(0, 0, 0)\";\noled.primaryTextColor = \"rgb(255, 255, 255)\";\noled.darkenedPrimaryColor = \"rgb(0, 0, 0)\";\noled.secondaryColor = \"rgb(8, 8, 8)\";\noled.secondaryTextColor = \"rgb(240, 240, 240)\";\noled.activeColor = \"rgb(0, 122, 255)\";\noled.activeIconColor = \"rgba(0, 122, 255, 0.8)\";\noled.linkTextColor = \"rgb(10, 132, 255)\";\noled.borderColor = \"rgba(255, 255, 255, 0.08)\";\noled.popupIconColor = \"rgb(255, 255, 255)\";\noled.popupBackgroundColor = \"rgb(0, 0, 0)\";\noled.popupTextColor = \"rgb(255, 255, 255)\";\noled.popupActiveColor = \"rgb(0, 122, 255)\";\noled.popupBorderColor = \"rgba(255, 255, 255, 0.1)\";\noled.boxShadowColor = \"rgba(0, 0, 0, 0.8)\";\noled.buttonBackgroundColor = \"rgb(0, 122, 255)\";\noled.buttonTextColor = \"rgb(255, 255, 255)\";\noled.buttonActiveColor = \"rgb(10, 132, 255)\";\noled.activeTextColor = \"rgb(255, 255, 255)\";\noled.errorTextColor = \"rgb(255, 69, 58)\";\noled.dangerColor = \"rgb(255, 69, 58)\";\noled.scrollbarColor = \"rgba(255, 255, 255, 0.1)\";\noled.preferredEditorTheme = \"tokyoNight\";\n\nconst ocean = createBuiltInTheme(\"Ocean\");\nocean.darkenedPrimaryColor = \"rgb(19, 19, 26)\";\nocean.primaryColor = \"rgb(32, 32, 44)\";\nocean.primaryTextColor = WHITE;\nocean.secondaryColor = \"rgb(38, 38, 53)\";\nocean.secondaryTextColor = WHITE;\nocean.activeColor = \"rgb(51, 153, 255)\";\nocean.linkTextColor = \"rgb(181, 180, 233)\";\nocean.borderColor = \"rgb(122, 122, 163)\";\nocean.popupIconColor = WHITE;\nocean.popupBackgroundColor = \"rgb(32, 32, 44)\";\nocean.popupTextColor = WHITE;\nocean.popupActiveColor = \"rgb(255, 215, 0)\";\nocean.boxShadowColor = \"rgba(0, 0, 0, 0.5)\";\nocean.preferredEditorTheme = \"solarizedDark\";\nocean.preferredFont = \"Fira Code\";\n\nconst bump = createBuiltInTheme(\"Bump\");\nbump.darkenedPrimaryColor = \"rgb(24, 28, 36)\";\nbump.primaryColor = \"rgb(36, 40, 52)\";\nbump.primaryTextColor = \"rgb(230, 232, 238)\";\nbump.secondaryColor = \"rgb(44, 50, 64)\";\nbump.secondaryTextColor = \"rgb(175, 180, 192)\";\nbump.activeColor = \"rgb(240, 113, 103)\";\nbump.linkTextColor = \"rgb(255, 150, 130)\";\nbump.borderColor = \"rgba(175, 180, 192, 0.2)\";\nbump.popupIconColor = \"rgb(230, 232, 238)\";\nbump.popupBackgroundColor = \"rgb(40, 44, 58)\";\nbump.popupTextColor = \"rgb(230, 232, 238)\";\nbump.popupActiveColor = \"rgb(240, 113, 103)\";\nbump.buttonBackgroundColor = \"rgb(240, 113, 103)\";\nbump.buttonTextColor = \"rgb(255, 255, 255)\";\nbump.buttonActiveColor = \"rgb(210, 90, 80)\";\nbump.boxShadowColor = \"rgba(0, 0, 0, 0.35)\";\nbump.activeTextColor = \"rgb(255, 255, 255)\";\nbump.errorTextColor = \"rgb(255, 180, 100)\";\nbump.dangerColor = \"rgb(240, 70, 60)\";\nbump.scrollbarColor = \"rgba(230, 232, 238, 0.12)\";\nbump.preferredEditorTheme = \"one_dark\";\n\nconst bling = createBuiltInTheme(\"Bling\");\nbling.darkenedPrimaryColor = \"rgb(16, 12, 28)\";\nbling.primaryColor = \"rgb(25, 20, 40)\";\nbling.primaryTextColor = \"rgb(228, 225, 240)\";\nbling.secondaryColor = \"rgb(35, 28, 55)\";\nbling.secondaryTextColor = \"rgb(170, 165, 190)\";\nbling.activeColor = \"rgb(80, 200, 155)\";\nbling.linkTextColor = \"rgb(120, 220, 180)\";\nbling.borderColor = \"rgba(80, 200, 155, 0.2)\";\nbling.popupIconColor = \"rgb(228, 225, 240)\";\nbling.popupBackgroundColor = \"rgb(30, 24, 48)\";\nbling.popupTextColor = \"rgb(228, 225, 240)\";\nbling.popupActiveColor = \"rgb(80, 200, 155)\";\nbling.buttonBackgroundColor = \"rgb(80, 200, 155)\";\nbling.buttonTextColor = \"rgb(16, 12, 28)\";\nbling.buttonActiveColor = \"rgb(55, 170, 130)\";\nbling.boxShadowColor = \"rgba(0, 0, 0, 0.45)\";\nbling.activeTextColor = \"rgb(16, 12, 28)\";\nbling.errorTextColor = \"rgb(255, 170, 100)\";\nbling.dangerColor = \"rgb(240, 85, 85)\";\nbling.scrollbarColor = \"rgba(228, 225, 240, 0.1)\";\nbling.preferredEditorTheme = \"tokyoNight\";\n\nconst moon = createBuiltInTheme(\"Moon\");\nmoon.darkenedPrimaryColor = \"rgb(16, 20, 26)\";\nmoon.primaryColor = \"rgb(26, 32, 42)\";\nmoon.primaryTextColor = \"rgb(210, 225, 230)\";\nmoon.secondaryColor = \"rgb(34, 42, 54)\";\nmoon.secondaryTextColor = \"rgb(150, 170, 180)\";\nmoon.activeColor = \"rgb(0, 188, 194)\";\nmoon.linkTextColor = \"rgb(80, 220, 225)\";\nmoon.borderColor = \"rgba(0, 188, 194, 0.2)\";\nmoon.popupIconColor = \"rgb(210, 225, 230)\";\nmoon.popupBackgroundColor = \"rgb(30, 38, 48)\";\nmoon.popupTextColor = \"rgb(210, 225, 230)\";\nmoon.popupActiveColor = \"rgb(0, 188, 194)\";\nmoon.buttonBackgroundColor = \"rgb(0, 188, 194)\";\nmoon.buttonTextColor = \"rgb(16, 20, 26)\";\nmoon.buttonActiveColor = \"rgb(0, 155, 160)\";\nmoon.boxShadowColor = \"rgba(0, 0, 0, 0.4)\";\nmoon.activeTextColor = \"rgb(16, 20, 26)\";\nmoon.errorTextColor = \"rgb(255, 170, 105)\";\nmoon.dangerColor = \"rgb(235, 75, 70)\";\nmoon.scrollbarColor = \"rgba(210, 225, 230, 0.12)\";\nmoon.preferredEditorTheme = \"tokyoNight\";\n\nconst atticus = createBuiltInTheme(\"Atticus\");\natticus.darkenedPrimaryColor = \"rgb(26, 24, 22)\";\natticus.primaryColor = \"rgb(38, 36, 33)\";\natticus.primaryTextColor = \"rgb(228, 222, 212)\";\natticus.secondaryColor = \"rgb(48, 45, 40)\";\natticus.secondaryTextColor = \"rgb(175, 168, 155)\";\natticus.activeColor = \"rgb(130, 170, 90)\";\natticus.linkTextColor = \"rgb(155, 195, 115)\";\natticus.borderColor = \"rgba(130, 170, 90, 0.2)\";\natticus.popupIconColor = \"rgb(228, 222, 212)\";\natticus.popupBackgroundColor = \"rgb(42, 40, 36)\";\natticus.popupTextColor = \"rgb(228, 222, 212)\";\natticus.popupActiveColor = \"rgb(130, 170, 90)\";\natticus.buttonBackgroundColor = \"rgb(130, 170, 90)\";\natticus.buttonTextColor = \"rgb(38, 36, 33)\";\natticus.buttonActiveColor = \"rgb(105, 145, 70)\";\natticus.boxShadowColor = \"rgba(0, 0, 0, 0.35)\";\natticus.activeTextColor = \"rgb(38, 36, 33)\";\natticus.errorTextColor = \"rgb(240, 160, 90)\";\natticus.dangerColor = \"rgb(210, 65, 55)\";\natticus.scrollbarColor = \"rgba(228, 222, 212, 0.12)\";\natticus.preferredEditorTheme = \"monokai\";\n\nconst tomyris = createBuiltInTheme(\"Tomyris\");\ntomyris.darkenedPrimaryColor = \"rgb(22, 12, 20)\";\ntomyris.primaryColor = \"rgb(32, 18, 28)\";\ntomyris.primaryTextColor = \"rgb(235, 225, 232)\";\ntomyris.secondaryColor = \"rgb(45, 26, 38)\";\ntomyris.secondaryTextColor = \"rgb(185, 170, 178)\";\ntomyris.activeColor = \"rgb(232, 75, 145)\";\ntomyris.linkTextColor = \"rgb(250, 130, 180)\";\ntomyris.borderColor = \"rgba(232, 75, 145, 0.2)\";\ntomyris.popupIconColor = \"rgb(235, 225, 232)\";\ntomyris.popupBackgroundColor = \"rgb(38, 22, 33)\";\ntomyris.popupTextColor = \"rgb(235, 225, 232)\";\ntomyris.popupActiveColor = \"rgb(232, 75, 145)\";\ntomyris.buttonBackgroundColor = \"rgb(232, 75, 145)\";\ntomyris.buttonTextColor = \"rgb(255, 255, 255)\";\ntomyris.buttonActiveColor = \"rgb(200, 55, 120)\";\ntomyris.boxShadowColor = \"rgba(0, 0, 0, 0.45)\";\ntomyris.activeTextColor = \"rgb(255, 255, 255)\";\ntomyris.errorTextColor = \"rgb(255, 175, 100)\";\ntomyris.dangerColor = \"rgb(235, 65, 65)\";\ntomyris.scrollbarColor = \"rgba(235, 225, 232, 0.1)\";\ntomyris.preferredEditorTheme = \"monokai\";\n\nconst menes = createBuiltInTheme(\"Menes\");\nmenes.darkenedPrimaryColor = \"rgb(18, 22, 28)\";\nmenes.primaryColor = \"rgb(28, 32, 40)\";\nmenes.primaryTextColor = \"rgb(225, 230, 240)\";\nmenes.secondaryColor = \"rgb(36, 42, 52)\";\nmenes.secondaryTextColor = \"rgb(140, 155, 175)\";\nmenes.activeColor = \"rgb(72, 210, 120)\";\nmenes.linkTextColor = \"rgb(100, 230, 150)\";\nmenes.borderColor = \"rgba(72, 210, 120, 0.18)\";\nmenes.popupIconColor = \"rgb(225, 230, 240)\";\nmenes.popupBackgroundColor = \"rgb(32, 38, 48)\";\nmenes.popupTextColor = \"rgb(225, 230, 240)\";\nmenes.popupActiveColor = \"rgb(72, 210, 120)\";\nmenes.buttonBackgroundColor = \"rgb(72, 210, 120)\";\nmenes.buttonTextColor = \"rgb(18, 22, 30)\";\nmenes.buttonActiveColor = \"rgb(50, 180, 95)\";\nmenes.boxShadowColor = \"rgba(0, 0, 0, 0.4)\";\nmenes.activeTextColor = \"rgb(18, 22, 30)\";\nmenes.errorTextColor = \"rgb(255, 165, 95)\";\nmenes.dangerColor = \"rgb(240, 75, 65)\";\nmenes.scrollbarColor = \"rgba(225, 230, 240, 0.12)\";\nmenes.preferredEditorTheme = \"one_dark\";\n\nconst light = createBuiltInTheme(\"Light\", \"light\");\nlight.primaryColor = \"rgb(255, 255, 255)\";\nlight.primaryTextColor = \"rgb(15, 23, 42)\";\nlight.secondaryColor = \"rgb(248, 250, 252)\";\nlight.secondaryTextColor = \"rgb(51, 65, 85)\";\nlight.activeColor = \"rgb(59, 130, 246)\";\nlight.linkTextColor = \"rgb(37, 99, 235)\";\nlight.borderColor = \"rgb(226, 232, 240)\";\nlight.popupIconColor = \"rgb(15, 23, 42)\";\nlight.popupBackgroundColor = \"rgb(255, 255, 255)\";\nlight.popupTextColor = \"rgb(15, 23, 42)\";\nlight.popupActiveColor = \"rgb(59, 130, 246)\";\nlight.activeTextColor = \"rgb(255, 255, 255)\";\nlight.errorTextColor = \"rgb(185, 28, 28)\";\nlight.dangerColor = \"rgb(220, 38, 38)\";\nlight.scrollbarColor = \"rgba(0, 0, 0, 0.2)\";\nlight.preferredEditorTheme = getSystemEditorTheme(false);\n\nconst system = createBuiltInTheme(\"System\", \"dark\", \"free\");\n\nexport function getSystemEditorTheme(darkTheme) {\n\tif (darkTheme) {\n\t\treturn \"one_dark\";\n\t} else {\n\t\treturn \"noctisLilac\";\n\t}\n}\n\n/**\n * Update the system theme based on the user's preference.\n * @param {boolean} darkTheme Whether the user prefers a dark theme.\n */\nexport function updateSystemTheme(darkTheme) {\n\tif (darkTheme) {\n\t\tsystem.type = \"dark\";\n\t\tsystem.primaryColor = \"rgb(35, 39, 42)\";\n\t\tsystem.primaryTextColor = \"rgb(245, 245, 245)\";\n\t\tsystem.darkenedPrimaryColor = \"rgb(24, 27, 30)\";\n\t\tsystem.secondaryColor = \"rgb(45, 49, 52)\";\n\t\tsystem.secondaryTextColor = \"rgb(228, 228, 228)\";\n\t\tsystem.activeColor = \"rgb(66, 133, 244)\";\n\t\tsystem.linkTextColor = \"rgb(138, 180, 248)\";\n\t\tsystem.borderColor = \"rgba(188, 188, 188, 0.15)\";\n\t\tsystem.popupIconColor = \"rgb(245, 245, 245)\";\n\n\t\tsystem.popupBackgroundColor = \"rgb(35, 39, 42)\";\n\t\tsystem.popupTextColor = \"rgb(245, 245, 245)\";\n\t\tsystem.popupActiveColor = \"rgb(66, 133, 244)\";\n\t} else {\n\t\tsystem.type = \"light\";\n\t\tsystem.primaryColor = \"rgb(255, 255, 255)\";\n\t\tsystem.primaryTextColor = \"rgb(15, 23, 42)\";\n\t\tsystem.secondaryColor = \"rgb(248, 250, 252)\";\n\t\tsystem.secondaryTextColor = \"rgb(51, 65, 85)\";\n\t\tsystem.activeColor = \"rgb(59, 130, 246)\";\n\t\tsystem.linkTextColor = \"rgb(37, 99, 235)\";\n\t\tsystem.borderColor = \"rgb(226, 232, 240)\";\n\t\tsystem.popupIconColor = \"rgb(15, 23, 42)\";\n\n\t\tsystem.popupBackgroundColor = \"rgb(255, 255, 255)\";\n\t\tsystem.popupTextColor = \"rgb(15, 23, 42)\";\n\t\tsystem.popupActiveColor = \"rgb(59, 130, 246)\";\n\t}\n\n\tsystem.preferredEditorTheme = getSystemEditorTheme(darkTheme);\n\n\tif (appSettings?.value?.appTheme === \"system\") {\n\t\tapply(system.id);\n\t}\n}\n\nconst glass = createBuiltInTheme(\"Glass\");\nglass.darkenedPrimaryColor = \"rgb(250, 250, 255)\";\nglass.primaryColor = \"rgb(255, 255, 255)\";\nglass.primaryTextColor = \"rgb(17, 24, 39)\";\nglass.secondaryColor = \"rgba(255, 255, 255, 0.8)\";\nglass.secondaryTextColor = \"rgb(55, 65, 81)\";\nglass.activeColor = \"rgb(99, 102, 241)\";\nglass.linkTextColor = \"rgb(79, 70, 229)\";\nglass.borderColor = \"rgba(99, 102, 241, 0.2)\";\nglass.popupIconColor = \"rgb(17, 24, 39)\";\nglass.popupBackgroundColor = \"rgba(255, 255, 255, 0.95)\";\nglass.popupTextColor = \"rgb(17, 24, 39)\";\nglass.popupActiveColor = \"rgb(99, 102, 241)\";\nglass.buttonBackgroundColor = \"rgb(99, 102, 241)\";\nglass.buttonTextColor = \"rgb(255, 255, 255)\";\nglass.buttonActiveColor = \"rgb(79, 70, 229)\";\nglass.boxShadowColor = \"rgba(0, 0, 0, 0.1)\";\nglass.activeTextColor = \"rgb(255, 255, 255)\";\nglass.errorTextColor = \"rgb(185, 28, 28)\";\nglass.dangerColor = \"rgb(220, 38, 38)\";\nglass.scrollbarColor = \"rgba(0, 0, 0, 0.15)\";\n\nconst neon = createBuiltInTheme(\"Neon\");\nneon.darkenedPrimaryColor = \"rgb(9, 9, 11)\";\nneon.primaryColor = \"rgb(15, 15, 17)\";\nneon.primaryTextColor = \"rgb(10, 255, 200)\";\nneon.secondaryColor = \"rgb(24, 24, 27)\";\nneon.secondaryTextColor = \"rgb(255, 255, 255)\";\nneon.activeColor = \"rgb(255, 20, 147)\";\nneon.linkTextColor = \"rgb(0, 255, 255)\";\nneon.borderColor = \"rgba(10, 255, 200, 0.3)\";\nneon.popupIconColor = \"rgb(10, 255, 200)\";\nneon.popupBackgroundColor = \"rgb(15, 15, 17)\";\nneon.popupTextColor = \"rgb(10, 255, 200)\";\nneon.popupActiveColor = \"rgb(255, 20, 147)\";\nneon.buttonBackgroundColor = \"rgb(255, 20, 147)\";\nneon.buttonTextColor = \"rgb(0, 0, 0)\";\nneon.buttonActiveColor = \"rgb(0, 255, 255)\";\nneon.boxShadowColor = \"rgba(10, 255, 200, 0.2)\";\nneon.preferredEditorTheme = \"monokai\";\nneon.activeTextColor = \"rgb(0, 0, 0)\";\nneon.errorTextColor = \"rgb(255, 20, 147)\";\nneon.dangerColor = \"rgb(255, 20, 147)\";\nneon.scrollbarColor = \"rgba(10, 255, 200, 0.3)\";\n\nconst glassDark = createBuiltInTheme(\"Glass Dark\", \"dark\");\nglassDark.darkenedPrimaryColor = \"rgb(15, 15, 20)\";\nglassDark.primaryColor = \"rgb(24, 24, 32)\";\nglassDark.primaryTextColor = \"rgb(229, 231, 235)\";\nglassDark.secondaryColor = \"rgba(31, 31, 42, 0.8)\";\nglassDark.secondaryTextColor = \"rgb(156, 163, 175)\";\nglassDark.activeColor = \"rgb(99, 102, 241)\";\nglassDark.linkTextColor = \"rgb(129, 140, 248)\";\nglassDark.borderColor = \"rgba(99, 102, 241, 0.3)\";\nglassDark.popupIconColor = \"rgb(229, 231, 235)\";\nglassDark.popupBackgroundColor = \"rgba(31, 31, 42, 0.95)\";\nglassDark.popupTextColor = \"rgb(229, 231, 235)\";\nglassDark.popupActiveColor = \"rgb(99, 102, 241)\";\nglassDark.buttonBackgroundColor = \"rgb(99, 102, 241)\";\nglassDark.buttonTextColor = \"rgb(255, 255, 255)\";\nglassDark.buttonActiveColor = \"rgb(79, 70, 229)\";\nglassDark.boxShadowColor = \"rgba(0, 0, 0, 0.4)\";\nglassDark.activeTextColor = \"rgb(255, 255, 255)\";\nglassDark.errorTextColor = \"rgb(248, 113, 113)\";\nglassDark.dangerColor = \"rgb(239, 68, 68)\";\nglassDark.scrollbarColor = \"rgba(255, 255, 255, 0.2)\";\nglassDark.preferredEditorTheme = \"tokyoNight\";\n\nconst sunset = createBuiltInTheme(\"Sunset\");\nsunset.darkenedPrimaryColor = \"rgb(251, 243, 235)\";\nsunset.primaryColor = \"rgb(255, 251, 247)\";\nsunset.primaryTextColor = \"rgb(124, 45, 18)\";\nsunset.secondaryColor = \"rgb(254, 235, 217)\";\nsunset.secondaryTextColor = \"rgb(154, 52, 18)\";\nsunset.activeColor = \"rgb(251, 146, 60)\";\nsunset.linkTextColor = \"rgb(234, 88, 12)\";\nsunset.borderColor = \"rgb(253, 186, 116)\";\nsunset.popupIconColor = \"rgb(124, 45, 18)\";\nsunset.popupBackgroundColor = \"rgb(255, 251, 247)\";\nsunset.popupTextColor = \"rgb(124, 45, 18)\";\nsunset.popupActiveColor = \"rgb(251, 146, 60)\";\nsunset.buttonBackgroundColor = \"rgb(251, 146, 60)\";\nsunset.buttonTextColor = \"rgb(255, 255, 255)\";\nsunset.buttonActiveColor = \"rgb(234, 88, 12)\";\nsunset.activeTextColor = \"rgb(255, 255, 255)\";\nsunset.errorTextColor = \"rgb(185, 28, 28)\";\nsunset.dangerColor = \"rgb(220, 38, 38)\";\nsunset.scrollbarColor = \"rgba(124, 45, 18, 0.2)\";\n\nconst obsidian = createBuiltInTheme(\"Obsidian\");\nobsidian.darkenedPrimaryColor = \"rgb(18, 17, 21)\";\nobsidian.primaryColor = \"rgb(28, 27, 31)\";\nobsidian.primaryTextColor = \"rgb(232, 228, 220)\";\nobsidian.secondaryColor = \"rgb(38, 37, 42)\";\nobsidian.secondaryTextColor = \"rgb(185, 180, 172)\";\nobsidian.activeColor = \"rgb(212, 175, 55)\";\nobsidian.linkTextColor = \"rgb(230, 200, 120)\";\nobsidian.borderColor = \"rgba(212, 175, 55, 0.18)\";\nobsidian.popupIconColor = \"rgb(232, 228, 220)\";\nobsidian.popupBackgroundColor = \"rgb(32, 31, 36)\";\nobsidian.popupTextColor = \"rgb(232, 228, 220)\";\nobsidian.popupActiveColor = \"rgb(212, 175, 55)\";\nobsidian.buttonBackgroundColor = \"rgb(212, 175, 55)\";\nobsidian.buttonTextColor = \"rgb(28, 27, 31)\";\nobsidian.buttonActiveColor = \"rgb(184, 148, 36)\";\nobsidian.boxShadowColor = \"rgba(0, 0, 0, 0.45)\";\nobsidian.activeTextColor = \"rgb(28, 27, 31)\";\nobsidian.errorTextColor = \"rgb(255, 152, 100)\";\nobsidian.dangerColor = \"rgb(220, 80, 60)\";\nobsidian.scrollbarColor = \"rgba(212, 175, 55, 0.18)\";\nobsidian.preferredEditorTheme = \"one_dark\";\n\nconst ember = createBuiltInTheme(\"Ember\");\nember.darkenedPrimaryColor = \"rgb(22, 16, 13)\";\nember.primaryColor = \"rgb(32, 24, 20)\";\nember.primaryTextColor = \"rgb(240, 228, 210)\";\nember.secondaryColor = \"rgb(45, 35, 28)\";\nember.secondaryTextColor = \"rgb(200, 185, 165)\";\nember.activeColor = \"rgb(217, 130, 60)\";\nember.linkTextColor = \"rgb(240, 170, 100)\";\nember.borderColor = \"rgba(217, 130, 60, 0.22)\";\nember.popupIconColor = \"rgb(240, 228, 210)\";\nember.popupBackgroundColor = \"rgb(38, 30, 24)\";\nember.popupTextColor = \"rgb(240, 228, 210)\";\nember.popupActiveColor = \"rgb(217, 130, 60)\";\nember.buttonBackgroundColor = \"rgb(217, 130, 60)\";\nember.buttonTextColor = \"rgb(32, 24, 20)\";\nember.buttonActiveColor = \"rgb(190, 105, 40)\";\nember.boxShadowColor = \"rgba(0, 0, 0, 0.4)\";\nember.activeTextColor = \"rgb(32, 24, 20)\";\nember.errorTextColor = \"rgb(255, 160, 85)\";\nember.dangerColor = \"rgb(220, 60, 50)\";\nember.scrollbarColor = \"rgba(240, 228, 210, 0.12)\";\nember.preferredEditorTheme = \"monokai\";\n\nconst dusk = createBuiltInTheme(\"Dusk\");\ndusk.darkenedPrimaryColor = \"rgb(13, 11, 24)\";\ndusk.primaryColor = \"rgb(20, 18, 35)\";\ndusk.primaryTextColor = \"rgb(215, 210, 235)\";\ndusk.secondaryColor = \"rgb(30, 27, 50)\";\ndusk.secondaryTextColor = \"rgb(160, 155, 185)\";\ndusk.activeColor = \"rgb(167, 105, 220)\";\ndusk.linkTextColor = \"rgb(190, 150, 240)\";\ndusk.borderColor = \"rgba(167, 105, 220, 0.2)\";\ndusk.popupIconColor = \"rgb(215, 210, 235)\";\ndusk.popupBackgroundColor = \"rgb(25, 23, 42)\";\ndusk.popupTextColor = \"rgb(215, 210, 235)\";\ndusk.popupActiveColor = \"rgb(167, 105, 220)\";\ndusk.buttonBackgroundColor = \"rgb(167, 105, 220)\";\ndusk.buttonTextColor = \"rgb(255, 255, 255)\";\ndusk.buttonActiveColor = \"rgb(140, 80, 195)\";\ndusk.boxShadowColor = \"rgba(0, 0, 0, 0.5)\";\ndusk.activeTextColor = \"rgb(255, 255, 255)\";\ndusk.errorTextColor = \"rgb(255, 170, 110)\";\ndusk.dangerColor = \"rgb(235, 80, 100)\";\ndusk.scrollbarColor = \"rgba(215, 210, 235, 0.12)\";\ndusk.preferredEditorTheme = \"tokyoNight\";\n\nconst carbon = createBuiltInTheme(\"Carbon\");\ncarbon.darkenedPrimaryColor = \"rgb(14, 14, 16)\";\ncarbon.primaryColor = \"rgb(22, 22, 24)\";\ncarbon.primaryTextColor = \"rgb(235, 235, 240)\";\ncarbon.secondaryColor = \"rgb(32, 32, 35)\";\ncarbon.secondaryTextColor = \"rgb(155, 155, 165)\";\ncarbon.activeColor = \"rgb(55, 142, 240)\";\ncarbon.linkTextColor = \"rgb(85, 165, 255)\";\ncarbon.borderColor = \"rgba(255, 255, 255, 0.08)\";\ncarbon.popupIconColor = \"rgb(235, 235, 240)\";\ncarbon.popupBackgroundColor = \"rgb(28, 28, 31)\";\ncarbon.popupTextColor = \"rgb(235, 235, 240)\";\ncarbon.popupActiveColor = \"rgb(55, 142, 240)\";\ncarbon.buttonBackgroundColor = \"rgb(55, 142, 240)\";\ncarbon.buttonTextColor = \"rgb(255, 255, 255)\";\ncarbon.buttonActiveColor = \"rgb(38, 118, 210)\";\ncarbon.boxShadowColor = \"rgba(0, 0, 0, 0.5)\";\ncarbon.activeTextColor = \"rgb(255, 255, 255)\";\ncarbon.errorTextColor = \"rgb(255, 140, 100)\";\ncarbon.dangerColor = \"rgb(235, 70, 60)\";\ncarbon.scrollbarColor = \"rgba(255, 255, 255, 0.1)\";\ncarbon.preferredEditorTheme = \"one_dark\";\n\nconst mint = createBuiltInTheme(\"Mint\", \"light\");\nmint.darkenedPrimaryColor = \"rgb(235, 245, 240)\";\nmint.primaryColor = \"rgb(250, 253, 252)\";\nmint.primaryTextColor = \"rgb(28, 42, 38)\";\nmint.secondaryColor = \"rgb(240, 248, 245)\";\nmint.secondaryTextColor = \"rgb(72, 92, 85)\";\nmint.activeColor = \"rgb(4, 120, 87)\";\nmint.linkTextColor = \"rgb(2, 100, 72)\";\nmint.borderColor = \"rgb(209, 233, 225)\";\nmint.popupIconColor = \"rgb(28, 42, 38)\";\nmint.popupBackgroundColor = \"rgb(250, 253, 252)\";\nmint.popupTextColor = \"rgb(28, 42, 38)\";\nmint.popupActiveColor = \"rgb(4, 120, 87)\";\nmint.buttonBackgroundColor = \"rgb(4, 120, 87)\";\nmint.buttonTextColor = \"rgb(255, 255, 255)\";\nmint.buttonActiveColor = \"rgb(2, 100, 72)\";\nmint.boxShadowColor = \"rgba(0, 0, 0, 0.06)\";\nmint.activeTextColor = \"rgb(255, 255, 255)\";\nmint.errorTextColor = \"rgb(190, 40, 40)\";\nmint.dangerColor = \"rgb(220, 38, 38)\";\nmint.scrollbarColor = \"rgba(28, 42, 38, 0.12)\";\nmint.preferredEditorTheme = \"noctisLilac\";\n\nconst sandstone = createBuiltInTheme(\"Sandstone\", \"light\");\nsandstone.darkenedPrimaryColor = \"rgb(238, 230, 218)\";\nsandstone.primaryColor = \"rgb(252, 248, 242)\";\nsandstone.primaryTextColor = \"rgb(60, 45, 35)\";\nsandstone.secondaryColor = \"rgb(244, 238, 228)\";\nsandstone.secondaryTextColor = \"rgb(110, 90, 70)\";\nsandstone.activeColor = \"rgb(192, 92, 52)\";\nsandstone.linkTextColor = \"rgb(175, 75, 40)\";\nsandstone.borderColor = \"rgb(222, 210, 195)\";\nsandstone.popupIconColor = \"rgb(60, 45, 35)\";\nsandstone.popupBackgroundColor = \"rgb(252, 248, 242)\";\nsandstone.popupTextColor = \"rgb(60, 45, 35)\";\nsandstone.popupActiveColor = \"rgb(192, 92, 52)\";\nsandstone.buttonBackgroundColor = \"rgb(192, 92, 52)\";\nsandstone.buttonTextColor = \"rgb(255, 255, 255)\";\nsandstone.buttonActiveColor = \"rgb(165, 72, 38)\";\nsandstone.boxShadowColor = \"rgba(0, 0, 0, 0.06)\";\nsandstone.activeTextColor = \"rgb(255, 255, 255)\";\nsandstone.errorTextColor = \"rgb(180, 40, 35)\";\nsandstone.dangerColor = \"rgb(200, 50, 45)\";\nsandstone.scrollbarColor = \"rgba(60, 45, 35, 0.12)\";\nsandstone.preferredEditorTheme = \"noctisLilac\";\n\nconst blossom = createBuiltInTheme(\"Blossom\", \"light\");\nblossom.darkenedPrimaryColor = \"rgb(242, 234, 237)\";\nblossom.primaryColor = \"rgb(254, 250, 251)\";\nblossom.primaryTextColor = \"rgb(48, 38, 42)\";\nblossom.secondaryColor = \"rgb(248, 240, 243)\";\nblossom.secondaryTextColor = \"rgb(100, 80, 88)\";\nblossom.activeColor = \"rgb(190, 75, 115)\";\nblossom.linkTextColor = \"rgb(170, 55, 95)\";\nblossom.borderColor = \"rgb(232, 218, 223)\";\nblossom.popupIconColor = \"rgb(48, 38, 42)\";\nblossom.popupBackgroundColor = \"rgb(254, 250, 251)\";\nblossom.popupTextColor = \"rgb(48, 38, 42)\";\nblossom.popupActiveColor = \"rgb(190, 75, 115)\";\nblossom.buttonBackgroundColor = \"rgb(190, 75, 115)\";\nblossom.buttonTextColor = \"rgb(255, 255, 255)\";\nblossom.buttonActiveColor = \"rgb(160, 55, 95)\";\nblossom.boxShadowColor = \"rgba(0, 0, 0, 0.06)\";\nblossom.activeTextColor = \"rgb(255, 255, 255)\";\nblossom.errorTextColor = \"rgb(200, 45, 40)\";\nblossom.dangerColor = \"rgb(210, 50, 45)\";\nblossom.scrollbarColor = \"rgba(48, 38, 42, 0.12)\";\nblossom.preferredEditorTheme = \"noctisLilac\";\n\nconst custom = createBuiltInTheme(\"Custom\");\ncustom.autoDarkened = true;\n\nexport default [\n\tsystem,\n\tcreateBuiltInTheme(\"Legacy\", \"dark\", \"free\"),\n\tdark,\n\tlight,\n\tglass,\n\tglassDark,\n\tneon,\n\tsunset,\n\toled,\n\tocean,\n\tbump,\n\tbling,\n\tmoon,\n\tatticus,\n\ttomyris,\n\tmenes,\n\tobsidian,\n\tember,\n\tdusk,\n\tcarbon,\n\tmint,\n\tsandstone,\n\tblossom,\n\tcustom,\n];\n"
  },
  {
    "path": "src/utils/Path.js",
    "content": "export default {\n\t/**\n\t * The path.dirname() method returns the directory name of a path,\n\t * similar to the Unix dirname command.\n\t * Trailing directory separators are ignored.\n\t * @param {string} path\n\t * @returns {string}\n\t */\n\tdirname(path) {\n\t\tif (path.endsWith(\"/\")) path = path.slice(0, -1);\n\t\tconst parts = path.split(\"/\").slice(0, -1);\n\t\tif (!/^(\\.|\\.\\.|)$/.test(parts[0])) parts.unshift(\".\");\n\t\tconst res = parts.join(\"/\");\n\n\t\tif (!res) return \"/\";\n\t\telse return res;\n\t},\n\n\t/**\n\t * The path.basename() methods returns the last portion of a path,\n\t * similar to the Unix basename command.\n\t * Trailing directory separators are ignored, see path.sep.\n\t * @param {string} path\n\t * @returns {string}\n\t */\n\tbasename(path, ext = \"\") {\n\t\text = ext || \"\";\n\t\tif (path === \"\" || path === \"/\") return path;\n\t\tconst ar = path.split(\"/\");\n\t\tconst last = ar.slice(-1)[0];\n\t\tif (!last) return ar.slice(-2)[0];\n\t\tlet res = decodeURI(last.split(\"?\")[0] || \"\");\n\t\tif (this.extname(res) === ext) res = res.replace(new RegExp(ext + \"$\"), \"\");\n\t\treturn res;\n\t},\n\n\t/**\n\t * returns the extension of the path, from the last occurrence of the . (period)\n\t * character to end of string in the last portion of the path.\n\t * If there is no . in the last portion of the path, or if there are no . characters\n\t * other than the first character of the basename of path (see path.basename()) , an\n\t * empty string is returned.\n\t * @param {string} path\n\t */\n\textname(path) {\n\t\tconst filename = path.split(\"/\").slice(-1)[0];\n\t\tif (/.+\\..*$/.test(filename)) {\n\t\t\treturn /(?:\\.([^.]*))?$/.exec(filename)[0] || \"\";\n\t\t}\n\n\t\treturn \"\";\n\t},\n\n\t/**\n\t * returns a path string from an object.\n\t * @param {PathObject} pathObject\n\t */\n\tformat(pathObject) {\n\t\tlet { root, dir, ext, name, base } = pathObject;\n\n\t\tif (base || !ext.startsWith(\".\")) {\n\t\t\text = \"\";\n\t\t\tif (base) name = \"\";\n\t\t}\n\n\t\tdir = dir || root;\n\n\t\tif (!dir.endsWith(\"/\")) dir += \"/\";\n\n\t\treturn dir + (base || name) + ext;\n\t},\n\n\t/**\n\t * The path.isAbsolute() method determines if path is an absolute path.\n\t * @param {string} path\n\t */\n\tisAbsolute(path) {\n\t\treturn path.startsWith(\"/\");\n\t},\n\n\t/**\n\t * Joins the given number of paths\n\t * @param  {...string} paths\n\t */\n\tjoin(...paths) {\n\t\tlet res = paths.join(\"/\");\n\t\treturn this.normalize(res);\n\t},\n\n\t/**\n\t * Normalizes the given path, resolving '..' and '.' segments.\n\t * @param {string} path\n\t */\n\tnormalize(path) {\n\t\tpath = path.replace(/\\.\\/+/g, \"./\");\n\t\tpath = path.replace(/\\/+/g, \"/\");\n\n\t\tconst resolved = [];\n\t\tconst pathAr = path.split(\"/\");\n\n\t\tpathAr.forEach((dir) => {\n\t\t\tif (dir === \"..\") {\n\t\t\t\tif (resolved.length) resolved.pop();\n\t\t\t} else if (dir === \".\") {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tresolved.push(dir);\n\t\t\t}\n\t\t});\n\n\t\treturn resolved.join(\"/\");\n\t},\n\n\t/**\n\t *\n\t * @param {string} path\n\t * @returns {PathObject}\n\t */\n\tparse(path) {\n\t\tconst root = path.startsWith(\"/\") ? \"/\" : \"\";\n\t\tconst dir = this.dirname(path);\n\t\tconst ext = this.extname(path);\n\t\tconst name = this.basename(path, ext);\n\t\tconst base = this.basename(path);\n\n\t\treturn {\n\t\t\troot,\n\t\t\tdir,\n\t\t\tbase,\n\t\t\text,\n\t\t\tname,\n\t\t};\n\t},\n\n\t/**\n * Resolve the path eg.\n```js\nresolvePath('path/to/some/dir/', '../../dir') //returns 'path/to/dir'\n```\n * @param {...string} paths \n */\n\tresolve(...paths) {\n\t\tif (!paths.length) throw new Error(\"resolve(...path) : Arguments missing!\");\n\n\t\tlet result = \"\";\n\n\t\tpaths.forEach((path) => {\n\t\t\tif (path.startsWith(\"/\")) {\n\t\t\t\tresult = path;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tresult = this.normalize(this.join(result, path));\n\t\t});\n\n\t\tif (result.startsWith(\"/\")) return result;\n\t\telse return \"/\" + result;\n\t},\n\n\t/**\n\t * Gets path for path2 relative to path1\n\t * @param {String} path1\n\t * @param {String} path2\n\t */\n\tconvertToRelative(path1, path2) {\n\t\tpath1 = this.normalize(path1).split(\"/\");\n\t\tpath2 = this.normalize(path2).split(\"/\");\n\n\t\tconst p1len = path1.length;\n\t\tconst p2len = path2.length;\n\n\t\tlet flag = false;\n\t\tlet path = [];\n\n\t\tpath1.forEach((dir, i) => {\n\t\t\tif (dir === path2[i] && !flag) return;\n\n\t\t\tpath.push(path2[i]);\n\t\t\tif (!flag) {\n\t\t\t\tflag = true;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (flag) path.unshift(\"..\");\n\t\t});\n\n\t\tif (p2len > p1len) path.push(...path2.slice(p1len));\n\n\t\treturn path.join(\"/\");\n\t},\n};\n"
  },
  {
    "path": "src/utils/Uri.js",
    "content": "import escapeStringRegexp from \"escape-string-regexp\";\nimport path from \"./Path\";\n\nfunction parseStorageList() {\n\ttry {\n\t\tconst storageList = JSON.parse(localStorage.storageList || \"[]\");\n\t\treturn Array.isArray(storageList) ? storageList : [];\n\t} catch (_) {\n\t\treturn [];\n\t}\n}\n\nexport default {\n\t/**\n\t * Parse content uri to rootUri and docID\n\t *\n\t * eg.\n\t *```js\n\t * parse(\"content://.../AA98-181D%3A::.../index.html\")\n\t *```\n\t * `returns` {rootUri: \"content://.../AA98-181D%3A\", docId: \"...index.html\"}\n\t *\n\t * @param {string} contentUri\n\t * @returns {{rootUri: string, docId: string, isFileUri: boolean}}\n\t */\n\tparse(contentUri) {\n\t\tlet rootUri,\n\t\t\tdocId = \"\";\n\n\t\tconst DOC_PROVIDER =\n\t\t\t/^content:\\/\\/com\\.((?![:<>\"\\/\\\\\\|\\?\\*]).)*\\.documents\\//;\n\t\tconst TREE_URI =\n\t\t\t/^content:\\/\\/com\\.((?![:<>\"\\/\\\\\\|\\?\\*]).)*\\.documents\\/tree\\//;\n\t\tconst SINGLE_URI =\n\t\t\t/^content:\\/\\/com\\.(((?![:<>\"\\/\\\\\\|\\?\\*]).)*)\\.documents\\/document/;\n\n\t\tif (DOC_PROVIDER.test(contentUri)) {\n\t\t\tif (TREE_URI.test(contentUri)) {\n\t\t\t\tif (/::/.test(contentUri)) {\n\t\t\t\t\t[rootUri, docId] = contentUri.split(\"::\");\n\t\t\t\t} else {\n\t\t\t\t\trootUri = contentUri;\n\t\t\t\t\tdocId = decodeURIComponent(contentUri.split(\"/\").slice(-1)[0]);\n\t\t\t\t}\n\t\t\t} else if (SINGLE_URI.test(contentUri)) {\n\t\t\t\tconst [provider, providerId] = SINGLE_URI.exec(contentUri);\n\t\t\t\tdocId = decodeURIComponent(contentUri); //DecodeUri\n\t\t\t\tdocId = docId.replace(provider, \"\"); //replace single to tree\n\t\t\t\tdocId = path.normalize(docId); //normalize docid\n\n\t\t\t\tif (docId.startsWith(\"/\")) docId = docId.slice(1); // remove leading '/'\n\n\t\t\t\trootUri =\n\t\t\t\t\t`content://com.${providerId}.documents/tree/` +\n\t\t\t\t\tdocId.split(\":\")[0] +\n\t\t\t\t\t\"%3A\";\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trootUri,\n\t\t\t\tdocId,\n\t\t\t\tisFileUri: /^file:\\/\\/\\//.test(rootUri),\n\t\t\t};\n\t\t} else {\n\t\t\tthrow new Error(\"Invalid uri format.\");\n\t\t}\n\t},\n\t/**\n\t * Formats the five contentUri object to string\n\t * @param {{rootUri: string, docId: string} | String} contentUriObject or rootId\n\t * @param {string} [docId]\n\t * @returns {string}\n\t */\n\tformat(contentUriObject, docId) {\n\t\tlet rootUri;\n\n\t\tif (typeof contentUriObject === \"string\") {\n\t\t\trootUri = contentUriObject;\n\t\t} else {\n\t\t\trootUri = contentUriObject.rootUri;\n\t\t\tdocId = contentUriObject.docId;\n\t\t}\n\n\t\tif (docId) return [rootUri, docId].join(\"::\");\n\t\telse return rootUri;\n\t},\n\t/**\n\t * Gets virtual address by replacing root with name i.e. added in file explorer\n\t * @param {string} url\n\t */\n\tgetVirtualAddress(url) {\n\t\ttry {\n\t\t\tconst storageList = parseStorageList();\n\n\t\t\tconst matches = [];\n\t\t\tfor (let storage of storageList) {\n\t\t\t\tconst regex = new RegExp(\n\t\t\t\t\t\"^\" + escapeStringRegexp(storage.uri ?? storage.url),\n\t\t\t\t);\n\t\t\t\tmatches.push({\n\t\t\t\t\tregex,\n\t\t\t\t\tcharMatched: url.length - url.replace(regex, \"\").length,\n\t\t\t\t\tstorage,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst matched = matches.sort((a, b) => {\n\t\t\t\treturn b.charMatched - a.charMatched;\n\t\t\t})[0];\n\n\t\t\tif (matched) {\n\t\t\t\tconst { storage, regex } = matched;\n\t\t\t\tconst { name } = storage;\n\t\t\t\tconst [base, paths] = url.split(\"::\");\n\t\t\t\turl = base + \"/\" + paths.split(\"/\").slice(1).join(\"/\");\n\t\t\t\treturn url.replace(regex, name).replace(/\\/+/g, \"/\");\n\t\t\t}\n\n\t\t\treturn url;\n\t\t} catch (e) {\n\t\t\treturn url;\n\t\t}\n\t},\n\t/**\n\t * Gets primary address of a content url.\n\t * @param {string} url\n\t * @returns {string}\n\t */\n\tgetPrimaryAddress(url) {\n\t\tconst [, primary] = url.split(\"::primary:\");\n\t\treturn primary;\n\t},\n};\n"
  },
  {
    "path": "src/utils/Url.js",
    "content": "import URLParse from \"url-parse\";\nimport path from \"./Path\";\nimport Uri from \"./Uri\";\n\nexport default {\n\t/**\n\t * Returns basename from a url eg. 'index.html' from 'ftp://localhost/foo/bar/index.html'\n\t * @param {string} url\n\t * @returns {string}\n\t */\n\tbasename(url) {\n\t\turl = this.parse(url).url;\n\t\tconst protocol = this.getProtocol(url);\n\t\tif (protocol === \"content:\") {\n\t\t\ttry {\n\t\t\t\tlet { rootUri, docId, isFileUri } = Uri.parse(url);\n\n\t\t\t\tif (isFileUri) return this.basename(rootUri);\n\n\t\t\t\tif (docId.endsWith(\"/\")) docId = docId.slice(0, -1);\n\t\t\t\tdocId = docId.split(\":\").pop();\n\t\t\t\treturn this.pathname(docId).split(\"/\").pop();\n\t\t\t} catch (error) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\tif (url.endsWith(\"/\")) url = url.slice(0, -1);\n\t\t\treturn this.pathname(url).split(\"/\").pop();\n\t\t}\n\t},\n\n\t/**\n\t * Checks if given urls are same or not\n\t * @param  {...String} urls\n\t * @returns {Boolean}\n\t */\n\tareSame(...urls) {\n\t\tlet firstUrl = urls[0];\n\t\tif (firstUrl.endsWith(\"/\")) firstUrl = firstUrl.slice(0, -1);\n\t\treturn urls.every((url) => {\n\t\t\tif (url.endsWith(\"/\")) url = url.slice(0, -1);\n\t\t\treturn firstUrl === url;\n\t\t});\n\t},\n\n\t/**\n\t *\n\t * @param {String} url\n\t * returns the extension of the path, from the last occurrence of the . (period)\n\t * character to end of string in the last portion of the path.\n\t * If there is no . in the last portion of the path, or if there are no .\n\t * characters other than the first character of the basename of path (see path.basename()),\n\t * an empty string is returned.\n\t * @returns {String}\n\t */\n\textname(url) {\n\t\tconst name = this.basename(url);\n\t\tif (name) return path.extname(name);\n\t\telse return null;\n\t},\n\t/**\n\t * Join all arguments together and normalize the resulting path.\n\t * @param  {...string} pathnames\n\t * @returns {String}\n\t */\n\tjoin(...pathnames) {\n\t\tif (pathnames.length < 2)\n\t\t\tthrow new Error(\"Join(), requires at least two parameters\");\n\n\t\tlet { url, query } = this.parse(pathnames[0]);\n\n\t\tconst protocol = (this.PROTOCOL_PATTERN.exec(url) || [])[0] || \"\";\n\n\t\tif (protocol === \"content://\") {\n\t\t\ttry {\n\t\t\t\tif (pathnames[1].startsWith(\"/\")) pathnames[1] = pathnames[1].slice(1);\n\t\t\t\tconst contentUri = Uri.parse(url);\n\t\t\t\tlet [root, pathname] = contentUri.docId.split(\":\");\n\t\t\t\tlet newDocId = path.join(pathname, ...pathnames.slice(1));\n\t\t\t\tif (/^content:\\/\\/com.termux/.test(url)) {\n\t\t\t\t\tconst rootCondition = root.endsWith(\"/\");\n\t\t\t\t\tconst newDocIdCondition = newDocId.startsWith(\"/\");\n\t\t\t\t\tif (rootCondition === newDocIdCondition) {\n\t\t\t\t\t\troot = root.slice(0, -1);\n\t\t\t\t\t} else if (!rootCondition === !newDocIdCondition) {\n\t\t\t\t\t\troot += \"/\";\n\t\t\t\t\t}\n\t\t\t\t\treturn `${contentUri.rootUri}::${root}${newDocId}${query}`;\n\t\t\t\t}\n\n\t\t\t\t// if pathname is undefined, meaning a docId/volume (e.g :primary:)\n\t\t\t\t// has not been detected, so no newDocId's \":\" will be added.\n\t\t\t\tif (!pathname) {\n\t\t\t\t\t// Ensure proper path separator between root and newDocId\n\t\t\t\t\tlet separator = \"\";\n\t\t\t\t\tif (root.endsWith(\"/\") && newDocId.startsWith(\"/\")) {\n\t\t\t\t\t\t// Both have separator, strip one from newDocId\n\t\t\t\t\t\tnewDocId = newDocId.slice(1);\n\t\t\t\t\t} else if (!root.endsWith(\"/\") && !newDocId.startsWith(\"/\")) {\n\t\t\t\t\t\t// Neither has separator, add one\n\t\t\t\t\t\tseparator = \"/\";\n\t\t\t\t\t}\n\t\t\t\t\treturn `${contentUri.rootUri}::${root}${separator}${newDocId}${query}`;\n\t\t\t\t}\n\t\t\t\treturn `${contentUri.rootUri}::${root}:${newDocId}${query}`;\n\t\t\t} catch (error) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else if (protocol) {\n\t\t\turl = url.replace(new RegExp(\"^\" + protocol), \"\");\n\t\t\tpathnames[0] = url;\n\t\t\treturn protocol + path.join(...pathnames) + query;\n\t\t} else {\n\t\t\treturn path.join(url, ...pathnames.slice(1)) + query;\n\t\t}\n\t},\n\t/**\n\t * Make url safe by encoding url components\n\t * @param {string} url\n\t * @returns {string}\n\t */\n\tsafe(url) {\n\t\tlet { url: uri, query } = this.parse(url);\n\t\turl = uri;\n\t\tconst protocol = (this.PROTOCOL_PATTERN.exec(url) || [])[0] || \"\";\n\t\tif (protocol) url = url.replace(new RegExp(\"^\" + protocol), \"\");\n\t\tconst parts = url.split(\"/\").map((part, i) => {\n\t\t\tif (i === 0) return part;\n\t\t\treturn fixedEncodeURIComponent(part);\n\t\t});\n\t\treturn protocol + parts.join(\"/\") + query;\n\n\t\tfunction fixedEncodeURIComponent(str) {\n\t\t\treturn encodeURIComponent(str).replace(/[!'()*]/g, function (c) {\n\t\t\t\treturn \"%\" + c.charCodeAt(0).toString(16);\n\t\t\t});\n\t\t}\n\t},\n\t/**\n\t * Gets pathname from url eg. gets '/foo/bar' from 'ftp://myhost.com/foo/bar'\n\t * @param {string} url\n\t * @returns {string}\n\t */\n\tpathname(url) {\n\t\tif (typeof url !== \"string\" || !this.PROTOCOL_PATTERN.test(url)) return url;\n\n\t\turl = url.split(\"?\")[0];\n\t\tconst protocol = (this.PROTOCOL_PATTERN.exec(url) || [])[0] || \"\";\n\n\t\tif (protocol === \"content://\") {\n\t\t\ttry {\n\t\t\t\tconst { rootUri, docId, isFileUri } = Uri.parse(url);\n\t\t\t\tif (isFileUri) return this.pathname(rootUri);\n\t\t\t\telse return \"/\" + (docId.split(\":\")[1] || docId);\n\t\t\t} catch (error) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\tif (protocol) url = url.replace(new RegExp(\"^\" + protocol), \"\");\n\n\t\t\tif (protocol !== \"file:///\")\n\t\t\t\treturn \"/\" + url.split(\"/\").slice(1).join(\"/\");\n\n\t\t\treturn \"/\" + url;\n\t\t}\n\t},\n\n\t/**\n\t * Returns dirname from url eg. 'ftp://localhost/foo/'  from 'ftp://localhost/foo/bar'\n\t * @param {string} url\n\t * @returns {string}\n\t */\n\tdirname(url) {\n\t\tif (typeof url !== \"string\") throw new Error(\"URL must be string\");\n\n\t\tconst urlObj = this.parse(url);\n\t\turl = urlObj.url;\n\t\tconst protocol = this.getProtocol(url);\n\n\t\tif (protocol === \"content:\") {\n\t\t\ttry {\n\t\t\t\tlet { rootUri, docId, isFileUri } = Uri.parse(url);\n\n\t\t\t\tif (isFileUri) return this.dirname(rootUri);\n\t\t\t\telse {\n\t\t\t\t\tif (docId.endsWith(\"/\")) docId = docId.slice(0, -1);\n\t\t\t\t\tdocId = [...docId.split(\"/\").slice(0, -1), \"\"].join(\"/\");\n\t\t\t\t\treturn Uri.format(rootUri, docId);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\tif (url.endsWith(\"/\")) url = url.slice(0, -1);\n\t\t\treturn [...url.split(\"/\").slice(0, -1), \"\"].join(\"/\") + urlObj.query;\n\t\t}\n\t},\n\n\t/**\n\t * Parse given url into url and query\n\t * @param {string} url\n\t * @returns {{url:string, query:string}}}\n\t */\n\tparse(url) {\n\t\tconst [uri, query = \"\"] = url.split(/(?=\\?)/);\n\t\treturn {\n\t\t\turl: uri,\n\t\t\tquery,\n\t\t};\n\t},\n\n\t/**\n\t * Formate Url object to string\n\t * @param {object} urlObj\n\t * @param {\"ftp:\"|\"sftp:\"|\"http:\"|\"https:\"} urlObj.protocol\n\t * @param {string|number} urlObj.hostname\n\t * @param {string} [urlObj.path]\n\t * @param {string} [urlObj.username]\n\t * @param {string} [urlObj.password]\n\t * @param {string|number} [urlObj.port]\n\t * @param {object} [urlObj.query]\n\t * @returns {string}\n\t */\n\tformate(urlObj) {\n\t\tlet { protocol, hostname, username, password, path, port, query } = urlObj;\n\n\t\tconst enc = (str) => encodeURIComponent(str);\n\n\t\tif (!protocol || !hostname)\n\t\t\tthrow new Error(\"Cannot formate url. Missing 'protocol' and 'hostname'.\");\n\n\t\tlet string = `${protocol}//`;\n\n\t\tif (username && password) string += `${enc(username)}:${enc(password)}@`;\n\t\telse if (username) string += `${username}@`;\n\n\t\tstring += hostname;\n\n\t\tif (port) string += `:${port}`;\n\n\t\tif (path) {\n\t\t\tif (!path.startsWith(\"/\")) path = \"/\" + path;\n\n\t\t\tstring += path;\n\t\t}\n\n\t\tif (query && typeof query === \"object\") {\n\t\t\tstring += \"?\";\n\n\t\t\tfor (let key in query) string += `${enc(key)}=${enc(query[key])}&`;\n\n\t\t\tstring = string.slice(0, -1);\n\t\t}\n\n\t\treturn string;\n\t},\n\t/**\n\t * Returns protocol of a url e.g. 'ftp:' from 'ftp://localhost/foo/bar'\n\t * @param {string} url\n\t * @returns {\"ftp:\"|\"sftp:\"|\"http:\"|\"https:\"}\n\t */\n\tgetProtocol(url) {\n\t\treturn (/^([a-z]+:)\\/\\/\\/?/i.exec(url) || [])[1] || \"\";\n\t},\n\t/**\n\t *\n\t * @param {string} url\n\t * @returns {string}\n\t */\n\thidePassword(url) {\n\t\tconst { protocol, username, hostname, pathname } = URLParse(url);\n\t\tif (protocol === \"file:\") {\n\t\t\treturn url;\n\t\t} else {\n\t\t\treturn `${protocol}//${username}@${hostname}${pathname}`;\n\t\t}\n\t},\n\t/**\n\t * Decodes url and returns username, password, hostname, pathname, port and query\n\t * @param {string} url\n\t * @returns {URLObject}\n\t */\n\tdecodeUrl(url) {\n\t\tconst uuid = \"uuid\" + Math.floor(Math.random() + Date.now() * 1000000);\n\n\t\tif (/#/.test(url)) {\n\t\t\turl = url.replace(/#/g, uuid);\n\t\t}\n\n\t\tlet { username, password, hostname, pathname, port, query } = URLParse(\n\t\t\turl,\n\t\t\ttrue,\n\t\t);\n\n\t\tif (pathname) {\n\t\t\tpathname = decodeURIComponent(pathname);\n\t\t\tpathname = pathname.replace(new RegExp(uuid, \"g\"), \"#\");\n\t\t}\n\n\t\tif (username) {\n\t\t\tusername = decodeURIComponent(username);\n\t\t}\n\n\t\tif (password) {\n\t\t\tpassword = decodeURIComponent(password);\n\t\t}\n\n\t\tif (port) {\n\t\t\tport = Number.parseInt(port);\n\t\t}\n\n\t\tlet { keyFile, passPhrase } = query;\n\n\t\tif (keyFile) {\n\t\t\tquery.keyFile = decodeURIComponent(keyFile);\n\t\t}\n\n\t\tif (passPhrase) {\n\t\t\tquery.passPhrase = decodeURIComponent(passPhrase);\n\t\t}\n\n\t\treturn { username, password, hostname, pathname, port, query };\n\t},\n\t/**\n\t * Removes trailing slash from url\n\t * @param {string} url\n\t * @returns\n\t */\n\ttrimSlash(url) {\n\t\tconst parsed = this.parse(url);\n\t\tif (parsed.url.endsWith(\"/\")) {\n\t\t\tparsed.url = parsed.url.slice(0, -1);\n\t\t}\n\t\treturn this.join(parsed.url, parsed.query);\n\t},\n\tPROTOCOL_PATTERN: /^[a-z]+:\\/\\/\\/?/i,\n};\n"
  },
  {
    "path": "src/utils/codeHighlight.js",
    "content": "import { classHighlighter, highlightCode } from \"@lezer/highlight\";\nimport { getModeForPath, getModesByName } from \"cm/modelist\";\nimport { getThemeConfig } from \"cm/themes\";\nimport DOMPurify from \"dompurify\";\nimport settings from \"lib/settings\";\n\nconst highlightCache = new Map();\nconst MAX_CACHE_SIZE = 500;\n\nlet styleElement = null;\nlet currentThemeId = null;\n\nexport function sanitize(text) {\n\tif (!text) return \"\";\n\treturn DOMPurify.sanitize(text, { ALLOWED_TAGS: [] });\n}\n\nfunction escapeHtml(text) {\n\treturn text\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\");\n}\n\nfunction escapeRegExp(string) {\n\treturn string.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction addSymbolHighlight(html, symbol) {\n\tif (!symbol) return html;\n\tconst escapedSymbol = escapeRegExp(sanitize(symbol));\n\tconst regex = new RegExp(`(${escapedSymbol})`, \"gi\");\n\treturn html.replace(regex, '<span class=\"symbol-match\">$1</span>');\n}\n\nfunction setCache(key, value) {\n\tif (highlightCache.size >= MAX_CACHE_SIZE) {\n\t\tconst firstKey = highlightCache.keys().next().value;\n\t\thighlightCache.delete(firstKey);\n\t}\n\thighlightCache.set(key, value);\n}\n\n/**\n * Generates CSS styles for syntax highlighting tokens\n * @param {Object} config - Theme config with color values\n * @param {string} selector - CSS selector to scope styles\n * @param {boolean} includeBackground - Whether to include background/foreground base styles\n */\nfunction generateStyles(config, selector, includeBackground = true) {\n\tconst c = config;\n\tconst keyword = c.keyword || \"#c678dd\";\n\tconst string = c.string || \"#98c379\";\n\tconst number = c.number || \"#d19a66\";\n\tconst comment = c.comment || \"#5c6370\";\n\tconst func = c.function || \"#61afef\";\n\tconst variable = c.variable || \"#e06c75\";\n\tconst type = c.type || \"#e5c07b\";\n\tconst className = c.class || type;\n\tconst constant = c.constant || number;\n\tconst operator = c.operator || keyword;\n\tconst invalid = c.invalid || \"#ff6b6b\";\n\tconst foreground = c.foreground || \"#abb2bf\";\n\tconst background = c.background || \"#282c34\";\n\n\tconst baseStyles = includeBackground\n\t\t? `\n${selector} {\n  background: ${background};\n  color: ${foreground};\n}`\n\t\t: \"\";\n\n\treturn `${baseStyles}\n${selector} .tok-keyword { color: ${keyword}; }\n${selector} .tok-operator { color: ${operator}; }\n${selector} .tok-number { color: ${number}; }\n${selector} .tok-string { color: ${string}; }\n${selector} .tok-comment { color: ${comment}; font-style: italic; }\n${selector} .tok-variableName { color: ${variable}; }\n${selector} .tok-propertyName { color: ${func}; }\n${selector} .tok-typeName { color: ${type}; }\n${selector} .tok-className { color: ${className}; }\n${selector} .tok-function { color: ${func}; }\n${selector} .tok-bool { color: ${constant}; }\n${selector} .tok-null { color: ${constant}; }\n${selector} .tok-punctuation { color: ${foreground}; }\n${selector} .tok-definition { color: ${variable}; }\n${selector} .tok-labelName { color: ${variable}; }\n${selector} .tok-namespace { color: ${type}; }\n${selector} .tok-macroName { color: ${keyword}; }\n${selector} .tok-atom { color: ${constant}; }\n${selector} .tok-meta { color: ${foreground}; }\n${selector} .tok-heading { color: ${variable}; font-weight: bold; }\n${selector} .tok-link { color: ${func}; text-decoration: underline; }\n${selector} .tok-strikethrough { text-decoration: line-through; }\n${selector} .tok-emphasis { font-style: italic; }\n${selector} .tok-strong { font-weight: bold; }\n${selector} .tok-invalid { color: ${invalid}; }\n${selector} .tok-name { color: ${variable}; }\n${selector} .tok-deleted { color: ${invalid}; }\n${selector} .tok-inserted { color: ${string}; }\n${selector} .tok-changed { color: ${number}; }\n`.trim();\n}\n\n/**\n * Injects dynamic CSS for syntax highlighting based on current editor theme\n */\nfunction injectStyles() {\n\tconst themeId = settings?.value?.editorTheme || \"one_dark\";\n\tconst config = getThemeConfig(themeId);\n\n\t// Code blocks need background, references panel uses parent's background\n\tconst codeBlockStyles = generateStyles(config, \".cm-highlighted\", true);\n\tconst refPreviewStyles = generateStyles(config, \".ref-preview\", false);\n\tconst allStyles = `${codeBlockStyles}\\n${refPreviewStyles}`;\n\n\tif (!styleElement) {\n\t\tstyleElement = document.createElement(\"style\");\n\t\tstyleElement.id = \"cm-static-highlight-styles\";\n\t\tdocument.head.appendChild(styleElement);\n\t}\n\n\tstyleElement.textContent = allStyles;\n\tcurrentThemeId = themeId;\n}\n\n/**\n * Gets the language parser for a given URI using the modelist\n */\nasync function getLanguageParser(uri) {\n\tconst mode = getModeForPath(uri);\n\tif (!mode?.languageExtension) return null;\n\n\ttry {\n\t\tconst langExt = await mode.languageExtension();\n\t\tif (!langExt) return null;\n\n\t\tconst langArray = Array.isArray(langExt) ? langExt : [langExt];\n\t\tfor (const ext of langArray) {\n\t\t\tif (ext && typeof ext === \"object\" && \"language\" in ext) {\n\t\t\t\treturn ext.language.parser;\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"Failed to get language parser for\", uri, e);\n\t}\n\treturn null;\n}\n\n/**\n * Gets language parser by language name (e.g., \"javascript\", \"python\")\n * Uses modelist to find the mode and get first valid extension for file matching\n */\nasync function getParserForLanguage(langName) {\n\tif (!langName) return null;\n\n\tconst modesByName = getModesByName();\n\tconst normalizedName = langName.toLowerCase();\n\n\t// Try to find mode by name (case-insensitive)\n\tconst mode = modesByName[normalizedName];\n\tif (mode?.languageExtension) {\n\t\ttry {\n\t\t\tconst langExt = await mode.languageExtension();\n\t\t\tif (!langExt) return null;\n\n\t\t\tconst langArray = Array.isArray(langExt) ? langExt : [langExt];\n\t\t\tfor (const ext of langArray) {\n\t\t\t\tif (ext && typeof ext === \"object\" && \"language\" in ext) {\n\t\t\t\t\treturn ext.language.parser;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn(\"Failed to get parser for language:\", langName, e);\n\t\t}\n\t}\n\n\t// Fallback: create a fake filename and use getModeForPath\n\t// This handles cases where the language name doesn't match mode name exactly\n\tconst fakeUri = `file.${normalizedName}`;\n\treturn await getLanguageParser(fakeUri);\n}\n\n/**\n * Highlights a single line of code for display in references panel\n * @param {string} text - The line of code to highlight\n * @param {string} uri - File URI for language detection\n * @param {string|null} symbolName - Optional symbol to highlight with special styling\n */\nexport async function highlightLine(text, uri, symbolName = null) {\n\tif (!text || !text.trim()) return \"\";\n\n\tconst themeId = settings?.value?.editorTheme || \"one_dark\";\n\tconst cacheKey = `line:${themeId}:${uri}:${text}:${symbolName || \"\"}`;\n\n\tif (highlightCache.has(cacheKey)) {\n\t\treturn highlightCache.get(cacheKey);\n\t}\n\n\tconst trimmedText = text.trim();\n\n\ttry {\n\t\tconst parser = await getLanguageParser(uri);\n\t\tif (parser) {\n\t\t\tconst tree = parser.parse(trimmedText);\n\t\t\tlet result = \"\";\n\n\t\t\thighlightCode(\n\t\t\t\ttrimmedText,\n\t\t\t\ttree,\n\t\t\t\tclassHighlighter,\n\t\t\t\t(code, classes) => {\n\t\t\t\t\tif (classes) {\n\t\t\t\t\t\tresult += `<span class=\"${classes}\">${escapeHtml(code)}</span>`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult += escapeHtml(code);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {},\n\t\t\t);\n\n\t\t\tif (result) {\n\t\t\t\tconst highlighted = symbolName\n\t\t\t\t\t? addSymbolHighlight(result, symbolName)\n\t\t\t\t\t: result;\n\t\t\t\tsetCache(cacheKey, highlighted);\n\t\t\t\treturn highlighted;\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"Highlighting failed for\", uri, e);\n\t}\n\n\tconst escaped = escapeHtml(trimmedText);\n\tconst highlighted = symbolName\n\t\t? addSymbolHighlight(escaped, symbolName)\n\t\t: escaped;\n\tsetCache(cacheKey, highlighted);\n\treturn highlighted;\n}\n\n/**\n * Highlights a code block for display in markdown/plugin pages\n * @param {string} code - The code to highlight\n * @param {string} language - Language identifier from markdown fence (e.g., \"javascript\", \"python\")\n */\nexport async function highlightCodeBlock(code, language) {\n\tif (!code) return \"\";\n\n\tconst themeId = settings?.value?.editorTheme || \"one_dark\";\n\tconst langKey = (language || \"text\").toLowerCase();\n\n\tconst cacheKey = `block:${themeId}:${langKey}:${code}`;\n\tif (highlightCache.has(cacheKey)) {\n\t\treturn highlightCache.get(cacheKey);\n\t}\n\n\ttry {\n\t\tconst parser = await getParserForLanguage(langKey);\n\t\tif (parser) {\n\t\t\tconst tree = parser.parse(code);\n\t\t\tlet result = \"\";\n\n\t\t\thighlightCode(\n\t\t\t\tcode,\n\t\t\t\ttree,\n\t\t\t\tclassHighlighter,\n\t\t\t\t(text, classes) => {\n\t\t\t\t\tif (classes) {\n\t\t\t\t\t\tresult += `<span class=\"${classes}\">${escapeHtml(text)}</span>`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult += escapeHtml(text);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tresult += \"\\n\";\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (result) {\n\t\t\t\tsetCache(cacheKey, result);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"Code block highlighting failed for\", language, e);\n\t}\n\n\tconst escaped = escapeHtml(code);\n\tsetCache(cacheKey, escaped);\n\treturn escaped;\n}\n\nexport function clearHighlightCache() {\n\thighlightCache.clear();\n}\n\n/**\n * Initializes the static code highlighting system.\n * Injects theme-based CSS and sets up listener for theme changes.\n */\nexport function initHighlighting() {\n\tinjectStyles();\n\n\tsettings.on(\"update:editorTheme:after\", () => {\n\t\tconst newThemeId = settings?.value?.editorTheme || \"one_dark\";\n\t\tif (newThemeId !== currentThemeId) {\n\t\t\tinjectStyles();\n\t\t\thighlightCache.clear();\n\t\t}\n\t});\n}\n\nexport default {\n\tsanitize,\n\thighlightLine,\n\thighlightCodeBlock,\n\tclearHighlightCache,\n\tinitHighlighting,\n};\n"
  },
  {
    "path": "src/utils/color/hex.js",
    "content": "import Rgb from \"./rgb\";\n\nexport default class Hex {\n\tr = 0;\n\tg = 0;\n\tb = 0;\n\ta = 1;\n\n\t/**\n\t * Hex color constructor\n\t * @param {number} r Red value in hexadecimals\n\t * @param {number} g Green value in hexadecimals\n\t * @param {number} b Blue value in hexadecimals\n\t * @param {number} a Alpha value between 0 and 1\n\t */\n\tconstructor(r, g, b, a = 1) {\n\t\tthis.r = r;\n\t\tthis.g = g;\n\t\tthis.b = b;\n\t\tthis.a = a;\n\t}\n\n\t/**\n\t * Creates a Hex color from an RGB color\n\t * @param {Rgb} rgb\n\t */\n\tstatic fromRgb(rgb) {\n\t\tconst { r, g, b, a } = rgb;\n\t\treturn new Hex(r, g, b, a * 255);\n\t}\n\n\t/**\n\t * Gets the color as a string\n\t * @param {boolean} alpha Whether to include alpha\n\t */\n\ttoString(alpha) {\n\t\tlet r = this.r.toString(16);\n\t\tlet g = this.g.toString(16);\n\t\tlet b = this.b.toString(16);\n\t\tlet a = this.a.toString(16);\n\n\t\tif (r.length === 1) r = `0${r}`;\n\t\tif (g.length === 1) g = `0${g}`;\n\t\tif (b.length === 1) b = `0${b}`;\n\t\tif (a.length === 1) a = `0${a}`;\n\n\t\tconst hex = () => `#${r}${g}${b}`.toUpperCase();\n\t\tconst hexA = () => `#${r}${g}${b}${a}`.toUpperCase();\n\n\t\tif (alpha === undefined) {\n\t\t\treturn this.a === 255 ? hex() : hexA();\n\t\t}\n\n\t\treturn alpha ? hexA() : hex();\n\t}\n\n\t/**\n\t * Gets the color as an RGB object\n\t * @returns {Rgb}\n\t */\n\tget rgb() {\n\t\treturn new Rgb(this.r, this.g, this.b, this.a);\n\t}\n}\n"
  },
  {
    "path": "src/utils/color/hsl.js",
    "content": "import Rgb from \"./rgb\";\n\nexport default class Hsl {\n\th = 0;\n\ts = 0;\n\tl = 0;\n\ta = 1;\n\n\t/**\n\t * HSL color constructor\n\t * @param {number} h Hue value between 0 and 1\n\t * @param {number} s Saturation value between 0 and 1\n\t * @param {number} l Lightness value between 0 and 1\n\t * @param {number} a Alpha value between 0 and 1\n\t */\n\tconstructor(h, s, l, a = 1) {\n\t\tthis.h = h;\n\t\tthis.s = s;\n\t\tthis.l = l;\n\t\tthis.a = a;\n\t}\n\n\t/**\n\t * Creates an HSL color from an RGB color\n\t * @param {Rgb} rgb\n\t * @returns\n\t */\n\tstatic fromRgb(rgb) {\n\t\tlet { r, g, b, a } = rgb;\n\t\tr /= 255;\n\t\tg /= 255;\n\t\tb /= 255;\n\n\t\tconst max = Math.max(r, g, b);\n\t\tconst min = Math.min(r, g, b);\n\t\tconst l = (max + min) / 2;\n\t\tlet h = 0;\n\t\tlet s = 0;\n\n\t\tif (max !== min) {\n\t\t\tconst d = max - min;\n\t\t\ts = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n\t\t\tswitch (max) {\n\t\t\t\tcase r:\n\t\t\t\t\th = (g - b) / d + (g < b ? 6 : 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase g:\n\t\t\t\t\th = (b - r) / d + 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase b:\n\t\t\t\t\th = (r - g) / d + 4;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\th /= 6;\n\t\t}\n\n\t\treturn new Hsl(h, s, l, a);\n\t}\n\n\t/**\n\t * Gets the color as a string\n\t * @param {boolean} [alpha] Whether to include alpha\n\t * @returns\n\t */\n\ttoString(alpha) {\n\t\tconst hsl = () => `hsl(${this.hValue}, ${this.sValue}%, ${this.lValue}%)`;\n\t\tconst hsla = () =>\n\t\t\t`hsla(${this.hValue}, ${this.sValue}%, ${this.lValue}%, ${this.a})`;\n\t\tif (alpha === undefined) {\n\t\t\treturn this.a === 1 ? hsl() : hsla();\n\t\t}\n\n\t\treturn alpha ? hsla() : hsl();\n\t}\n\n\tget hValue() {\n\t\treturn this.h * 360;\n\t}\n\n\tget sValue() {\n\t\treturn this.s * 100;\n\t}\n\n\tget lValue() {\n\t\treturn this.l * 100;\n\t}\n\n\tget lightness() {\n\t\treturn this.lValue;\n\t}\n\n\tget hue() {\n\t\treturn this.hValue;\n\t}\n\n\tget saturation() {\n\t\treturn this.sValue;\n\t}\n\n\t/**\n\t * Gets the color as an rgb object\n\t * @returns {Rgb}\n\t */\n\tget rgb() {\n\t\tif (this.l === 0) {\n\t\t\treturn new Rgb(0, 0, 0, this.a);\n\t\t}\n\n\t\tif (this.l === 1) {\n\t\t\treturn new Rgb(255, 255, 255, this.a);\n\t\t}\n\n\t\t// now convert hsl value to rgb\n\t\tlet c = (1 - Math.abs(2 * this.l - 1)) * this.s;\n\t\tlet x = c * (1 - Math.abs(((this.h * 6) % 2) - 1));\n\t\tlet m = this.l - c / 2;\n\t\tlet r = 0;\n\t\tlet g = 0;\n\t\tlet b = 0;\n\n\t\tif (this.h < 1 / 6) {\n\t\t\tr = c;\n\t\t\tg = x;\n\t\t} else if (this.h < 2 / 6) {\n\t\t\tr = x;\n\t\t\tg = c;\n\t\t} else if (this.h < 3 / 6) {\n\t\t\tg = c;\n\t\t\tb = x;\n\t\t} else if (this.h < 4 / 6) {\n\t\t\tg = x;\n\t\t\tb = c;\n\t\t} else if (this.h < 5 / 6) {\n\t\t\tr = x;\n\t\t\tb = c;\n\t\t} else {\n\t\t\tr = c;\n\t\t\tb = x;\n\t\t}\n\n\t\tr = Math.round((r + m) * 255);\n\t\tg = Math.round((g + m) * 255);\n\t\tb = Math.round((b + m) * 255);\n\n\t\treturn new Rgb(r, g, b, this.a);\n\t}\n}\n"
  },
  {
    "path": "src/utils/color/index.js",
    "content": "import Hex from \"./hex\";\nimport Hsl from \"./hsl\";\nimport Rgb from \"./rgb\";\n\n/**@type {CanvasRenderingContext2D} */\nconst ctx = (<canvas></canvas>).getContext(\"2d\", {\n\twillReadFrequently: true,\n});\n\nexport default (/**@type {string}*/ color) => {\n\treturn new Color(color);\n};\n\nclass Color {\n\trgb = new Rgb(0, 0, 0, 1);\n\n\t/**\n\t * Create a color from a string\n\t * @param {string} color\n\t */\n\tconstructor(color) {\n\t\tconst { canvas } = ctx;\n\t\tctx.clearRect(0, 0, canvas.width, canvas.height);\n\t\tctx.fillStyle = color;\n\t\tctx.fillRect(0, 0, 1, 1);\n\t\tconst [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;\n\t\tthis.rgb = new Rgb(r, g, b, a / 255);\n\t}\n\n\tdarken(ratio) {\n\t\tconst hsl = Hsl.fromRgb(this.rgb);\n\t\thsl.l = Math.max(0, hsl.l - ratio * hsl.l);\n\t\tthis.rgb = hsl.rgb;\n\t\treturn this;\n\t}\n\n\tlighten(ratio) {\n\t\tconst hsl = Hsl.fromRgb(this.rgb);\n\t\thsl.l = Math.min(1, hsl.l + ratio * hsl.l);\n\t\tthis.rgb = hsl.rgb;\n\t\treturn this;\n\t}\n\n\tget isDark() {\n\t\treturn this.luminance < 0.5;\n\t}\n\n\tget isLight() {\n\t\treturn this.luminance >= 0.5;\n\t}\n\n\tget lightness() {\n\t\treturn this.hsl.l;\n\t}\n\n\t/**\n\t * Get the luminance of the color\n\t * Returns a value between 0 and 1\n\t */\n\tget luminance() {\n\t\tlet { r, g, b } = this.rgb;\n\t\tr /= 255;\n\t\tg /= 255;\n\t\tb /= 255;\n\t\treturn 0.2126 * r + 0.7152 * g + 0.0722 * b;\n\t}\n\n\tget hex() {\n\t\treturn Hex.fromRgb(this.rgb);\n\t}\n\n\tget hsl() {\n\t\treturn Hsl.fromRgb(this.rgb);\n\t}\n}\n"
  },
  {
    "path": "src/utils/color/regex.js",
    "content": "export const MAX_16_BASE = \"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\";\nexport const HUE_VALUE =\n\t\"(360(\\\\.0+)?|(3[0-5][0-9]|([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-9][0-9])(\\\\.\\\\d+)?)|(\\\\.\\\\d+))\";\nexport const MAX_PERCENTAGE = \"((100(\\\\.0+)?|[1-9]?[0-9](\\\\.\\\\d+)?|\\\\.\\\\d+)%)\";\nexport const MAX_ALPHA = `((0(\\\\.\\\\d+)?|1(\\\\.0+)?|\\\\.\\\\d+)|${MAX_PERCENTAGE})`;\nexport const RGB_VALUE = `(\\\\s*${MAX_16_BASE}|${MAX_PERCENTAGE}\\\\s*)`;\nexport const RGB_VALUES = `${RGB_VALUE},${RGB_VALUE},${RGB_VALUE}`;\nexport const RGB_VALUES_NO_COMMA = `${RGB_VALUE}{3}`;\nexport const RGB = `(rgb\\\\s*\\\\(${RGB_VALUES}\\\\)|rgb\\\\s*\\\\(${RGB_VALUES_NO_COMMA}\\\\))`;\nexport const RGBA = `(rgba\\\\s*\\\\(${RGB_VALUES}\\\\s*,\\\\s*${MAX_ALPHA}\\\\s*\\\\)|rgba\\\\s*\\\\(${RGB_VALUES_NO_COMMA}\\\\s+${MAX_ALPHA}\\\\s*\\\\))`;\nexport const HSL_VALUE = `\\\\s*${HUE_VALUE}\\\\s*,\\\\s*${MAX_PERCENTAGE}\\\\s*,\\\\s*${MAX_PERCENTAGE}\\\\s*`;\nexport const HSL_VALUE_NO_COMMA = `\\\\s*${HUE_VALUE}\\\\s+${MAX_PERCENTAGE}\\\\s+${MAX_PERCENTAGE}\\\\s*`;\nexport const HSL = `(hsl\\\\s*\\\\(${HSL_VALUE}\\\\)|hsl\\\\s*\\\\(${HSL_VALUE_NO_COMMA}\\\\))`;\nexport const HSLA = `(hsla\\\\s*\\\\(${HSL_VALUE}\\\\s*,\\\\s*${MAX_ALPHA}\\\\s*\\\\)|hsla\\\\s*\\\\(${HSL_VALUE_NO_COMMA}\\\\s+\\/\\/\\\\s+${MAX_ALPHA}\\\\s*\\\\)|hsla\\\\s*\\\\(${HSL_VALUE_NO_COMMA}\\\\s+${MAX_ALPHA}\\\\s*\\\\))`;\nexport const HEX = \"(#[0-9a-f]{3,8})\";\nexport const NAMED_COLORS = {\n\tblack: \"rgb(0, 0, 0)\",\n\tsilver: \"rgb(192, 192, 192)\",\n\tgray: \"rgb(128, 128, 128)\",\n\twhite: \"rgb(255, 255, 255)\",\n\tmaroon: \"rgb(128, 0, 0)\",\n\tred: \"rgb(255, 0, 0)\",\n\tpurple: \"rgb(128, 0, 128)\",\n\tfuchsia: \"rgb(255, 0, 255)\",\n\tgreen: \"rgb(0, 128, 0)\",\n\tlime: \"rgb(0, 255, 0)\",\n\tolive: \"rgb(128, 128, 0)\",\n\tyellow: \"rgb(255, 255, 0)\",\n\tnavy: \"rgb(0, 0, 128)\",\n\tblue: \"rgb(0, 0, 255)\",\n\tteal: \"rgb(0, 128, 128)\",\n\taqua: \"rgb(0, 255, 255)\",\n\taliceblue: \"rgb(240, 248, 255)\",\n\tantiquewhite: \"rgb(250, 235, 215)\",\n\taquamarine: \"rgb(127, 255, 212)\",\n\tazure: \"rgb(240, 255, 255)\",\n\tbeige: \"rgb(245, 245, 220)\",\n\tbisque: \"rgb(255, 228, 196)\",\n\tblanchedalmond: \"rgb(255, 235, 205)\",\n\tblueviolet: \"rgb(138, 43, 226)\",\n\tbrown: \"rgb(165, 42, 42)\",\n\tburlywood: \"rgb(222, 184, 135)\",\n\tcadetblue: \"rgb(95, 158, 160)\",\n\tchartreuse: \"rgb(127, 255, 0)\",\n\tchocolate: \"rgb(210, 105, 30)\",\n\tcoral: \"rgb(255, 127, 80)\",\n\tcornflowerblue: \"rgb(100, 149, 237)\",\n\tcornsilk: \"rgb(255, 248, 220)\",\n\tcrimson: \"rgb(220, 20, 60)\",\n\tcyan: \"rgb(0, 255, 255)\",\n\tdarkblue: \"rgb(0, 0, 139)\",\n\tdarkcyan: \"rgb(0, 139, 139)\",\n\tdarkgoldenrod: \"rgb(184, 134, 11)\",\n\tdarkgray: \"rgb(169, 169, 169)\",\n\tdarkgreen: \"rgb(0, 100, 0)\",\n\tdarkgrey: \"rgb(169, 169, 169)\",\n\tdarkkhaki: \"rgb(189, 183, 107)\",\n\tdarkmagenta: \"rgb(139, 0, 139)\",\n\tdarkolivegreen: \"rgb(85, 107, 47)\",\n\tdarkorange: \"rgb(255, 140, 0)\",\n\tdarkorchid: \"rgb(153, 50, 204)\",\n\tdarkred: \"rgb(139, 0, 0)\",\n\tdarksalmon: \"rgb(233, 150, 122)\",\n\tdarkseagreen: \"rgb(143, 188, 143)\",\n\tdarkslateblue: \"rgb(72, 61, 139)\",\n\tdarkslategray: \"rgb(47, 79, 79)\",\n\tdarkslategrey: \"rgb(47, 79, 79)\",\n\tdarkturquoise: \"rgb(0, 206, 209)\",\n\tdarkviolet: \"rgb(148, 0, 211)\",\n\tdeeppink: \"rgb(255, 20, 147)\",\n\tdeepskyblue: \"rgb(0, 191, 255)\",\n\tdimgray: \"rgb(105, 105, 105)\",\n\tdimgrey: \"rgb(105, 105, 105)\",\n\tdodgerblue: \"rgb(30, 144, 255)\",\n\tfirebrick: \"rgb(178, 34, 34)\",\n\tfloralwhite: \"rgb(255, 250, 240)\",\n\tforestgreen: \"rgb(34, 139, 34)\",\n\tgainsboro: \"rgb(220, 220, 220)\",\n\tghostwhite: \"rgb(248, 248, 255)\",\n\tgold: \"rgb(255, 215, 0)\",\n\tgoldenrod: \"rgb(218, 165, 32)\",\n\tgreenyellow: \"rgb(173, 255, 47)\",\n\tgrey: \"rgb(128, 128, 128)\",\n\thoneydew: \"rgb(240, 255, 240)\",\n\thotpink: \"rgb(255, 105, 180)\",\n\tindianred: \"rgb(205, 92, 92)\",\n\tindigo: \"rgb(75, 0, 130)\",\n\tivory: \"rgb(255, 255, 240)\",\n\tkhaki: \"rgb(240, 230, 140)\",\n\tlavender: \"rgb(230, 230, 250)\",\n\tlavenderblush: \"rgb(255, 240, 245)\",\n\tlawngreen: \"rgb(124, 252, 0)\",\n\tlemonchiffon: \"rgb(255, 250, 205)\",\n\tlightblue: \"rgb(173, 216, 230)\",\n\tlightcoral: \"rgb(240, 128, 128)\",\n\tlightcyan: \"rgb(224, 255, 255)\",\n\tlightgoldenrodyellow: \"rgb(250, 250, 210)\",\n\tlightgray: \"rgb(211, 211, 211)\",\n\tlightgreen: \"rgb(144, 238, 144)\",\n\tlightgrey: \"rgb(211, 211, 211)\",\n\tlightpink: \"rgb(255, 182, 193)\",\n\tlightsalmon: \"rgb(255, 160, 122)\",\n\tlightseagreen: \"rgb(32, 178, 170)\",\n\tlightskyblue: \"rgb(135, 206, 250)\",\n\tlightslategray: \"rgb(119, 136, 153)\",\n\tlightslategrey: \"rgb(119, 136, 153)\",\n\tlightsteelblue: \"rgb(176, 196, 222)\",\n\tlightyellow: \"rgb(255, 255, 224)\",\n\tlimegreen: \"rgb(50, 205, 50)\",\n\tlinen: \"rgb(250, 240, 230)\",\n\tmagenta: \"rgb(255, 0, 255)\",\n\tmediumaquamarine: \"rgb(102, 205, 170)\",\n\tmediumblue: \"rgb(0, 0, 205)\",\n\tmediumorchid: \"rgb(186, 85, 211)\",\n\tmediumpurple: \"rgb(147, 112, 219)\",\n\tmediumseagreen: \"rgb(60, 179, 113)\",\n\tmediumslateblue: \"rgb(123, 104, 238)\",\n\tmediumspringgreen: \"rgb(0, 250, 154)\",\n\tmediumturquoise: \"rgb(72, 209, 204)\",\n\tmediumvioletred: \"rgb(199, 21, 133)\",\n\tmidnightblue: \"rgb(25, 25, 112)\",\n\tmintcream: \"rgb(245, 255, 250)\",\n\tmistyrose: \"rgb(255, 228, 225)\",\n\tmoccasin: \"rgb(255, 228, 181)\",\n\tnavajowhite: \"rgb(255, 222, 173)\",\n\toldlace: \"rgb(253, 245, 230)\",\n\tolivedrab: \"rgb(107, 142, 35)\",\n\torangered: \"rgb(255, 69, 0)\",\n\torchid: \"rgb(218, 112, 214)\",\n\tpalegoldenrod: \"rgb(238, 232, 170)\",\n\tpalegreen: \"rgb(152, 251, 152)\",\n\tpaleturquoise: \"rgb(175, 238, 238)\",\n\tpalevioletred: \"rgb(219, 112, 147)\",\n\tpapayawhip: \"rgb(255, 239, 213)\",\n\tpeachpuff: \"rgb(255, 218, 185)\",\n\tperu: \"rgb(205, 133, 63)\",\n\tpink: \"rgb(255, 192, 203)\",\n\tplum: \"rgb(221, 160, 221)\",\n\tpowderblue: \"rgb(176, 224, 230)\",\n\trebeccapurple: \"rgb(102, 51, 153)\",\n\trosybrown: \"rgb(188, 143, 143)\",\n\troyalblue: \"rgb(65, 105, 225)\",\n\tsaddlebrown: \"rgb(139, 69, 19)\",\n\tsalmon: \"rgb(250, 128, 114)\",\n\tsandybrown: \"rgb(244, 164, 96)\",\n\tseagreen: \"rgb(46, 139, 87)\",\n\tseashell: \"rgb(255, 245, 238)\",\n\tsienna: \"rgb(160, 82, 45)\",\n\tskyblue: \"rgb(135, 206, 235)\",\n\tslateblue: \"rgb(106, 90, 205)\",\n\tslategray: \"rgb(112, 128, 144)\",\n\tslategrey: \"rgb(112, 128, 144)\",\n\tsnow: \"rgb(255, 250, 250)\",\n\tspringgreen: \"rgb(0, 255, 127)\",\n\tsteelblue: \"rgb(70, 130, 180)\",\n\ttan: \"rgb(210, 180, 140)\",\n\tthistle: \"rgb(216, 191, 216)\",\n\ttomato: \"rgb(255, 99, 71)\",\n\tturquoise: \"rgb(64, 224, 208)\",\n\tviolet: \"rgb(238, 130, 238)\",\n\twheat: \"rgb(245, 222, 179)\",\n\twhitesmoke: \"rgb(245, 245, 245)\",\n\tyellowgreen: \"rgb(154, 205, 50)\",\n};\n\nconst namedColors = Object.keys(NAMED_COLORS).join(\"|\");\n\nexport const colorRegex = {\n\t/**\n\t * Regular expression to match rgb colors\n\t * @type {RegExp}\n\t */\n\tget rgb() {\n\t\tdelete this.rgb;\n\t\tthis.rgb = new RegExp(`^${RGB}$`);\n\t\treturn this.rgb;\n\t},\n\t/**\n\t * Regular expression to match rgba colors\n\t * @type {RegExp}\n\t */\n\tget rgba() {\n\t\tdelete this.rgba;\n\t\tthis.rgba = new RegExp(`^${RGBA}$`);\n\t\treturn this.rgba;\n\t},\n\t/**\n\t * Regular expression to match hsl colors\n\t * @type {RegExp}\n\t */\n\tget hsl() {\n\t\tdelete this.hsl;\n\t\tthis.hsl = new RegExp(`^${HSL}$`);\n\t\treturn this.hsl;\n\t},\n\t/**\n\t * Regular expression to match hsla colors\n\t * @type {RegExp}\n\t */\n\tget hsla() {\n\t\tdelete this.hsla;\n\t\tthis.hsla = new RegExp(`^${HSLA}$`);\n\t\treturn this.hsla;\n\t},\n\t/**\n\t * Regular expression to match hex colors\n\t * @type {RegExp}\n\t */\n\tget hex() {\n\t\tdelete this.hex;\n\t\tthis.hex = new RegExp(`^${HEX}$`);\n\t\treturn this.hex;\n\t},\n\t/**\n\t * Regular expression to match any color\n\t * @type {RegExp}\n\t */\n\tget named() {\n\t\tdelete this.named;\n\t\tthis.named = new RegExp(`^(${namedColors})$`);\n\t\treturn this.named;\n\t},\n\t/**\n\t * Regular expression to match any color\n\t * @type {RegExp}\n\t */\n\tget anyStrict() {\n\t\tdelete this.any;\n\t\tthis.any = new RegExp(\n\t\t\t`^(${namedColors}|${RGB}|${RGBA}|${HSL}|${HSLA}|${HEX})$`,\n\t\t\t\"i\",\n\t\t);\n\t\treturn this.any;\n\t},\n\t/**\n\t * Regular expression to match any color\n\t * Always return a new RegExp instance\n\t * @type {RegExp}\n\t */\n\tget anyGlobal() {\n\t\t// Negative lookbehind is not supported in older browsers\n\t\treturn new RegExp(\n\t\t\t`(^|\\\\W)(${namedColors}|${RGB}|${RGBA}|${HSL}|${HSLA}|${HEX})($|\\\\W)`,\n\t\t\t\"gi\",\n\t\t);\n\t},\n};\n\n/**\n * Select the color at current line and cursor position (CodeMirror)\n * @returns {{from:number,to:number}|null}\n */\nexport function getColorRange() {\n\tconst { editor } = editorManager;\n\n\ttry {\n\t\tconst sel = editor.state.selection.main;\n\t\tconst from = sel.from;\n\t\tconst to = sel.to;\n\n\t\t// If there is a selection, validate and return it\n\t\tif (from !== to) {\n\t\t\tconst text = editor.state.doc.sliceString(from, to);\n\t\t\tif (!isValidColor(text)) return null;\n\t\t\treturn { from, to };\n\t\t}\n\n\t\t// No selection: find color under cursor in the current line\n\t\tconst head = sel.head;\n\t\tconst line = editor.state.doc.lineAt(head);\n\t\tconst lineText = line.text;\n\t\tconst col = head - line.from;\n\n\t\tconst regex = colorRegex.anyGlobal;\n\t\tlet match;\n\n\t\twhile ((match = regex.exec(lineText))) {\n\t\t\tconst startCol = match.index + match[1].length;\n\t\t\tconst endCol = startCol + match[2].length;\n\n\t\t\tif (col >= startCol && col <= endCol) {\n\t\t\t\treturn { from: line.from + startCol, to: line.from + endCol };\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function isValidColor(value) {\n\treturn colorRegex.anyStrict.test(value);\n}\n"
  },
  {
    "path": "src/utils/color/rgb.js",
    "content": "export default class Rgb {\n\tr = 0;\n\tg = 0;\n\tb = 0;\n\ta = 1;\n\n\tconstructor(r, g, b, a = 1) {\n\t\tthis.r = r;\n\t\tthis.g = g;\n\t\tthis.b = b;\n\t\tthis.a = a;\n\t}\n\n\t/**\n\t * Get the color as a string\n\t * @param {boolean} alpha Whether to include alpha channel\n\t * @returns\n\t */\n\ttoString(alpha) {\n\t\tconst rgb = () => `rgb(${this.r}, ${this.g}, ${this.b})`;\n\t\tconst rgba = () => `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;\n\t\tif (alpha === undefined) {\n\t\t\treturn this.a === 1 ? rgb() : rgba();\n\t\t}\n\t\treturn alpha ? rgba() : rgb();\n\t}\n}\n"
  },
  {
    "path": "src/utils/encodings.js",
    "content": "import alert from \"dialogs/alert\";\nimport settings from \"lib/settings\";\n\nlet encodings = {};\n\n/**\n * @typedef {Object} Encoding\n * @property {string} label\n * @property {string[]} aliases\n * @property {string} name\n */\n\n/**\n * Get the encoding label from the charset\n * @param {string} charset\n * @returns {Encoding|undefined}\n */\nexport function getEncoding(charset) {\n\tcharset = charset.toLowerCase();\n\n\tconst found = Object.keys(encodings).find((key) => {\n\t\tif (key.toLowerCase() === charset) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst alias = encodings[key].aliases.find(\n\t\t\t(alias) => alias.toLowerCase() === charset,\n\t\t);\n\t\tif (alias) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t});\n\n\tif (found) {\n\t\treturn encodings[found];\n\t}\n\n\treturn encodings[\"UTF-8\"];\n}\n\nfunction detectBOM(bytes) {\n\tif (\n\t\tbytes.length >= 3 &&\n\t\tbytes[0] === 0xef &&\n\t\tbytes[1] === 0xbb &&\n\t\tbytes[2] === 0xbf\n\t)\n\t\treturn \"UTF-8\";\n\tif (bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xfe)\n\t\treturn \"UTF-16LE\";\n\tif (bytes.length >= 2 && bytes[0] === 0xfe && bytes[1] === 0xff)\n\t\treturn \"UTF-16BE\";\n\treturn null;\n}\n\nfunction isValidUTF8(bytes) {\n\tlet i = 0;\n\twhile (i < bytes.length) {\n\t\tconst byte = bytes[i];\n\n\t\tif (byte < 0x80) {\n\t\t\ti++;\n\t\t} else if (byte >> 5 === 0x06) {\n\t\t\tif (i + 1 >= bytes.length || bytes[i + 1] >> 6 !== 0x02) return false;\n\t\t\ti += 2;\n\t\t} else if (byte >> 4 === 0x0e) {\n\t\t\tif (\n\t\t\t\ti + 2 >= bytes.length ||\n\t\t\t\tbytes[i + 1] >> 6 !== 0x02 ||\n\t\t\t\tbytes[i + 2] >> 6 !== 0x02\n\t\t\t)\n\t\t\t\treturn false;\n\t\t\ti += 3;\n\t\t} else if (byte >> 3 === 0x1e) {\n\t\t\tif (\n\t\t\t\ti + 3 >= bytes.length ||\n\t\t\t\tbytes[i + 1] >> 6 !== 0x02 ||\n\t\t\t\tbytes[i + 2] >> 6 !== 0x02 ||\n\t\t\t\tbytes[i + 3] >> 6 !== 0x02\n\t\t\t)\n\t\t\t\treturn false;\n\t\t\ti += 4;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nexport async function detectEncoding(buffer) {\n\tif (!buffer || buffer.byteLength === 0) {\n\t\tconst def = settings.value.defaultFileEncoding;\n\t\treturn def === \"auto\" ? \"UTF-8\" : def || \"UTF-8\";\n\t}\n\n\tconst bytes = new Uint8Array(buffer);\n\n\tconst bomEncoding = detectBOM(bytes);\n\tif (bomEncoding) return bomEncoding;\n\n\tconst sample = bytes.subarray(0, Math.min(2048, bytes.length));\n\tlet nulls = 0,\n\t\tascii = 0;\n\n\tfor (const byte of sample) {\n\t\tif (byte === 0) nulls++;\n\t\telse if (byte < 0x80) ascii++;\n\t}\n\n\tif (nulls > sample.length * 0.3) return \"UTF-16LE\";\n\n\tif (isValidUTF8(sample)) return \"UTF-8\";\n\n\tconst encodings = [\n\t\t...new Set([\n\t\t\t\"UTF-8\",\n\t\t\tsettings.value.defaultFileEncoding === \"auto\"\n\t\t\t\t? \"UTF-8\"\n\t\t\t\t: settings.value.defaultFileEncoding || \"UTF-8\",\n\t\t\t\"windows-1252\",\n\t\t\t\"ISO-8859-1\",\n\t\t]),\n\t];\n\n\tconst testSample = sample.subarray(0, 512);\n\tconst testBuffer = testSample.buffer.slice(\n\t\ttestSample.byteOffset,\n\t\ttestSample.byteOffset + testSample.byteLength,\n\t);\n\n\tfor (const encoding of encodings) {\n\t\ttry {\n\t\t\tconst encodingObj = getEncoding(encoding);\n\t\t\tif (!encodingObj) continue;\n\n\t\t\tconst text = await execDecode(testBuffer, encodingObj.name);\n\t\t\tif (\n\t\t\t\t!text.includes(\"\\uFFFD\") &&\n\t\t\t\t!/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(text)\n\t\t\t) {\n\t\t\t\treturn encoding;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tconst def = settings.value.defaultFileEncoding;\n\treturn def === \"auto\" ? \"UTF-8\" : def || \"UTF-8\";\n}\n\n/**\n * Decodes arrayBuffer to String according given encoding type\n * @param {ArrayBuffer} buffer\n * @param {string} [charset]\n * @returns {Promise<string>}\n */\nexport async function decode(buffer, charset) {\n\tlet isJson = false;\n\n\tif (charset === \"json\") {\n\t\tcharset = null;\n\t\tisJson = true;\n\t}\n\n\tif (!charset) {\n\t\tcharset = settings.value.defaultFileEncoding;\n\t}\n\n\tif (charset === \"auto\") charset = \"UTF-8\";\n\n\tcharset = getEncoding(charset).name;\n\tconst text = await execDecode(buffer, charset);\n\n\tif (isJson) {\n\t\treturn JSON.parse(text);\n\t}\n\n\treturn text;\n}\n\n/**\n * Encodes text to ArrayBuffer according given encoding type\n * @param {string} text\n * @param {string} charset\n * @returns {Promise<ArrayBuffer>}\n */\nexport function encode(text, charset) {\n\tif (!charset) {\n\t\tcharset = settings.value.defaultFileEncoding;\n\t}\n\n\tif (charset === \"auto\") charset = \"UTF-8\";\n\n\tcharset = getEncoding(charset).name;\n\treturn execEncode(text, charset);\n}\n\nexport async function initEncodings() {\n\treturn new Promise((resolve, reject) => {\n\t\tcordova.exec(\n\t\t\t(map) => {\n\t\t\t\tObject.keys(map).forEach((key) => {\n\t\t\t\t\tconst encoding = map[key];\n\t\t\t\t\tencodings[key] = encoding;\n\t\t\t\t});\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\talert(strings.error, error.message || error);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t\t\"System\",\n\t\t\t\"get-available-encodings\",\n\t\t\t[],\n\t\t);\n\t});\n}\n\n/**\n * Decodes arrayBuffer to String according given encoding type\n * @param {ArrayBuffer} buffer\n * @param {string} charset\n * @returns {Promise<string>}\n */\nfunction execDecode(buffer, charset) {\n\treturn new Promise((resolve, reject) => {\n\t\tcordova.exec(\n\t\t\t(text) => {\n\t\t\t\tresolve(text);\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\treject(error);\n\t\t\t},\n\t\t\t\"System\",\n\t\t\t\"decode\",\n\t\t\t[buffer, charset],\n\t\t);\n\t});\n}\n\n/**\n * Encodes text to ArrayBuffer according given encoding type\n * @param {string} text\n * @param {string} charset\n * @returns {Promise<ArrayBuffer>}\n */\nfunction execEncode(text, charset) {\n\treturn new Promise((resolve, reject) => {\n\t\tcordova.exec(\n\t\t\t(buffer) => {\n\t\t\t\tresolve(buffer);\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\treject(error);\n\t\t\t},\n\t\t\t\"System\",\n\t\t\t\"encode\",\n\t\t\t[text, charset],\n\t\t);\n\t});\n}\n\nexport default encodings;\n"
  },
  {
    "path": "src/utils/helpers.js",
    "content": "import fsOperation from \"fileSystem\";\nimport ajax from \"@deadlyjack/ajax\";\nimport { getModeForPath as getCMModeForPath } from \"cm/modelist\";\nimport alert from \"dialogs/alert\";\nimport escapeStringRegexp from \"escape-string-regexp\";\nimport adRewards from \"lib/adRewards\";\nimport constants from \"lib/constants\";\nimport path from \"./Path\";\nimport Uri from \"./Uri\";\nimport Url from \"./Url\";\n\n/**\n * Gets programming language name according to filename\n * @param {String} filename\n * @returns\n */\nfunction getFileType(filename) {\n\tconst regex = {\n\t\tbabel: /\\.babelrc$/i,\n\t\tjsmap: /\\.js\\.map$/i,\n\t\tyarn: /^yarn\\.lock$/i,\n\t\ttestjs: /\\.test\\.js$/i,\n\t\ttestts: /\\.test\\.ts$/i,\n\t\tcssmap: /\\.css\\.map$/i,\n\t\ttypescriptdef: /\\.d\\.ts$/i,\n\t\tclojurescript: /\\.cljs$/i,\n\t\tcppheader: /\\.(hh|hpp)$/i,\n\t\tjsconfig: /^jsconfig.json$/i,\n\t\ttsconfig: /^tsconfig.json$/i,\n\t\tandroid: /\\.(apk|aab|slim)$/i,\n\t\tjsbeautify: /^\\.jsbeautifyrc$/i,\n\t\twebpack: /^webpack\\.config\\.js$/i,\n\t\taudio: /\\.(mp3|wav|ogg|flac|aac)$/i,\n\t\tgit: /(^\\.gitignore$)|(^\\.gitmodules$)/i,\n\t\tvideo: /\\.(mp4|m4a|mov|3gp|wmv|flv|avi)$/i,\n\t\timage: /\\.(png|jpg|jpeg|gif|bmp|ico|webp)$/i,\n\t\tnpm: /(^package\\.json$)|(^package\\-lock\\.json$)/i,\n\t\tcompressed: /\\.(zip|rar|7z|tar|gz|gzip|dmg|iso)$/i,\n\t\teslint:\n\t\t\t/(^\\.eslintrc(\\.(json5?|ya?ml|toml))?$|eslint\\.config\\.(c?js|json)$)/i,\n\t\tpostcssconfig:\n\t\t\t/(^\\.postcssrc(\\.(json5?|ya?ml|toml))?$|postcss\\.config\\.(c?js|json)$)/i,\n\t\tprettier:\n\t\t\t/(^\\.prettierrc(\\.(json5?|ya?ml|toml))?$|prettier\\.config\\.(c?js|json)$)/i,\n\t};\n\n\tconst fileType = Object.keys(regex).find((type) =>\n\t\tregex[type].test(filename),\n\t);\n\tif (fileType) return fileType;\n\n\treturn Url.extname(filename).substring(1);\n}\n\nexport default {\n\t/**\n\t * @deprecated This method is deprecated, use 'encodings.decode' instead.\n\t * Decodes arrayBuffer to String according given encoding type\n\t * @param {ArrayBuffer} arrayBuffer\n\t * @param {String} [encoding='utf-8']\n\t */\n\tdecodeText(arrayBuffer, encoding = \"utf-8\") {\n\t\tconst isJson = encoding === \"json\";\n\t\tif (isJson) encoding = \"utf-8\";\n\n\t\tconst uint8Array = new Uint8Array(arrayBuffer);\n\t\tconst result = new TextDecoder(encoding).decode(uint8Array);\n\t\tif (isJson) {\n\t\t\treturn this.parseJSON(result);\n\t\t}\n\t\treturn result;\n\t},\n\t/**\n\t * Gets icon according to filename\n\t * @param {string} filename\n\t */\n\tgetIconForFile(filename) {\n\t\tconst type = getFileType(filename);\n\t\t// Use CodeMirror's modelist to determine mode name\n\t\tlet modeName = \"text\";\n\t\ttry {\n\t\t\tconst mode = getCMModeForPath?.(filename);\n\t\t\tmodeName = mode?.name || modeName;\n\t\t} catch (e) {\n\t\t\t// fallback to default if CodeMirror modelist isn't available yet\n\t\t}\n\n\t\tconst iconForMode = `file_type_${modeName}`;\n\t\tconst iconForType = `file_type_${type}`;\n\n\t\treturn `file file_type_default ${iconForMode} ${iconForType}`;\n\t},\n\t/**\n\t *\n\t * @param {FileEntry[]} list\n\t * @param {object} fileBrowser settings\n\t * @param {'both'|'file'|'folder'}\n\t */\n\tsortDir(list, fileBrowser, mode = \"both\") {\n\t\tconst dir = [];\n\t\tconst file = [];\n\t\tconst sortByName = fileBrowser.sortByName;\n\t\tconst showHiddenFile = fileBrowser.showHiddenFiles;\n\n\t\tlist.forEach((item) => {\n\t\t\tlet hidden;\n\n\t\t\titem.name = item.name || path.basename(item.url || \"\");\n\t\t\thidden = item.name[0] === \".\";\n\n\t\t\tif (typeof item.isDirectory !== \"boolean\") {\n\t\t\t\tif (this.isDir(item.type)) item.isDirectory = true;\n\t\t\t}\n\t\t\tif (!item.type) item.type = item.isDirectory ? \"dir\" : \"file\";\n\t\t\tif (!item.url) item.url = item.url || item.uri;\n\t\t\tif ((hidden && showHiddenFile) || !hidden) {\n\t\t\t\tif (item.isDirectory) {\n\t\t\t\t\tdir.push(item);\n\t\t\t\t} else if (item.isFile) {\n\t\t\t\t\tfile.push(item);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (item.isDirectory) {\n\t\t\t\titem.icon = \"folder\";\n\t\t\t} else {\n\t\t\t\tif (mode === \"folder\") {\n\t\t\t\t\titem.disabled = true;\n\t\t\t\t}\n\t\t\t\titem.icon = this.getIconForFile(item.name);\n\t\t\t}\n\t\t});\n\n\t\tif (sortByName) {\n\t\t\tdir.sort(compare);\n\t\t\tfile.sort(compare);\n\t\t}\n\n\t\treturn dir.concat(file);\n\n\t\tfunction compare(a, b) {\n\t\t\treturn a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;\n\t\t}\n\t},\n\t/**\n\t * Gets error message from error object\n\t * @param {Error} err\n\t * @param  {...string} args\n\t */\n\terrorMessage(err, ...args) {\n\t\targs.forEach((arg, i) => {\n\t\t\tif (/^(content|file|ftp|sftp|https?):/.test(arg)) {\n\t\t\t\targs[i] = this.getVirtualPath(arg);\n\t\t\t}\n\t\t});\n\n\t\tconst extra = args.join(\"<br>\");\n\t\tlet msg;\n\n\t\tif (typeof err === \"string\" && err) {\n\t\t\tmsg = err;\n\t\t} else if (err instanceof Error) {\n\t\t\tmsg = err.message;\n\t\t} else {\n\t\t\tmsg = strings[\"an error occurred\"];\n\t\t}\n\n\t\treturn msg + (extra ? \"<br>\" + extra : \"\");\n\t},\n\t/**\n\t *\n\t * @param {Error} err\n\t * @param  {...string} args\n\t * @returns {PromiseLike<void>}\n\t */\n\terror(err, ...args) {\n\t\tif (err.code === 0) {\n\t\t\ttoast(err);\n\t\t\treturn;\n\t\t}\n\n\t\tlet hide = null;\n\t\tconst onhide = () => {\n\t\t\tif (hide) hide();\n\t\t};\n\n\t\tconst msg = this.errorMessage(err, ...args);\n\t\talert(strings.error, msg, onhide);\n\n\t\treturn new Promise((resolve) => {\n\t\t\thide = resolve;\n\t\t});\n\t},\n\t/**\n\t * Returns unique ID\n\t * @returns {string}\n\t */\n\tuuid() {\n\t\treturn (\n\t\t\tnew Date().getTime() + Number.parseInt(Math.random() * 100000000000)\n\t\t).toString(36);\n\t},\n\t/**\n\t * Parses JSON string, if fails returns null\n\t * @param {Object|Array} string\n\t */\n\tparseJSON(string) {\n\t\tif (!string) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(string);\n\t\t} catch (e) {\n\t\t\treturn null;\n\t\t}\n\t},\n\t/**\n\t * Checks whether given type is directory or not\n\t * @param {'dir'|'directory'|'folder'} type\n\t * @returns {Boolean}\n\t */\n\tisDir(type) {\n\t\treturn /^(dir|directory|folder)$/.test(type);\n\t},\n\t/**\n\t * Checks whether given type is file or not\n\t * @param {'file'|'link'} type\n\t * @returns {Boolean}\n\t */\n\tisFile(type) {\n\t\treturn /^(file|link)$/.test(type);\n\t},\n\t/**\n\t * Replace matching part of url to alias name by which storage is added\n\t * @param {String} url\n\t * @returns {String}\n\t */\n\tgetVirtualPath(url) {\n\t\turl = Url.parse(url).url;\n\n\t\tif (/^content:/.test(url)) {\n\t\t\tconst primary = Uri.getPrimaryAddress(url);\n\t\t\tif (primary) {\n\t\t\t\treturn primary;\n\t\t\t}\n\t\t}\n\n\t\t/**@type {string[]} */\n\t\tconst storageList = this.parseJSON(localStorage.storageList);\n\t\tif (!Array.isArray(storageList)) return url;\n\t\tconst storageListLen = storageList.length;\n\n\t\tfor (let i = 0; i < storageListLen; ++i) {\n\t\t\tconst uuid = storageList[i];\n\t\t\tlet storageUrl = Url.parse(uuid.uri || uuid.url || \"\").url;\n\t\t\tif (!storageUrl) continue;\n\t\t\tif (storageUrl.endsWith(\"/\")) {\n\t\t\t\tstorageUrl = storageUrl.slice(0, -1);\n\t\t\t}\n\t\t\tconst regex = new RegExp(\"^\" + escapeStringRegexp(storageUrl));\n\t\t\tif (regex.test(url)) {\n\t\t\t\turl = url.replace(regex, uuid.name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn url;\n\t},\n\t/**\n\t * Updates uri of all active which matches the oldUrl as location\n\t * of the file\n\t * @param {String} oldUrl\n\t * @param {String} newUrl\n\t */\n\tupdateUriOfAllActiveFiles(oldUrl, newUrl) {\n\t\tconst files = editorManager.files;\n\t\tconst { url } = Url.parse(oldUrl);\n\n\t\tfor (let file of files) {\n\t\t\tif (!file.uri) continue;\n\t\t\tconst fileUrl = Url.parse(file.uri).url;\n\t\t\tif (new RegExp(\"^\" + escapeStringRegexp(url)).test(fileUrl)) {\n\t\t\t\tif (newUrl) {\n\t\t\t\t\tfile.uri = Url.join(newUrl, file.filename);\n\t\t\t\t} else {\n\t\t\t\t\tfile.uri = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\teditorManager.onupdate(\"file-delete\");\n\t\teditorManager.emit(\"update\", \"file-delete\");\n\t},\n\tcanShowAds() {\n\t\treturn Boolean(IS_FREE_VERSION && adRewards.canShowAds());\n\t},\n\tasync showInterstitialIfReady() {\n\t\tif (!this.canShowAds()) return false;\n\t\tif (await window.iad?.isLoaded()) {\n\t\t\twindow.iad.show();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n\t/**\n\t * Displays ad on the current page\n\t */\n\tshowAd() {\n\t\tconst { ad } = window;\n\t\tif (this.canShowAds() && innerHeight * devicePixelRatio > 600 && ad) {\n\t\t\tconst $page = tag.getAll(\"wc-page:not(#root)\").pop();\n\t\t\tif ($page) {\n\t\t\t\tad.active = true;\n\t\t\t\tad.show();\n\t\t\t}\n\t\t}\n\t},\n\tasync toInternalUri(uri) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\twindow.resolveLocalFileSystemURL(\n\t\t\t\turi,\n\t\t\t\t(entry) => {\n\t\t\t\t\tresolve(entry.toInternalURL());\n\t\t\t\t},\n\t\t\t\treject,\n\t\t\t);\n\t\t});\n\t},\n\tpromisify(func, ...args) {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tfunc(...args, resolve, reject);\n\t\t});\n\t},\n\tasync checkAPIStatus() {\n\t\ttry {\n\t\t\tconst { status } = await ajax.get(Url.join(constants.API_BASE, \"status\"));\n\t\t\treturn status === \"ok\";\n\t\t} catch (error) {\n\t\t\twindow.log(\"error\", error);\n\t\t\treturn false;\n\t\t}\n\t},\n\tfixFilename(name) {\n\t\tif (!name) return name;\n\t\treturn name.replace(/(\\r\\n)+|\\r+|\\n+|\\t+/g, \"\").trim();\n\t},\n\t/**\n\t * Creates a debounced function that delays invoking the input function until after 'wait' milliseconds have elapsed\n\t * since the last time the debounced function was invoked. Useful for implementing behavior that should only happen\n\t * after the input is complete.\n\t *\n\t * @param {Function} func - The function to debounce.\n\t * @param {number} wait - The number of milliseconds to delay.\n\t * @returns {Function} The new debounced function.\n\t * @example\n\t * window.addEventListener('resize', debounce(myFunction, 200));\n\t */\n\tdebounce(func, wait) {\n\t\tlet timeout;\n\t\treturn function debounced(...args) {\n\t\t\tconst later = () => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tfunc.apply(this, args);\n\t\t\t};\n\t\t\tclearTimeout(timeout);\n\t\t\ttimeout = setTimeout(later, wait);\n\t\t};\n\t},\n\tdefineDeprecatedProperty(obj, name, getter, setter) {\n\t\tObject.defineProperty(obj, name, {\n\t\t\tget: function () {\n\t\t\t\tconsole.warn(`Property '${name}' is deprecated.`);\n\t\t\t\treturn getter.call(this);\n\t\t\t},\n\t\t\tset: function (value) {\n\t\t\t\tconsole.warn(`Property '${name}' is deprecated.`);\n\t\t\t\tsetter.call(this, value);\n\t\t\t},\n\t\t});\n\t},\n\tparseHTML(html) {\n\t\tconst parser = new DOMParser();\n\t\tconst doc = parser.parseFromString(html, \"text/html\");\n\t\tconst children = doc.body.children;\n\t\tif (children.length === 1) {\n\t\t\treturn children[0];\n\t\t}\n\t\treturn Array.from(children);\n\t},\n\tasync createFileStructure(uri, pathString, isFile = true) {\n\t\tconst parts = pathString.split(\"/\").filter(Boolean);\n\t\tlet currentUri = uri;\n\n\t\t// Determine if it's a special case URI\n\t\tconst isSpecialCase = currentUri.includes(\"::\");\n\t\tlet baseFolder;\n\t\tconst isExternalStorageUri = currentUri.includes(\n\t\t\t\"com.android.externalstorage.documents\",\n\t\t);\n\t\tconst isTermuxUri = currentUri.includes(\"com.termux.documents\");\n\t\tconst isAcodeTerminalPublicSafUri = currentUri.includes(\n\t\t\t\"com.foxdebug.acode.documents\",\n\t\t);\n\t\tconst [, treeSegment = \"\"] = currentUri.split(\"/tree/\");\n\t\tconst terminalBasePath = isAcodeTerminalPublicSafUri\n\t\t\t? decodeURIComponent(treeSegment.split(\"::\")[0] || \"\")\n\t\t\t: \"\";\n\t\tconst getTargetUri = (baseUri, name, index) => {\n\t\t\tif (\n\t\t\t\t!(isExternalStorageUri || isTermuxUri || isAcodeTerminalPublicSafUri)\n\t\t\t) {\n\t\t\t\treturn Url.join(baseUri, name);\n\t\t\t}\n\n\t\t\tlet fullUri = baseUri;\n\t\t\tif (isExternalStorageUri) {\n\t\t\t\tif (!isSpecialCase && index === 0) {\n\t\t\t\t\tfullUri += `::primary:${baseFolder}/${name}`;\n\t\t\t\t} else {\n\t\t\t\t\tfullUri += `/${name}`;\n\t\t\t\t}\n\t\t\t} else if (isTermuxUri) {\n\t\t\t\tif (!isSpecialCase && index === 0) {\n\t\t\t\t\tfullUri += `::/data/data/com.termux/files/home/${name}`;\n\t\t\t\t} else {\n\t\t\t\t\tfullUri += `/${name}`;\n\t\t\t\t}\n\t\t\t} else if (isAcodeTerminalPublicSafUri) {\n\t\t\t\tif (!isSpecialCase && index === 0) {\n\t\t\t\t\tconst sanitizedBase = terminalBasePath.endsWith(\"/\")\n\t\t\t\t\t\t? `${terminalBasePath}${name}`\n\t\t\t\t\t\t: `${terminalBasePath}/${name}`;\n\t\t\t\t\tfullUri += `::${sanitizedBase}`;\n\t\t\t\t} else {\n\t\t\t\t\tfullUri += `/${name}`;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fullUri;\n\t\t};\n\t\tconst getExpectedType = (isLastPart) =>\n\t\t\tisLastPart && isFile ? \"file\" : \"folder\";\n\t\tconst ensureEntry = async (baseUri, targetUri, name, expectedType) => {\n\t\t\tconst entries = await fsOperation(baseUri).lsDir();\n\t\t\tconst existingEntry = entries.find((entry) => entry.name === name);\n\n\t\t\tif (existingEntry) {\n\t\t\t\tconst actualType = existingEntry.isDirectory ? \"folder\" : \"file\";\n\t\t\t\tif (actualType !== expectedType) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`${name} already exists as a ${actualType}, expected ${expectedType}.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\turl: existingEntry.url || existingEntry.uri || targetUri,\n\t\t\t\t\tcreated: false,\n\t\t\t\t\ttype: expectedType,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst createdUrl =\n\t\t\t\texpectedType === \"file\"\n\t\t\t\t\t? await fsOperation(baseUri).createFile(name)\n\t\t\t\t\t: await fsOperation(baseUri).createDirectory(name);\n\n\t\t\treturn {\n\t\t\t\turl: createdUrl || targetUri,\n\t\t\t\tcreated: true,\n\t\t\t\ttype: expectedType,\n\t\t\t};\n\t\t};\n\t\tlet firstCreatedPath = null;\n\t\tlet firstCreatedType = null;\n\t\tlet firstTargetUri = uri;\n\n\t\tif (isExternalStorageUri) {\n\t\t\tbaseFolder = decodeURIComponent(currentUri.split(\"%3A\")[1].split(\"/\")[0]);\n\t\t}\n\t\tif (parts[0]) {\n\t\t\tfirstTargetUri = getTargetUri(uri, parts[0], 0);\n\t\t}\n\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tconst isLastElement = i === parts.length - 1;\n\t\t\tconst name = parts[i];\n\t\t\tconst targetUri = getTargetUri(currentUri, name, i);\n\t\t\tconst expectedType = getExpectedType(isLastElement);\n\t\t\tconst entry = await ensureEntry(\n\t\t\t\tcurrentUri,\n\t\t\t\ttargetUri,\n\t\t\t\tname,\n\t\t\t\texpectedType,\n\t\t\t);\n\n\t\t\tif (entry.created && firstCreatedPath === null) {\n\t\t\t\tfirstCreatedPath = entry.url;\n\t\t\t\tfirstCreatedType = expectedType;\n\t\t\t}\n\n\t\t\tcurrentUri = entry.url;\n\t\t}\n\n\t\treturn {\n\t\t\turi: firstCreatedPath || firstTargetUri,\n\t\t\tcreated: Boolean(firstCreatedPath),\n\t\t\ttype:\n\t\t\t\tfirstCreatedType || (isFile && parts.length === 1 ? \"file\" : \"folder\"),\n\t\t};\n\t},\n\tformatDownloadCount(downloadCount) {\n\t\tconst units = [\"\", \"K\", \"M\", \"B\", \"T\"];\n\t\tlet index = 0;\n\n\t\twhile (downloadCount >= 1000 && index < units.length - 1) {\n\t\t\tdownloadCount /= 1000;\n\t\t\tindex++;\n\t\t}\n\n\t\tconst countStr =\n\t\t\tdownloadCount < 10 ? downloadCount.toFixed(2) : downloadCount.toFixed(1);\n\t\tconst trimmedCountStr = countStr.replace(/\\.?0+$/, \"\");\n\n\t\treturn `${trimmedCountStr}${units[index]}`;\n\t},\n\tisBinary(file) {\n\t\t// binary file extensions\n\t\tconst binaryExtensions = [\n\t\t\t\"exe\",\n\t\t\t\"dll\",\n\t\t\t\"so\",\n\t\t\t\"dylib\",\n\t\t\t\"bin\",\n\t\t\t\"o\",\n\t\t\t\"apk\",\n\t\t\t\"aab\",\n\t\t\t\"zip\",\n\t\t\t\"rar\",\n\t\t\t\"7z\",\n\t\t\t\"gz\",\n\t\t\t\"tar\",\n\t\t\t\"tgz\",\n\t\t\t\"jpg\",\n\t\t\t\"jpeg\",\n\t\t\t\"png\",\n\t\t\t\"gif\",\n\t\t\t\"bmp\",\n\t\t\t\"ico\",\n\t\t\t\"mp3\",\n\t\t\t\"mp4\",\n\t\t\t\"wav\",\n\t\t\t\"avi\",\n\t\t\t\"mov\",\n\t\t\t\"dds\",\n\t\t\t\"tga\",\n\t\t\t\"swf\",\n\t\t\t\"ttf\",\n\t\t\t\"eot\",\n\t\t\t\"otf\",\n\t\t\t\"woff\",\n\t\t\t\"woff2\",\n\t\t\t\"pdf\",\n\t\t\t\"doc\",\n\t\t\t\"docx\",\n\t\t\t\"xls\",\n\t\t\t\"xlsx\",\n\t\t\t\"class\",\n\t\t\t\"pyc\",\n\t\t\t\"jar\",\n\t\t\t\"war\",\n\t\t];\n\n\t\tconst extension = Url.basename(file)?.split(\".\")?.pop()?.toLowerCase();\n\n\t\tif (extension && binaryExtensions.includes(extension)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t},\n};\n"
  },
  {
    "path": "src/utils/keyboardEvent.js",
    "content": "/**\n * @typedef {object} KeyEvent\n * @property {'keydown' | 'keypress' | 'keyup'} type the type of the event\n * @property {boolean} bubbles whether the event bubbles up through the DOM or not\n * @property {boolean} cancelable whether the event is cancelable or not\n * @property {number} which the key code of the key pressed\n * @property {number} keyCode the key code of the key pressed\n * @property {string} key the key pressed\n * @property {boolean} ctrlKey whether the ctrl key was pressed or not\n * @property {boolean} shiftKey whether the shift key was pressed or not\n * @property {boolean} altKey whether the alt key was pressed or not\n * @property {boolean} metaKey whether the meta key was pressed or not\n */\n\nconst keys = {\n\t// arrow keys\n\t37: \"ArrowLeft\",\n\t38: \"ArrowUp\",\n\t39: \"ArrowRight\",\n\t40: \"ArrowDown\",\n\t// special keys\n\t8: \"Backspace\",\n\t9: \"Tab\",\n\t13: \"Enter\",\n\t16: \"ShiftLeft\",\n\t17: \"ControlLeft\",\n\t18: \"AltLeft\",\n\t19: \"Pause\",\n\t20: \"CapsLock\",\n\t27: \"Escape\",\n\t32: \" \",\n\t33: \"PageUp\",\n\t34: \"PageDown\",\n\t35: \"End\",\n\t36: \"Home\",\n\t45: \"Insert\",\n\t46: \"Delete\",\n};\n\nconst initKeyboardEventType = (function (event) {\n\ttry {\n\t\tevent.initKeyboardEvent(\n\t\t\t\"keyup\", // in DOMString typeArg\n\t\t\tfalse, // in boolean canBubbleArg\n\t\t\tfalse, // in boolean cancelableArg\n\t\t\twindow, // in views::AbstractView viewArg\n\t\t\t\"+\", // [test]in DOMString keyIdentifierArg | webkit event.keyIdentifier | IE9 event.key\n\t\t\t3, // [test]in unsigned long keyLocationArg | webkit event.keyIdentifier | IE9 event.location\n\t\t\ttrue, // [test]in boolean ctrlKeyArg | webkit event.shiftKey | old webkit event.ctrlKey | IE9 event.modifiersList\n\t\t\tfalse, // [test]shift | alt\n\t\t\ttrue, // [test]shift | alt\n\t\t\tfalse, // meta\n\t\t\tfalse, // altGraphKey\n\t\t);\n\n\t\treturn (\n\t\t\t((((event[\"keyIdentifier\"] || event[\"key\"]) === \"+\" &&\n\t\t\t\tevent[\"location\"]) ||\n\t\t\t\tevent[\"keyLocation\"] === 3) &&\n\t\t\t\t(event.ctrlKey\n\t\t\t\t\t? event.altKey\n\t\t\t\t\t\t? // webkit\n\t\t\t\t\t\t\t1\n\t\t\t\t\t\t: 3\n\t\t\t\t\t: event.shiftKey\n\t\t\t\t\t\t? 2\n\t\t\t\t\t\t: // webkit\n\t\t\t\t\t\t\t4)) || // IE9\n\t\t\t9\n\t\t); // FireFox|w3c\n\t} catch (error) {\n\t\tinitKeyboardEventType = 0;\n\t}\n})(document.createEvent(\"KeyboardEvent\"));\n\nconst keyboardEventPropertiesDictionary = {\n\tchar: \"\",\n\tkey: \"\",\n\tlocation: 0,\n\tctrlKey: false,\n\tshiftKey: false,\n\taltKey: false,\n\tmetaKey: false,\n\trepeat: false,\n\tlocale: \"\",\n\n\tdetail: 0,\n\tbubbles: false,\n\tcancelable: false,\n\n\t//legacy properties\n\tkeyCode: 0,\n\tcharCode: 0,\n\twhich: 0,\n};\n\nconst own = Function.prototype.call.bind(Object.prototype.hasOwnProperty);\n\nconst ObjectDefineProperty =\n\tObject.defineProperty ||\n\tfunction (obj, prop, val) {\n\t\tif (\"value\" in val) {\n\t\t\tobj[prop] = val[\"value\"];\n\t\t}\n\t};\n\n/**\n * Creates a keyboard event\n * @param {'keydown' | 'keyup'} type type of the event\n * @param {KeyEvent} dict\n * @returns\n */\nexport default function KeyboardEvent(type, dict) {\n\tlet event;\n\n\tif (initKeyboardEventType) {\n\t\tevent = document.createEvent(\"KeyboardEvent\");\n\t} else {\n\t\tevent = document.createEvent(\"Event\");\n\t}\n\n\tlet propName;\n\tlet localDict = {};\n\n\tif (!dict.key && (dict.keyCode || dict.which)) {\n\t\tlet key = keys[dict.keyCode || dict.which];\n\t\tif (!key) key = String.fromCharCode(dict.keyCode || dict.which);\n\t\tdict.key = key;\n\t} else if (dict.key && !dict.which && !dict.keyCode) {\n\t\tlet keyCode = Object.keys(keys).find((key) => keys[key] === dict.key);\n\t\tif (!keyCode) keyCode = dict.key.charCodeAt(0);\n\t\tdict.keyCode = keyCode;\n\t\tdict.which = keyCode;\n\t}\n\n\tfor (propName in keyboardEventPropertiesDictionary)\n\t\tif (own(keyboardEventPropertiesDictionary, propName)) {\n\t\t\tlocalDict[propName] = ((own(dict, propName) && dict) ||\n\t\t\t\tkeyboardEventPropertiesDictionary)[propName];\n\t\t}\n\n\tconst ctrlKey = localDict[\"ctrlKey\"];\n\tconst shiftKey = localDict[\"shiftKey\"];\n\tconst altKey = localDict[\"altKey\"];\n\tconst metaKey = localDict[\"metaKey\"];\n\tconst altGraphKey = localDict[\"altGraphKey\"];\n\n\tconst modifiersListArg =\n\t\tinitKeyboardEventType > 3\n\t\t\t? (\n\t\t\t\t\t(ctrlKey ? \"Control\" : \"\") +\n\t\t\t\t\t(shiftKey ? \" Shift\" : \"\") +\n\t\t\t\t\t(altKey ? \" Alt\" : \"\") +\n\t\t\t\t\t(metaKey ? \" Meta\" : \"\") +\n\t\t\t\t\t(altGraphKey ? \" AltGraph\" : \"\")\n\t\t\t\t).trim()\n\t\t\t: null;\n\n\tconst key = localDict[\"key\"] + \"\";\n\tconst char = localDict[\"char\"] + \"\";\n\tconst location = localDict[\"location\"];\n\tconst keyCode =\n\t\tlocalDict[\"keyCode\"] ||\n\t\t(localDict[\"keyCode\"] = (key && key.charCodeAt(0)) || 0);\n\tconst charCode =\n\t\tlocalDict[\"charCode\"] ||\n\t\t(localDict[\"charCode\"] = (char && char.charCodeAt(0)) || 0);\n\tconst bubbles = localDict[\"bubbles\"];\n\tconst cancelable = localDict[\"cancelable\"];\n\tconst repeat = localDict[\"repeat\"];\n\tconst locale = localDict[\"locale\"];\n\tconst view = window;\n\n\tlocalDict[\"which\"] || (localDict[\"which\"] = localDict[\"keyCode\"]);\n\n\tif (\"initKeyEvent\" in event) {\n\t\t//FF\n\t\t//https://developer.mozilla.org/en/DOM/event.initKeyEvent\n\t\tevent.initKeyEvent(\n\t\t\ttype,\n\t\t\tbubbles,\n\t\t\tcancelable,\n\t\t\tview,\n\t\t\tctrlKey,\n\t\t\taltKey,\n\t\t\tshiftKey,\n\t\t\tmetaKey,\n\t\t\tkeyCode,\n\t\t\tcharCode,\n\t\t);\n\t} else if (initKeyboardEventType && \"initKeyboardEvent\" in event) {\n\t\t//https://developer.mozilla.org/en/DOM/KeyboardEvent#initKeyboardEvent()\n\t\tif (initKeyboardEventType === 1) {\n\t\t\t// webkit\n\t\t\t//http://stackoverflow.com/a/8490774/1437207\n\t\t\t//https://bugs.webkit.org/show_bug.cgi?id=13368\n\t\t\tevent.initKeyboardEvent(\n\t\t\t\ttype,\n\t\t\t\tbubbles,\n\t\t\t\tcancelable,\n\t\t\t\tview,\n\t\t\t\tkey,\n\t\t\t\tlocation,\n\t\t\t\tctrlKey,\n\t\t\t\tshiftKey,\n\t\t\t\taltKey,\n\t\t\t\tmetaKey,\n\t\t\t\taltGraphKey,\n\t\t\t);\n\t\t} else if (initKeyboardEventType === 2) {\n\t\t\t// old webkit\n\t\t\t//http://code.google.com/p/chromium/issues/detail?id=52408\n\t\t\tevent.initKeyboardEvent(\n\t\t\t\ttype,\n\t\t\t\tbubbles,\n\t\t\t\tcancelable,\n\t\t\t\tview,\n\t\t\t\tctrlKey,\n\t\t\t\taltKey,\n\t\t\t\tshiftKey,\n\t\t\t\tmetaKey,\n\t\t\t\tkeyCode,\n\t\t\t\tcharCode,\n\t\t\t);\n\t\t} else if (initKeyboardEventType === 3) {\n\t\t\t// webkit\n\t\t\tevent.initKeyboardEvent(\n\t\t\t\ttype,\n\t\t\t\tbubbles,\n\t\t\t\tcancelable,\n\t\t\t\tview,\n\t\t\t\tkey,\n\t\t\t\tlocation,\n\t\t\t\tctrlKey,\n\t\t\t\taltKey,\n\t\t\t\tshiftKey,\n\t\t\t\tmetaKey,\n\t\t\t\taltGraphKey,\n\t\t\t);\n\t\t} else if (initKeyboardEventType === 4) {\n\t\t\t// IE9\n\t\t\t//http://msdn.microsoft.com/en-us/library/ie/ff975297(v=vs.85).aspx\n\t\t\tevent.initKeyboardEvent(\n\t\t\t\ttype,\n\t\t\t\tbubbles,\n\t\t\t\tcancelable,\n\t\t\t\tview,\n\t\t\t\tkey,\n\t\t\t\tlocation,\n\t\t\t\tmodifiersListArg,\n\t\t\t\trepeat,\n\t\t\t\tlocale,\n\t\t\t);\n\t\t} else {\n\t\t\t// FireFox|w3c\n\t\t\t//http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent-initKeyboardEvent\n\t\t\t//https://developer.mozilla.org/en/DOM/KeyboardEvent#initKeyboardEvent()\n\t\t\tevent.initKeyboardEvent(\n\t\t\t\ttype,\n\t\t\t\tbubbles,\n\t\t\t\tcancelable,\n\t\t\t\tview,\n\t\t\t\tchar,\n\t\t\t\tkey,\n\t\t\t\tlocation,\n\t\t\t\tmodifiersListArg,\n\t\t\t\trepeat,\n\t\t\t\tlocale,\n\t\t\t);\n\t\t}\n\t} else {\n\t\tevent.initEvent(type, bubbles, cancelable);\n\t}\n\n\tfor (propName in keyboardEventPropertiesDictionary)\n\t\tif (own(keyboardEventPropertiesDictionary, propName)) {\n\t\t\tif (event[propName] !== localDict[propName]) {\n\t\t\t\ttry {\n\t\t\t\t\tdelete event[propName];\n\t\t\t\t\tObjectDefineProperty(event, propName, {\n\t\t\t\t\t\twritable: true,\n\t\t\t\t\t\tvalue: localDict[propName],\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\t//Some properties is read-only\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\treturn event;\n}\n"
  },
  {
    "path": "src/utils/polyfill.js",
    "content": "export default function loadPolyFill() {\n\tif (!(\"isConnected\" in Node.prototype)) {\n\t\tObject.defineProperty(Node.prototype, \"isConnected\", {\n\t\t\tget() {\n\t\t\t\treturn (\n\t\t\t\t\t!this.ownerDocument ||\n\t\t\t\t\t!(\n\t\t\t\t\t\tthis.ownerDocument.compareDocumentPosition(this) &\n\t\t\t\t\t\tthis.DOCUMENT_POSITION_DISCONNECTED\n\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t},\n\t\t});\n\t}\n\n\tif (!DOMTokenList.prototype.replace) {\n\t\tDOMTokenList.prototype.replace = function (a, b) {\n\t\t\tif (this.contains(a)) {\n\t\t\t\tthis.add(b);\n\t\t\t\tthis.remove(a);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\t}\n\n\tif (!HTMLElement.prototype.append) {\n\t\tHTMLElement.prototype.append = function (...nodes) {\n\t\t\tnodes.map((node) => this.appendChild(node));\n\t\t};\n\t}\n\n\tif (!HTMLElement.prototype.remove) {\n\t\tHTMLElement.prototype.remove = function () {\n\t\t\tthis.parentElement.removeChild(this);\n\t\t};\n\t}\n\n\tif (!HTMLElement.prototype.getParent) {\n\t\tHTMLElement.prototype.getParent = function (queryString) {\n\t\t\tconst $$ = [...document.querySelectorAll(queryString)];\n\t\t\tfor (let $ of $$) if ($.contains(this)) return $;\n\t\t\treturn null;\n\t\t};\n\t}\n\n\tif (!String.prototype.hashCode) {\n\t\tObject.defineProperty(String.prototype, \"hashCode\", {\n\t\t\tvalue: function () {\n\t\t\t\tconst str = this.toString();\n\t\t\t\tconst len = str.length;\n\n\t\t\t\tif (len === 0) return \"0\";\n\n\t\t\t\t// Produces a 48-char hex string (192 bits)\n\t\t\t\tconst FNV_PRIME = 0x01000193;\n\t\t\t\tconst FNV_OFFSET = 0x811c9dc5;\n\n\t\t\t\t// Generate 6 different 32-bit hashes with different seeds/offsets\n\t\t\t\tconst hashes = [];\n\t\t\t\tfor (let pass = 0; pass < 6; pass++) {\n\t\t\t\t\tlet hash = FNV_OFFSET ^ (pass * 0x1234567);\n\n\t\t\t\t\tfor (let i = 0; i < len; i++) {\n\t\t\t\t\t\tconst char = str.charCodeAt(i);\n\t\t\t\t\t\t// XOR with byte and multiply by prime\n\t\t\t\t\t\thash ^= char;\n\t\t\t\t\t\thash = Math.imul(hash, FNV_PRIME);\n\t\t\t\t\t\t// Mix in position and pass for additional entropy\n\t\t\t\t\t\thash ^= (i + pass) & 0xff;\n\t\t\t\t\t\thash = Math.imul(hash, FNV_PRIME);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Additional mixing\n\t\t\t\t\thash ^= len;\n\t\t\t\t\thash = Math.imul(hash, FNV_PRIME);\n\t\t\t\t\thash ^= hash >>> 16;\n\n\t\t\t\t\thashes.push((hash >>> 0).toString(16).padStart(8, \"0\"));\n\t\t\t\t}\n\n\t\t\t\treturn hashes.join(\"\");\n\t\t\t},\n\t\t});\n\t}\n\n\tif (!String.prototype.subtract) {\n\t\tObject.defineProperty(String.prototype, \"subtract\", {\n\t\t\tvalue: function (str) {\n\t\t\t\treturn this.replace(new RegExp(\"^\" + str), \"\");\n\t\t\t},\n\t\t});\n\t}\n\n\tif (!String.prototype.capitalize) {\n\t\tObject.defineProperty(String.prototype, \"capitalize\", {\n\t\t\tvalue: function (index) {\n\t\t\t\tif (typeof index === \"number\" && index >= 0) {\n\t\t\t\t\tconst strs = [\n\t\t\t\t\t\tthis.slice(0, index),\n\t\t\t\t\t\tthis.slice(index, index + 1),\n\t\t\t\t\t\tthis.slice(index + 1),\n\t\t\t\t\t];\n\t\t\t\t\treturn strs[0] + (strs[1] ? strs[1].toUpperCase() : \"\") + strs[2];\n\t\t\t\t} else {\n\t\t\t\t\tlet strs = this.split(\" \");\n\t\t\t\t\tstrs = strs.map((str) => {\n\t\t\t\t\t\tif (str.length > 0) return str[0].toUpperCase() + str.slice(1);\n\t\t\t\t\t\treturn \"\";\n\t\t\t\t\t});\n\t\t\t\t\treturn strs.join(\" \");\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "src/utils/taskManager.js",
    "content": "export default class TaskManager {\n\t/**\n\t * @typedef {'linear'|'parallel'} TaskManagerMode\n\t */\n\n\t/**\n\t * @type {Array<()=>Promise>}\n\t */\n\t#queue = [];\n\t/**\n\t * @type {TaskManagerMode}\n\t */\n\t#mode = \"linear\";\n\t/**\n\t * @type {boolean}\n\t */\n\t#busy = false;\n\t/**\n\t * @type {TaskCallback[]}\n\t */\n\t#listeners = [];\n\t#count = 0;\n\n\t/**\n\t * Create new TaskManager\n\t * @param {TaskManagerMode} mode\n\t */\n\tconstructor(mode) {\n\t\tthis.#mode = mode;\n\n\t\tthis.queueTask = this.queueTask.bind(this);\n\t}\n\n\t/**\n\t * Add task to queue\n\t * @param {()=>Promise} task\n\t */\n\tasync queueTask(task) {\n\t\tthis.#queue.push(task);\n\t\tthis.#execNext();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst listener = (t, result, error) => {\n\t\t\t\tif (t !== task) return;\n\n\t\t\t\tthis.#listeners = this.#listeners.filter((l) => l !== listener);\n\n\t\t\t\tif (error) reject(error);\n\t\t\t\telse resolve(result);\n\t\t\t};\n\n\t\t\tthis.#listeners.push(listener);\n\t\t});\n\t}\n\n\tasync #execNext() {\n\t\tif (this.#mode === \"linear\" && this.#busy) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst task = this.#queue.shift();\n\t\tif (!task) return;\n\n\t\tlet result;\n\t\tlet error;\n\n\t\ttry {\n\t\t\tthis.#busy = true;\n\t\t\tconst id = this.#count++;\n\t\t\tresult = await task(id);\n\t\t} catch (err) {\n\t\t\terror = err;\n\t\t} finally {\n\t\t\tthis.#busy = false;\n\t\t}\n\n\t\tthis.#listeners.forEach((l) => l(task, result, error));\n\t\tif (this.#mode === \"linear\") this.#execNext();\n\t}\n}\n"
  },
  {
    "path": "src/views/console.hbs",
    "content": "<html lang='en'>\r\n\r\n  <head>\r\n    <meta charset='UTF-8' />\r\n    <meta name='viewport' content='width=device-width, initial-scale=1.0' />\r\n    <meta http-equiv='X-UA-Compatible' content='ie=edge' />\r\n    <link rel=\"shortcut icon\" href=\"data:image/x-icon;base64,AAABAAEAGBgAAAEAIACICQAAFgAAACgAAAAYAAAAMAAAAAEAIAAAAAAAAAkAAMMOAADDDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAGwAAACuAAAA2AAAAO8AAADuAAAA5AAAAK8AAAByAAAAGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAACaAAAA+QAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA+QAAAKAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAAOwAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADvAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAA+wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAG8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAAAD9Ly8v/3Nzc/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/9zc3P/Li4u/wAAAP4AAABaAAAAAAAAAAAAAAAAAAAAFQAAAOYdHR3/9/f3////////////////////////////////////////////////////////////////////////////9/f3/xwcHP8AAADyAAAAGwAAAAAAAAAAAAAAkAAAAP9KSkr//////35+fv8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/8zMzP/MzMz/zMzM/9+fn7//////0pKSv8AAAD/AAAApgAAAAAAAAARAAAA8wAAAP9LS0v//////15eXv8ICAj/IiIi/yIiIv8EBAT/AAAA/wAAAP8GBgb/IiIi/yIiIv8iIiL/ISEh/wAAAP9eXl7//////0tLS/8AAAD/AAAA/QAAABwAAABmAAAA/wAAAP9LS0v//////15eXv8DAwP/pKSk//////+rq6v/BAQE/wAAAP8sLCz/////////////////+fn5/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAAHYAAAChAAAA/wAAAP9LS0v//////15eXv8AAAD/BAQE/6mpqf//////qamp/wQEBP8XFxf/iIiI/4iIiP+IiIj/hYWF/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAALcAAADUAAAA/wAAAP9LS0v//////15eXv8AAAD/AAAA/wQEBP+pqan//////5+fn/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAAOAAAADiAAAA/wAAAP9LS0v//////15eXv8AAAD/AAAA/wAAAP9dXV3//////9LS0v8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAAPsAAADpAAAA/wAAAP9LS0v//////15eXv8AAAD/AAAA/1ZWVv/7+/v/4eHh/yIiIv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAAPQAAADOAAAA/wAAAP9LS0v//////15eXv8AAAD/VlZW//v7+//h4eH/IiIi/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAAOcAAACkAAAA/wAAAP9LS0v//////15eXv8MDAz/dHR0/3d3d/8gICD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAALIAAABiAAAA/wAAAP9LS0v//////15eXv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9eXl7//////0tLS/8AAAD/AAAA/wAAAHYAAAAOAAAA9QAAAP9LS0v//////+np6f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/p6en//////0tLS/8AAAD/AAAA+gAAAB0AAAAAAAAAkwAAAP9KSkr//////////////////////////////////////////////////////////////////////////////////////0pKSv8AAAD/AAAAoQAAAAAAAAAAAAAADgAAAOYeHh7/9/f3////////////////////////////////////////////////////////////////////////////9/f3/x0dHf8AAADvAAAAHwAAAAAAAAAAAAAAAAAAAEgAAAD6MDAw/3R0dP93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/93d3f/d3d3/3d3d/9zc3P/Ly8v/wAAAP8AAABSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbAAAA/QAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/QAAAGsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQwAAAOUAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADvAAAAVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAACRAAAA9AAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA+gAAAJ8AAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgAAAGkAAAClAAAA2AAAAOsAAADuAAAA1wAAAKwAAABtAAAAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+AH8A+AAfAPAADwDgAAcAwAADAIAAAQCAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAQCAAAEAwAADAOAABwDwAA8A+AAfAP4AfwA=\" type=\"image/x-icon\">\r\n    <title>Console</title>\r\n    <script>\r\n      sessionStorage.setItem('__mode', 'console');\r\n    </script>\r\n    <script src='{{CONSOLE_SCRIPT}}' crossorigin='anonymous'></script>\r\n    <script src='{{EXECUTING_SCRIPT}}' crossorigin='anonymous'></script>\r\n  </head>\r\n\r\n  <body style='background-color: {{BACKGROUND_COLOR_VALUE}};'></body>\r\n\r\n</html>"
  },
  {
    "path": "src/views/file-info.hbs",
    "content": "<div id=\"file-info\">\r\n    <div class=\"file-header\">\r\n        <div class=\"file-name\">\r\n            <span class=\"name-part\">{{name}}</span>\r\n            <span class=\"file-extension\">{{extension}}</span>\r\n        </div>\r\n    </div>\r\n\r\n    <div class=\"info-grid\">\r\n      <div class=\"info-item\">\r\n        <span class=\"info-label\">{{lang.size}}</span>\r\n        <span class=\"info-value\">{{length}}</span>\r\n      </div>\r\n      <div class=\"info-item\">\r\n        <span class=\"info-label\">{{lang.type}}</span>\r\n        <span class=\"info-value\">{{type}}</span>\r\n      </div>\r\n      <div class=\"info-item\">\r\n        <span class=\"info-label\">{{lang.last modified}}</span>\r\n        <span class=\"info-value\">{{lastModified}}</span>\r\n      </div>\r\n      {{#isEditor}}\r\n      <div class=\"info-item\">\r\n        <span class=\"info-label\">{{lang.line count}}</span>\r\n        <span class=\"info-value\">{{lineCount}}</span>\r\n      </div>\r\n      <div class=\"info-item\">\r\n        <span class=\"info-label\">{{lang.word count}}</span>\r\n        <span class=\"info-value\">{{wordCount}}</span>\r\n      </div>\r\n      {{/isEditor}}\r\n    </div>\r\n\r\n    <div class=\"path-section\">\r\n      <div class=\"info-item\">\r\n        <span class=\"info-label\">{{lang.path}}</span>\r\n        <span class=\"info-value path-value\" action=\"copy\">{{showUri}}</span>\r\n      </div>\r\n    </div>\r\n</div>\r\n"
  },
  {
    "path": "src/views/file-menu.hbs",
    "content": "<li action=\"file-info\" {{^file_on_disk}}class=\"disabled\" {{/file_on_disk}}>\n  <span class=\"text\">{{file properties}}</span>\n  <span class=\"icon info\"></span>\n</li>\n<hr>\n<li action=\"rename\">\n  <span class=\"text\">{{rename}}</span>\n</li>\n{{#is_editor}}\n<li action=\"syntax\">\n  <div class=\"text\">\n    <span>{{syntax highlighting}}</span>\n    <span class=\"value\">{{file_mode}}</span>\n  </div>\n</li>\n<li action=\"encoding\" {{^file_on_disk}}class=\"disabled\" {{/file_on_disk}}>\n  <div class=\"text\">\n    <span>{{encoding}}</span>\n    <span class=\"value\">{{file_encoding}}</span>\n  </div>\n</li>\n<li action=\"eol\">\n  <div class=\"text\">\n    <span>{{new line mode}}</span>\n    <span class=\"value\">{{file_eol}}</span>\n  </div>\n</li>\n{{/is_editor}}\n\n{{#is_editor}}\n<li action=\"read-only\">\n  <div class=\"text\">\n    <span>{{read only}}</span>\n  </div>\n  <label class=\"input-checkbox\">\n    <input type=\"checkbox\" {{#file_read_only}}checked{{/file_read_only}}>\n    <span class=\"box\"></span>\n  </label>\n</li>\n<li action=\"format\">\n  <div class=\"text\">\n    <span>{{format}}</span>\n  </div>\n</li>\n{{#has_lsp_servers}}\n<li action=\"lsp-info\">\n  <span class=\"text\">LSP Info</span>\n  <span class=\"icon zap\"></span>\n</li>\n{{/has_lsp_servers}}\n{{/is_editor}}\n<hr>\n{{#file_on_disk}}\n<li action=\"share\">\n  <span class=\"text\">{{share}}</span>\n  <span class=\"icon share\"></span>\n</li>\n<li action=\"open-with\">\n  <span class=\"text\">{{open with}}</span>\n  <span class=\"icon open_in_browser\"></span>\n</li>\n<li action=\"edit-with\">\n  <span class=\"text\">{{edit with}}</span>\n  <span class=\"icon edit\"></span>\n</li>\n<li action=\"pin-file-shortcut\">\n  <span class=\"text\">{{add to home screen}}</span>\n  <span class=\"icon home\"></span>\n</li>\n{{/file_on_disk}}\n\n<li action=\"toggle-pin-tab\">\n  <span class=\"text\">{{toggle_pin_tab_text}}</span>\n  <span class=\"{{toggle_pin_tab_icon}}\"></span>\n</li>\n\n{{#is_editor}}\n<hr>\n<li action=\"find\">\n  <span class=\"text\">{{search}}</span>\n  <span class=\"icon search\"></span>\n</li>\n<li action=\"goto\">\n  <span class=\"text\">{{goto}}</span>\n  <span class=\"icon subdirectory_arrow_left\"></span>\n</li>\n<hr>\n{{^file_read_only}}\n<li action=\"insert-color\">\n  <span class=\"text\">{{insert color}}</span>\n  <span class=\"icon color_lenspalette\"></span>\n</li>\n<hr>\n{{#copy_text}}\n<li action=\"cut\">\n  <div class=\"text\">\n    <span>{{cut}}</span>\n  </div>\n</li>\n{{/copy_text}}\n<li action=\"paste\">\n  <div class=\"text\">\n    <span>{{paste}}</span>\n  </div>\n</li>\n{{/file_read_only}}\n{{#copy_text}}\n<li action=\"copy\">\n  <div class=\"text\">\n    <span>{{copy}}</span>\n  </div>\n</li>\n{{/copy_text}}\n<li action=\"select-all\">\n  <div class=\"text\">\n    <span>{{select all}}</span>\n  </div>\n</li>\n{{/is_editor}}\n"
  },
  {
    "path": "src/views/markdown.hbs",
    "content": "<html lang='en'>\r\n\r\n  <head>\r\n    <meta charset='UTF-8' />\r\n    <meta name='viewport' content='width=device-width, initial-scale=1.0' />\r\n    <meta http-equiv='X-UA-Compatible' content='ie=edge' />\r\n    <link rel='stylesheet' href='{{MARKDOWN_STYLE}}' />\r\n    <title>{{filename}}</title>\r\n  </head>\r\n\r\n  <body>{{{html}}}</body>\r\n\r\n</html>"
  },
  {
    "path": "src/views/menu.hbs",
    "content": "<li action=\"new-file\">\r\n  <span class=\"text\">{{new file}}</span>\r\n  <span class=\"icon file-control document-add\"></span>\r\n</li>\r\n<li action=\"save\">\r\n  <span class=\"text\">{{save}}</span>\r\n  <span class=\"icon save\"></span>\r\n</li>\r\n<li action=\"save-as\">\r\n  <span class=\"text\">{{save as}}</span>\r\n  <span class=\"icon save\" style=\"filter: brightness(85%)\"></span>\r\n</li>\r\n<li action=\"files\">\r\n  <span class=\"text\">{{files}}</span>\r\n  <span class=\"icon folder\"></span></span>\r\n</li>\r\n<li action=\"close-current-tab\">\r\n  <span class=\"text\">{{close file}}</span>\r\n  <span class=\"icon clearclose\"></span></span>\r\n</li>\r\n<li action=\"recent\">\r\n  <span class=\"text\">{{open recent}}</span>\r\n  <span class=\"icon historyrestore\"></span>\r\n</li>\r\n<li action=\"find-file\">\r\n  <span class=\"text\">{{find file}}</span>\r\n  <span class=\"icon search\"></span>\r\n</li>\r\n<li action=\"console\">\r\n  <span class=\"text\">{{console}}</span>\r\n  <span class=\"icon code\"></span>\r\n</li>\r\n<li action=\"new-terminal\">\r\n  <span class=\"text\">{{terminal}}</span>\r\n  <span class=\"icon terminal\"></span>\r\n</li>\r\n<hr>\r\n<li action=\"open\" value=\"settings\">\r\n  <span class=\"text\">{{settings}}</span>\r\n  <span class=\"icon settings\"></span>\r\n</li>\r\n<li action=\"open\" value=\"help\">\r\n  <span class=\"text\">{{help}}</span>\r\n  <span class=\"icon help\"></span>\r\n</li>\r\n<hr>\r\n<li action=\"exit\">\r\n  <span class=\"text\">{{exit}}</span>\r\n  <span class=\"icon logout\"></span>\r\n</li>\r\n"
  },
  {
    "path": "src/views/rating.hbs",
    "content": "<style>\r\n  span.rating { font-size: 2.4em; height: 48px; width: 48px; } div.rating {\r\n  display: flex; align-items: center; justify-content: center; }\r\n</style>\r\n<div class='rating'>\r\n  <span class='icon star_outline rating' value='1'></span>\r\n  <span class='icon star_outline rating' value='2'></span>\r\n  <span class='icon star_outline rating' value='3'></span>\r\n  <span class='icon star_outline rating' value='4'></span>\r\n  <span class='icon star_outline rating' value='5'></span>\r\n</div>"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"declaration\": false,\n    \"allowJs\": true,\n    \"checkJs\": false,\n    \"paths\": {\n      \"*\": [\"./src/*\"]\n    },\n    \"jsx\": \"preserve\",\n    \"types\": [\"node\"]\n  },\n  \"include\": [\"src/**/*\"],\n  \"exclude\": [\"node_modules\", \"platforms\", \"www\", \"www/js/ace/**/*\"]\n}\n"
  },
  {
    "path": "utils/code-editor-icon.icomoon.json",
    "content": "{\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]},\"formats\":[{\"order\":0,\"mOpen\":false,\"item\":{\"tag\":\"ItemFont\",\"args\":[{\"addPalettes\":false,\"classPerGlyph\":true,\"fontFamily\":{\"value\":\"code-editor-icon\"},\"prefix\":{\"value\":\"icon-\"},\"scss\":true}]}}],\"glyphs\":[{\"extras\":{\"name\":\"text-search\",\"codePoint\":61482},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]},\"xmlns:xlink\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/1999/xlink\"]}},\"children\":[{\"tag\":\"Text\",\"args\":[\"\\n  \"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[21,6],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,6]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n  \"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[10,12],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,12]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n  \"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[10,18],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,18]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n  \"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[17]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[15]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[3]}]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n  \"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[21,19],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19.1,17.1]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n\"]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"wand\",\"codePoint\":61460},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-wand-icon lucide-wand\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,4],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,2]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,16],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,14]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[8,9],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[10,9]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[20,9],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[22,9]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[17.8,11.8],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,13]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,9],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15.01,9]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[17.8,6.2],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,5]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[3,21],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,12]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12.2,6.2],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[11,5]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"wand-sparkles\",\"codePoint\":61459},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-wand-sparkles-icon lucide-wand-sparkles\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[21.64,3.64],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[20.36,2.3600000000000003]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.21,\"ry\":1.21,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[18.64,2.3600000000000003]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[2.36,18.64]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.21,\"ry\":1.21,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[2.36,20.36]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[3.6399999999999997,21.64]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.2,\"ry\":1.2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[5.359999999999999,21.64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[21.64,5.36]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.2,\"ry\":1.2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[21.64,3.6400000000000006]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[14,7],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[17,10]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[5,6],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[5,10]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[19,14],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,18]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[10,2],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[10,4]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[7,8],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,8]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[21,16],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[17,16]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[11,3],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,3]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"link\",\"codePoint\":61458},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-link-icon lucide-link\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[10,13],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[17.54,13.54]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[20.54,10.54]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[13.469999999999999,3.469999999999999]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[11.749999999999998,5.179999999999999]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[14,11],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[6.46,10.46]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[3.46,13.46]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[10.530000000000001,20.53]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[12.240000000000002,18.82]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"brain\",\"codePoint\":61457},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-brain-icon lucide-brain\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,18],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,5]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,13],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.17,\"ry\":4.17,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[12,9]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.17,\"ry\":4.17,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[9,13]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[17.598,6.5],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":3,\"ry\":3,\"phi\":0,\"fA\":true,\"fS\":false,\"end\":[12,5]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":3,\"ry\":3,\"phi\":0,\"fA\":true,\"fS\":false,\"end\":[6.402,6.5]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[17.997,5.125],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[20.523,10.895]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[18,18],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[20,10.536]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[19.967,17.483],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":true,\"fS\":true,\"end\":[12,18]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":true,\"fS\":true,\"end\":[4.033,17.483]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[6,18],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[4,10.536]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[6.003,5.125],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[3.4770000000000003,10.895]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"paperclip\",\"codePoint\":61456},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-paperclip-icon lucide-paperclip\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[16,6],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[7.586,14.586]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[10.415000000000001,17.415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[18.829,8.828999999999999]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4,\"ry\":4,\"phi\":0,\"fA\":true,\"fS\":false,\"end\":[13.172,3.171999999999999]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[4.793000000000001,11.722999999999999]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":6,\"ry\":6,\"phi\":0,\"fA\":true,\"fS\":false,\"end\":[13.278,20.208]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[21.657,11.656999999999998]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"palette\",\"codePoint\":61455},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-palette-icon lucide-palette\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,22],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[12,2]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":10,\"ry\":9,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[22,11]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[17,16]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[14.75,16]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.75,\"ry\":1.75,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[13.35,18.8]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[13.65,19.2]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.75,\"ry\":1.75,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[12.25,22]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,22]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[13.5]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[6.5]}]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[0.5]}]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[17.5]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[10.5]}]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[0.5]}]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[6.5]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[12.5]}]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[0.5]}]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[8.5]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[7.5]}]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[0.5]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"loader\",\"codePoint\":61454},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-loader-icon lucide-loader\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,2],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,6]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[16.2,7.8],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19.099999999999998,4.9]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[18,12],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[22,12]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[16.2,16.2],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19.099999999999998,19.099999999999998]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,18],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,22]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[4.9,19.1],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[7.800000000000001,16.200000000000003]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[2,12],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[6,12]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[4.9,4.9],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[7.800000000000001,7.800000000000001]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"square-terminal\",\"codePoint\":61453},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-square-terminal-icon lucide-square-terminal\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[7,11],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,9]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[7,7]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[11,13],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,13]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"rect\",\"attributes\":{\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[18]}]}]},\"rx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"ry\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[18]}]}]},\"x\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[3]}]}]},\"y\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[3]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"house\",\"codePoint\":61452},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-house-icon lucide-house\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,21],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,13]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[14,12]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[10,12]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[9,13]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,21]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[3,10],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[3.709,8.472]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[10.709,2.4719999999999995]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[13.291,2.4719999999999995]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[20.291,8.472]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[21,10]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[21,19]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[19,21]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[5,21]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[3,19]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,10]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"message-circle\",\"codePoint\":61451},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-message-circle-icon lucide-message-circle\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[2.992,16.342],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[3.086,17.509]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[2.021,20.799]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[3.2569999999999997,21.967]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6.67,20.968999999999998]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[7.769,21.060999999999996]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":10,\"ry\":10,\"phi\":0,\"fA\":true,\"fS\":false,\"end\":[2.992,16.341999999999995]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"user-round\",\"codePoint\":61450},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-user-round-icon lucide-user-round\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[12]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[8]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[5]}]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[20,21],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":8,\"ry\":8,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[4,21]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"funnel\",\"codePoint\":61449},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-funnel-icon lucide-funnel\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[10,20],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[10.553,20.895]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[12.553,21.895]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[14,21]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[14,14]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[14.517,12.659]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[21.74,4.67]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[21,3]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,3]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[2.258,4.67]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9.483,12.658999999999999]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[10,14]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[10,20]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"zap\",\"codePoint\":61440},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-zap-icon lucide-zap\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[4,14],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[3.2199999999999998,12.370000000000001]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[13.120000000000001,2.1700000000000017]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.5,\"ry\":0.5,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[13.98,2.6300000000000017]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[12.06,8.650000000000002]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[13,10]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[20,10]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[20.78,11.629999999999999]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[10.88,21.83]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.5,\"ry\":0.5,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[10.020000000000001,21.369999999999997]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[11.940000000000001,15.349999999999998]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[11,14]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[4,14]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"verified\",\"codePoint\":61441},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"size-6\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Text\",\"args\":[\"\\n  \"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"clip-rule\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"FillRule\",\"args\":[{\"tag\":\"EvenOdd\",\"args\":[]}]}]},\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[8.603,3.799],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[12,2.25]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[13.357,2.25],\"c2\":[14.573,2.85],\"end\":[15.397,3.799]}]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[18.895,5.106]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.491,\"ry\":4.491,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[20.201999999999998,8.603]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[21.75,12]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[20.201,15.397]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.491,\"ry\":4.491,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[18.894000000000002,18.894]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.491,\"ry\":4.491,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[15.397000000000002,20.200999999999997]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[12,21.75]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[8.603,20.201]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[5.1049999999999995,18.895]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.491,\"ry\":4.491,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[3.7979999999999996,15.396999999999998]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[2.25,12]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[2.25,10.643],\"c2\":[2.85,9.427],\"end\":[3.799,8.603]}]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[5.106,5.106]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":4.49,\"ry\":4.49,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[8.603,3.799]}]}]},{\"start\":[15.61,10.186],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.75,\"ry\":0.75,\"phi\":0,\"fA\":true,\"fS\":false,\"end\":[14.389999999999999,9.314]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[11.153999999999998,13.844000000000001]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9.53,12.22]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.75,\"ry\":0.75,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[8.469999999999999,13.280000000000001]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[10.719999999999999,15.530000000000001]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.75,\"ry\":0.75,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[11.86,15.436000000000002]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[15.61,10.186]}]}]}]]}]},\"fill-rule\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"FillRule\",\"args\":[{\"tag\":\"EvenOdd\",\"args\":[]}]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n\"]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"terminal\",\"codePoint\":61442},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[1024]}]}]},\"version\":{\"tag\":\"StringValue\",\"args\":[\"1.1\"]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[1024]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Text\",\"args\":[\"\\n\"]},{\"tag\":\"Text\",\"args\":[\"\\n\"]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[155.435,173.611],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[140.416,178.902],\"c2\":[128,196.86399999999998],\"end\":[128,213.334]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[128,216.747],\"c2\":[129.237,223.318],\"end\":[130.773,227.969]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[133.29,235.606],\"c2\":[144.085,246.956],\"end\":[249.76999999999998,352.854]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366.037,469.334]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[249.76999999999998,585.814]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[144.08499999999998,691.713],\"c2\":[133.28999999999996,703.062],\"end\":[130.77299999999997,710.699]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[125.26899999999996,727.3389999999999],\"c2\":[128.89599999999996,743.382],\"end\":[140.79999999999995,755.286]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[148.42199999999997,762.954],\"c2\":[158.97599999999994,767.6999999999999],\"end\":[170.63899999999995,767.6999999999999]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[177.55899999999994,767.6999999999999],\"c2\":[184.08899999999994,766.0289999999999],\"end\":[189.84699999999995,763.069]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[189.61099999999996,763.179]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[195.02999999999997,760.576],\"c2\":[234.32599999999996,722.304],\"end\":[330.45399999999995,625.963]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[456.96099999999996,499.24299999999994],\"c2\":[463.95799999999997,491.904],\"end\":[466.77399999999994,483.24299999999994]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[468.18299999999994,479.1599999999999],\"c2\":[468.99699999999996,474.4549999999999],\"end\":[468.99699999999996,469.55999999999995]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[468.99699999999996,462.57599999999996],\"c2\":[467.34099999999995,455.97999999999996],\"end\":[464.4,450.14099999999996]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[464.513,450.38899999999995]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[461.90999999999997,444.96999999999997],\"c2\":[423.638,405.717],\"end\":[327.29699999999997,309.54599999999994]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[205.48399999999998,187.94599999999994],\"c2\":[192.98199999999997,175.95699999999994],\"end\":[185.30199999999996,173.43899999999994]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[180.92499999999995,171.84899999999993],\"c2\":[175.87299999999996,170.92999999999995],\"end\":[170.60699999999997,170.92999999999995]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[165.16199999999998,170.92999999999995],\"c2\":[159.94699999999997,171.91199999999995],\"end\":[155.12899999999996,173.70999999999995]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[155.435,173.611]}]}]},{\"start\":[497.664,770.688],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[487.908,774.264],\"c2\":[480.01599999999996,780.983],\"end\":[475.033,789.596]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[474.923,789.802]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[470.87,796.671],\"c2\":[470.187,799.743],\"end\":[470.187,810.666]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[470.187,821.589],\"c2\":[470.87,824.6610000000001],\"end\":[474.923,831.5300000000001]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[477.526,835.9250000000001],\"c2\":[482.176,841.5140000000001],\"end\":[485.291,843.8610000000001]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[498.219,853.7170000000001],\"c2\":[489.984,853.3330000000001],\"end\":[682.582,853.3330000000001]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[881.622,853.3330000000001],\"c2\":[869.718,854.1010000000001],\"end\":[883.2429999999999,840.5760000000001]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[890.9739999999999,832.9570000000001],\"c2\":[895.7639999999999,822.3710000000001],\"end\":[895.7639999999999,810.6670000000001]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[895.7639999999999,798.9630000000002],\"c2\":[890.9739999999999,788.3770000000002],\"end\":[883.2479999999999,780.7630000000001]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[883.2429999999999,780.7580000000002]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[869.718,767.2330000000002],\"c2\":[881.7499999999999,768.0010000000002],\"end\":[681.942,768.1290000000001]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[532.225,768.2140000000002],\"c2\":[503.254,768.6410000000001],\"end\":[497.664,770.688]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]},{\"tag\":\"Text\",\"args\":[\"\\n\"]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"tag\",\"codePoint\":61443},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[16]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":16,\"height\":16}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[16]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1,7.775],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1,2.75]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[1,1.784],\"c2\":[1.784,1],\"end\":[2.75,1]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[7.775,1]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[8.239,1],\"c2\":[8.685,1.184],\"end\":[9.013,1.513]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[15.263,7.763]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.75,\"ry\":1.75,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[15.263,10.237]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[10.237,15.263]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.75,\"ry\":1.75,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[7.763,15.263]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1.513,9.013]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1.752,\"ry\":1.752,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[1,7.775]}]}]},{\"start\":[2.5,7.775],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[2.5,7.841],\"c2\":[2.526,7.905],\"end\":[2.573,7.952]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8.823,14.202]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.25,\"ry\":0.25,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[9.177,14.202]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[14.202,9.177]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.25,\"ry\":0.25,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[14.202,8.823]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[7.952,2.5730000000000004]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.25,\"ry\":0.25,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[7.775,2.5000000000000004]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[2.75,2.5000000000000004]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":0.25,\"ry\":0.25,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[2.5,2.7500000000000004]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[2.5,7.775]}]}]},{\"start\":[6,5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":true,\"fS\":true,\"end\":[6,7]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[6,5]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"scale\",\"codePoint\":61444},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-scale-icon lucide-scale\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,3],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,21]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[19,8],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[22,16]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[16,16]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,8]}]}]},{\"start\":[19,8],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,7]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[3,7],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[4,7]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":17,\"ry\":17,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[12,5]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":17,\"ry\":17,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[20,7]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[21,7]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[5,8],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,16]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":5,\"ry\":5,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[2,16]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[5,8]}]}]},{\"start\":[5,8],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[5,7]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[7,21],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[17,21]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cart\",\"codePoint\":61445},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-shopping-cart-icon lucide-shopping-cart\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[8]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[21]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[1]}]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"circle\",\"attributes\":{\"cx\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[19]}]}]},\"cy\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[21]}]}]},\"r\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[1]}]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[2.05,2.05],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[4.05,2.05]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6.71,14.469999999999999]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[8.71,16.049999999999997]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[18.490000000000002,16.049999999999997]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[20.44,14.479999999999997]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[22.09,7.049999999999997]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[5.12,7.049999999999997]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"lightbulb\",\"codePoint\":61446},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-lightbulb-icon lucide-lightbulb\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,14],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[15.2,13],\"c2\":[15.7,12.3],\"end\":[16.5,11.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[17.5,10.6],\"c2\":[18,9.3],\"end\":[18,8]}]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":6,\"ry\":6,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[6,8]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[6,9],\"c2\":[6.2,10.2],\"end\":[7.5,11.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"CParams\",\"args\":[{\"c1\":[8.2,12.2],\"c2\":[8.8,13],\"end\":[9,14]}]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[9,18],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,18]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[10,22],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[14,22]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"pin\",\"codePoint\":61447},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-pin-icon lucide-pin\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,17],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,22]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[9,10.76],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[7.89,12.55]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6.109999999999999,13.450000000000001]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[5,15.24]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[5,16]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[6,17]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[18,17]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[19,16]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,15.24]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[17.89,13.45]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[16.11,12.549999999999999]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[15,10.76]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,7]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[16,6]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[16,2]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,2]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[8,6]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[9,7]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,10.76]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"pin-off\",\"codePoint\":61448},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"class\":{\"tag\":\"StringValue\",\"args\":[\"lucide lucide-pin-off-icon lucide-pin-off\"]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"NoPaint\",\"args\":[]}]}]},\"height\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"stroke\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]},\"stroke-linecap\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineCap\",\"args\":[{\"tag\":\"RoundCap\",\"args\":[]}]}]},\"stroke-linejoin\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"StrokeLineJoin\",\"args\":[{\"tag\":\"RoundJoin\",\"args\":[]}]}]},\"stroke-width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[2]}]}]},\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":24,\"height\":24}]}]},\"width\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Length\",\"args\":[{\"tag\":\"Px\",\"args\":[24]}]}]},\"xmlns\":{\"tag\":\"StringValue\",\"args\":[\"http://www.w3.org/2000/svg\"]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[12,17],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[12,22]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[15,9.34],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[15,7]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[16,6]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[16,2]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[7.89,2]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[2,2],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[22,22]}]}]}]]}]}},\"children\":[]}]},{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[9,9],\"endings\":{\"tag\":\"Disconnected\",\"args\":[{}]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,10.76]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":true,\"end\":[7.89,12.55]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6.109999999999999,13.450000000000001]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":2,\"ry\":2,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[5,15.24]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[5,16]}]},{\"tag\":\"EllipticArcTo\",\"args\":[{\"rx\":1,\"ry\":1,\"phi\":0,\"fA\":false,\"fS\":false,\"end\":[6,17]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[17,17]}]}]}]]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-information\",\"codePoint\":59648},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[513,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,904],\"end\":[488.5,875.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,847],\"end\":[480,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,773],\"end\":[496,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,697],\"end\":[541,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,641],\"end\":[607,624]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,608],\"end\":[688,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,608],\"end\":[729.5,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,616],\"end\":[768,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]},{\"start\":[812.5,940.5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,743],\"end\":[812.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,889],\"end\":[563.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,992],\"end\":[688,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[812.5,940.5]}]}]}]},{\"start\":[704,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,800]}]}]},{\"start\":[704,736],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,736]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-information-outline\",\"codePoint\":59649},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[552,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,681],\"end\":[837.5,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,765],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[647,992],\"end\":[611.5,974.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,957],\"end\":[552,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]}]},{\"start\":[531,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,878],\"end\":[517,858]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,838],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,640],\"end\":[712.5,641.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,643],\"end\":[736,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[790,918],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,876],\"end\":[832,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,756],\"end\":[790,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,672],\"end\":[688,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,672],\"end\":[586,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,756],\"end\":[544,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,876],\"end\":[586,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,960],\"end\":[688,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,960],\"end\":[790,918]}]}]}]},{\"start\":[704,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,800]}]}]},{\"start\":[704,736],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,736]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-forbidden\",\"codePoint\":59650},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[825,705],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[577,953]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[600,971],\"end\":[628,981.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[656,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,784],\"end\":[853.5,756]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[843,728],\"end\":[825,705]}]}]}]},{\"start\":[802,682],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,930]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[534,907],\"end\":[523,878]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,849],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[721,640],\"end\":[750,651]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[779,662],\"end\":[802,682]}]}]}]},{\"start\":[513,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,904],\"end\":[488.5,875.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,847],\"end\":[480,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,773],\"end\":[496,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,697],\"end\":[541,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,641],\"end\":[607,624]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,608],\"end\":[688,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,608],\"end\":[729.5,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,616],\"end\":[768,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-forbidden-outline\",\"codePoint\":59651},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,929],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,943],\"end\":[640,951.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[663,960],\"end\":[688,960]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,960],\"end\":[790,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,876],\"end\":[832,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,790],\"end\":[823.5,767.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[815,745],\"end\":[801,726]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,929]}]}]},{\"start\":[736,680.5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[713,672],\"end\":[688,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,672],\"end\":[586,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,756],\"end\":[544,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,842],\"end\":[552.5,864.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,887],\"end\":[575,906]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[778,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[759,689],\"end\":[736,680.5]}]}]}]},{\"start\":[552,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,681],\"end\":[837.5,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,765],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[647,992],\"end\":[611.5,974.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,957],\"end\":[552,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]}]},{\"start\":[531,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,878],\"end\":[517,858]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,838],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,640],\"end\":[712.5,641.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,643],\"end\":[736,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-remove\",\"codePoint\":59652},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[513,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,904],\"end\":[488.5,875.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,847],\"end\":[480,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,773],\"end\":[496,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,697],\"end\":[541,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,641],\"end\":[607,624]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,608],\"end\":[688,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,608],\"end\":[729.5,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,616],\"end\":[768,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]},{\"start\":[812.5,940.5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,743],\"end\":[812.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,889],\"end\":[563.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,992],\"end\":[688,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[812.5,940.5]}]}]}]},{\"start\":[800,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-remove-outline\",\"codePoint\":59653},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[552,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,681],\"end\":[837.5,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,765],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[647,992],\"end\":[611.5,974.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,957],\"end\":[552,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]}]},{\"start\":[531,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,878],\"end\":[517,858]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,838],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,640],\"end\":[712.5,641.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,643],\"end\":[736,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[790,918],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,876],\"end\":[832,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,756],\"end\":[790,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,672],\"end\":[688,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,672],\"end\":[586,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,756],\"end\":[544,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,876],\"end\":[586,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,960],\"end\":[688,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,960],\"end\":[790,918]}]}]}]},{\"start\":[800,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-checked\",\"codePoint\":59654},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[513,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,904],\"end\":[488.5,875.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,847],\"end\":[480,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,773],\"end\":[496,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,697],\"end\":[541,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,641],\"end\":[607,624]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,608],\"end\":[688,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,608],\"end\":[729.5,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,616],\"end\":[768,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]},{\"start\":[688,992],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,992],\"end\":[563.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,889],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,640],\"end\":[812.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,743],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]}]},{\"start\":[672,904],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[795,780]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[772,757]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,857]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,805]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[597,828]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,904]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-checked-outline\",\"codePoint\":59655},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[552,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,681],\"end\":[837.5,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,765],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[647,992],\"end\":[611.5,974.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,957],\"end\":[552,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]}]},{\"start\":[531,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,878],\"end\":[517,858]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,838],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,640],\"end\":[712.5,641.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,643],\"end\":[736,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[790,918],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,876],\"end\":[832,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,756],\"end\":[790,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,672],\"end\":[688,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,672],\"end\":[586,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,756],\"end\":[544,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,876],\"end\":[586,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,960],\"end\":[688,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,960],\"end\":[790,918]}]}]}]},{\"start\":[672,904],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[795,780]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[772,757]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,857]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,805]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[597,828]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,904]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-cancel\",\"codePoint\":59656},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[756,907],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[779,884]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[711,816]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[779,748]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,793]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[597,748]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[665,816]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[597,884]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,907]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,839]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,907]}]}]},{\"start\":[513,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,904],\"end\":[488.5,875.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,847],\"end\":[480,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,773],\"end\":[496,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,697],\"end\":[541,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,641],\"end\":[607,624]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,608],\"end\":[688,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,608],\"end\":[729.5,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,616],\"end\":[768,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]},{\"start\":[688,992],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,992],\"end\":[563.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,889],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,640],\"end\":[812.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,743],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-cancel-outline\",\"codePoint\":59657},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[756,907],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[779,884]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[711,816]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[779,748]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,793]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[597,748]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[665,816]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[597,884]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,907]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,839]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,907]}]}]},{\"start\":[552,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,681],\"end\":[837.5,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,765],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[647,992],\"end\":[611.5,974.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,957],\"end\":[552,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]}]},{\"start\":[531,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,878],\"end\":[517,858]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,838],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,640],\"end\":[712.5,641.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,643],\"end\":[736,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[790,918],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,876],\"end\":[832,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,756],\"end\":[790,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,672],\"end\":[688,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,672],\"end\":[586,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,756],\"end\":[544,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,876],\"end\":[586,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,960],\"end\":[688,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,960],\"end\":[790,918]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-error\",\"codePoint\":59658},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[448,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[656,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,766]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]},{\"start\":[448,992],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,992]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[656,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,992]}]}]},{\"start\":[672,768],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,768]}]}]},{\"start\":[672,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,896]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-error-outline\",\"codePoint\":59659},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,830],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,992]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,992]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[486,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,830]}]}]},{\"start\":[505,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[656,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,775]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[505,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[504,960],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[808,960]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[656,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[504,960]}]}]},{\"start\":[672,768],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,768]}]}]},{\"start\":[672,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,896]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-locked\",\"codePoint\":59660},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,584]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,580],\"end\":[745,578]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[733,576],\"end\":[720,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[660,576],\"end\":[618,618.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,661],\"end\":[576,720]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,739]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[562,744],\"end\":[553,756]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,768],\"end\":[544,784]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[608,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]}]},{\"start\":[576,64],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]}]},{\"start\":[608,720],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,674],\"end\":[641,641]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,608],\"end\":[720,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[766,608],\"end\":[799,641]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,674],\"end\":[832,720]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,768],\"end\":[854.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,787],\"end\":[864,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,973],\"end\":[854.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,992],\"end\":[832,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,992],\"end\":[585.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,973],\"end\":[576,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,787],\"end\":[585.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,768],\"end\":[608,768]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,720]}]}]},{\"start\":[640,768],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,720]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,687],\"end\":[776.5,663.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,640],\"end\":[720,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[687,640],\"end\":[663.5,663.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,687],\"end\":[640,720]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,768]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-locked-outline\",\"codePoint\":59661},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[576,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,619]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[796,632],\"end\":[814,659.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,687],\"end\":[832,720]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,768],\"end\":[854.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,787],\"end\":[864,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,973],\"end\":[854.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,992],\"end\":[832,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,992],\"end\":[585.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,973],\"end\":[576,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,928]}]}]},{\"start\":[576,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,787],\"end\":[585.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,768],\"end\":[608,768]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,720]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,674],\"end\":[641,641]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,608],\"end\":[720,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[724,608],\"end\":[728,608.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,609],\"end\":[736,609]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,896]}]}]},{\"start\":[800,768],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,720]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,687],\"end\":[776.5,663.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,640],\"end\":[720,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[687,640],\"end\":[663.5,663.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,687],\"end\":[640,720]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,768]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[624,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,800],\"end\":[612.5,804.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,809],\"end\":[608,816]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,944]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,951],\"end\":[613,955.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,960],\"end\":[624,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[816,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[823,960],\"end\":[827.5,955.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,951],\"end\":[832,944]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,816]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,809],\"end\":[827,804.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[822,800],\"end\":[816,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-unlocked\",\"codePoint\":59662},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,552]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,548],\"end\":[745,546]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[733,544],\"end\":[720,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[660,544],\"end\":[618,586]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,628],\"end\":[576,688]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,744]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[562,753],\"end\":[553,767.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,782],\"end\":[544,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[608,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]}]},{\"start\":[576,64],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]}]},{\"start\":[816,768],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,688]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,655],\"end\":[663.5,631.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[687,608],\"end\":[720,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,608],\"end\":[776.5,631.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,655],\"end\":[800,688]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,688]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,642],\"end\":[799,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[766,576],\"end\":[720,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,576],\"end\":[641,608.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,641],\"end\":[608,688]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,768],\"end\":[585.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,787],\"end\":[576,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,973],\"end\":[585.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,992],\"end\":[608,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,992],\"end\":[854.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,973],\"end\":[864,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,787],\"end\":[854.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,768],\"end\":[832,768]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[816,768]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-unlocked-outline\",\"codePoint\":59663},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,587],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,587]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,587]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[796,600],\"end\":[814,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,655],\"end\":[832,688]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,688]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,655],\"end\":[776.5,631.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,608],\"end\":[720,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[687,608],\"end\":[663.5,631.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,655],\"end\":[640,688]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,768],\"end\":[854.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,787],\"end\":[864,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,973],\"end\":[854.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[845,992],\"end\":[832,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,992],\"end\":[585.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,973],\"end\":[576,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,587]}]}]},{\"start\":[576,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,787],\"end\":[585.5,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,768],\"end\":[608,768]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,688]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,642],\"end\":[641,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,576],\"end\":[720,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[724,576],\"end\":[728,576.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,577],\"end\":[736,577]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[624,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,800],\"end\":[612.5,804.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,809],\"end\":[608,816]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,944]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,951],\"end\":[613,955.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,960],\"end\":[624,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[816,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[823,960],\"end\":[827.5,955.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,951],\"end\":[832,944]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,816]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,809],\"end\":[827,804.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[822,800],\"end\":[816,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-search\",\"codePoint\":59664},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[725,876],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[748,853]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[577,682]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[591,663],\"end\":[599.5,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,617],\"end\":[608,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,532],\"end\":[566,490]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[524,448],\"end\":[464,448]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,448],\"end\":[362,490]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,532],\"end\":[320,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,652],\"end\":[362,694]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,736],\"end\":[464,736]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,736],\"end\":[512.5,727.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[535,719],\"end\":[554,705]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[725,876]}]}]},{\"start\":[608,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[627,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,352],\"end\":[672,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[768,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[288,96]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,96]}]}]},{\"start\":[832,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]}]},{\"start\":[464,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[418,704],\"end\":[385,671]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,638],\"end\":[352,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,546],\"end\":[385,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[418,480],\"end\":[464,480]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,480],\"end\":[543,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,546],\"end\":[576,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,638],\"end\":[543,671]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,704],\"end\":[464,704]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-search-outline\",\"codePoint\":59665},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[725,876],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[748,853]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[577,682]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[591,663],\"end\":[599.5,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,617],\"end\":[608,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,532],\"end\":[566,490]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[524,448],\"end\":[464,448]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,448],\"end\":[362,490]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,532],\"end\":[320,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,652],\"end\":[362,694]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,736],\"end\":[464,736]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,736],\"end\":[512.5,727.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[535,719],\"end\":[554,705]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[725,876]}]}]},{\"start\":[624,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,96]}]}]},{\"start\":[608,128],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[626.5,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,352],\"end\":[672,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,909],\"end\":[790.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[781,928],\"end\":[768,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,928],\"end\":[265.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,909],\"end\":[256,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,147],\"end\":[265.5,137.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,128],\"end\":[288,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,128]}]}]},{\"start\":[640,144],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[790,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,144]}]}]},{\"start\":[464,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[418,704],\"end\":[385,671]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,638],\"end\":[352,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,546],\"end\":[385,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[418,480],\"end\":[464,480]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,480],\"end\":[543,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,546],\"end\":[576,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,638],\"end\":[543,671]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,704],\"end\":[464,704]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-code\",\"codePoint\":59666},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[608,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,352],\"end\":[627,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,96]}]}]},{\"start\":[832,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]}]},{\"start\":[288,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,598]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[333,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,810]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,704]}]}]},{\"start\":[618,810],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[723,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,598]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,810]}]}]},{\"start\":[576,576],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,576]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-code-outline\",\"codePoint\":59667},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[624,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,96]}]}]},{\"start\":[608,128],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[626.5,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,352],\"end\":[672,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,909],\"end\":[790.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[781,928],\"end\":[768,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,928],\"end\":[265.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,909],\"end\":[256,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,147],\"end\":[265.5,137.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,128],\"end\":[288,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,128]}]}]},{\"start\":[640,144],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[790,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,144]}]}]},{\"start\":[576,576],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,576]}]}]},{\"start\":[438,778],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[365,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,630]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,778]}]}]},{\"start\":[618,778],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,630]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,778]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-text\",\"codePoint\":59668},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[608,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,352],\"end\":[627,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,96]}]}]},{\"start\":[480,192],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,192]}]}]},{\"start\":[832,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]}]},{\"start\":[736,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,512]}]}]},{\"start\":[736,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]}]},{\"start\":[736,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,608]}]}]},{\"start\":[736,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,704]}]}]},{\"start\":[736,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-text-outline\",\"codePoint\":59669},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[624,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,96]}]}]},{\"start\":[608,128],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[626.5,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,352],\"end\":[672,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,909],\"end\":[790.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[781,928],\"end\":[768,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,928],\"end\":[265.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,909],\"end\":[256,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,147],\"end\":[265.5,137.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,128],\"end\":[288,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,128]}]}]},{\"start\":[640,144],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[790,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,144]}]}]},{\"start\":[480,192],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,192]}]}]},{\"start\":[736,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,512]}]}]},{\"start\":[736,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]}]},{\"start\":[736,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,608]}]}]},{\"start\":[736,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,704]}]}]},{\"start\":[736,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"text_format\",\"codePoint\":59670},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[592,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[432,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[592,491]}]}]},{\"start\":[618,567],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[746,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[278,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[406,567]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,567]}]}]},{\"start\":[214,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"chat_bubble\",\"codePoint\":59671},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,107],\"end\":[111,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,157],\"end\":[86,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,789],\"end\":[913,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,737],\"end\":[938,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,157],\"end\":[913,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,107],\"end\":[854,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,107]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"movie\",\"codePoint\":59672},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[854,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,363]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"videocam\",\"codePoint\":59673},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[726,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,301],\"end\":[713,289]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,277],\"end\":[682,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[152,277],\"end\":[140,289]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,301],\"end\":[128,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,765],\"end\":[140,777]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[152,789],\"end\":[170,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,789],\"end\":[713,777]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,765],\"end\":[726,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,767]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,299]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-add\",\"codePoint\":59674},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[576,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]}]},{\"start\":[513,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,904],\"end\":[488.5,875.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,847],\"end\":[480,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,773],\"end\":[496,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,697],\"end\":[541,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,641],\"end\":[607,624]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,608],\"end\":[688,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,608],\"end\":[729.5,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,616],\"end\":[768,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,320],\"end\":[563,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[160,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[224,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[513,928]}]}]},{\"start\":[768,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[576,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]}]},{\"start\":[688,992],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,992],\"end\":[563.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,889],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,640],\"end\":[812.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,743],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-add-outline\",\"codePoint\":59675},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[576,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,800]}]}]},{\"start\":[552,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,928],\"end\":[178.5,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,890],\"end\":[160,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,102],\"end\":[179,83]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,64],\"end\":[224,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,64]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,681],\"end\":[837.5,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,765],\"end\":[864,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,889],\"end\":[812.5,940.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[761,992],\"end\":[688,992]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[647,992],\"end\":[611.5,974.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,957],\"end\":[552,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,928]}]}]},{\"start\":[531,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,878],\"end\":[517,858]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,838],\"end\":[512,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,743],\"end\":[563.5,691.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,640],\"end\":[688,640]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,640],\"end\":[712.5,641.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,643],\"end\":[736,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,320],\"end\":[562.5,301.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,283],\"end\":[544,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,96],\"end\":[201.5,105.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,115],\"end\":[192,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,877],\"end\":[201.5,886.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[211,896],\"end\":[224,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,896]}]}]},{\"start\":[576,112],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,269],\"end\":[585.5,278.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,288],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,112]}]}]},{\"start\":[790,918],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,876],\"end\":[832,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,756],\"end\":[790,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,672],\"end\":[688,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,672],\"end\":[586,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,756],\"end\":[544,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,876],\"end\":[586,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,960],\"end\":[688,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[688,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,960],\"end\":[790,918]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"documents\",\"codePoint\":59676},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[736,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,987],\"end\":[717.5,1005.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[699,1024],\"end\":[672,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[165,1024],\"end\":[146.5,1005]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,986],\"end\":[128,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,198],\"end\":[147,179]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[166,160],\"end\":[192,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,379],\"end\":[531,397.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,416],\"end\":[576,416]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,371]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[560,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,38],\"end\":[339,19]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,0],\"end\":[384,0]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,219],\"end\":[723,237.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[742,256],\"end\":[768,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,827],\"end\":[909.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[891,864],\"end\":[864,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,864]}]}]},{\"start\":[928,224],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[755,224],\"end\":[745.5,214.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,205],\"end\":[736,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,224]}]}]},{\"start\":[736,384],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,384]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[563,384],\"end\":[553.5,374.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,365],\"end\":[544,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,384]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"documents-outline\",\"codePoint\":59677},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[736,384],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,64]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,51],\"end\":[361.5,41.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[371,32],\"end\":[384,32]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,32]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,219],\"end\":[722.5,237.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[741,256],\"end\":[768,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,813],\"end\":[886.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[877,832],\"end\":[864,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,400]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,384]}]}]},{\"start\":[320,160],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[166,160],\"end\":[147,179]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,198],\"end\":[128,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,986],\"end\":[146.5,1005]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[165,1024],\"end\":[192,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[699,1024],\"end\":[717.5,1005.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,987],\"end\":[736,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[891,864],\"end\":[909.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,827],\"end\":[928,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,0]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,0],\"end\":[339,19]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,38],\"end\":[320,64]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,160]}]}]},{\"start\":[736,48],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[886,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[755,224],\"end\":[745.5,214.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,205],\"end\":[736,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,48]}]}]},{\"start\":[512,192],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,379],\"end\":[530.5,397.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,416],\"end\":[576,416]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,973],\"end\":[694.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[685,992],\"end\":[672,992]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,992]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[179,992],\"end\":[169.5,982.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,973],\"end\":[160,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,211],\"end\":[169.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[179,192],\"end\":[192,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,192]}]}]},{\"start\":[544,208],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[694,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,384]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[563,384],\"end\":[553.5,374.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,365],\"end\":[544,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,208]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-information\",\"codePoint\":59678},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[673,832],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[657,808],\"end\":[648.5,779.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,751],\"end\":[640,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,677],\"end\":[656,639]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[673,601],\"end\":[701,573]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[729,545],\"end\":[767,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[805,512],\"end\":[848,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[869,512],\"end\":[889.5,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[910,520],\"end\":[928,528]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,293],\"end\":[909,274.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,256],\"end\":[864,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,128],\"end\":[19,146.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,165],\"end\":[0,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,795],\"end\":[19,813.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,832],\"end\":[64,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,832]}]}]},{\"start\":[928,384],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,384]}]}]},{\"start\":[972.5,844.5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,793],\"end\":[1024,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,647],\"end\":[972.5,595.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,544],\"end\":[848,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,544],\"end\":[723.5,595.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,647],\"end\":[672,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,793],\"end\":[723.5,844.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,896],\"end\":[848,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[848,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,896],\"end\":[972.5,844.5]}]}]}]},{\"start\":[864,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,704]}]}]},{\"start\":[864,640],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,672]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,672]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,640]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-information-outline\",\"codePoint\":59679},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[64,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,800],\"end\":[41.5,790.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,781],\"end\":[32,768]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,551]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[885,547],\"end\":[872.5,545.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[860,544],\"end\":[848,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,544],\"end\":[723.5,595.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,647],\"end\":[672,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,742],\"end\":[677,762]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,782],\"end\":[691,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,800]}]}]},{\"start\":[712,832],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,861],\"end\":[771.5,878.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[807,896],\"end\":[848,896]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,896],\"end\":[972.5,844.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,793],\"end\":[1024,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,669],\"end\":[997.5,627]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[971,585],\"end\":[928,563]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,293],\"end\":[909,274.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,256],\"end\":[864,256]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,128],\"end\":[19,146.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,165],\"end\":[0,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,795],\"end\":[19,813.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,832],\"end\":[64,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,832]}]}]},{\"start\":[32,384],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,179],\"end\":[41.5,169.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,160],\"end\":[64,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[397,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[460,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[877,288],\"end\":[886.5,297.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,307],\"end\":[896,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,384]}]}]},{\"start\":[848,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,864],\"end\":[746,822]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,780],\"end\":[704,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,660],\"end\":[746,618]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,576],\"end\":[848,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,576],\"end\":[950,618]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[992,660],\"end\":[992,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[992,780],\"end\":[950,822]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,864],\"end\":[848,864]}]}]}]},{\"start\":[864,640],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,672]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,672]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,640]}]}]},{\"start\":[864,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,704]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-remove\",\"codePoint\":59680},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[673,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[657,840],\"end\":[648.5,811.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,783],\"end\":[640,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,709],\"end\":[656,671]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[673,633],\"end\":[701,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[729,577],\"end\":[767,560]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[805,544],\"end\":[848,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[869,544],\"end\":[889.5,548]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[910,552],\"end\":[928,560]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,827],\"end\":[19,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,864],\"end\":[64,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,864]}]}]},{\"start\":[928,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,325],\"end\":[909,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,288],\"end\":[864,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,160],\"end\":[19,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,197],\"end\":[0,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,416]}]}]},{\"start\":[972.5,876.5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,825],\"end\":[1024,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,679],\"end\":[972.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,576],\"end\":[848,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,576],\"end\":[723.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,679],\"end\":[672,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,825],\"end\":[723.5,876.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,928],\"end\":[848,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[848,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,928],\"end\":[972.5,876.5]}]}]}]},{\"start\":[960,736],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,736]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-remove-outline\",\"codePoint\":59681},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[64,832],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,832],\"end\":[41.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,813],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[885,579],\"end\":[872.5,577.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[860,576],\"end\":[848,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,576],\"end\":[723.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,679],\"end\":[672,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,774],\"end\":[677,794]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,814],\"end\":[691,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,832]}]}]},{\"start\":[712,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,893],\"end\":[771.5,910.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[807,928],\"end\":[848,928]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,928],\"end\":[972.5,876.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,825],\"end\":[1024,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,701],\"end\":[997.5,659]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[971,617],\"end\":[928,595]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,325],\"end\":[909,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,288],\"end\":[864,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,160],\"end\":[19,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,197],\"end\":[0,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,827],\"end\":[19,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,864],\"end\":[64,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,864]}]}]},{\"start\":[32,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,211],\"end\":[41.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,192],\"end\":[64,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[397,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[460,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[877,320],\"end\":[886.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,339],\"end\":[896,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,416]}]}]},{\"start\":[848,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,896],\"end\":[746,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,812],\"end\":[704,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,692],\"end\":[746,650]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,608],\"end\":[848,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,608],\"end\":[950,650]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[992,692],\"end\":[992,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[992,812],\"end\":[950,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,896],\"end\":[848,896]}]}]}]},{\"start\":[960,736],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,736]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-add\",\"codePoint\":59682},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[832,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,864]}]}]},{\"start\":[673,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[657,840],\"end\":[648.5,811.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,783],\"end\":[640,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,709],\"end\":[656,671]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[673,633],\"end\":[701,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[729,577],\"end\":[767,560]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[805,544],\"end\":[848,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[869,544],\"end\":[889.5,548]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[910,552],\"end\":[928,560]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,827],\"end\":[19,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,864],\"end\":[64,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,864]}]}]},{\"start\":[928,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,325],\"end\":[909,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,288],\"end\":[864,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,160],\"end\":[19,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,197],\"end\":[0,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,416]}]}]},{\"start\":[848,928],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,928],\"end\":[723.5,876.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,825],\"end\":[672,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,679],\"end\":[723.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,576],\"end\":[848,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,576],\"end\":[972.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,679],\"end\":[1024,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,825],\"end\":[972.5,876.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,928],\"end\":[848,928]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-add-outline\",\"codePoint\":59683},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[736,736],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,768]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]}]},{\"start\":[64,832],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,832],\"end\":[41.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,813],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[885,579],\"end\":[872.5,577.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[860,576],\"end\":[848,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,576],\"end\":[723.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,679],\"end\":[672,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,774],\"end\":[677,794]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,814],\"end\":[691,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,832]}]}]},{\"start\":[712,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,893],\"end\":[771.5,910.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[807,928],\"end\":[848,928]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,928],\"end\":[972.5,876.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,825],\"end\":[1024,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,701],\"end\":[997.5,659]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[971,617],\"end\":[928,595]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,325],\"end\":[909,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,288],\"end\":[864,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[416,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,160],\"end\":[19,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,197],\"end\":[0,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,827],\"end\":[19,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[38,864],\"end\":[64,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,864]}]}]},{\"start\":[32,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,211],\"end\":[41.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,192],\"end\":[64,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[397,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[460,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[877,320],\"end\":[886.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,339],\"end\":[896,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,416]}]}]},{\"start\":[848,896],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,896],\"end\":[746,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,812],\"end\":[704,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,692],\"end\":[746,650]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,608],\"end\":[848,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,608],\"end\":[950,650]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[992,692],\"end\":[992,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[992,812],\"end\":[950,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,896],\"end\":[848,896]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-upload\",\"codePoint\":59684},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[480,864],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,680]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,656]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[496,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,656]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[616,680]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,864]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[960,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[96,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,864]}]}]},{\"start\":[960,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[896,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[32,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,416]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-upload-outline\",\"codePoint\":59685},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[480,544],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,648]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,624]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[496,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,624]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[616,648]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,832],\"end\":[918.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,813],\"end\":[928,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,813],\"end\":[73.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,832],\"end\":[96,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,544]}]}]},{\"start\":[928,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,339],\"end\":[918.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,320],\"end\":[896,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[492,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[429,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,192],\"end\":[73.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,211],\"end\":[64,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,416]}]}]},{\"start\":[512,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[960,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[896,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[96,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-download\",\"codePoint\":59686},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,448],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,738]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[616,634]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,658]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[496,802]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,658]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,634]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,738]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[96,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[960,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,448]}]}]},{\"start\":[960,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[896,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[32,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,416]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-download-outline\",\"codePoint\":59687},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,448],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,738]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[616,634]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,658]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[496,802]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,658]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,634]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,738]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,813],\"end\":[73.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,832],\"end\":[96,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,832],\"end\":[918.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,813],\"end\":[928,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,448]}]}]},{\"start\":[928,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,339],\"end\":[918.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,320],\"end\":[896,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[492,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[429,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,192],\"end\":[73.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,211],\"end\":[64,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,416]}]}]},{\"start\":[512,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[960,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[896,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[96,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-search\",\"codePoint\":59688},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[693,812],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[716,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[545,618]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[559,599],\"end\":[567.5,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,553],\"end\":[576,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,468],\"end\":[534,426]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[492,384],\"end\":[432,384]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,384],\"end\":[330,426]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,468],\"end\":[288,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,588],\"end\":[330,630]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,672],\"end\":[432,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[458,672],\"end\":[480.5,663.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[503,655],\"end\":[522,641]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[693,812]}]}]},{\"start\":[512,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[960,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[896,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[96,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]}]},{\"start\":[432,640],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[386,640],\"end\":[353,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,574],\"end\":[320,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,482],\"end\":[353,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[386,416],\"end\":[432,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,416],\"end\":[511,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,482],\"end\":[544,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,574],\"end\":[511,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,640],\"end\":[432,640]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-search-outline\",\"codePoint\":59689},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[693,812],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[716,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[545,618]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[559,599],\"end\":[567.5,576]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,553],\"end\":[576,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,468],\"end\":[534,426]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[492,384],\"end\":[432,384]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,384],\"end\":[330,426]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,468],\"end\":[288,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,588],\"end\":[330,630]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,672],\"end\":[432,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[458,672],\"end\":[480.5,663.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[503,655],\"end\":[522,641]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[693,812]}]}]},{\"start\":[448,160],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[32,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[96,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[960,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[896,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]}]},{\"start\":[492,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,320],\"end\":[918.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,339],\"end\":[928,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,813],\"end\":[918.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,832],\"end\":[896,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,832],\"end\":[73.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,813],\"end\":[64,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,211],\"end\":[73.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,192],\"end\":[96,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[429,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[492,320]}]}]},{\"start\":[432,640],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[386,640],\"end\":[353,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,574],\"end\":[320,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,482],\"end\":[353,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[386,416],\"end\":[432,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,416],\"end\":[511,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,482],\"end\":[544,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,574],\"end\":[511,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,640],\"end\":[432,640]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder\",\"codePoint\":59690},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[960,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[896,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[32,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,416]}]}]},{\"start\":[32,448],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[896,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,448]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder-outline\",\"codePoint\":59691},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[928,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,339],\"end\":[918.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,320],\"end\":[896,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[492,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[429,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,192],\"end\":[73.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,211],\"end\":[64,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,416]}]}]},{\"start\":[64,448],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,813],\"end\":[73.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,832],\"end\":[96,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,832],\"end\":[918.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,813],\"end\":[928,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,448]}]}]},{\"start\":[512,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[960,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[896,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[96,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder2\",\"codePoint\":59692},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,288],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[960,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[896,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[32,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[96,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder2-outline\",\"codePoint\":59693},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[448,160],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,160],\"end\":[51,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,197],\"end\":[32,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[32,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,827],\"end\":[51,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[70,864],\"end\":[96,864]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,864]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,864],\"end\":[941.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,827],\"end\":[960,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[960,325],\"end\":[941,306.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,288],\"end\":[896,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,160]}]}]},{\"start\":[492,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,320],\"end\":[918.5,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,339],\"end\":[928,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,800]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[928,813],\"end\":[918.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[909,832],\"end\":[896,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,832],\"end\":[73.5,822.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,813],\"end\":[64,800]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,211],\"end\":[73.5,201.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[83,192],\"end\":[96,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[429,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[492,320]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"android\",\"codePoint\":59694},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[945,393],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[945,393]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[945,658]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[945,684],\"end\":[926.5,702.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,721],\"end\":[882,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[856,721],\"end\":[837.5,702.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[819,684],\"end\":[819,658]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[819,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[819,368],\"end\":[837.5,349.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[856,331],\"end\":[882,331]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,331],\"end\":[926.5,349.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[945,368],\"end\":[945,394]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[945,393]}]}]},{\"start\":[230,343],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[793,342]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[793,753]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[793,781],\"end\":[773,800.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,820],\"end\":[726,820]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,820]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,987],\"end\":[661.5,1005.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[643,1024],\"end\":[616,1024]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,1024],\"end\":[572,1005.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,987],\"end\":[554,961]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,821]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[469,821]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[469,961]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[468,987],\"end\":[449.5,1005]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[431,1023],\"end\":[405,1023]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,1023],\"end\":[361.5,1004.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[343,986],\"end\":[343,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[343,820]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[297,820]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,820],\"end\":[250,800.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[230,781],\"end\":[230,752]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[230,343]}]}]},{\"start\":[659,211],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,204],\"end\":[666,194]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,184],\"end\":[659,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,170],\"end\":[642,170]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[632,170],\"end\":[625,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,184],\"end\":[618,194]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,204],\"end\":[625,211]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[632,218],\"end\":[642,218]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,218],\"end\":[659,211]}]}]}]},{\"start\":[383,218],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,218],\"end\":[400,211]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[407,204],\"end\":[407,194]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[407,184],\"end\":[400,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,170],\"end\":[383,170]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[373,170],\"end\":[366,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[359,184],\"end\":[359,194]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[359,204],\"end\":[366,211]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[373,218],\"end\":[383,218]}]}]}]},{\"start\":[651,93],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[716,126],\"end\":[756,186.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[796,247],\"end\":[796,320]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[228,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[228,247],\"end\":[268,186.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,126],\"end\":[373,93]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[329,13]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[327,10],\"end\":[328,6.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[329,3],\"end\":[332,1]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[335,-1],\"end\":[338.5,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,1],\"end\":[344,4]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[388,86]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[416,73],\"end\":[447,66.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,60],\"end\":[512,60]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[545,60],\"end\":[576,67]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[607,74],\"end\":[635,87]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,6]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,2],\"end\":[685,1]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,0],\"end\":[692,2]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,4],\"end\":[696,7.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[697,11],\"end\":[695,14]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[651,95]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[651,93]}]}]},{\"start\":[143,331],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[168,331],\"end\":[186.5,349.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[205,368],\"end\":[205,394]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[205,658]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[205,684],\"end\":[186.5,702.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[168,721],\"end\":[142,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[116,721],\"end\":[97.5,702.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[79,684],\"end\":[79,658]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[79,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[79,367],\"end\":[97.5,348.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[116,330],\"end\":[142,330]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[143,331]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"angular\",\"codePoint\":59695},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,330],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[600,540]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[424,540]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,330]}]}]},{\"start\":[512,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[994,170]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[921,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[103,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[29,170]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,0]}]}]},{\"start\":[512,113],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,781]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[323,781]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,632]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,632]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[701,781]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[813,781]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,113]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"css3\",\"codePoint\":59696},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[64,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[146,920]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[879,920]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,0]}]}]},{\"start\":[793,188],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[741,758]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,823]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[279,750]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[266,586]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[378,586]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[386,676]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,710]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[636,676]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[652,526]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[388,526]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[378,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[661,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,300]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[240,300]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[231,188]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[793,188]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"dev-dot-to\",\"codePoint\":59697},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[317,429],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[325,436],\"end\":[327,448.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[329,461],\"end\":[329,522]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[329,581],\"end\":[327.5,594.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,608],\"end\":[318,616]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[311,622],\"end\":[303.5,624.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,627],\"end\":[282,627]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[259,628]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[257,523]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,419]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[281,419]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[293,419],\"end\":[302,421.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[311,424],\"end\":[317,429]}]}]}]},{\"start\":[1024,211],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,813]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,813]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,211]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,211]}]}]},{\"start\":[365,653],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,635],\"end\":[383.5,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[387,583],\"end\":[385,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,449],\"end\":[382,432.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,416],\"end\":[372,402]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,379],\"end\":[336,371.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[314,364],\"end\":[261,364]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[201,364]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[201,686]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[257,686]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[304,686],\"end\":[327.5,678.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[351,671],\"end\":[365,653]}]}]}]},{\"start\":[582,420],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,364]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,364]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,364],\"end\":[450,366]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,368],\"end\":[432,378]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[425,387],\"end\":[423.5,408]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,429],\"end\":[422,526]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[422,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[434,673]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[443,682],\"end\":[454,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[465,686],\"end\":[514,686]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,686]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,631]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[531,629]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[479,628]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[479,553]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,551]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,550]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,495]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[477,495]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[477,419]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,419]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,420]}]}]},{\"start\":[782,652],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[784,647],\"end\":[796,603]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,559],\"end\":[822,507]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[836,455],\"end\":[847,412]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,369],\"end\":[858,367]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,366],\"end\":[849,365.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[840,365],\"end\":[827,365]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[796,367]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,474]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[754,526],\"end\":[746,551.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[738,577],\"end\":[736,573]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[735,568],\"end\":[726,536]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[718,503],\"end\":[708,466]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,429],\"end\":[690,398]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,367],\"end\":[683,367]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,366],\"end\":[673,365]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[663,364],\"end\":[651,364]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,364]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[637,436]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[676,580]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,627],\"end\":[694.5,643]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,659],\"end\":[711,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[718,677],\"end\":[726,681.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,686],\"end\":[739,686]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[751,686],\"end\":[764,676]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[777,666],\"end\":[782,652]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"facebook\",\"codePoint\":59698},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1024,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,608],\"end\":[991,693]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[958,779],\"end\":[900,846.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[842,914],\"end\":[763,958]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[684,1003],\"end\":[592,1018]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[592,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[711,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[734,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[592,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[592,416]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,386],\"end\":[610,361]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,336],\"end\":[675,336]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[740,336]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[740,210]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[740,210],\"end\":[703.5,205]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[667,200],\"end\":[625,200]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[538,200],\"end\":[485,251.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,303],\"end\":[432,399]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[432,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[302,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[302,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[432,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[432,1018]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[340,1003],\"end\":[261,958]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[182,914],\"end\":[124,846.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[66,779],\"end\":[33,693]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,608],\"end\":[0,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,406],\"end\":[40,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[80,219],\"end\":[149.5,149.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[219,80],\"end\":[313,40]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,0],\"end\":[512,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,0],\"end\":[711,40]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[804,80],\"end\":[873.5,149.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[943,219],\"end\":[984,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,406],\"end\":[1024,512]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"git\",\"codePoint\":59699},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1005,466],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,486],\"end\":[1024,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,540],\"end\":[1005,560]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[560,1005]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[540,1024],\"end\":[513,1024]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[486,1024],\"end\":[466,1005]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[19,558]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,538],\"end\":[0,511]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,484],\"end\":[19,464]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[326,158]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[442,274]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[433,295],\"end\":[437,318.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[441,342],\"end\":[458,360]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,366],\"end\":[470.5,370]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[477,374],\"end\":[484,377]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[484,658]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[477,661],\"end\":[470.5,665.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,670],\"end\":[458,675]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[435,698],\"end\":[435,730.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[435,763],\"end\":[458,786]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[481,809],\"end\":[514,809]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[547,809],\"end\":[570,786]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,763],\"end\":[592,730.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,698],\"end\":[570,675]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[565,671],\"end\":[559.5,667]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,663],\"end\":[549,660]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[549,382]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[655,488]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,509],\"end\":[650.5,532.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[655,556],\"end\":[672,573]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,596],\"end\":[727.5,596]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[760,596],\"end\":[783,573]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,550],\"end\":[806,517.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,485],\"end\":[783,462]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[767,445],\"end\":[745,440.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,436],\"end\":[702,443]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[589,330]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[596,309],\"end\":[591,287]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[586,265],\"end\":[570,249]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,232],\"end\":[532,227.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,223],\"end\":[489,230]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[372,112]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[464,19]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[484,0],\"end\":[511,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[538,0],\"end\":[558,19]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1005,466]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"github\",\"codePoint\":59700},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,13],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,13],\"end\":[711,53]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[805,93],\"end\":[874.5,162.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[944,232],\"end\":[984,325]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,419],\"end\":[1024,525]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,609],\"end\":[998,686]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[972,763],\"end\":[925,826.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[878,890],\"end\":[814,937]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[749,985],\"end\":[673,1010]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,1014],\"end\":[645,1005]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[637,996],\"end\":[637,985]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[637,973],\"end\":[637.5,935]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[638,897],\"end\":[638,845]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[638,809],\"end\":[627.5,785.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,762],\"end\":[603,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,746],\"end\":[688,733]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,721],\"end\":[763,693.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[796,666],\"end\":[817,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[837,572],\"end\":[837,498]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[837,456],\"end\":[823,421.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[809,387],\"end\":[785,361]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,351],\"end\":[794,314.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,278],\"end\":[779,225]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[779,225],\"end\":[745.5,226.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[712,228],\"end\":[639,278]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[609,269],\"end\":[576.5,264.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,260],\"end\":[511,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[479,260],\"end\":[446.5,264.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[414,269],\"end\":[383,278]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[310,228],\"end\":[276.5,226.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[243,225],\"end\":[243,225]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,278],\"end\":[228,314.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[234,351],\"end\":[238,361]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[213,387],\"end\":[199,421.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[185,456],\"end\":[185,498]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[185,571],\"end\":[206,618]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[226,665],\"end\":[259,693]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[292,721],\"end\":[334,734]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[376,746],\"end\":[419,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,761],\"end\":[399,778]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[390,795],\"end\":[386,819]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[364,829],\"end\":[317.5,832.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[271,836],\"end\":[237,777]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[237,777],\"end\":[217,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,727],\"end\":[159,724]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[159,724],\"end\":[139.5,727.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[120,731],\"end\":[155,755]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[155,755],\"end\":[174.5,770.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[194,786],\"end\":[212,830]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[212,830],\"end\":[245,876]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,922],\"end\":[384,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[385,931],\"end\":[385,954]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[385,977],\"end\":[385,986]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[385,996],\"end\":[377,1005]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[369,1014],\"end\":[350,1010]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[274,985],\"end\":[210,938]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[145,891],\"end\":[98.5,827.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[52,764],\"end\":[26,687]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,610],\"end\":[0,525]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,419],\"end\":[40,325]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[80,232],\"end\":[149.5,162.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[219,93],\"end\":[313,53]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,13],\"end\":[512,13]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"gmail\",\"codePoint\":59701},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1024,192],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,178],\"end\":[1019,166.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1014,155],\"end\":[1006,146]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[997,138],\"end\":[985.5,133]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[974,128],\"end\":[960,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[939,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,437]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[85,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[50,128],\"end\":[38.5,133]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[27,138],\"end\":[18,146]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[10,155],\"end\":[5,166.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,178],\"end\":[0,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,859],\"end\":[18.5,877.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[37,896],\"end\":[64,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,315]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,591]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,315]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,896]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[987,896],\"end\":[1005.5,877.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,859],\"end\":[1024,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,192]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"googlechrome\",\"codePoint\":59702},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[692,371],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,370]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[699,379],\"end\":[705,389]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[711,399],\"end\":[717,410]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[716,408]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[728,432],\"end\":[734.5,458.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[741,485],\"end\":[741,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[741,540],\"end\":[735,565]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[729,590],\"end\":[719,612]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[712,627],\"end\":[702.5,640.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[693,654],\"end\":[681,666]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[472,1023]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[482,1023],\"end\":[491.5,1023.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[501,1024],\"end\":[511,1024]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[616,1024],\"end\":[710,983]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[803,943],\"end\":[873,873]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[943,803],\"end\":[983,710]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,616],\"end\":[1024,511]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,511]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,453],\"end\":[1012,399.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1000,346],\"end\":[978,299]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[692,371]}]}]},{\"start\":[554,737],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,743],\"end\":[489,740]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[456,737],\"end\":[424,725]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,701],\"end\":[330.5,650.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,600],\"end\":[293,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[87,225]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[44,289],\"end\":[22,362]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,435],\"end\":[0,511]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,610],\"end\":[36,698]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[71,787],\"end\":[133.5,855.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[196,924],\"end\":[281,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[365,1012],\"end\":[463,1022]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,737]}]}]},{\"start\":[511,325],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,325]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[511,325],\"end\":[511.5,325]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,325],\"end\":[512,325]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[534,325],\"end\":[554,329.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,334],\"end\":[591,343]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,368],\"end\":[676,424.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,481],\"end\":[698,540]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[691,588],\"end\":[659.5,628]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[628,668],\"end\":[583,687]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[532,707],\"end\":[476,696]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,685],\"end\":[381,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[381,647]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[355,621],\"end\":[340,586]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[325,551],\"end\":[325,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[325,502],\"end\":[326,492.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[327,483],\"end\":[328,475]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[341,412],\"end\":[394,369]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[447,326],\"end\":[511,325]}]}]}]},{\"start\":[301,425],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[301,424]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[310,402],\"end\":[323.5,382.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[337,363],\"end\":[354,347]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[385,317],\"end\":[431.5,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,281],\"end\":[536,285]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[972,285]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[939,219],\"end\":[891,167]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[842,114],\"end\":[782,77]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[722,40],\"end\":[654,20]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[585,0],\"end\":[512,0]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,0]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,0],\"end\":[388,15]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[329,30],\"end\":[276,57.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[223,85],\"end\":[177,124]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[132,163],\"end\":[96,212]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[301,425]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"googledrive\",\"codePoint\":59703},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[189,956],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[853,956]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[360,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[189,956]}]}]},{\"start\":[341,660],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[503,380]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,84]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[171,956]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[341,660]}]}]},{\"start\":[1015,644],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[683,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[341,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,644]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1015,644]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"googleplay\",\"codePoint\":59704},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[544,508],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[57,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[53,1020],\"end\":[51,1015.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[49,1011],\"end\":[49,1004]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[49,13]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[49,9],\"end\":[50,6]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,3],\"end\":[52,0]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,508]}]}]},{\"start\":[577,542],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[169,977]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[216,950],\"end\":[277,915]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[339,880],\"end\":[393.5,848.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,817],\"end\":[486,796]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[523,775],\"end\":[523,775]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[703,672]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[577,542]}]}]},{\"start\":[611,507],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[748,362]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,368],\"end\":[788,386]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[820,404],\"end\":[855,424.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,445],\"end\":[920,462]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[950,479],\"end\":[958,483]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[974,491],\"end\":[974.5,504]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[975,517],\"end\":[957,528]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[948,533],\"end\":[917,550]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[886,568],\"end\":[850.5,588]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[815,608],\"end\":[785,625]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[754,643],\"end\":[746,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[611,507]}]}]},{\"start\":[577,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,8]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[165,29],\"end\":[231,67]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,105],\"end\":[362.5,142]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[427,179],\"end\":[475,206]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[523,234],\"end\":[523,234]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[577,473]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"html5\",\"codePoint\":59705},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[64,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[146,920]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[879,920]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[960,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,0]}]}]},{\"start\":[364,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[773,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[742,764]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,826]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[281,764]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[267,586]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[378,586]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[386,676]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,710]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[636,676]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[650,530]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[261,530]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[231,188]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[793,188]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[783,300]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[354,300]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[364,416]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"instagram\",\"codePoint\":59706},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[616,0],\"end\":[649,0.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,1],\"end\":[723,3]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[764,5],\"end\":[793.5,11]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[823,17],\"end\":[847,27]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[873,37],\"end\":[895,51]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[917,65],\"end\":[938,86]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[959,107],\"end\":[973,129]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[987,151],\"end\":[997,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1007,201],\"end\":[1013,230.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1019,260],\"end\":[1021,301]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1023,342],\"end\":[1023.5,375]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,408],\"end\":[1024,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,616],\"end\":[1023.5,649]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1023,682],\"end\":[1021,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1019,764],\"end\":[1013,793.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1007,823],\"end\":[997,847]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[987,873],\"end\":[973,895]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[959,917],\"end\":[938,938]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[917,959],\"end\":[895,973]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[873,987],\"end\":[847,997]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[823,1007],\"end\":[793.5,1013]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[764,1019],\"end\":[723,1021]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,1023],\"end\":[649,1023.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[616,1024],\"end\":[512,1024]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,1024],\"end\":[375,1023.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,1023],\"end\":[301,1021]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,1019],\"end\":[230.5,1013]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[201,1007],\"end\":[177,997]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[151,987],\"end\":[129,973]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[107,959],\"end\":[86,938]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[65,917],\"end\":[51,895]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[37,873],\"end\":[27,847]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[17,823],\"end\":[11,793.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[5,764],\"end\":[3,723]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1,682],\"end\":[0.5,649]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,616],\"end\":[0,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,408],\"end\":[0.5,375]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1,342],\"end\":[3,301]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[5,260],\"end\":[11,230.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[17,201],\"end\":[27,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[37,151],\"end\":[51,129]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[65,107],\"end\":[86,86]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[107,65],\"end\":[129,51]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[151,37],\"end\":[177,27]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[201,17],\"end\":[230.5,11]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,5],\"end\":[301,3]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,1],\"end\":[375,0.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,0],\"end\":[512,0]}]}]}]},{\"start\":[512,92],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[510,91]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[407,91],\"end\":[375.5,91.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[344,92],\"end\":[303,93]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[265,95],\"end\":[243,100.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[221,106],\"end\":[208,111]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[190,118],\"end\":[176.5,127]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[163,136],\"end\":[149,150]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,163],\"end\":[126.5,176.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[117,190],\"end\":[111,209]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[105,222],\"end\":[100,244.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[95,267],\"end\":[93,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[91,345],\"end\":[90.5,377]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[90,409],\"end\":[90,511]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[90,614],\"end\":[90.5,646]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[91,678],\"end\":[93,718]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[95,755],\"end\":[100,777.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[105,800],\"end\":[111,813]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[117,831],\"end\":[126.5,845]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,859],\"end\":[149,872]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[163,886],\"end\":[176.5,894.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[190,903],\"end\":[208,911]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[221,916],\"end\":[243.5,921.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[266,927],\"end\":[303,929]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[344,930],\"end\":[376,931]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,932],\"end\":[511,932]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[613,932],\"end\":[645,931.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,931],\"end\":[718,929]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[755,927],\"end\":[777.5,921.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,916],\"end\":[813,911]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[831,904],\"end\":[845,895]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[859,886],\"end\":[872,873]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[886,859],\"end\":[894.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[903,832],\"end\":[911,814]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[916,800],\"end\":[921.5,778]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[927,756],\"end\":[929,719]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,678],\"end\":[931,646.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[932,615],\"end\":[932,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[932,409],\"end\":[931.5,377.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[931,346],\"end\":[929,305]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[927,268],\"end\":[921.5,246]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[916,224],\"end\":[911,210]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[904,192],\"end\":[895,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[886,165],\"end\":[873,151]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[859,138],\"end\":[845.5,129]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,120],\"end\":[814,113]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,108],\"end\":[778,102.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[756,97],\"end\":[719,95]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[678,93],\"end\":[646,92.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,92],\"end\":[512,92]}]}]}]},{\"start\":[614,270],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[614,270]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,290],\"end\":[698,326]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,362],\"end\":[754,410]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,458],\"end\":[775,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[775,566],\"end\":[754,614]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,662],\"end\":[698,698]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,734],\"end\":[614,754]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[566,775],\"end\":[512,775]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[458,775],\"end\":[410,754]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,734],\"end\":[326,698]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,662],\"end\":[270,614]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[249,566],\"end\":[249,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[249,458],\"end\":[270,410]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,362],\"end\":[326,326]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,290],\"end\":[410,270]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[458,249],\"end\":[512,249]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[566,249],\"end\":[614,270]}]}]}]},{\"start\":[633,633],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,583],\"end\":[683,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,441],\"end\":[633,391]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[583,341],\"end\":[512,341]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[441,341],\"end\":[391,391]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[341,441],\"end\":[341,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[341,583],\"end\":[391,633]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[441,683],\"end\":[512,683]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[583,683],\"end\":[633,633]}]}]}]},{\"start\":[847,239],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[847,213],\"end\":[829,195]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,177],\"end\":[785,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[760,177],\"end\":[742,195]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[724,213],\"end\":[724,239]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[724,264],\"end\":[742,282]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[760,300],\"end\":[785,300]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[811,300],\"end\":[829,282]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[847,264],\"end\":[847,239]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"ionic\",\"codePoint\":59707},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[978,300],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1001,350],\"end\":[1012.5,403.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,457],\"end\":[1024,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,618],\"end\":[984,711]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[943,804],\"end\":[873.5,873.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[804,943],\"end\":[711,984]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,1024],\"end\":[512,1024]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,1024],\"end\":[313,984]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[220,943],\"end\":[150.5,873.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[81,804],\"end\":[40,711]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,618],\"end\":[0,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,406],\"end\":[40,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[81,220],\"end\":[150.5,150.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[220,81],\"end\":[313,40]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,0],\"end\":[512,0]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,0]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,0],\"end\":[512.5,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[513,0],\"end\":[513,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[577,0],\"end\":[636.5,15]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[696,30],\"end\":[747,57]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,62]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[748,69]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[748,69]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[733,81],\"end\":[721,96.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,112],\"end\":[701,131]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[698,137]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,134]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[649,114],\"end\":[604,104]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[559,94],\"end\":[512,94]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,94],\"end\":[349,127]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[273,159],\"end\":[216.5,216]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[160,273],\"end\":[127,349]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,426],\"end\":[94,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,598],\"end\":[127,675]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[159,751],\"end\":[216,807.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[273,864],\"end\":[349,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[425,930],\"end\":[512,930]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,930],\"end\":[675,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[751,864],\"end\":[807.5,807.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,751],\"end\":[897,674]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,598],\"end\":[930,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[930,512]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,512],\"end\":[930,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,512],\"end\":[930,511]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,468],\"end\":[921.5,427.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[913,387],\"end\":[898,351]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[895,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[902,342]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[921,335],\"end\":[937,324]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,313],\"end\":[966,298]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[974,290]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[978,300]}]}]},{\"start\":[512,279],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,279]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[560,279],\"end\":[603,297]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,315],\"end\":[677,347]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,379],\"end\":[727,421]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[745,464],\"end\":[745,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[745,512]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[745,560],\"end\":[727,603]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[709,645],\"end\":[677,677]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,709],\"end\":[603,727]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[560,745],\"end\":[512,745]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,745]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,745],\"end\":[421,727]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[379,709],\"end\":[347,677]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[315,645],\"end\":[297,603]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[279,560],\"end\":[279,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[279,512]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[279,464],\"end\":[297,421]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[315,379],\"end\":[347,347]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[379,315],\"end\":[421,297]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,279],\"end\":[512,279]}]}]}]},{\"start\":[953,193],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,149],\"end\":[922,118]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[891,87],\"end\":[847,87]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[803,87],\"end\":[772,118]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[741,149],\"end\":[741,193]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[741,193]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[741,237],\"end\":[772,268]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[803,299],\"end\":[847,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[891,299],\"end\":[922,268]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,237],\"end\":[953,193]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"javascript\",\"codePoint\":59708},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[0,1024],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,1024]}]}]},{\"start\":[940,780],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,777]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[943,807],\"end\":[939.5,825.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[936,844],\"end\":[935,847]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[923,890],\"end\":[886,913]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[849,935],\"end\":[804,939.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[759,944],\"end\":[714,931]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,918],\"end\":[642,890]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[630,877],\"end\":[622,867.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,858],\"end\":[607,843]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[685,798]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,822],\"end\":[717.5,835.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,849],\"end\":[760,855]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,859],\"end\":[823.5,845.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[852,832],\"end\":[844,795]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[836,764],\"end\":[785,748.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,733],\"end\":[690,701]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,671],\"end\":[637.5,611]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,551],\"end\":[666,510]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[678,495],\"end\":[697.5,483.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,472],\"end\":[739,467]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[769,463]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[812,462],\"end\":[840.5,473.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[869,485],\"end\":[890,507]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[895,513],\"end\":[900.5,520]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[906,527],\"end\":[915,541]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[891,555],\"end\":[882,561]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[873,567],\"end\":[840,589]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,573],\"end\":[821,563.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,554],\"end\":[798,550]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[779,545],\"end\":[758.5,551.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[738,558],\"end\":[733,578]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[731,585],\"end\":[731.5,591.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,598],\"end\":[735,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[743,626],\"end\":[765.5,636]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,646],\"end\":[812,657]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[880,685],\"end\":[907.5,715]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[935,745],\"end\":[940,780]}]}]}]},{\"start\":[557,471],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[557,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,539],\"end\":[557,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,671],\"end\":[557,737]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,778],\"end\":[556.5,814]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,850],\"end\":[540,879]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[528,902],\"end\":[508,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[488,932],\"end\":[462,940]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[424,948],\"end\":[388.5,943.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[353,939],\"end\":[326,922]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[307,911],\"end\":[293,894.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[279,878],\"end\":[269,858]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[347,810]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[348,810],\"end\":[349,812.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[350,815],\"end\":[352,818]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[360,830],\"end\":[367.5,839]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[375,848],\"end\":[388,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[400,858],\"end\":[422,857]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[444,856],\"end\":[455,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[461,823],\"end\":[461,790.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[461,758],\"end\":[461,718]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[461,656],\"end\":[461,594.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[461,533],\"end\":[461,471]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[557,471]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"jekyll\",\"codePoint\":59709},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[344,1024],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[344,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[345,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[379,1024],\"end\":[408,1005.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[437,987],\"end\":[453,957]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,957]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[755,171]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[758,165],\"end\":[769.5,152.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[781,140],\"end\":[791,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[793,130],\"end\":[794.5,128]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[796,126],\"end\":[797,124]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[798,123]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[799,122]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[804,110],\"end\":[796,97.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[788,85],\"end\":[769,70]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,58],\"end\":[730.5,46]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[708,34],\"end\":[682,24]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,13],\"end\":[623.5,6.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,0],\"end\":[574,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,0],\"end\":[545,4.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[534,9],\"end\":[531,19]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[530,20]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[530,20]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[530,20]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[530,21]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[529,24],\"end\":[529,26.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[529,29],\"end\":[529,32]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[531,43],\"end\":[531.5,60]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[532,77],\"end\":[529,86]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[230,861]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[227,871]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[227,871]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,915],\"end\":[236,957]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[257,999],\"end\":[301,1016]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[312,1020],\"end\":[322.5,1022]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[333,1024],\"end\":[344,1024]}]}]}]},{\"start\":[251,865],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[548,92]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,77],\"end\":[552,54]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,31],\"end\":[550,27]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[550,26]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,24],\"end\":[555,22]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[560,20],\"end\":[573,20]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,20],\"end\":[619,26.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,33],\"end\":[674,43]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,53],\"end\":[719,64]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[740,75],\"end\":[755,86]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[771,98],\"end\":[775,105]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[779,112],\"end\":[779,114]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[779,114],\"end\":[778.5,114.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[778,115],\"end\":[778,115]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[777,116]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[773,120],\"end\":[756.5,136]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[740,152],\"end\":[736,164]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[439,936]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,939]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,968],\"end\":[400.5,985.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[375,1003],\"end\":[344,1003]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[335,1003],\"end\":[326,1001.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[317,1000],\"end\":[308,996]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,982],\"end\":[253,944]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[236,906],\"end\":[250,867]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[251,865]}]}]},{\"start\":[633,374],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[418,933]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[407,963],\"end\":[376.5,976.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[346,990],\"end\":[316,978]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[285,966],\"end\":[270.5,935.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,905],\"end\":[268,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[405,518]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[405,518],\"end\":[417.5,502]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[430,486],\"end\":[455,471]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[481,456],\"end\":[503.5,454]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[526,452],\"end\":[554,439]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,426],\"end\":[607.5,400]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[633,374],\"end\":[633,374]}]}]}]},{\"start\":[418,805.5],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[417,809],\"end\":[418,812]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[419,815],\"end\":[422.5,816.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,818],\"end\":[429,816]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[433,815],\"end\":[434,811.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[435,808],\"end\":[434,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[433,802],\"end\":[429.5,800.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,799],\"end\":[423,800]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[419,802],\"end\":[418,805.5]}]}]}]},{\"start\":[379,723],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[385,721],\"end\":[386.5,716]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,711],\"end\":[386,707]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,702],\"end\":[379,700]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[374,698],\"end\":[369,700]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[364,702],\"end\":[362,707]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[360,712],\"end\":[362,717]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[364,722],\"end\":[369,724]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[374,726],\"end\":[379,724]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[379,723]}]}]},{\"start\":[392,677],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[391,677]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[395,686],\"end\":[404.5,689.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[414,693],\"end\":[424,689]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[433,685],\"end\":[436.5,675.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,666],\"end\":[436,657]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[431,647],\"end\":[422,643.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,640],\"end\":[404,644]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[395,649],\"end\":[391.5,658.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,668],\"end\":[392,677]}]}]}]},{\"start\":[495,580],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,580]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,582],\"end\":[488,587]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[486,592],\"end\":[488,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,602],\"end\":[495.5,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[501,606],\"end\":[506,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,602],\"end\":[512,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,592],\"end\":[512,587]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,582],\"end\":[505,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[500,578],\"end\":[495,580]}]}]}]},{\"start\":[440,522],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[437,523],\"end\":[435.5,526.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[434,530],\"end\":[435,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[437,536],\"end\":[440,537.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[443,539],\"end\":[447,537]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,536],\"end\":[451.5,532.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[453,529],\"end\":[451,526]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,523],\"end\":[446.5,521.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[443,520],\"end\":[440,522]}]}]}]},{\"start\":[584,49],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[584,49]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,57],\"end\":[600.5,71.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[621,86],\"end\":[653,99]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[685,111],\"end\":[710,114]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[735,117],\"end\":[738,108]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[742,100],\"end\":[721.5,85.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,71],\"end\":[669,58]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[637,46],\"end\":[612,43]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,40],\"end\":[584,49]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"linkedin\",\"codePoint\":59710},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[872,873],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[872,604]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[872,506],\"end\":[839,439]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,372],\"end\":[690,372]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[635,372],\"end\":[598.5,397]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[562,422],\"end\":[547,451]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[545,451]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[545,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[399,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[399,873]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[551,873]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[551,631]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,583],\"end\":[566.5,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,505],\"end\":[642,505]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,505],\"end\":[710.5,549]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[721,593],\"end\":[721,635]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[721,873]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[872,873]}]}]},{\"start\":[228,317],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,317],\"end\":[290,291]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[316,265],\"end\":[316,229]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[316,193],\"end\":[290,167]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,141],\"end\":[228,141]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[191,141],\"end\":[165.5,167]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[140,193],\"end\":[140,229]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[140,265],\"end\":[165.5,291]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[191,317],\"end\":[228,317]}]}]}]},{\"start\":[304,384],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[152,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[152,873]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[304,873]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[304,384]}]}]},{\"start\":[948,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[948,0]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[979,0],\"end\":[1001.5,21.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,43],\"end\":[1024,74]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,950]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,981],\"end\":[1001.5,1002.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[979,1024],\"end\":[948,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[76,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[44,1024],\"end\":[22,1002.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,981],\"end\":[0,950]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,74]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,43],\"end\":[22,21.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[44,0],\"end\":[76,0]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[948,0]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"markdown\",\"codePoint\":59711},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[950,827],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[950,827]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[981,827],\"end\":[1002.5,805.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,784],\"end\":[1024,753]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,753],\"end\":[1024,753]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,753],\"end\":[1024,753]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,271]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,240],\"end\":[1002.5,218.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[981,197],\"end\":[950,197]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[74,197]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[43,197],\"end\":[21.5,218.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,240],\"end\":[0,271]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,753]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,753]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,784],\"end\":[21.5,805.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[43,827],\"end\":[74,827]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[950,827]}]}]},{\"start\":[148,679],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[148,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[246,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[345,468]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[443,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[443,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[443,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[345,610]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[246,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[246,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[148,679]}]}]},{\"start\":[758,684],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[610,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[709,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[709,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[807,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[807,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[906,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[758,684]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"npm\",\"codePoint\":59712},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1024,313],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,654]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,654]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,711]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[284,711]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[284,654]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,654]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,313]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,313]}]}]},{\"start\":[284,370],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[57,370]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[57,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[171,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[171,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[228,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[228,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[284,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[284,370]}]}]},{\"start\":[569,597],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[569,370]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[341,370]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[341,654]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[455,654]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[455,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[569,597]}]}]},{\"start\":[967,597],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[967,370]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[626,370]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[626,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[740,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[740,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[796,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[796,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[853,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[853,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[910,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[910,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[967,597]}]}]},{\"start\":[455,540],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,540]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[455,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[455,540]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"python\",\"codePoint\":59713},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[611,8],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,2]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[475,1]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[442,3]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[412,5]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,9]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[360,15]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[338,21]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[319,29]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[303,37]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[290,47]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[279,58]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[272,70]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[267,83]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[265,98]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[266,113]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[266,231]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[515,231]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[515,266]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[167,266]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[165,266]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[158,266]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[148,266]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[134,268]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[119,273]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[102,279]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[84,290]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[66,304]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[49,323]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[33,347]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[20,377]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,414]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[3,459]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,511]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[2,563]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[17,646]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[29,677]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,702]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[58,721]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[73,737]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[88,748]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[103,756]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[117,761]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[129,764]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[138,765]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[233,765]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[233,634]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[234,625]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[236,614]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[239,600]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[243,585]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[249,570]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[258,554]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[269,539]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[283,526]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[301,514]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[322,505]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[347,499]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[377,497]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[631,497]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,496]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[652,494]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[664,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[678,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[693,481]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[722,462]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[735,449]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[746,433]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[755,413]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[761,390]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[763,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[763,135]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[763,129]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[762,121]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[761,110]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,97]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,83]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[739,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[724,54]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[705,40]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,27]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[649,16]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[611,8]}]}]},{\"start\":[356,83],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[373,79]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[391,83]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[405,92]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[415,106]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[418,124]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[415,141]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[405,156]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[391,165]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[373,169]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[356,165]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,156]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,141]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[329,124]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,106]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,92]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[356,83]}]}]},{\"start\":[900,261],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[891,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[885,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[796,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[796,390]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[795,399]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[794,410]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[791,424]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[786,439]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[780,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[771,470]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[760,485]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[746,498]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[728,510]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,519]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,525]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[652,527]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,527]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[398,528]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[389,529]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[378,530]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[365,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[351,537]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[336,543]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[321,552]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[307,562]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[294,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[283,592]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,611]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[268,634]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[266,662]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[266,890]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[266,895]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[267,904]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[268,914]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[273,927]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[279,941]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[290,956]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[305,970]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[324,984]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[349,997]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,1008]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[419,1016]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[464,1022]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[518,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[587,1022]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[617,1019]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[645,1015]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[669,1009]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[691,1003]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[710,996]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,987]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[739,977]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,966]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[757,954]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[762,941]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[764,926]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[763,911]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[763,793]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,793]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,758]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,758]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[871,759]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[881,758]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[895,756]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[911,752]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[928,745]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[945,735]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[963,721]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[980,701]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[996,677]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1009,647]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1020,610]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1027,566]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1029,513]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1027,461]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1021,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1012,379]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1000,348]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[986,323]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[972,303]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[956,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[941,276]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[926,268]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[912,263]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[900,261]}]}]},{\"start\":[638,859],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[656,855]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,859]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[687,869]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[697,883]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[701,900]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[697,918]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[687,932]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,942]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[656,945]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[638,942]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,932]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[614,918]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[611,900]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[614,883]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,869]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[638,859]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"react\",\"codePoint\":59714},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,421],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,421],\"end\":[576.5,447.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[603,474],\"end\":[603,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[603,550],\"end\":[576.5,576.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,603],\"end\":[512,603]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,603]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[474,603],\"end\":[447.5,576.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[421,550],\"end\":[421,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[421,474],\"end\":[447.5,447.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[474,421],\"end\":[512,421]}]}]}]},{\"start\":[256,694],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[262,674]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[264,667]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,627],\"end\":[289.5,590]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[304,553],\"end\":[320,521]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[324,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,503]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[323,509]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[305,472],\"end\":[290,433.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,395],\"end\":[262,350]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,330]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[236,335]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[124,364],\"end\":[62,410]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,456],\"end\":[0,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,568],\"end\":[62,614]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[124,660],\"end\":[236,688]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,694]}]}]},{\"start\":[227,382],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[229,389]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[240,423],\"end\":[252.5,454]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[265,485],\"end\":[277,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[280,505]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[265,537],\"end\":[252,570]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[239,603],\"end\":[227,642]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[141,618],\"end\":[92,583]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[43,548],\"end\":[43,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[43,475],\"end\":[92,440.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[141,406],\"end\":[227,382]}]}]}]},{\"start\":[768,694],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[788,688]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[900,660],\"end\":[962,614]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,568],\"end\":[1024,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,456],\"end\":[962,410]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[900,364],\"end\":[788,335]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,330]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[762,350]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[760,357]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,396],\"end\":[734,433.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,471],\"end\":[704,503]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[699,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,521]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[701,515]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[719,551],\"end\":[734,590]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[749,629],\"end\":[762,674]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,694]}]}]},{\"start\":[747,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[744,518]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[759,487],\"end\":[772,454]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[785,421],\"end\":[797,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[883,406],\"end\":[932,440.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[981,475],\"end\":[981,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[981,548],\"end\":[932,583]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[883,618],\"end\":[797,642]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[787,610],\"end\":[774.5,577.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[762,545],\"end\":[747,512]}]}]}]},{\"start\":[227,382],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[247,377]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[240,378]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[280,368],\"end\":[321.5,361]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[363,354],\"end\":[408,351]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[418,350]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[424,341]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,338]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[449,305],\"end\":[474.5,274]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[500,243],\"end\":[527,215]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,200]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,185]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[447,103],\"end\":[375,72.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[303,42],\"end\":[256,69]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[208,97],\"end\":[198.5,173.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[189,250],\"end\":[221,362]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[227,382]}]}]},{\"start\":[307,99],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[341,99],\"end\":[386.5,125.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,152],\"end\":[482,200]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[483,200]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,225],\"end\":[438,252]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[416,279],\"end\":[395,309]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[390,309]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[354,313],\"end\":[320,318]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[286,323],\"end\":[257,330]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[235,244],\"end\":[240.5,184]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[246,124],\"end\":[277,106]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[284,102],\"end\":[291.5,100.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,99],\"end\":[307,99]}]}]}]},{\"start\":[717,968],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[717,968]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,968],\"end\":[717.5,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[718,968],\"end\":[718,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,968],\"end\":[744.5,964.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,961],\"end\":[768,954]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[816,927],\"end\":[825.5,850]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[835,773],\"end\":[803,662]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[797,642]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[777,647]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[784,646]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[744,656],\"end\":[702.5,662.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[661,669],\"end\":[616,673]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[606,674]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[600,682]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,685]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[575,718],\"end\":[549.5,749]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[524,780],\"end\":[497,809]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[482,824]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[497,839]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,901],\"end\":[614,934.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,968],\"end\":[717,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,968],\"end\":[717,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,968],\"end\":[717,968]}]}]}]},{\"start\":[542,823],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[541,824]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,799],\"end\":[586,772]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,745],\"end\":[629,715]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[633,714]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[669,711],\"end\":[703.5,705.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[738,700],\"end\":[767,693]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[789,779],\"end\":[783.5,839]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[778,899],\"end\":[747,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[740,921],\"end\":[732.5,923]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,925],\"end\":[717,925]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[717,925]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,925],\"end\":[637.5,898.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,872],\"end\":[542,823]}]}]}]},{\"start\":[803,362],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[835,250],\"end\":[825.5,173.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[816,97],\"end\":[768,69]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,42],\"end\":[648,72.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,103],\"end\":[497,185]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[482,200]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[497,215]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[496,215]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[524,243],\"end\":[549.5,274.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[575,306],\"end\":[600,341]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[606,350]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[616,351]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[619,351]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,354],\"end\":[702,361]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[742,368],\"end\":[777,377]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[797,382]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[803,362]}]}]},{\"start\":[629,309],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[627,306]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[607,278],\"end\":[585.5,251.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,225],\"end\":[542,200]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[604,139],\"end\":[659.5,113.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[715,88],\"end\":[747,106]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[778,124],\"end\":[783.5,184]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[789,244],\"end\":[767,330]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[735,323],\"end\":[700.5,317.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,312],\"end\":[629,309]}]}]}]},{\"start\":[307,968],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[353,968],\"end\":[409.5,934.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[466,901],\"end\":[527,839]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,824]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,809]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[528,809]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[500,780],\"end\":[474,749]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,718],\"end\":[424,682]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[418,674]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,673]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[365,669],\"end\":[324.5,662.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[284,656],\"end\":[247,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[227,642]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[221,662]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[189,773],\"end\":[198.5,850]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[208,927],\"end\":[256,954]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,954]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[267,961],\"end\":[279.5,964.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[292,968],\"end\":[306,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[306,968],\"end\":[306.5,968]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[307,968],\"end\":[307,968]}]}]}]},{\"start\":[257,693],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,692]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[284,700],\"end\":[320,705.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[356,711],\"end\":[395,715]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[397,718]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[417,746],\"end\":[438.5,772.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,799],\"end\":[482,823]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,884],\"end\":[364.5,910]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[309,936],\"end\":[277,918]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[246,899],\"end\":[240.5,839]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[235,779],\"end\":[257,693]}]}]}]},{\"start\":[512,720],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[538,720],\"end\":[565,719]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,718],\"end\":[619,716]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[629,715]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[635,707]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,711]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[663,668],\"end\":[690,621]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,574],\"end\":[742,521]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[747,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[742,503]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[739,495]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[716,447],\"end\":[689.5,402]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[663,357],\"end\":[635,317]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[629,309]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[619,308]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,308]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[596,306],\"end\":[568,305]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[540,304],\"end\":[512,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[484,304],\"end\":[456.5,305]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[429,306],\"end\":[405,308]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[395,309]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[389,317]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[373,339],\"end\":[359,362]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[345,385],\"end\":[331,408]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[335,402]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[321,424],\"end\":[308.5,448.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,473],\"end\":[282,503]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[277,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,521]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[285,529]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,553],\"end\":[308,575.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,598],\"end\":[331,616]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[335,622]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[348,645],\"end\":[362,666.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[376,688],\"end\":[389,707]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[395,715]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[405,716]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,718],\"end\":[459,719]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[486,720],\"end\":[512,720]}]}]}]},{\"start\":[418,674],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[421,678]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[395,640],\"end\":[371,599.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[347,559],\"end\":[325,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[328,505]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[348,463],\"end\":[371,424]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[394,385],\"end\":[418,350]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[414,350]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,348],\"end\":[462.5,347]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[487,346],\"end\":[512,346]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,346],\"end\":[561,347]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[585,348],\"end\":[606,350]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[603,346]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,383],\"end\":[653,424]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,465],\"end\":[699,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[696,519]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[676,561],\"end\":[653,600]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[630,639],\"end\":[606,674]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[558,677],\"end\":[512,677]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[466,677],\"end\":[418,674]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"stackexchange\",\"codePoint\":59715},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[927,665],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,665]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,709]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[97,765],\"end\":[136,804.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[175,844],\"end\":[229,844]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,844]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,844]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[795,844]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[849,844],\"end\":[888,804.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[927,765],\"end\":[927,709]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[927,665]}]}]},{\"start\":[97,446],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,616]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,616]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,446]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,446]}]}]},{\"start\":[97,227],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,397]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,397]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,227]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,227]}]}]},{\"start\":[793,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[229,0]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[175,0],\"end\":[136,39.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[97,79],\"end\":[97,136]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,180]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,180]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,136]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[922,79],\"end\":[884,39.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[846,0],\"end\":[793,0]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"stackoverflow\",\"codePoint\":59716},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[810,933],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,933]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[79,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[79,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[901,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[901,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,660]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,933]}]}]},{\"start\":[261,751],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[719,751]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[719,842]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[261,842]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[261,751]}]}]},{\"start\":[272,635],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[291,547]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[738,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[719,728]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[272,635]}]}]},{\"start\":[330,419],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[368,336]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[782,529]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[744,612]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[330,419]}]}]},{\"start\":[446,215],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[446,215]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[504,146]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,438]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[796,507]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[446,215]}]}]},{\"start\":[945,367],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[871,422]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,55]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[945,367]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"telegram\",\"codePoint\":59717},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1020,161],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1032,114],\"end\":[1008.5,94]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[985,74],\"end\":[954,87]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[47,437]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1,456],\"end\":[0,478.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[-1,501],\"end\":[36,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[269,585]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[350,849]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,870],\"end\":[360.5,879.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[363,889],\"end\":[386,889]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[403,889],\"end\":[413,882]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[423,875],\"end\":[433,865]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[546,756]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[781,929]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[813,947],\"end\":[835.5,937.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,928],\"end\":[866,889]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1020,162]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1020,161]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"twitter\",\"codePoint\":59718},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1022,195],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,196]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1003,227],\"end\":[976.5,254.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[950,282],\"end\":[919,305]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[920,311],\"end\":[920,318]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[920,325],\"end\":[920,331]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[920,435],\"end\":[881,541]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[842,648],\"end\":[766,734]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[690,820],\"end\":[579,874]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[467,928],\"end\":[322,928]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,928],\"end\":[235,922]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,915],\"end\":[151.5,903]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[111,891],\"end\":[73,874]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[35,856],\"end\":[0,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[13,835],\"end\":[25,836]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[37,837],\"end\":[50,837]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[123,837],\"end\":[190,813]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[257,789],\"end\":[310,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[241,746],\"end\":[187.5,705]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,664],\"end\":[114,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[123,603],\"end\":[133,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[143,605],\"end\":[153,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[167,605],\"end\":[181,603]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[195,601],\"end\":[208,598]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,583],\"end\":[88,525.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[40,468],\"end\":[40,392]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[40,389]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[61,401],\"end\":[85,407.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[109,414],\"end\":[135,415]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[93,387],\"end\":[67.5,341]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,295],\"end\":[42,240]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,211],\"end\":[49,184.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[56,158],\"end\":[70,135]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[109,182],\"end\":[157,221]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[205,260],\"end\":[260,288.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[315,317],\"end\":[376,334]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[437,350],\"end\":[503,354]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[500,342],\"end\":[498.5,330]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,318],\"end\":[497,306]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,262],\"end\":[514,224]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,186],\"end\":[558.5,157.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,129],\"end\":[625,112]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[664,96],\"end\":[707,96]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[753,96],\"end\":[792.5,114]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,132],\"end\":[860,162]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,155],\"end\":[929.5,142.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[963,130],\"end\":[994,112]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,149],\"end\":[958,178.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[934,208],\"end\":[902,228]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[933,224],\"end\":[963.5,215.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[994,207],\"end\":[1022,195]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"visualstudiocode\",\"codePoint\":59719},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,765]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,0]}]}]},{\"start\":[103,595],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,484]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,663]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,190]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,369]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[103,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,294]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[187,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,559]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[103,595]}]}]},{\"start\":[512,287],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,567]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[326,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,287]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"webpack\",\"codePoint\":59720},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[897,773],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[757,693]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,819]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,981]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[897,773]}]}]},{\"start\":[922,750],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[787,673]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[787,393]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,315]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,750]}]}]},{\"start\":[125,773],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[264,693]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,819]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,981]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[125,773]}]}]},{\"start\":[100,750],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[235,673]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[235,393]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[100,315]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[100,750]}]}]},{\"start\":[115,287],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,364]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,230]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,73]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[115,287]}]}]},{\"start\":[906,287],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[772,364]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,230]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,73]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[906,287]}]}]},{\"start\":[495,782],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,542]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[267,411]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[267,658]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[495,782]}]}]},{\"start\":[527,782],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,542]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[754,411]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[754,658]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[527,782]}]}]},{\"start\":[283,383],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,514]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[739,383]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,258]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[283,383]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"yarn\",\"codePoint\":59721},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[711,40],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[711,40]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[805,80],\"end\":[874.5,149.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[944,219],\"end\":[984,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,406],\"end\":[1024,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,618],\"end\":[984,711]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[944,805],\"end\":[874.5,874.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[805,944],\"end\":[711,984]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,1024],\"end\":[512,1024]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,1024],\"end\":[313,984]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[219,944],\"end\":[149.5,874.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[80,805],\"end\":[40,711]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,618],\"end\":[0,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,406],\"end\":[40,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[80,219],\"end\":[149.5,149.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[219,80],\"end\":[313,40]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,0],\"end\":[512,0]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,0],\"end\":[711,40]}]}]}]},{\"start\":[545,175],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[545,175]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[541,175],\"end\":[537.5,175.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[534,176],\"end\":[531,178]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[518,182],\"end\":[507.5,194]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[497,206],\"end\":[487,227]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[485,231],\"end\":[484,234]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[483,237],\"end\":[482,240]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[481,240]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[452,242],\"end\":[427,254]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[402,266],\"end\":[384,287]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,290],\"end\":[374.5,292.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[369,295],\"end\":[363,298]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[363,298]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[350,302],\"end\":[342.5,313.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[335,325],\"end\":[328,344]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[318,371],\"end\":[325,396]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[332,421],\"end\":[342,438]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[328,451],\"end\":[312,470]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,489],\"end\":[286,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[286,511]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,532],\"end\":[273,554.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[268,577],\"end\":[268,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[268,603],\"end\":[268,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[268,607],\"end\":[268,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[258,619],\"end\":[245.5,638.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[233,658],\"end\":[231,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[228,714],\"end\":[236.5,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[245,756],\"end\":[250,764]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,765]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[252,767],\"end\":[253.5,769]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[255,771],\"end\":[257,773]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[257,772]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,774],\"end\":[256,776.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,779],\"end\":[256,781]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,794],\"end\":[263,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,816],\"end\":[281,822]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[301,832],\"end\":[326,835]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[351,838],\"end\":[372,828]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,836],\"end\":[394.5,842.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[409,849],\"end\":[434,849]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,849]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[442,849],\"end\":[501,845]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[560,841],\"end\":[584,835]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[584,835]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[593,833],\"end\":[600,829.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[607,826],\"end\":[614,821]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,816],\"end\":[666,801.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[703,787],\"end\":[742,761]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[770,743],\"end\":[784.5,736.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[799,730],\"end\":[820,725]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[820,725]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[838,721],\"end\":[849.5,707]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[861,693],\"end\":[861,674]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[861,672],\"end\":[861,670.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[861,669],\"end\":[860,667]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,646],\"end\":[841.5,633]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[825,620],\"end\":[803,620]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,621],\"end\":[740.5,634.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[713,648],\"end\":[694,660]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[695,659]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[689,663],\"end\":[682.5,666.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[676,670],\"end\":[668,674]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[669,657],\"end\":[667,635]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[665,613],\"end\":[656,588]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,558],\"end\":[632,539.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[619,521],\"end\":[609,510]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[621,492],\"end\":[635,466]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[649,440],\"end\":[657,400]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[664,366],\"end\":[661,321]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[658,276],\"end\":[643,246]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,240],\"end\":[634.5,236]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,232],\"end\":[623,230]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,230],\"end\":[614.5,229]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[609,228],\"end\":[599,231]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[584,200],\"end\":[577.5,192.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[571,185],\"end\":[567,182]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[562,179],\"end\":[556.5,177]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,175],\"end\":[545,175]}]}]}]},{\"start\":[545,205],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[545,205]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[545,205],\"end\":[545,205]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[545,205],\"end\":[545,205]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[547,205],\"end\":[548.5,205.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,206],\"end\":[552,207]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,211],\"end\":[571,240.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[585,270],\"end\":[585,270]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[585,270],\"end\":[599.5,262.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,255],\"end\":[617,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,285],\"end\":[631.5,325.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[634,366],\"end\":[628,394]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[619,442],\"end\":[600,469.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,497],\"end\":[571,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[569,517],\"end\":[590,533.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[611,550],\"end\":[629,598]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,643],\"end\":[638.5,675.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[632,708],\"end\":[634,712]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[635,714]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[635,714],\"end\":[653.5,711]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,708],\"end\":[709,685]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[729,673],\"end\":[752.5,661.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[776,650],\"end\":[803,650]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[829,650],\"end\":[831,670.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[833,691],\"end\":[813,696]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[790,702],\"end\":[773,709.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[756,717],\"end\":[727,736]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[681,766],\"end\":[640,780]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[599,794],\"end\":[599,794]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[599,794],\"end\":[594,798.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[589,803],\"end\":[577,806]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,811],\"end\":[499.5,815]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[443,819],\"end\":[436,819]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[417,819],\"end\":[405,814.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,810],\"end\":[390,802]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,778],\"end\":[394.5,767]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[409,756],\"end\":[409,756]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[409,756],\"end\":[405,753.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[401,751],\"end\":[398,748]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[395,745],\"end\":[392.5,741]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[390,737],\"end\":[389,739]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[385,749],\"end\":[381.5,766.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[378,784],\"end\":[368,794]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[355,808],\"end\":[333,806]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[311,804],\"end\":[295,796]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[279,787],\"end\":[288,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[297,756],\"end\":[297,756]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[297,756],\"end\":[289.5,758]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[282,760],\"end\":[275,749]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[269,739],\"end\":[264,722]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[259,705],\"end\":[261,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,660],\"end\":[280.5,640.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,621],\"end\":[299,621]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,621],\"end\":[298.5,591]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,561],\"end\":[313,524]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[327,491],\"end\":[355,467.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[383,444],\"end\":[383,444]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[383,444],\"end\":[363.5,415]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[344,386],\"end\":[356,354]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[364,334],\"end\":[367.5,330.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[371,327],\"end\":[374,326]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[383,322],\"end\":[391,318]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[399,314],\"end\":[406,307]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,273],\"end\":[470,271.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[502,270],\"end\":[502,270]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[502,270],\"end\":[515.5,238]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[529,206],\"end\":[545,205]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"youtube\",\"codePoint\":59722},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1002,265],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1001,257]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1012,316],\"end\":[1018,377.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,439],\"end\":[1024,502]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,505],\"end\":[1024,507.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,510],\"end\":[1024,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,512]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,514],\"end\":[1024,516.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,519],\"end\":[1024,521]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,584],\"end\":[1018.5,645]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1013,706],\"end\":[1002,759]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1002,760]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[993,792],\"end\":[969.5,815.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[946,839],\"end\":[913,848]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[883,856],\"end\":[817,861]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[750,865],\"end\":[682.5,867]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,869],\"end\":[564,869]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,870],\"end\":[512,870]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,870],\"end\":[461,869]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,869],\"end\":[342,867]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[274,865],\"end\":[208,861]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[141,856],\"end\":[112,848]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[111,848]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[79,839],\"end\":[55.5,815.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,792],\"end\":[22,759]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[24,767]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[12,707],\"end\":[6,644]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,581],\"end\":[0,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,515],\"end\":[0,514]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,513],\"end\":[0,512]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,513]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,511],\"end\":[0,510]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,509],\"end\":[0,507]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,443],\"end\":[6,381]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[12,319],\"end\":[22,265]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[23,264]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[32,232],\"end\":[55.5,208.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[79,185],\"end\":[112,176]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[142,167],\"end\":[208,163]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,159],\"end\":[342.5,157]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,155],\"end\":[461,155]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,154],\"end\":[512,154]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[512,154],\"end\":[564,155]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[615,155],\"end\":[683,157]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[751,159],\"end\":[817,163]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[883,168],\"end\":[913,176]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[914,176]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[946,185],\"end\":[969.5,208.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[993,232],\"end\":[1002,265]}]}]}]},{\"start\":[677,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[410,359]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[410,666]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[677,512]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"error\",\"codePoint\":59723},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[470,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]}]},{\"start\":[470,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"error_outline\",\"codePoint\":59724},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[470,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]}]},{\"start\":[470,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"warningreport_problem\",\"codePoint\":59725},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[470,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,619]}]}]},{\"start\":[470,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,789]}]}]},{\"start\":[982,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,917]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"library_addqueueadd_to_photos\",\"codePoint\":59726},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,491]}]}]},{\"start\":[342,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,107],\"end\":[282,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,157],\"end\":[256,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,737],\"end\":[282,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,789],\"end\":[342,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,789],\"end\":[913,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,737],\"end\":[938,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,157],\"end\":[913,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,107],\"end\":[854,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,107]}]}]},{\"start\":[86,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,909],\"end\":[111,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,959],\"end\":[170,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"library_music\",\"codePoint\":59727},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[86,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,909],\"end\":[111,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,959],\"end\":[170,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,277]}]}]},{\"start\":[640,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,555]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,599],\"end\":[609,630]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,661],\"end\":[534,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,661],\"end\":[458,630]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,599],\"end\":[426,555]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,511],\"end\":[458,479]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,447],\"end\":[534,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[570,447],\"end\":[598,469]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,319]}]}]},{\"start\":[342,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,107],\"end\":[282,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,157],\"end\":[256,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,737],\"end\":[282,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,789],\"end\":[342,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,789],\"end\":[913,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,737],\"end\":[938,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,157],\"end\":[913,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,107],\"end\":[854,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,107]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"new_releases\",\"codePoint\":59728},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[470,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]}]},{\"start\":[470,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]}]},{\"start\":[878,415],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[892,257]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[738,223]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,87]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,87]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[286,223]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[132,257]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[146,413]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[146,651]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[132,809]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[286,845]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,979]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,979]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[738,843]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[892,809]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[878,651]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[878,415]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"not_interesteddo_not_disturb\",\"codePoint\":59729},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[302,263],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[340,233],\"end\":[402,212]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,653],\"end\":[782,743]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[302,263]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,413],\"end\":[242,323]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[722,803]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[684,833],\"end\":[622,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[560,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"pause\",\"codePoint\":59730},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,831]}]}]},{\"start\":[426,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"pause_circle_filled\",\"codePoint\":59731},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,703]}]}]},{\"start\":[384,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,703]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"pause_circle_outline\",\"codePoint\":59732},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,703]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[470,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,703]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"play_arrow\",\"codePoint\":59733},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[342,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"play_circle_filled\",\"codePoint\":59734},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[426,341],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,341]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"play_circle_outline\",\"codePoint\":59735},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[682,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,341]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"repeat\",\"codePoint\":59736},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[298,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,747]}]}]},{\"start\":[726,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"repeat_one\",\"codePoint\":59737},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]}]},{\"start\":[298,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,747]}]}]},{\"start\":[726,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"replay\",\"codePoint\":59738},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,63],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,319]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,319],\"end\":[693,394]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,469],\"end\":[768,575]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,681],\"end\":[693,756]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,831],\"end\":[512,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,831],\"end\":[331,756]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,681],\"end\":[256,575]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,575]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,717],\"end\":[271,817]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,917],\"end\":[512,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,917],\"end\":[753,817]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,717],\"end\":[854,575]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,435],\"end\":[754,335]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,235],\"end\":[512,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,63]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"shuffle\",\"codePoint\":59739},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[572,653],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,787]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,639]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[766,727]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,653]}]}]},{\"start\":[706,279],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[230,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[766,339]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,279]}]}]},{\"start\":[230,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,251]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,413]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[230,191]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"skip_next\",\"codePoint\":59740},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,789]}]}]},{\"start\":[618,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"skip_previous\",\"codePoint\":59741},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[406,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,789]}]}]},{\"start\":[256,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"emailmailmarkunreadlocal_post_office\",\"codePoint\":59742},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,575]}]}]},{\"start\":[170,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,243],\"end\":[913,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,191],\"end\":[854,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"vpn_key\",\"codePoint\":59743},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[239,593],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[239,593]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,567],\"end\":[214,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,499],\"end\":[239,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,447],\"end\":[298,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[332,447],\"end\":[358,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,499],\"end\":[384,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,567],\"end\":[358,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[332,619],\"end\":[298,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,619],\"end\":[239,593]}]}]}]},{\"start\":[443,327],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[443,327]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,277],\"end\":[298,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,277],\"end\":[117,352]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,427],\"end\":[42,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,639],\"end\":[117,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,789],\"end\":[298,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,789],\"end\":[443,739]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,689],\"end\":[540,619]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[540,447]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,377],\"end\":[443,327]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"add\",\"codePoint\":59744},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[810,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"add_box\",\"codePoint\":59745},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]}]},{\"start\":[214,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,199],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,201],\"end\":[870,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"add_circle\",\"codePoint\":59746},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"add_circle_outlinecontrol_point\",\"codePoint\":59747},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[470,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"block\",\"codePoint\":59748},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[302,803],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[302,803]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[782,323]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[812,361],\"end\":[833,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,485],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[392,875],\"end\":[302,803]}]}]}]},{\"start\":[271,292],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,292]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[632,191],\"end\":[722,263]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[242,743]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[212,705],\"end\":[191,643]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,581],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"clearclose\",\"codePoint\":59749},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[750,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,295]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,771]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,771]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,295]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,235]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"copy\",\"codePoint\":59750},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[342,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,917]}]}]},{\"start\":[342,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,235],\"end\":[282,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,285],\"end\":[256,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,951],\"end\":[282,977]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,1003],\"end\":[342,1003]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,1003]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,1003],\"end\":[870,977]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,951],\"end\":[896,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,319]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,285],\"end\":[870,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,235],\"end\":[810,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,235]}]}]},{\"start\":[170,63],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,63],\"end\":[111,89]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,115],\"end\":[86,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,63]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cut\",\"codePoint\":59751},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]}]},{\"start\":[490,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,511],\"end\":[512,511]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[534,511],\"end\":[534,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[534,555],\"end\":[512,555]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,555],\"end\":[490,533]}]}]}]},{\"start\":[196,850],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[196,850]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,825],\"end\":[170,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,753],\"end\":[196,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,703],\"end\":[256,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,703],\"end\":[316,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,753],\"end\":[342,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,825],\"end\":[316,850]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,875],\"end\":[256,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,875],\"end\":[196,850]}]}]}]},{\"start\":[196,338],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[196,338]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,313],\"end\":[170,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,241],\"end\":[196,216]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,191],\"end\":[256,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,191],\"end\":[316,216]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,241],\"end\":[342,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,313],\"end\":[316,338]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,363],\"end\":[256,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,363],\"end\":[196,338]}]}]}]},{\"start\":[426,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,207],\"end\":[376,157]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,107],\"end\":[256,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[186,107],\"end\":[136,157]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,207],\"end\":[86,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,347],\"end\":[136,397]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[186,447],\"end\":[256,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,447],\"end\":[326,433]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[326,633]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,619],\"end\":[256,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[186,619],\"end\":[136,669]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,719],\"end\":[86,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,859],\"end\":[136,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[186,959],\"end\":[256,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,959],\"end\":[376,909]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,859],\"end\":[426,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,749],\"end\":[412,719]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[412,347]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,317],\"end\":[426,277]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"paste\",\"codePoint\":59752},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,875]}]}]},{\"start\":[542,119],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[542,119]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,131],\"end\":[554,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,167],\"end\":[542,179]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[494,191],\"end\":[482,179]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,167],\"end\":[470,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,131],\"end\":[482,119]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[494,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,107],\"end\":[542,119]}]}]}]},{\"start\":[632,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,69],\"end\":[586,45]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,21],\"end\":[512,21]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,21],\"end\":[438,45]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,69],\"end\":[392,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,107]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,107],\"end\":[154,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,157],\"end\":[128,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,909],\"end\":[154,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,959],\"end\":[214,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,959],\"end\":[870,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,909],\"end\":[896,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,157],\"end\":[870,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,107],\"end\":[810,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,107]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"edit\",\"codePoint\":59753},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[896,291],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,291]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,273],\"end\":[884,261]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[784,161]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[772,149],\"end\":[754,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,149],\"end\":[724,161]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[646,239]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[806,399]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[884,321]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,309],\"end\":[896,291]}]}]}]},{\"start\":[128,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[760,445]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[600,285]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,757]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,917]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"drafts\",\"codePoint\":59754},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[160,355],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,355]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[160,355]}]}]},{\"start\":[898,289],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[898,289]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[126,289]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,313],\"end\":[86,363]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,313],\"end\":[898,289]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"forward\",\"codePoint\":59755},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,363]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"remove\",\"codePoint\":59756},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[810,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"remove_circledo_not_disturb_on\",\"codePoint\":59757},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[298,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"remove_circle_outline\",\"codePoint\":59758},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[298,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"send\",\"codePoint\":59759},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[982,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"undo\",\"codePoint\":59760},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[238,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[238,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[314,549]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,469],\"end\":[534,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,469],\"end\":[735,534]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[824,599],\"end\":[858,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[958,671]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[912,535],\"end\":[796,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,363],\"end\":[534,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[364,363],\"end\":[238,473]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"save_alt\",\"codePoint\":59761},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,561]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,451]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,511]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,511]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,451]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,561]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,149]}]}]},{\"start\":[810,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,865],\"end\":[154,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"file_copy\",\"codePoint\":59762},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,299],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,299]}]}]},{\"start\":[342,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,235],\"end\":[282,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,285],\"end\":[256,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,951],\"end\":[281,977]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[306,1003],\"end\":[340,1003]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,1003]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,1003],\"end\":[870,977]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,951],\"end\":[896,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,235]}]}]},{\"start\":[170,63],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,63],\"end\":[111,89]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,115],\"end\":[86,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,63]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"sd_storagesd_card\",\"codePoint\":59763},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,363]}]}]},{\"start\":[554,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,363]}]}]},{\"start\":[426,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,363]}]}]},{\"start\":[426,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[172,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,909],\"end\":[196,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,959],\"end\":[256,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,959],\"end\":[828,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,909],\"end\":[854,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,157],\"end\":[828,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,107],\"end\":[768,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,107]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"attach_file\",\"codePoint\":59764},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[704,767],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,837],\"end\":[654,888]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[604,939],\"end\":[534,939]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,939],\"end\":[413,888]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,837],\"end\":[362,767]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,191],\"end\":[394,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,127],\"end\":[470,127]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,127],\"end\":[545,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,191],\"end\":[576,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,683]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,701],\"end\":[564,713]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[552,725],\"end\":[534,725]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[516,725],\"end\":[503,713]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,701],\"end\":[490,683]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,683]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,727],\"end\":[458,758]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[490,789],\"end\":[534,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,789],\"end\":[609,758]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,727],\"end\":[640,683]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,165],\"end\":[590,114]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[540,63],\"end\":[470,63]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[400,63],\"end\":[349,114]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,165],\"end\":[298,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,767]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,865],\"end\":[367,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,1003],\"end\":[534,1003]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[632,1003],\"end\":[700,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,865],\"end\":[768,767]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,767]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"attach_money\",\"codePoint\":59765},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[376,395],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,395]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[376,359],\"end\":[407,337]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,315],\"end\":[490,315]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[594,315],\"end\":[598,405]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[692,405]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[690,347],\"end\":[655,303]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,259],\"end\":[554,243]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,241]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,255],\"end\":[320,296]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,337],\"end\":[278,395]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,525],\"end\":[478,571]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[606,603],\"end\":[606,675]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[606,705],\"end\":[579,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[552,751],\"end\":[490,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,751],\"end\":[364,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[270,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[274,727],\"end\":[317,769]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[360,811],\"end\":[426,825]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,825]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[622,813],\"end\":[663,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,735],\"end\":[704,673]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,629],\"end\":[687,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,565],\"end\":[638,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[606,523],\"end\":[577,511]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,499],\"end\":[504,487]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[376,453],\"end\":[376,395]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"format_bold\",\"codePoint\":59766},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[426,683],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,555]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,555]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[604,555],\"end\":[622,574]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,593],\"end\":[640,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,645],\"end\":[622,664]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[604,683],\"end\":[576,683]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,683]}]}]},{\"start\":[554,299],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,299],\"end\":[599,318]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,337],\"end\":[618,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,389],\"end\":[599,408]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,427],\"end\":[554,427]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,299]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,299]}]}]},{\"start\":[736,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,291],\"end\":[687,241]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[638,191],\"end\":[566,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[600,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[668,789],\"end\":[713,742]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[758,695],\"end\":[758,627]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[758,523],\"end\":[666,481]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,435],\"end\":[736,363]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"format_color_fill\",\"codePoint\":59767},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[0,1045],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,1045]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,1045]}]}]},{\"start\":[788,537],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,561],\"end\":[747,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,637],\"end\":[726,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,695],\"end\":[751,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[776,747],\"end\":[810,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,747],\"end\":[870,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,695],\"end\":[896,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,605],\"end\":[810,511]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[788,537]}]}]},{\"start\":[426,243],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[222,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,243]}]}]},{\"start\":[326,21],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[264,81]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,183]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[146,403]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,421],\"end\":[128,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,475],\"end\":[146,493]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[382,727]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[402,747],\"end\":[426,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[452,747],\"end\":[472,727]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,493]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,475],\"end\":[726,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,423],\"end\":[706,403]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[326,21]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"music_video\",\"codePoint\":59768},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[380,751],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,751]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[418,789],\"end\":[470,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,789],\"end\":[560,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,715],\"end\":[598,663]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,541]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[484,533],\"end\":[470,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[418,533],\"end\":[380,571]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,609],\"end\":[342,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,713],\"end\":[380,751]}]}]}]},{\"start\":[128,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]}]},{\"start\":[128,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,149],\"end\":[68,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,201],\"end\":[42,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,865],\"end\":[68,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,917],\"end\":[128,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,917],\"end\":[956,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,865],\"end\":[982,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,201],\"end\":[956,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,149],\"end\":[896,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"format_size\",\"codePoint\":59769},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[256,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,533]}]}]},{\"start\":[384,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"format_underlined\",\"codePoint\":59770},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,917]}]}]},{\"start\":[693,672],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[693,672]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,597],\"end\":[768,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,491]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,553],\"end\":[618,596]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,639],\"end\":[512,639]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,639],\"end\":[406,596]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,553],\"end\":[362,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,491]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,597],\"end\":[331,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,747],\"end\":[512,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,747],\"end\":[693,672]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"insert_chartpollassessment\",\"codePoint\":59771},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,747]}]}]},{\"start\":[470,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]}]},{\"start\":[298,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,747]}]}]},{\"start\":[214,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,149],\"end\":[154,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,201],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,865],\"end\":[154,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,201],\"end\":[870,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"insert_emoticontag_facesmood\",\"codePoint\":59772},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[645,726],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[645,726]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,685],\"end\":[730,619]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[294,619]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,685],\"end\":[379,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,767],\"end\":[512,767]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[586,767],\"end\":[645,726]}]}]}]},{\"start\":[407,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[407,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,453],\"end\":[426,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,401],\"end\":[407,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,363],\"end\":[362,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,363],\"end\":[317,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,401],\"end\":[298,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,453],\"end\":[317,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,491],\"end\":[362,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,491],\"end\":[407,472]}]}]}]},{\"start\":[707,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,453],\"end\":[726,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,401],\"end\":[707,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,363],\"end\":[662,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,363],\"end\":[617,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,401],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,453],\"end\":[617,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,491],\"end\":[662,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,491],\"end\":[707,472]}]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"insert_invitationevent\",\"codePoint\":59773},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]}]},{\"start\":[682,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,201],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,201],\"end\":[870,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,149]}]}]},{\"start\":[512,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"image\",\"codePoint\":59774},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[470,725],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,725]}]}]},{\"start\":[896,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,201],\"end\":[870,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,149],\"end\":[154,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,201],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,865],\"end\":[154,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"publish\",\"codePoint\":59775},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[384,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,619]}]}]},{\"start\":[214,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"vertical_align_bottom\",\"codePoint\":59776},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,917]}]}]},{\"start\":[554,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"vertical_align_top\",\"codePoint\":59777},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,235]}]}]},{\"start\":[470,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"monetization_on\",\"codePoint\":59778},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[572,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[458,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[458,791]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[324,761],\"end\":[318,647]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,647]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,727],\"end\":[516,727]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,727],\"end\":[595,706]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,685],\"end\":[618,659]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,593],\"end\":[504,567]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,523],\"end\":[326,411]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,357],\"end\":[363,322]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[400,287],\"end\":[458,275]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[458,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,275]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[690,305],\"end\":[694,419]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[610,419]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[606,339],\"end\":[516,339]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[468,339],\"end\":[441,359]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[414,379],\"end\":[414,409]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[414,463],\"end\":[526,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,537],\"end\":[706,659]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,767],\"end\":[572,793]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,875]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cloud\",\"codePoint\":59779},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[713,267],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[713,267]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,191],\"end\":[346,239]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,287],\"end\":[228,365]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,375],\"end\":[67,450]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,525],\"end\":[0,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,725],\"end\":[75,800]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[150,875],\"end\":[256,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[898,875],\"end\":[961,812]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,749],\"end\":[1024,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,577],\"end\":[966,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,455],\"end\":[826,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,343],\"end\":[713,267]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cloud_done\",\"codePoint\":59780},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[278,597],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[338,537]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,625]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,465]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[278,597]}]}]},{\"start\":[713,267],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[713,267]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,191],\"end\":[346,239]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,287],\"end\":[228,365]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,375],\"end\":[67,450]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,525],\"end\":[0,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,725],\"end\":[75,800]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[150,875],\"end\":[256,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[898,875],\"end\":[961,812]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,749],\"end\":[1024,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,577],\"end\":[966,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,455],\"end\":[826,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,343],\"end\":[713,267]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cloud_download\",\"codePoint\":59781},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,789]}]}]},{\"start\":[713,267],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[713,267]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,191],\"end\":[346,239]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,287],\"end\":[228,365]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,375],\"end\":[67,450]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,525],\"end\":[0,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,725],\"end\":[75,800]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[150,875],\"end\":[256,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[898,875],\"end\":[961,812]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,749],\"end\":[1024,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,577],\"end\":[966,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,455],\"end\":[826,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,343],\"end\":[713,267]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cloud_uploadbackup\",\"codePoint\":59782},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,747]}]}]},{\"start\":[713,267],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[713,267]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,191],\"end\":[346,239]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[270,287],\"end\":[228,365]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,375],\"end\":[67,450]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,525],\"end\":[0,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,725],\"end\":[75,800]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[150,875],\"end\":[256,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[898,875],\"end\":[961,812]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,749],\"end\":[1024,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,577],\"end\":[966,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[908,455],\"end\":[826,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,343],\"end\":[713,267]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"file_downloadget_app\",\"codePoint\":59783},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,875]}]}]},{\"start\":[640,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,405]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"file_upload\",\"codePoint\":59784},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,875]}]}]},{\"start\":[640,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,703]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"audiotrack\",\"codePoint\":59785},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,545],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[476,533],\"end\":[448,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[368,533],\"end\":[312,589]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,645],\"end\":[256,725]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,805],\"end\":[312,861]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[368,917],\"end\":[448,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,917],\"end\":[576,868]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[630,819],\"end\":[638,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,545]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"music_note\",\"codePoint\":59786},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,599],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[468,575],\"end\":[426,575]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[356,575],\"end\":[306,626]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,677],\"end\":[256,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,817],\"end\":[306,867]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[356,917],\"end\":[426,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[496,917],\"end\":[547,867]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,817],\"end\":[598,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,599]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"movie_filter\",\"codePoint\":59787},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[642,531]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[642,451]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[722,451]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[722,531]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,619]}]}]},{\"start\":[426,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[374,671]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[374,565]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,565]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,671]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,789]}]}]},{\"start\":[854,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"local_moviestheaters\",\"codePoint\":59788},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,405]}]}]},{\"start\":[682,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,575]}]}]},{\"start\":[682,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,747]}]}]},{\"start\":[256,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,405]}]}]},{\"start\":[256,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,575]}]}]},{\"start\":[256,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,747]}]}]},{\"start\":[768,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,235]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_arrow_down\",\"codePoint\":59789},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[256,447],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,387]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,583]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,387]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,447]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_arrow_left\",\"codePoint\":59790},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[462,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[462,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_arrow_right\",\"codePoint\":59791},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[426,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[562,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,789]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_arrow_up\",\"codePoint\":59792},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,483],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,483]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_backspace\",\"codePoint\":59793},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[292,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[444,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[444,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[292,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[292,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_capslock\",\"codePoint\":59794},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,789]}]}]},{\"start\":[708,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,515]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,515]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,379]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,575]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_hide\",\"codePoint\":59795},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,1003]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]}]},{\"start\":[726,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,363]}]}]},{\"start\":[726,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,491]}]}]},{\"start\":[598,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,363]}]}]},{\"start\":[598,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,491]}]}]},{\"start\":[342,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,661]}]}]},{\"start\":[214,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,363]}]}]},{\"start\":[214,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,491]}]}]},{\"start\":[426,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,405]}]}]},{\"start\":[426,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,277]}]}]},{\"start\":[554,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]}]},{\"start\":[554,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,277]}]}]},{\"start\":[170,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,149],\"end\":[111,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,201],\"end\":[86,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,695],\"end\":[111,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,747],\"end\":[170,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,747],\"end\":[913,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,695],\"end\":[938,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,201],\"end\":[913,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,149],\"end\":[854,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_tab\",\"codePoint\":59796},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[854,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]}]},{\"start\":[648,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[494,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[494,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_voice\",\"codePoint\":59797},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[671,689],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[671,689]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[604,751],\"end\":[512,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,751],\"end\":[353,689]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[286,627],\"end\":[286,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,641],\"end\":[289,722]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[364,803],\"end\":[470,819]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,819]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[660,803],\"end\":[735,722]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,641],\"end\":[810,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[738,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[738,627],\"end\":[671,689]}]}]}]},{\"start\":[602,623],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[602,623]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,585],\"end\":[640,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,225],\"end\":[602,187]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,149],\"end\":[512,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,149],\"end\":[422,187]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,225],\"end\":[384,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,585],\"end\":[422,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,661],\"end\":[512,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,661],\"end\":[602,623]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"laptop_chromebook\",\"codePoint\":59798},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,661]}]}]},{\"start\":[426,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,789]}]}]},{\"start\":[938,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"laptop_mac\",\"codePoint\":59799},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[482,819],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[482,819]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,807],\"end\":[470,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,771],\"end\":[482,759]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[494,747],\"end\":[512,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,747],\"end\":[542,759]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,771],\"end\":[554,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,807],\"end\":[542,819]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,831],\"end\":[512,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[494,831],\"end\":[482,819]}]}]}]},{\"start\":[854,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,235]}]}]},{\"start\":[913,763],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[913,763]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,737],\"end\":[938,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,201],\"end\":[913,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,149],\"end\":[854,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,149],\"end\":[111,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,201],\"end\":[86,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,737],\"end\":[111,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,789],\"end\":[170,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,823],\"end\":[26,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[52,875],\"end\":[86,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[972,875],\"end\":[998,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,823],\"end\":[1024,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,789],\"end\":[913,763]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"laptop_windows\",\"codePoint\":59800},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[854,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,235]}]}]},{\"start\":[854,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,747],\"end\":[913,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,695],\"end\":[938,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,201],\"end\":[913,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,149],\"end\":[854,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,149],\"end\":[111,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,201],\"end\":[86,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,695],\"end\":[111,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,747],\"end\":[170,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,747]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"phone_android\",\"codePoint\":59801},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[288,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,789]}]}]},{\"start\":[426,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,917]}]}]},{\"start\":[342,63],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,63],\"end\":[252,101]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,139],\"end\":[214,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,927],\"end\":[252,965]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,1003],\"end\":[342,1003]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,1003]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,1003],\"end\":[772,965]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,927],\"end\":[810,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,139],\"end\":[772,101]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,63],\"end\":[682,63]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,63]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"phone_iphone\",\"codePoint\":59802},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[298,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,789]}]}]},{\"start\":[445,940],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[445,940]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,921],\"end\":[426,895]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,869],\"end\":[445,850]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,831],\"end\":[490,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[516,831],\"end\":[535,850]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,869],\"end\":[554,895]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,921],\"end\":[535,940]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[516,959],\"end\":[490,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[464,959],\"end\":[445,940]}]}]}]},{\"start\":[320,63],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,63],\"end\":[245,95]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,127],\"end\":[214,171]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,895]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,939],\"end\":[245,971]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,1003],\"end\":[320,1003]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,1003]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,1003],\"end\":[737,971]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,939],\"end\":[768,895]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,171]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,127],\"end\":[737,95]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,63],\"end\":[662,63]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,63]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"color_lenspalette\",\"codePoint\":59803},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[701,515],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[701,515]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,497],\"end\":[682,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,441],\"end\":[701,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,405],\"end\":[746,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[772,405],\"end\":[791,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,441],\"end\":[810,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,497],\"end\":[791,515]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[772,533],\"end\":[746,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,533],\"end\":[701,515]}]}]}]},{\"start\":[573,344],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[573,344]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,325],\"end\":[554,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,273],\"end\":[573,254]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,235],\"end\":[618,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,235],\"end\":[663,254]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,273],\"end\":[682,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,325],\"end\":[663,344]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,363],\"end\":[618,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,363],\"end\":[573,344]}]}]}]},{\"start\":[361,344],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[361,344]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,325],\"end\":[342,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,273],\"end\":[361,254]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,235],\"end\":[406,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,235],\"end\":[451,254]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,273],\"end\":[470,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,325],\"end\":[451,344]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,363],\"end\":[406,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,363],\"end\":[361,344]}]}]}]},{\"start\":[233,515],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[233,515]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,497],\"end\":[214,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,441],\"end\":[233,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[252,405],\"end\":[278,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[304,405],\"end\":[323,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,441],\"end\":[342,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,497],\"end\":[323,515]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[304,533],\"end\":[278,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[252,533],\"end\":[233,515]}]}]}]},{\"start\":[240,261],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[240,261]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,373],\"end\":[128,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,693],\"end\":[240,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,917],\"end\":[512,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[540,917],\"end\":[558,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,881],\"end\":[576,853]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,829],\"end\":[560,809]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,789],\"end\":[544,767]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,741],\"end\":[562,722]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,703],\"end\":[608,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[770,703],\"end\":[833,641]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,579],\"end\":[896,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,349],\"end\":[783,249]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,149],\"end\":[512,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,149],\"end\":[240,261]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"colorize\",\"codePoint\":59804},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,749],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[558,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[296,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,749]}]}]},{\"start\":[784,161],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[772,149],\"end\":[754,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,149],\"end\":[724,161]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[590,295]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[508,213]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,273]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[508,333]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,715]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[330,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,537]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[772,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,537]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,455]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[884,321]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[914,291],\"end\":[884,261]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[784,161]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"navigate_beforechevron_left\",\"codePoint\":59805},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[462,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"navigate_nextchevron_right\",\"codePoint\":59806},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[366,337],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[562,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[366,337]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"remove_red_eyevisibility\",\"codePoint\":59807},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[422,443],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[422,443]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,481],\"end\":[384,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,585],\"end\":[422,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,661],\"end\":[512,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,661],\"end\":[602,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,585],\"end\":[640,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,481],\"end\":[602,443]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,405],\"end\":[512,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,405],\"end\":[422,443]}]}]}]},{\"start\":[361,684],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[361,684]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,621],\"end\":[298,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,445],\"end\":[361,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[424,319],\"end\":[512,319]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[600,319],\"end\":[663,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,445],\"end\":[726,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,621],\"end\":[663,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[600,747],\"end\":[512,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[424,747],\"end\":[361,684]}]}]}]},{\"start\":[226,301],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[226,301]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[98,389],\"end\":[42,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[98,677],\"end\":[226,765]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[354,853],\"end\":[512,853]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,853],\"end\":[798,765]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[926,677],\"end\":[982,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[926,389],\"end\":[798,301]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,213],\"end\":[512,213]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[354,213],\"end\":[226,301]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"tune\",\"codePoint\":59808},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[726,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,405]}]}]},{\"start\":[896,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,491]}]}]},{\"start\":[298,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,491]}]}]},{\"start\":[554,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,831]}]}]},{\"start\":[128,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,319]}]}]},{\"start\":[128,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"add_photo_alternate\",\"codePoint\":59809},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[342,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,661]}]}]},{\"start\":[682,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,235],\"end\":[154,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,285],\"end\":[128,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,865],\"end\":[154,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[760,917],\"end\":[785,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,865],\"end\":[810,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,363]}]}]},{\"start\":[938,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"image_search\",\"codePoint\":59810},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[586,374],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[586,374]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,343],\"end\":[554,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,255],\"end\":[586,223]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,191],\"end\":[662,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,191],\"end\":[737,223]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,255],\"end\":[768,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,343],\"end\":[737,374]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,405],\"end\":[662,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,405],\"end\":[586,374]}]}]}]},{\"start\":[854,299],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,299]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,219],\"end\":[798,163]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[742,107],\"end\":[662,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,107],\"end\":[526,163]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,219],\"end\":[470,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,379],\"end\":[525,435]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,491],\"end\":[660,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[712,491],\"end\":[762,461]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[956,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[824,401]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,351],\"end\":[854,299]}]}]}]},{\"start\":[552,589],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,739]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,639]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[234,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,589]}]}]},{\"start\":[768,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[386,233],\"end\":[406,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,909],\"end\":[111,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,959],\"end\":[170,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,959],\"end\":[828,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,909],\"end\":[854,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,875]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"beenhere\",\"codePoint\":59811},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,431]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,583]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,491]}]}]},{\"start\":[214,63],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,63],\"end\":[154,89]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,115],\"end\":[128,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,701]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,743],\"end\":[166,771]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,1003]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[858,771]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,743],\"end\":[896,701]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,115],\"end\":[870,89]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,63],\"end\":[810,63]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,63]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"apps\",\"codePoint\":59812},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[854,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]}]},{\"start\":[854,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,619]}]}]},{\"start\":[598,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,363]}]}]},{\"start\":[682,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,363]}]}]},{\"start\":[598,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,619]}]}]},{\"start\":[342,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,619]}]}]},{\"start\":[342,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,875]}]}]},{\"start\":[598,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,875]}]}]},{\"start\":[342,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,363]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_back\",\"codePoint\":59813},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[334,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,251]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[334,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[334,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_drop_down\",\"codePoint\":59814},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,661]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_drop_down_circle\",\"codePoint\":59815},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[342,447],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,447]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_drop_up\",\"codePoint\":59816},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[726,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,619]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_forward\",\"codePoint\":59817},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[452,251],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[690,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[690,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,251]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cancel\",\"codePoint\":59818},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[666,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,687]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,379]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,379]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,687]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,747]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"check\",\"codePoint\":59819},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[206,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[146,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[836,259]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,711]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[206,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"expand_less\",\"codePoint\":59820},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[256,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,483]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,619]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"expand_more\",\"codePoint\":59821},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,583],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,387]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,387]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,583]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"fullscreen\",\"codePoint\":59822},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,319]}]}]},{\"start\":[598,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,747]}]}]},{\"start\":[298,447],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,447]}]}]},{\"start\":[214,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,619]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"fullscreen_exit\",\"codePoint\":59823},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,235]}]}]},{\"start\":[682,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]}]},{\"start\":[214,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,363]}]}]},{\"start\":[342,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,703]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"menu\",\"codePoint\":59824},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[128,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,363]}]}]},{\"start\":[896,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,575]}]}]},{\"start\":[896,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,789]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"keyboard_control\",\"codePoint\":59825},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[452,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,499],\"end\":[426,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,567],\"end\":[452,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,619],\"end\":[512,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,619],\"end\":[572,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,567],\"end\":[598,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,499],\"end\":[572,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,447],\"end\":[512,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,447],\"end\":[452,473]}]}]}]},{\"start\":[708,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,499],\"end\":[682,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,567],\"end\":[708,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,619],\"end\":[768,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,619],\"end\":[828,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,567],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,499],\"end\":[828,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,447],\"end\":[768,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,447],\"end\":[708,473]}]}]}]},{\"start\":[196,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[196,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,499],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,567],\"end\":[196,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,619],\"end\":[256,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,619],\"end\":[316,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,567],\"end\":[342,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,499],\"end\":[316,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,447],\"end\":[256,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,447],\"end\":[196,473]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"more_vert\",\"codePoint\":59826},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[452,729],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,729]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,755],\"end\":[426,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,823],\"end\":[452,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,875],\"end\":[572,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,823],\"end\":[598,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,755],\"end\":[572,729]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,703],\"end\":[512,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,703],\"end\":[452,729]}]}]}]},{\"start\":[452,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,499],\"end\":[426,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,567],\"end\":[452,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,619],\"end\":[512,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,619],\"end\":[572,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,567],\"end\":[598,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,499],\"end\":[572,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,447],\"end\":[512,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,447],\"end\":[452,473]}]}]}]},{\"start\":[572,337],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,337]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,311],\"end\":[598,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,243],\"end\":[572,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,191],\"end\":[452,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,243],\"end\":[426,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,311],\"end\":[452,337]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,363],\"end\":[512,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,363],\"end\":[572,337]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"refresh\",\"codePoint\":59827},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[643,220],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[643,220]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[272,291]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[172,391],\"end\":[172,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[172,675],\"end\":[272,775]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[630,875],\"end\":[722,803]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[814,731],\"end\":[842,619]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[754,619]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[728,689],\"end\":[657,739]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[586,789],\"end\":[512,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,789],\"end\":[331,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,639],\"end\":[256,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,427],\"end\":[331,352]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,277],\"end\":[512,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[616,277],\"end\":[692,353]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[754,291]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[714,249],\"end\":[643,220]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"unfold_less\",\"codePoint\":59828},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[648,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,327]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,251]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,251]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,191]}]}]},{\"start\":[376,875],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,739]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,875]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"unfold_more\",\"codePoint\":59829},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[376,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,721]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,721]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,797]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,661]}]}]},{\"start\":[648,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,269]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,405]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_upward\",\"codePoint\":59830},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[230,593],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,355]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,355]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[792,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[230,593]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"subdirectory_arrow_left\",\"codePoint\":59831},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[530,857]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[530,465]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,661]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"subdirectory_arrow_right\",\"codePoint\":59832},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[494,465]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[494,857]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_downward\",\"codePoint\":59833},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[794,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,711]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,711]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[232,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[794,473]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"first_page\",\"codePoint\":59834},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[256,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]}]},{\"start\":[590,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[786,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[786,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[590,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"last_page\",\"codePoint\":59835},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,789]}]}]},{\"start\":[434,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[238,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[238,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[434,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_left\",\"codePoint\":59836},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[384,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_right\",\"codePoint\":59837},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_back_ios\",\"codePoint\":59838},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[422,111],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[422,955]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[498,879]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[152,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[498,187]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[422,111]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"arrow_forward_ios\",\"codePoint\":59839},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[588,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,869]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[250,197]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[588,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder_special\",\"codePoint\":59840},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,673],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[548,605]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,509]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[582,497]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[698,497]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[842,509]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[732,605]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[766,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,673]}]}]},{\"start\":[512,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,329],\"end\":[913,303]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,277],\"end\":[854,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"priority_high\",\"codePoint\":59841},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[426,661],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,661]}]}]},{\"start\":[451,892],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[451,892]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[476,917],\"end\":[512,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,917],\"end\":[573,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,867],\"end\":[598,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,795],\"end\":[573,771]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,747],\"end\":[512,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[476,747],\"end\":[451,771]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,795],\"end\":[426,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,867],\"end\":[451,892]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"notifications\",\"codePoint\":59842},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[768,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,391],\"end\":[717,317]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,243],\"end\":[576,221]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,165],\"end\":[558,146]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[540,127],\"end\":[512,127]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[484,127],\"end\":[466,146]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,165],\"end\":[448,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,221]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,243],\"end\":[307,317]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,391],\"end\":[256,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,491]}]}]},{\"start\":[572,934],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,934]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,909],\"end\":[598,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,911],\"end\":[451,935]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[476,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,959],\"end\":[572,934]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"notifications_none\",\"codePoint\":59843},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[342,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,491]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,409],\"end\":[388,354]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[434,299],\"end\":[512,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,299],\"end\":[636,354]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,409],\"end\":[682,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,747]}]}]},{\"start\":[768,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,391],\"end\":[717,317]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,243],\"end\":[576,221]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,165],\"end\":[558,146]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[540,127],\"end\":[512,127]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[484,127],\"end\":[466,146]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,165],\"end\":[448,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,221]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,243],\"end\":[307,317]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,391],\"end\":[256,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,491]}]}]},{\"start\":[572,934],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,934]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,909],\"end\":[598,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,909],\"end\":[452,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,959],\"end\":[572,934]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"person\",\"codePoint\":59844},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[287,666],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[287,666]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,713],\"end\":[170,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,713],\"end\":[737,666]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,619],\"end\":[512,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,619],\"end\":[287,666]}]}]}]},{\"start\":[632,483],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,483]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,433],\"end\":[682,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,293],\"end\":[632,242]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[442,191],\"end\":[392,242]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,293],\"end\":[342,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,433],\"end\":[392,483]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[442,533],\"end\":[512,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,533],\"end\":[632,483]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"public\",\"codePoint\":59845},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,575]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,557],\"end\":[628,545]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[616,533],\"end\":[598,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,447]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[444,447],\"end\":[457,435]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,423],\"end\":[470,405]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[588,319],\"end\":[614,294]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,269],\"end\":[640,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,217]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,255],\"end\":[795,341]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,427],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,667],\"end\":[764,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[742,703],\"end\":[682,703]}]}]}]},{\"start\":[257,759],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[257,759]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,663],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,497],\"end\":[180,457]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,737],\"end\":[410,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,789],\"end\":[470,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,871]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[344,855],\"end\":[257,759]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"share\",\"codePoint\":59846},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[684,739],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[684,739]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,563]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,543],\"end\":[384,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,523],\"end\":[380,503]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,327]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,363],\"end\":[768,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[820,363],\"end\":[858,325]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,287],\"end\":[896,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,183],\"end\":[858,145]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[820,107],\"end\":[768,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[716,107],\"end\":[678,145]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,183],\"end\":[640,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,245],\"end\":[644,265]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[344,439]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[306,405],\"end\":[256,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[204,405],\"end\":[166,443]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,481],\"end\":[128,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,585],\"end\":[166,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[204,661],\"end\":[256,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[306,661],\"end\":[344,627]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[646,803]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,811],\"end\":[644,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,883],\"end\":[681,920]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[718,957],\"end\":[768,957]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[818,957],\"end\":[855,920]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[892,883],\"end\":[892,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[892,781],\"end\":[856,744]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[820,707],\"end\":[768,707]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,707],\"end\":[684,739]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"sentiment_dissatisfied\",\"codePoint\":59847},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[596,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[596,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,743],\"end\":[617,722]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,701],\"end\":[640,699]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,661],\"end\":[512,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,661],\"end\":[384,699]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,721],\"end\":[406,723]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[428,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[468,725],\"end\":[512,725]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[558,725],\"end\":[596,747]}]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[317,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[317,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,491],\"end\":[362,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,491],\"end\":[407,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,453],\"end\":[426,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,401],\"end\":[407,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,363],\"end\":[362,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,363],\"end\":[317,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,401],\"end\":[298,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,453],\"end\":[317,472]}]}]}]},{\"start\":[617,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[617,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,491],\"end\":[662,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,491],\"end\":[707,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,453],\"end\":[726,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,401],\"end\":[707,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,363],\"end\":[662,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,363],\"end\":[617,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,401],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,453],\"end\":[617,472]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"sentiment_neutral\",\"codePoint\":59848},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[317,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[317,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,491],\"end\":[362,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,491],\"end\":[407,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,453],\"end\":[426,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,401],\"end\":[407,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,363],\"end\":[362,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,363],\"end\":[317,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,401],\"end\":[298,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,453],\"end\":[317,472]}]}]}]},{\"start\":[617,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[617,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,491],\"end\":[662,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,491],\"end\":[707,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,453],\"end\":[726,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,401],\"end\":[707,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,363],\"end\":[662,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,363],\"end\":[617,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,401],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,453],\"end\":[617,472]}]}]}]},{\"start\":[384,725],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,725]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,683]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,683]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,725]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"sentiment_satisfied\",\"codePoint\":59849},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[428,681],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[428,681]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,699],\"end\":[386,731]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[444,767],\"end\":[512,767]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,767],\"end\":[640,731]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[634,725],\"end\":[596,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,703],\"end\":[512,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[466,703],\"end\":[428,681]}]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[317,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[317,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,491],\"end\":[362,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,491],\"end\":[407,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,453],\"end\":[426,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,401],\"end\":[407,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,363],\"end\":[362,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,363],\"end\":[317,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,401],\"end\":[298,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,453],\"end\":[317,472]}]}]}]},{\"start\":[617,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[617,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,491],\"end\":[662,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,491],\"end\":[707,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,453],\"end\":[726,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,401],\"end\":[707,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,363],\"end\":[662,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,363],\"end\":[617,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,401],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,453],\"end\":[617,472]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"sentiment_very_dissatisfied\",\"codePoint\":59850},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[379,660],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[379,660]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,701],\"end\":[294,767]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[364,767]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[414,683],\"end\":[512,683]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[610,683],\"end\":[660,767]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,767]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,701],\"end\":[645,660]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[586,619],\"end\":[512,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,619],\"end\":[379,660]}]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[317,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[317,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,491],\"end\":[362,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,491],\"end\":[407,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,453],\"end\":[426,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,401],\"end\":[407,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,363],\"end\":[362,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,363],\"end\":[317,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,401],\"end\":[298,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,453],\"end\":[317,472]}]}]}]},{\"start\":[617,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[617,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,491],\"end\":[662,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,491],\"end\":[707,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,453],\"end\":[726,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,401],\"end\":[707,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,363],\"end\":[662,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,363],\"end\":[617,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,401],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,453],\"end\":[617,472]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"sentiment_very_satisfied\",\"codePoint\":59851},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[382,742],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[382,742]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,789],\"end\":[512,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[584,789],\"end\":[642,742]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,695],\"end\":[726,619]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,619]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[324,695],\"end\":[382,742]}]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[317,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[317,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,491],\"end\":[362,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,491],\"end\":[407,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,453],\"end\":[426,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,401],\"end\":[407,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,363],\"end\":[362,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,363],\"end\":[317,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,401],\"end\":[298,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,453],\"end\":[317,472]}]}]}]},{\"start\":[617,472],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[617,472]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,491],\"end\":[662,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,491],\"end\":[707,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,453],\"end\":[726,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,401],\"end\":[707,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,363],\"end\":[662,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,363],\"end\":[617,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,401],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,453],\"end\":[617,472]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"stargrade\",\"codePoint\":59852},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[776,917],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,617]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,389]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,389]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[318,617]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[248,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,757]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[776,917]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"star_half\",\"codePoint\":59853},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,281],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[584,453]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[772,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[630,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,775]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,281]}]}]},{\"start\":[632,389],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,389]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[318,617]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[248,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,757]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[776,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,617]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,389]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"star_outline\",\"codePoint\":59854},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[352,775],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[394,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[252,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[440,453]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,281]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[584,453]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[772,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[630,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,775]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,679]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,775]}]}]},{\"start\":[632,389],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,389]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[318,617]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[248,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,757]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[776,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,617]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,415]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,389]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"account_box\",\"codePoint\":59855},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[344,652],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[344,652]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,615],\"end\":[512,615]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,615],\"end\":[680,652]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,689],\"end\":[768,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,689],\"end\":[344,652]}]}]}]},{\"start\":[602,495],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[602,495]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,533],\"end\":[512,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,533],\"end\":[422,495]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,457],\"end\":[384,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,353],\"end\":[422,315]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,277],\"end\":[512,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,277],\"end\":[602,315]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,353],\"end\":[640,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,457],\"end\":[602,495]}]}]}]},{\"start\":[128,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,201],\"end\":[870,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,199],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"account_circle\",\"codePoint\":59856},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[369,801],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[369,801]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[294,761],\"end\":[256,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[258,645],\"end\":[346,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[434,571],\"end\":[512,571]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,571],\"end\":[678,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[766,647],\"end\":[768,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,761],\"end\":[655,801]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,841],\"end\":[512,841]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[444,841],\"end\":[369,801]}]}]}]},{\"start\":[602,273],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[602,273]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,311],\"end\":[640,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,415],\"end\":[602,453]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,491],\"end\":[512,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,491],\"end\":[422,453]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,415],\"end\":[384,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,311],\"end\":[422,273]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,235],\"end\":[512,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,235],\"end\":[602,273]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"android-full\",\"codePoint\":59857},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]}]},{\"start\":[384,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,235]}]}]},{\"start\":[718,57],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,41],\"end\":[718,27]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,13],\"end\":[688,27]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,91]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,63],\"end\":[512,63]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,63],\"end\":[398,91]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[334,27]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,13],\"end\":[304,27]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,41],\"end\":[304,57]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[360,113]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[316,145],\"end\":[286,205]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,265],\"end\":[256,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,319]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,189],\"end\":[662,113]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[718,57]}]}]},{\"start\":[829,382],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[829,382]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,401],\"end\":[810,427]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,725]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,753],\"end\":[829,771]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[848,789],\"end\":[874,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[900,789],\"end\":[919,771]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,753],\"end\":[938,725]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,427]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,401],\"end\":[919,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[900,363],\"end\":[874,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[848,363],\"end\":[829,382]}]}]}]},{\"start\":[105,382],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[105,382]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,401],\"end\":[86,427]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,725]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,753],\"end\":[105,771]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[124,789],\"end\":[150,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[176,789],\"end\":[195,771]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,753],\"end\":[214,725]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,427]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,401],\"end\":[195,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[176,363],\"end\":[150,363]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[124,363],\"end\":[105,382]}]}]}]},{\"start\":[268,819],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[268,819]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[280,831],\"end\":[298,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,981]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,1009],\"end\":[361,1027]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,1045],\"end\":[406,1045]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,1045],\"end\":[451,1027]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,1009],\"end\":[470,981]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,981]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,1009],\"end\":[573,1027]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,1045],\"end\":[618,1045]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,1045],\"end\":[663,1027]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,1009],\"end\":[682,981]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[744,831],\"end\":[756,819]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,807],\"end\":[768,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,807],\"end\":[268,819]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"autorenew\",\"codePoint\":59858},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[738,413],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,473],\"end\":[768,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,639],\"end\":[693,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,789],\"end\":[512,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,1003]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,433],\"end\":[800,351]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[738,413]}]}]},{\"start\":[512,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,633],\"end\":[224,715]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[286,653]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,597],\"end\":[256,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,427],\"end\":[331,352]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,277],\"end\":[512,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,405]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"cached\",\"codePoint\":59859},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[331,352],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[331,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,277],\"end\":[512,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,277],\"end\":[632,307]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[694,245]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[612,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[170,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,427],\"end\":[331,352]}]}]}]},{\"start\":[640,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,639],\"end\":[693,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,789],\"end\":[512,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,789],\"end\":[392,759]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[330,821]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[412,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[854,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"check_circle\",\"codePoint\":59860},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,625]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,301]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,533]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"code\",\"codePoint\":59861},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[820,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,789]}]}]},{\"start\":[204,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,729]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[204,533]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"delete\",\"codePoint\":59862},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[662,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[406,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,191]}]}]},{\"start\":[282,891],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,891]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,917],\"end\":[342,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[716,917],\"end\":[742,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,865],\"end\":[768,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,865],\"end\":[282,891]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"exit_to_app\",\"codePoint\":59863},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,199],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,201],\"end\":[870,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]}]},{\"start\":[490,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,379]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[540,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[540,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,687]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,747]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"extension\",\"codePoint\":59864},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[810,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,319]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,285],\"end\":[785,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[760,235],\"end\":[726,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,171]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,127],\"end\":[523,95]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[492,63],\"end\":[448,63]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,63],\"end\":[373,95]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,127],\"end\":[342,171]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,235],\"end\":[111,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,285],\"end\":[86,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,481]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[150,481]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,481],\"end\":[231,515]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,549],\"end\":[264,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,645],\"end\":[231,679]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,713],\"end\":[150,713]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,713]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,909],\"end\":[111,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,959],\"end\":[170,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,895]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[332,847],\"end\":[366,814]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[400,781],\"end\":[448,781]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[496,781],\"end\":[530,814]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,847],\"end\":[564,895]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[564,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[760,959],\"end\":[785,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,909],\"end\":[810,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[874,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[918,703],\"end\":[950,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,641],\"end\":[982,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,553],\"end\":[950,522]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[918,491],\"end\":[874,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"favorite\",\"codePoint\":59865},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[574,877],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[712,753],\"end\":[773,691]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[834,629],\"end\":[886,545]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,461],\"end\":[938,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,285],\"end\":[871,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[804,149],\"end\":[704,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[588,149],\"end\":[512,239]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,149],\"end\":[320,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[220,149],\"end\":[153,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,285],\"end\":[86,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,441],\"end\":[108,496]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[130,551],\"end\":[189,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[248,687],\"end\":[296,733]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[344,779],\"end\":[450,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,931]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[574,877]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"favorite_outline\",\"codePoint\":59866},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,817],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[508,813]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[412,727],\"end\":[366,683]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[320,639],\"end\":[266,579]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[212,519],\"end\":[191,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,427],\"end\":[170,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,319],\"end\":[213,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,235],\"end\":[320,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[370,235],\"end\":[413,263]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[456,291],\"end\":[472,335]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,335]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[568,291],\"end\":[611,263]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,235],\"end\":[704,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,235],\"end\":[811,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,319],\"end\":[854,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,427],\"end\":[833,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[812,519],\"end\":[758,579]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,639],\"end\":[658,683]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[612,727],\"end\":[516,813]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,817]}]}]},{\"start\":[512,239],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,239]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,149],\"end\":[320,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[220,149],\"end\":[153,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,285],\"end\":[86,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,461],\"end\":[138,545]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[190,629],\"end\":[251,691]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[312,753],\"end\":[450,877]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,931]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[574,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,779],\"end\":[728,733]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[776,687],\"end\":[835,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[894,551],\"end\":[916,496]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,441],\"end\":[938,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,285],\"end\":[871,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[804,149],\"end\":[704,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[588,149],\"end\":[512,239]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"help\",\"codePoint\":59867},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[604,541],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,595],\"end\":[554,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,639]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[470,573],\"end\":[520,519]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,465]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,439],\"end\":[598,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,371],\"end\":[572,345]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,319],\"end\":[512,319]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,319],\"end\":[452,345]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,371],\"end\":[426,405]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,405]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,335],\"end\":[392,285]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[442,235],\"end\":[512,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,235],\"end\":[632,285]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,335],\"end\":[682,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,461],\"end\":[642,501]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[604,541]}]}]},{\"start\":[470,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,831]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"highlight_remove\",\"codePoint\":59868},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[512,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,423]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,643]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,593]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,643]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,423]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,473]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"historyrestore\",\"codePoint\":59869},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[694,685]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,633]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,543]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,575]}]}]},{\"start\":[283,261],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[283,261]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,373],\"end\":[170,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[208,699]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[212,705]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,409],\"end\":[343,322]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[430,235],\"end\":[554,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[678,235],\"end\":[766,322]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,409],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,657],\"end\":[766,744]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[678,831],\"end\":[554,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,831],\"end\":[344,743]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[284,805]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[330,851],\"end\":[409,884]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[488,917],\"end\":[554,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[712,917],\"end\":[825,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,693],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,373],\"end\":[825,261]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[712,149],\"end\":[554,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,149],\"end\":[283,261]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"home\",\"codePoint\":59870},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[426,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,619]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"httpslock\",\"codePoint\":59871},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[380,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,223],\"end\":[419,184]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[458,145],\"end\":[512,145]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[566,145],\"end\":[605,184]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,223],\"end\":[644,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[644,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,363]}]}]},{\"start\":[452,721],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,721]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,695],\"end\":[426,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,627],\"end\":[452,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,575],\"end\":[512,575]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,575],\"end\":[572,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,627],\"end\":[598,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,695],\"end\":[572,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,747],\"end\":[512,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,747],\"end\":[452,721]}]}]}]},{\"start\":[726,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,189],\"end\":[663,126]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[600,63],\"end\":[512,63]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[424,63],\"end\":[361,126]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,189],\"end\":[298,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,363],\"end\":[196,388]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,413],\"end\":[170,447]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,909],\"end\":[196,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,959],\"end\":[256,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,959],\"end\":[828,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,909],\"end\":[854,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,447]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,413],\"end\":[828,388]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[802,363],\"end\":[768,363]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,363]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"info\",\"codePoint\":59872},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[470,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,405]}]}]},{\"start\":[470,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"info_outline\",\"codePoint\":59873},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,405]}]}]},{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[554,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"input\",\"codePoint\":59874},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,533]}]}]},{\"start\":[128,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,149],\"end\":[68,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,201],\"end\":[42,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,233]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,233]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,833]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,833]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,833]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,867],\"end\":[68,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,917],\"end\":[128,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,917],\"end\":[956,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,867],\"end\":[982,833]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,199],\"end\":[957,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[932,149],\"end\":[896,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"label\",\"codePoint\":59875},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,235],\"end\":[154,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,285],\"end\":[128,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,781],\"end\":[154,806]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,831],\"end\":[214,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,831],\"end\":[752,795]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[752,271]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,235],\"end\":[682,235]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"label_outline\",\"codePoint\":59876},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,747],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[834,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,747]}]}]},{\"start\":[682,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,235],\"end\":[154,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,285],\"end\":[128,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,781],\"end\":[154,806]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,831],\"end\":[214,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,831],\"end\":[752,795]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[752,271]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,235],\"end\":[682,235]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"perm_media\",\"codePoint\":59877},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[490,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[746,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,405]}]}]},{\"start\":[598,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,107]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,107],\"end\":[197,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[172,157],\"end\":[172,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,737],\"end\":[196,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,789],\"end\":[256,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[972,789],\"end\":[998,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,737],\"end\":[1024,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,243],\"end\":[998,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[972,191],\"end\":[938,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,191]}]}]},{\"start\":[0,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,909],\"end\":[26,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[52,959],\"end\":[86,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"power_settings_new\",\"codePoint\":59878},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[700,303],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[746,339],\"end\":[778,407]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,475],\"end\":[810,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,657],\"end\":[723,744]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,831],\"end\":[512,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,831],\"end\":[301,744]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,657],\"end\":[214,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,475],\"end\":[246,407]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,339],\"end\":[324,301]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[264,241]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[208,289],\"end\":[168,375]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,461],\"end\":[128,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,693],\"end\":[240,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,917],\"end\":[512,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,917],\"end\":[784,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,693],\"end\":[896,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,357],\"end\":[760,241]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[700,303]}]}]},{\"start\":[470,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"search\",\"codePoint\":59879},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[270,563],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[270,563]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,507],\"end\":[214,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,347],\"end\":[270,291]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,235],\"end\":[406,235]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[486,235],\"end\":[542,291]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,347],\"end\":[598,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,507],\"end\":[542,563]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[486,619],\"end\":[406,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,619],\"end\":[270,563]}]}]}]},{\"start\":[628,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[616,607]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[642,575],\"end\":[662,522]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,469],\"end\":[682,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,311],\"end\":[602,230]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,149],\"end\":[406,149]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,149],\"end\":[209,230]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,311],\"end\":[128,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,543],\"end\":[209,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,703],\"end\":[406,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[510,703],\"end\":[586,637]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,649]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,683]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,895]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[874,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[628,619]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"settings\",\"codePoint\":59880},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[406,639],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[406,639]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,595],\"end\":[362,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[362,471],\"end\":[406,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,383],\"end\":[512,383]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,383],\"end\":[618,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,471],\"end\":[662,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,595],\"end\":[618,639]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,683],\"end\":[512,683]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,683],\"end\":[406,639]}]}]}]},{\"start\":[832,533],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,505],\"end\":[830,491]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[920,421]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[934,411],\"end\":[924,393]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[838,245]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[830,231],\"end\":[812,237]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[706,279]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,253],\"end\":[634,237]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,125]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,107],\"end\":[598,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,107]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,107],\"end\":[406,125]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[390,237]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[360,249],\"end\":[318,279]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[212,237]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[194,231],\"end\":[186,245]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[100,393]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[90,411],\"end\":[104,421]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[194,491]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,505],\"end\":[192,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[192,561],\"end\":[194,575]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[104,645]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[90,655],\"end\":[100,673]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[186,821]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[194,835],\"end\":[212,829]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[318,787]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,813],\"end\":[390,829]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[406,941]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[410,959],\"end\":[426,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,959],\"end\":[618,941]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[634,829]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[664,817],\"end\":[706,787]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[812,829]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[830,835],\"end\":[838,821]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[924,673]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[934,655],\"end\":[920,645]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[830,575]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,561],\"end\":[832,533]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"settings_applications\",\"codePoint\":59881},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[734,563],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[734,563]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[798,611]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,619],\"end\":[800,631]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[740,733]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,745],\"end\":[722,741]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[648,711]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[630,725],\"end\":[598,739]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[586,819]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[584,831],\"end\":[572,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[442,831],\"end\":[438,819]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,741]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[402,731],\"end\":[376,711]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[302,741]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,745],\"end\":[284,735]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,631]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,629],\"end\":[222,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,613],\"end\":[226,611]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[290,563]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,553],\"end\":[288,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,513],\"end\":[290,503]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[226,455]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,453],\"end\":[222,443]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[222,437],\"end\":[224,435]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[284,333]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[292,321],\"end\":[302,325]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[376,355]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[394,341],\"end\":[426,327]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,247]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,235],\"end\":[452,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[572,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,235],\"end\":[586,247]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,325]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[622,335],\"end\":[648,355]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[722,325]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,321],\"end\":[740,331]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,435]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,447],\"end\":[798,455]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[734,503]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,513],\"end\":[736,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,553],\"end\":[734,563]}]}]}]},{\"start\":[214,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,199],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[846,917],\"end\":[871,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,867],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,235]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,199],\"end\":[871,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[846,149],\"end\":[810,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]}]},{\"start\":[452,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[452,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,499],\"end\":[426,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,567],\"end\":[452,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,619],\"end\":[512,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,619],\"end\":[572,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,567],\"end\":[598,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[598,499],\"end\":[572,473]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,447],\"end\":[512,447]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,447],\"end\":[452,473]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"shop\",\"codePoint\":59882},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[384,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,405]}]}]},{\"start\":[598,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,191]}]}]},{\"start\":[682,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,155],\"end\":[658,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[634,107],\"end\":[598,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,107]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[390,107],\"end\":[366,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,155],\"end\":[342,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,867],\"end\":[110,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,917],\"end\":[170,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,917],\"end\":[914,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,867],\"end\":[938,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,191]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"spellcheck\",\"codePoint\":59883},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[576,861],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[420,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[360,763]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,981]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[922,515]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,861]}]}]},{\"start\":[362,255],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[450,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,255]}]}]},{\"start\":[620,703],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[322,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[104,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[194,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[242,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[482,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[532,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[620,703]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"stars\",\"codePoint\":59884},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,681],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,583]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[220,445]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,429]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[594,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[804,445]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[644,583]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[692,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,681]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"translate\",\"codePoint\":59885},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[746,561],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[816,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[678,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[746,561]}]}]},{\"start\":[704,447],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[646,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[848,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[790,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,447]}]}]},{\"start\":[440,557],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[442,555]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[488,503],\"end\":[534,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,341],\"end\":[600,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,107]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[520,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[476,403],\"end\":[384,505]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[328,443],\"end\":[286,363]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[200,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[248,469],\"end\":[328,557]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[110,771]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[516,751]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[550,663]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[440,557]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"visibility_off\",\"codePoint\":59886},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,541],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,481],\"end\":[602,443]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,405],\"end\":[512,405]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[506,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,541]}]}]},{\"start\":[388,505],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,521],\"end\":[384,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,585],\"end\":[422,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,661],\"end\":[512,661]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[524,661],\"end\":[540,657]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[606,723]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[558,747],\"end\":[512,747]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[424,747],\"end\":[361,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,621],\"end\":[298,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[298,487],\"end\":[322,439]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[388,505]}]}]},{\"start\":[135,253],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[135,253]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,297],\"end\":[202,321]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[158,355],\"end\":[111,418]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[64,481],\"end\":[42,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[98,677],\"end\":[226,765]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[354,853],\"end\":[512,853]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[612,853],\"end\":[698,817]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,845],\"end\":[779,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,949],\"end\":[842,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,905]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[140,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,203]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[92,209],\"end\":[135,253]}]}]}]},{\"start\":[663,382],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[663,382]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,445],\"end\":[726,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,573],\"end\":[710,611]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[834,735]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[932,651],\"end\":[980,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[924,389],\"end\":[797,301]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,213],\"end\":[512,213]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,213],\"end\":[342,243]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[434,335]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[472,319],\"end\":[512,319]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[600,319],\"end\":[663,382]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"update\",\"codePoint\":59887},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[470,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[652,685]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,633]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[534,543]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[534,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,363]}]}]},{\"start\":[896,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[780,269]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,223],\"end\":[655,190]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,157],\"end\":[510,157]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[444,157],\"end\":[365,190]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[286,223],\"end\":[240,269]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,381],\"end\":[128,538]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,695],\"end\":[240,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,917],\"end\":[512,917]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,917],\"end\":[784,805]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,695],\"end\":[896,537]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,537]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[810,659],\"end\":[724,745]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,781],\"end\":[625,807]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[562,833],\"end\":[512,833]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[462,833],\"end\":[400,807]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[338,781],\"end\":[302,745]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[266,709],\"end\":[240,648]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,587],\"end\":[214,537]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,487],\"end\":[240,426]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[266,365],\"end\":[302,329]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[390,243],\"end\":[513,244]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[636,245],\"end\":[724,333]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[606,453]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,453]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"g_translate\",\"codePoint\":59888},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[905,926],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[905,926]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[892,939],\"end\":[874,939]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,939]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[638,699]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[770,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,793]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[670,653]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[742,573],\"end\":[772,473]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,417]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[660,417]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[660,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[606,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[606,417]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,417]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[488,255]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[874,255]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[892,255],\"end\":[905,268]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[918,281],\"end\":[918,299]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[918,895]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[918,913],\"end\":[905,926]}]}]}]},{\"start\":[720,473],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[694,553],\"end\":[632,621]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[596,573]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[562,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[720,473]}]}]},{\"start\":[148,641],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[148,641]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,579],\"end\":[86,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,403],\"end\":[148,340]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[210,277],\"end\":[298,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,277],\"end\":[442,333]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[386,387]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[352,353],\"end\":[298,353]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[242,353],\"end\":[203,393]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[164,433],\"end\":[164,491]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[164,547],\"end\":[203,587]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[242,627],\"end\":[298,627]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[356,627],\"end\":[388,595]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,563],\"end\":[424,525]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,525]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,451]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[498,451]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[504,493],\"end\":[504,495]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[504,587],\"end\":[447,645]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[390,703],\"end\":[298,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[210,703],\"end\":[148,641]}]}]}]},{\"start\":[470,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,63]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,63]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,63],\"end\":[68,89]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,115],\"end\":[42,149]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[42,823],\"end\":[68,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[94,875],\"end\":[128,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,1003]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,1003]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,1003],\"end\":[956,977]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,951],\"end\":[982,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,243],\"end\":[956,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,191],\"end\":[896,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,191]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"check_circle_outline\",\"codePoint\":59889},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[271,774],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[271,774]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,673],\"end\":[170,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,393],\"end\":[271,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,191],\"end\":[512,191]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,191],\"end\":[753,292]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,393],\"end\":[854,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,673],\"end\":[753,774]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,875],\"end\":[512,875]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[372,875],\"end\":[271,774]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]},{\"start\":[426,625],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[274,473]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,345]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,625]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"delete_outline\",\"codePoint\":59890},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[618,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[406,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[618,149]}]}]},{\"start\":[682,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,405]}]}]},{\"start\":[282,891],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,891]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,917],\"end\":[342,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[716,917],\"end\":[742,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,865],\"end\":[768,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,865],\"end\":[282,891]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"drive_folder_upload\",\"codePoint\":59891},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[402,637],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,569]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,569]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,637]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,577]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,577]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[402,637]}]}]},{\"start\":[170,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,789]}]}]},{\"start\":[512,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,329],\"end\":[913,303]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,277],\"end\":[854,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"library_add_check\",\"codePoint\":59892},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[86,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,909],\"end\":[111,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,959],\"end\":[170,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,959]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,277]}]}]},{\"start\":[384,469],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[444,409]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[532,497]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[532,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,469]}]}]},{\"start\":[342,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,107],\"end\":[282,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,157],\"end\":[256,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,737],\"end\":[282,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,789],\"end\":[342,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,789],\"end\":[913,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,737],\"end\":[938,703]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,157],\"end\":[913,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,107],\"end\":[854,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,107]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"replay_circle_filled\",\"codePoint\":59893},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[693,714],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[693,714]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,789],\"end\":[512,789]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[406,789],\"end\":[331,714]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,639],\"end\":[256,533]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,533]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[342,603],\"end\":[392,653]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[442,703],\"end\":[512,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,703],\"end\":[632,653]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,603],\"end\":[682,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[682,463],\"end\":[632,413]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[582,363],\"end\":[512,363]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,277],\"end\":[693,352]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,427],\"end\":[768,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,639],\"end\":[693,714]}]}]}]},{\"start\":[211,232],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[211,232]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,357],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,709],\"end\":[211,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,959],\"end\":[512,959]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,959],\"end\":[813,834]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,709],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,357],\"end\":[813,232]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,107],\"end\":[512,107]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[336,107],\"end\":[211,232]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"redo\",\"codePoint\":59894},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[490,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[344,363],\"end\":[227,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[110,535],\"end\":[66,671]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[166,703]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[198,605],\"end\":[293,537]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,469],\"end\":[490,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,469],\"end\":[710,549]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[786,473]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[660,363],\"end\":[490,363]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"save\",\"codePoint\":59895},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[214,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,405]}]}]},{\"start\":[422,793],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[422,793]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,755],\"end\":[384,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[384,651],\"end\":[422,613]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,575],\"end\":[512,575]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,575],\"end\":[602,613]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,651],\"end\":[640,703]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,755],\"end\":[602,793]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,831],\"end\":[512,831]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[460,831],\"end\":[422,793]}]}]}]},{\"start\":[214,149],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,199],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"zip\",\"codePoint\":59896},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,578],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,578]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,578]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[485,583],\"end\":[466.5,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,627],\"end\":[448,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,689],\"end\":[471.5,712.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[495,736],\"end\":[528,736]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,736],\"end\":[584.5,712.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,689],\"end\":[608,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,627],\"end\":[590,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,583],\"end\":[544,578]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,96],\"end\":[794.5,133.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,171],\"end\":[832,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,885],\"end\":[794.5,922.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,960],\"end\":[704,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,960],\"end\":[261.5,922.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,885],\"end\":[224,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,171],\"end\":[261.5,133.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,96],\"end\":[352,96]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,578]}]}]},{\"start\":[528,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,608],\"end\":[562,622]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,636],\"end\":[576,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,676],\"end\":[562,690]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,704],\"end\":[528,704]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[508,704],\"end\":[494,690]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,676],\"end\":[480,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,636],\"end\":[494,622]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[508,608],\"end\":[528,608]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"zip-outline\",\"codePoint\":59897},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,561],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,128]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[312,128],\"end\":[284,156]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,184],\"end\":[256,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,872],\"end\":[284,900]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[312,928],\"end\":[352,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[744,928],\"end\":[772,900]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,872],\"end\":[800,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,184],\"end\":[772,156]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[744,128],\"end\":[704,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,128]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,160]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,288]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,384]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,480]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,578]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,583],\"end\":[590,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,627],\"end\":[608,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,689],\"end\":[584.5,712.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,736],\"end\":[528,736]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[495,736],\"end\":[471.5,712.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,689],\"end\":[448,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[448,627],\"end\":[466.5,605]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[485,583],\"end\":[512,578]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,561]}]}]},{\"start\":[352,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,96],\"end\":[794.5,133.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,171],\"end\":[832,224]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,832]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,885],\"end\":[794.5,922.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[757,960],\"end\":[704,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[352,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,960],\"end\":[261.5,922.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,885],\"end\":[224,832]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,224]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,171],\"end\":[261.5,133.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[299,96],\"end\":[352,96]}]}]}]},{\"start\":[494,622],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,636],\"end\":[480,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[480,676],\"end\":[494,690]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[508,704],\"end\":[528,704]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,704],\"end\":[562,690]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,676],\"end\":[576,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,636],\"end\":[562,622]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,608],\"end\":[528,608]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[528,608]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[508,608],\"end\":[494,622]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"logout\",\"codePoint\":59898},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[512,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,149],\"end\":[111,175]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,201],\"end\":[86,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,865],\"end\":[111,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,917],\"end\":[170,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,235]}]}]},{\"start\":[666,379],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[776,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[776,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,685]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,379]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder_open\",\"codePoint\":59899},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,789]}]}]},{\"start\":[512,277],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,329],\"end\":[913,303]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,277],\"end\":[854,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"launchopen_in_new\",\"codePoint\":59900},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[598,235],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[750,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,653]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,713]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,295]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,235]}]}]},{\"start\":[214,831],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,149]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,149],\"end\":[153,174]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,199],\"end\":[128,235]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,831]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,867],\"end\":[153,892]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,917],\"end\":[214,917]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,917]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,917],\"end\":[870,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,865],\"end\":[896,831]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,533]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,831]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"open_in_browser\",\"codePoint\":59901},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[342,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,619]}]}]},{\"start\":[214,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,191],\"end\":[153,216]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,241],\"end\":[128,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[128,825],\"end\":[153,850]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[178,875],\"end\":[214,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,875]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[810,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[844,875],\"end\":[870,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,823],\"end\":[896,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,277]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[896,241],\"end\":[871,216]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[846,191],\"end\":[810,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[214,191]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"vue\",\"codePoint\":59902},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[1024,69],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,955]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,69]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[205,69]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[205,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[394,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,273]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[630,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[819,68]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[819,69]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,69]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"angularuniversal\",\"codePoint\":59903},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[666,481],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,481]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,473],\"end\":[660,467]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,461],\"end\":[645,461]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[379,461]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[379,461]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[370,461],\"end\":[364,467]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,473],\"end\":[358,481]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,543]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,543]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,551],\"end\":[364,557]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[370,563],\"end\":[379,563]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[645,563]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[645,563]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,563],\"end\":[660,557]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,551],\"end\":[666,543]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,481]}]}]},{\"start\":[512,666],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[533,666],\"end\":[548,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[563,696],\"end\":[563,717]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[563,738],\"end\":[548,753]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[533,768],\"end\":[512,768]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,768]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[491,768],\"end\":[476,753]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[461,738],\"end\":[461,717]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[461,696],\"end\":[476,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[491,666],\"end\":[512,666]}]}]}]},{\"start\":[645,307],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,307],\"end\":[660,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,319],\"end\":[666,328]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,389]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,389]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,398],\"end\":[660,404]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,410],\"end\":[645,410]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[379,410]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[370,410],\"end\":[364,404]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,398],\"end\":[358,389]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,328]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[358,328]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[358,319],\"end\":[364,313]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[370,307],\"end\":[379,307]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[645,307]}]}]},{\"start\":[511,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[36,170]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[108,799]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,1024]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[915,799]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[988,170]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[511,0]}]}]},{\"start\":[717,758],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[717,758]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,783],\"end\":[699,801]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[681,819],\"end\":[655,819]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[369,819]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[343,819],\"end\":[325,801]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[307,783],\"end\":[307,758]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[307,266]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[307,266]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[307,241],\"end\":[325,223]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[343,205],\"end\":[369,205]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[655,205]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[655,205]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[681,205],\"end\":[699,223]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,241],\"end\":[717,266]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[717,758]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"linkinsert_link\",\"codePoint\":59904},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[554,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,401]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,401]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[780,401],\"end\":[819,440]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,479],\"end\":[858,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,587],\"end\":[819,626]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[780,665],\"end\":[726,665]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,665]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,747]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[814,747],\"end\":[876,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,621],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,445],\"end\":[876,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[814,319],\"end\":[726,319]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]}]},{\"start\":[682,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,575]}]}]},{\"start\":[205,440],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[205,440]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[244,401],\"end\":[298,401]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,401]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,319]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[210,319],\"end\":[148,382]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,445],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,621],\"end\":[148,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[210,747],\"end\":[298,747]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,665]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,665]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[244,665],\"end\":[205,626]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[166,587],\"end\":[166,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[166,479],\"end\":[205,440]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-text2\",\"codePoint\":59905},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[608,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,352],\"end\":[627,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,96]}]}]},{\"start\":[480,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,320]}]}]},{\"start\":[544,224],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,224]}]}]},{\"start\":[736,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]}]},{\"start\":[640,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,512]}]}]},{\"start\":[736,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,608]}]}]},{\"start\":[608,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,704]}]}]},{\"start\":[736,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,800]}]}]},{\"start\":[832,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-text2-outline\",\"codePoint\":59906},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[624,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,96]}]}]},{\"start\":[608,128],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[626.5,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,352],\"end\":[672,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,909],\"end\":[790.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[781,928],\"end\":[768,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,928],\"end\":[265.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,909],\"end\":[256,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,147],\"end\":[265.5,137.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,128],\"end\":[288,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,128]}]}]},{\"start\":[640,144],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[790,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,144]}]}]},{\"start\":[480,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[480,320]}]}]},{\"start\":[544,224],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,224]}]}]},{\"start\":[736,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]}]},{\"start\":[640,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,512]}]}]},{\"start\":[736,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,608]}]}]},{\"start\":[608,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,704]}]}]},{\"start\":[736,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-text4\",\"codePoint\":59907},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[608,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,352]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[646,352],\"end\":[627,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[608,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,96]}]}]},{\"start\":[832,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]}]},{\"start\":[544,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,320]}]}]},{\"start\":[544,224],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,224]}]}]},{\"start\":[736,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]}]},{\"start\":[736,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,512]}]}]},{\"start\":[736,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,608]}]}]},{\"start\":[736,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,704]}]}]},{\"start\":[736,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"document-text5\",\"codePoint\":59908},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[624,96],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,96]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[262,96],\"end\":[243,115]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,134],\"end\":[224,160]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[224,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[224,922],\"end\":[242.5,941]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[261,960],\"end\":[288,960]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,960]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[795,960],\"end\":[813.5,941.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[832,923],\"end\":[832,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,96]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,96]}]}]},{\"start\":[608,128],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,288]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[608,315],\"end\":[626.5,333.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[645,352],\"end\":[672,352]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[800,896]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[800,909],\"end\":[790.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[781,928],\"end\":[768,928]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,928]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,928],\"end\":[265.5,918.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,909],\"end\":[256,896]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,160]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[256,147],\"end\":[265.5,137.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,128],\"end\":[288,128]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[608,128]}]}]},{\"start\":[640,144],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[790,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[672,320]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[659,320],\"end\":[649.5,310.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,301],\"end\":[640,288]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,144]}]}]},{\"start\":[544,320],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,352]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,320]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,320]}]}]},{\"start\":[544,224],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,256]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,224]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[544,224]}]}]},{\"start\":[736,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,448]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,416]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,416]}]}]},{\"start\":[736,512],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,544]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,512]}]}]},{\"start\":[736,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,640]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,608]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,608]}]}]},{\"start\":[736,704],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,736]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,704]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,704]}]}]},{\"start\":[736,800],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,832]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[320,800]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[736,800]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"folder4\",\"codePoint\":59909},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,191],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,191],\"end\":[111,217]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,243],\"end\":[86,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,789]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,823],\"end\":[111,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,875],\"end\":[170,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,875],\"end\":[913,849]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,823],\"end\":[938,789]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,363]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,329],\"end\":[913,303]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,277],\"end\":[854,277]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,191]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,191]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"shift\",\"codePoint\":59910},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[192,419],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[201,419],\"end\":[209.5,419.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[218,420],\"end\":[227,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[236,423],\"end\":[245,425.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[254,428],\"end\":[263,431]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[263,431]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[269,433],\"end\":[272.5,438]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,443],\"end\":[276,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,449],\"end\":[276,449]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,449],\"end\":[276,449]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[276,478]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[276,478]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,486],\"end\":[270.5,492]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[265,498],\"end\":[256,498]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[254,498],\"end\":[252,497.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[250,497],\"end\":[248,496]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[240,492],\"end\":[233,489.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[226,487],\"end\":[220,485]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[213,483],\"end\":[207,482.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[201,482],\"end\":[196,482]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[186,482],\"end\":[179.5,483.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[173,485],\"end\":[170,488]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[167,490],\"end\":[166,491.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[165,493],\"end\":[165,498]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[165,502],\"end\":[165.5,503.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[166,505],\"end\":[167,505]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[167,506],\"end\":[173.5,508.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[180,511],\"end\":[191,513]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[191,513]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[191,513],\"end\":[191,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[191,513],\"end\":[191,513]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[209,516]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[209,516]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[227,520],\"end\":[242,526.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[257,533],\"end\":[268,544]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[278,555],\"end\":[283.5,569.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[289,584],\"end\":[289,600]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[289,620],\"end\":[282,636.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,653],\"end\":[260,664]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[260,664]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[246,675],\"end\":[227,680]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[208,685],\"end\":[186,685]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[177,685],\"end\":[167.5,684]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[158,683],\"end\":[149,681]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[149,681]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[149,681],\"end\":[149,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[149,681],\"end\":[149,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[149,681],\"end\":[149,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[149,681],\"end\":[149,681]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[139,679],\"end\":[129.5,676]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[120,673],\"end\":[110,670]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[110,670]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[104,667],\"end\":[100.5,662]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[97,657],\"end\":[97,651]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[97,651],\"end\":[97,651]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[97,651],\"end\":[97,651]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[97,621]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[97,613],\"end\":[102.5,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[108,601],\"end\":[117,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[119,601],\"end\":[121.5,601.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[124,602],\"end\":[126,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,608],\"end\":[142,611.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[150,615],\"end\":[157,617]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[165,619],\"end\":[172,620]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[179,621],\"end\":[186,621]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[197,621],\"end\":[203.5,619.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[210,618],\"end\":[213,615]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[217,613],\"end\":[218,610.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[219,608],\"end\":[219,603]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[219,598],\"end\":[218,596]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[217,594],\"end\":[215,592]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[215,592]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,592],\"end\":[215,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,592],\"end\":[215,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[214,590],\"end\":[208,587.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[202,585],\"end\":[192,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[175,580]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[175,580]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[175,580],\"end\":[175,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[175,580],\"end\":[175,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[175,580],\"end\":[175,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[175,580],\"end\":[175,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[156,576],\"end\":[141.5,570]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[127,564],\"end\":[117,554]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[106,544],\"end\":[101,530]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[96,516],\"end\":[96,501]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[96,483],\"end\":[103,467]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[110,451],\"end\":[124,440]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[123,440]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[123,440],\"end\":[123.5,440]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[124,440],\"end\":[124,440]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[137,429],\"end\":[154.5,424]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[172,419],\"end\":[192,419]}]}]}]},{\"start\":[345,422],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[353,422],\"end\":[359,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[365,434],\"end\":[365,442]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[365,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[433,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[433,442]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[433,442]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[433,434],\"end\":[439,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[445,422],\"end\":[453,422]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[482,422]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[491,422],\"end\":[496.5,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[502,434],\"end\":[502,442]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[502,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[502,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[502,669],\"end\":[496.5,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[491,680],\"end\":[482,680]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[453,680]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[445,680],\"end\":[439,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[433,669],\"end\":[433,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[433,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[365,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[365,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[365,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[365,669],\"end\":[359,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[353,680],\"end\":[345,680]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[316,680]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,680],\"end\":[302,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,669],\"end\":[296,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[296,442]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[296,442]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[296,434],\"end\":[302,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[308,422],\"end\":[316,422]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[345,422]}]}]},{\"start\":[541,422],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[667,422]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[669,422],\"end\":[670,422.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,423],\"end\":[673,423]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,423]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,423],\"end\":[675.5,422.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,422],\"end\":[679,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[679,422],\"end\":[679,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[679,422],\"end\":[679,422]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[864,422]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[872,422],\"end\":[877.5,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[883,434],\"end\":[883,442]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[883,467]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[883,467]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[883,475],\"end\":[877.5,481]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[872,487],\"end\":[864,487]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[806,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[806,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[806,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[806,669],\"end\":[800,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[794,680],\"end\":[786,680]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[756,680]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[748,680],\"end\":[742.5,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[737,669],\"end\":[737,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[737,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[679,487]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,487],\"end\":[675.5,486.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,486],\"end\":[673,486]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[673,486]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,486],\"end\":[670.5,486.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[669,487],\"end\":[667,487]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[591,487]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[591,512]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[658,512]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,512],\"end\":[671.5,517.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,523],\"end\":[677,531]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[677,556]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[677,556]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,564],\"end\":[671.5,570]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[666,576],\"end\":[658,576]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[591,576]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[591,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[591,661]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[591,669],\"end\":[585,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[579,680],\"end\":[571,680]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[541,680]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[533,680],\"end\":[527.5,674.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,669],\"end\":[522,661]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[522,442]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[522,442]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[522,434],\"end\":[527.5,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[533,422],\"end\":[541,422]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"replace\",\"codePoint\":59911},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[942,570],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[942,570]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,570],\"end\":[960.5,577.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[968,585],\"end\":[968,595]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[968,998]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[968,998]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[968,1009],\"end\":[960.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,1024],\"end\":[942,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[539,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[529,1024],\"end\":[521.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,1009],\"end\":[514,998]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,595]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,595]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,585],\"end\":[521.5,577.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[529,570],\"end\":[539,570]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[942,570]}]}]},{\"start\":[565,973],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[917,973]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[917,621]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,621]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,973]}]}]},{\"start\":[777,220],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[777,542]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[777,542]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[777,542],\"end\":[777,542]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[777,542],\"end\":[777,542]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[777,553],\"end\":[769.5,560.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[762,568],\"end\":[751,568]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[740,568],\"end\":[732.5,560.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,553],\"end\":[725,542]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,542],\"end\":[725,542]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[725,542],\"end\":[725,542]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[725,271]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[441,271]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[441,271],\"end\":[440.5,271.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,272],\"end\":[440,272]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[430,272],\"end\":[422.5,264]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[415,256],\"end\":[415,246]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[415,235],\"end\":[422.5,227.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[430,220],\"end\":[440,220]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[440,220],\"end\":[440.5,220]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[441,220],\"end\":[441,220]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[777,220]}]}]},{\"start\":[26,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[77,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[93,6]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[102,24]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,43]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[77,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[26,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,46]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,27]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[7,8]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[26,0]}]}]},{\"start\":[231,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,6]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[307,24]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[301,43]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[231,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[215,46]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[205,27]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[212,8]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[231,0]}]}]},{\"start\":[427,7],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[446,14]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,33]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,84]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[449,100]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,110]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[411,103]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,84]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,33]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[409,17]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[427,7]}]}]},{\"start\":[24,126],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,132]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,151]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,202]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[46,218]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[27,228]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,221]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,202]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,151]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6,135]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[24,126]}]}]},{\"start\":[427,212],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[446,219]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,238]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,289]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[449,305]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,315]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[411,308]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,289]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,238]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[409,222]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[427,212]}]}]},{\"start\":[24,331],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,356]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,407]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[46,424]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[27,433]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,426]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,407]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,356]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6,340]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[24,331]}]}]},{\"start\":[158,403],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[209,403]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[226,409]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[235,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[228,446]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[209,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[158,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[142,449]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[133,430]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[139,411]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[158,403]}]}]},{\"start\":[363,403],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[414,403]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[431,409]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[440,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[433,446]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[414,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[363,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[347,449]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[338,430]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[344,411]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[363,403]}]}]},{\"start\":[629,394],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[629,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,394],\"end\":[629,394]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,394],\"end\":[629,394]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[634,394],\"end\":[639,396]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,398],\"end\":[647,402]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[751,505]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[855,402]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,398],\"end\":[863,396]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[868,394],\"end\":[873,394]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[884,394],\"end\":[891.5,401.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[899,409],\"end\":[899,419]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[899,425],\"end\":[897,429.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[895,434],\"end\":[891,438]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[769,560]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[766,563],\"end\":[761,565]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[756,567],\"end\":[751,567]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[746,567],\"end\":[741,565]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,563],\"end\":[733,560]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[611,438]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[607,434],\"end\":[605,429.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[603,425],\"end\":[603,419]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[603,409],\"end\":[610.5,401.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,394],\"end\":[629,394]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"replace_all\",\"codePoint\":59912},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[778,253],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[778,672]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[778,672]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[778,672],\"end\":[778,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[778,672],\"end\":[778,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[778,682],\"end\":[771,689]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[764,696],\"end\":[753,696]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[743,696],\"end\":[735.5,689]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[728,682],\"end\":[728,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[728,672],\"end\":[728,672]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[728,672],\"end\":[728,672]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[728,300]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[438,300]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,300],\"end\":[438,300]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,300],\"end\":[438,300]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[428,300],\"end\":[420.5,293]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,286],\"end\":[413,277]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,267],\"end\":[420.5,260]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[428,253],\"end\":[438,253]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,253],\"end\":[438,253]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,253],\"end\":[438,253]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[778,253]}]}]},{\"start\":[26,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[77,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[93,6]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[102,24]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[96,43]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[77,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[26,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[9,46]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,27]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[7,8]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[26,0]}]}]},{\"start\":[231,0],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,0]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,6]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[307,24]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[301,43]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[282,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[231,51]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[215,46]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[205,27]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[212,8]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[231,0]}]}]},{\"start\":[427,7],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[446,14]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,33]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,84]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[449,100]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,110]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[411,103]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,84]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,33]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[409,17]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[427,7]}]}]},{\"start\":[24,126],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,132]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,151]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,202]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[46,218]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[27,228]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,221]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,202]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,151]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6,135]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[24,126]}]}]},{\"start\":[427,212],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[446,219]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,238]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[454,289]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[449,305]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[430,315]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[411,308]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,289]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,238]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[409,222]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[427,212]}]}]},{\"start\":[24,331],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[43,337]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,356]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[51,407]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[46,424]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[27,433]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,426]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,407]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,356]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[6,340]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[24,331]}]}]},{\"start\":[158,403],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[209,403]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[226,409]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[235,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[228,446]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[209,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[158,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[142,449]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[133,430]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[139,411]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[158,403]}]}]},{\"start\":[363,403],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[414,403]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[431,409]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[440,427]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[433,446]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[414,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[363,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[347,449]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[338,430]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[344,411]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[363,403]}]}]},{\"start\":[629,580],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[629,580]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,580],\"end\":[629,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,580],\"end\":[629,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[634,580],\"end\":[639,582.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,585],\"end\":[647,588]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[751,692]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[855,588]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[858,585],\"end\":[863,582.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[868,580],\"end\":[873,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[884,580],\"end\":[891.5,587.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[899,595],\"end\":[899,606]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[899,611],\"end\":[897,616]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[895,621],\"end\":[891,624]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[769,746]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[766,750],\"end\":[761,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[756,754],\"end\":[751,754]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[746,754],\"end\":[741,752]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[736,750],\"end\":[733,746]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[611,624]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[607,621],\"end\":[605,616]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[603,611],\"end\":[603,606]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[603,595],\"end\":[610.5,587.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[618,580],\"end\":[629,580]}]}]}]},{\"start\":[372,764],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[372,764]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[382,764],\"end\":[389.5,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[397,779],\"end\":[397,790]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[397,998]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[397,998]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[397,1009],\"end\":[389.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[382,1024],\"end\":[372,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[163,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[153,1024],\"end\":[145.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[138,1009],\"end\":[138,998]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[138,790]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[138,790]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[138,779],\"end\":[145.5,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[153,764],\"end\":[163,764]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[372,764]}]}]},{\"start\":[189,973],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[346,973]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[346,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[189,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[189,973]}]}]},{\"start\":[657,764],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[657,764]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[668,764],\"end\":[675.5,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,779],\"end\":[683,790]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[683,998]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[683,998]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,1009],\"end\":[675.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[668,1024],\"end\":[657,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[449,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,1024],\"end\":[430.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[423,1009],\"end\":[423,998]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[423,790]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[423,790]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[423,779],\"end\":[430.5,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[438,764],\"end\":[449,764]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[657,764]}]}]},{\"start\":[474,973],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[631,973]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[631,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[474,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[474,973]}]}]},{\"start\":[942,764],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[942,764]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,764],\"end\":[960.5,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[968,779],\"end\":[968,790]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[968,998]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[968,998]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[968,1009],\"end\":[960.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[953,1024],\"end\":[942,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[734,1024]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,1024],\"end\":[715.5,1016.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[708,1009],\"end\":[708,998]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,790]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,790]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[708,779],\"end\":[715.5,771.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,764],\"end\":[734,764]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[942,764]}]}]},{\"start\":[759,973],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[917,973]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[917,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[759,815]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[759,973]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"moveline-up\",\"codePoint\":59913},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[530,304],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,304],\"end\":[530,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,304],\"end\":[530,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[525,304],\"end\":[520,302]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,300],\"end\":[512,296]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[304,296]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[300,300],\"end\":[295.5,302]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[291,304],\"end\":[285,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,304],\"end\":[267.5,296.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,289],\"end\":[260,279]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,273],\"end\":[262,268.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,264],\"end\":[268,260]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[390,138]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,135],\"end\":[398,133]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[403,131],\"end\":[408,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,131],\"end\":[417.5,133]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,135],\"end\":[426,138]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[548,260]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[552,264],\"end\":[554,268.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,273],\"end\":[556,278]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,289],\"end\":[548.5,296.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[541,304],\"end\":[530,304]}]}]}]},{\"start\":[408,899],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,899]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,899],\"end\":[408,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,899],\"end\":[408,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,899],\"end\":[388,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,883],\"end\":[380,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,871],\"end\":[380,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,871],\"end\":[380,871]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,192],\"end\":[380,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,192],\"end\":[380,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,180],\"end\":[388,172]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,164],\"end\":[408,164]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,164],\"end\":[428,172]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,180],\"end\":[436,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,192],\"end\":[436,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,192],\"end\":[436,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,871]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,871]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,871],\"end\":[436,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,871],\"end\":[436,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,883],\"end\":[428,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,899],\"end\":[408,899]}]}]}]},{\"start\":[521,394],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[566,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[568,394],\"end\":[570,395.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,397],\"end\":[573,399]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,530]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[671,399]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,397],\"end\":[674,395.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[676,394],\"end\":[679,394]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[723,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,394],\"end\":[728.5,396.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[731,399],\"end\":[731,402]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[731,622]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[731,625],\"end\":[728.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,630],\"end\":[723,630]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[694,630]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[691,630],\"end\":[688.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,625],\"end\":[686,622]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[686,471]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[644,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,585],\"end\":[642,586.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,588],\"end\":[637,588]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[607,588]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[605,588],\"end\":[603,586.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[601,585],\"end\":[600,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[558,471]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[558,622]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[558,625],\"end\":[555.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[553,630],\"end\":[550,630]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[521,630]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[518,630],\"end\":[516,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,625],\"end\":[514,622]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,402]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,399],\"end\":[516,396.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[518,394],\"end\":[521,394]}]}]}]},{\"start\":[697.5,422],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[698,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[698,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[698,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[697.5,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[697,422],\"end\":[697,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[697,422],\"end\":[697.5,422]}]}]}]},{\"start\":[715,613],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[715,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,613]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[715,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[715,613]}]}]},{\"start\":[529,615],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[543,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[536,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[529,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[529,613]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[529,615]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"moveline-down\",\"codePoint\":59914},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[285,726],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[285,726]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[285,726],\"end\":[285.5,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[286,726],\"end\":[286,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[291,726],\"end\":[295.5,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[300,730],\"end\":[304,733]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,837]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,733]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,730],\"end\":[520,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[525,726],\"end\":[530,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[541,726],\"end\":[548.5,733.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,741],\"end\":[556,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,757],\"end\":[553.5,761.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,766],\"end\":[548,770]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,892]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,895],\"end\":[417.5,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,899],\"end\":[408,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[402,899],\"end\":[397.5,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,895],\"end\":[390,892]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[268,770]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,766],\"end\":[262,761.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,757],\"end\":[260,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,741],\"end\":[267.5,733.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,726],\"end\":[285,726]}]}]}]},{\"start\":[407,131],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[407,131],\"end\":[407.5,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,131],\"end\":[408,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[419,131],\"end\":[427.5,139]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,147],\"end\":[436,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,159],\"end\":[436,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,159],\"end\":[436,159]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,837]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,837]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,837],\"end\":[436,837.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,838],\"end\":[436,838]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,849],\"end\":[427.5,857.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[419,866],\"end\":[408,866]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,866],\"end\":[388,857.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,849],\"end\":[380,838]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,838],\"end\":[380,837.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,837],\"end\":[380,837]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,159]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,159]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,159],\"end\":[380,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,159],\"end\":[380,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,147],\"end\":[388,139]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,131],\"end\":[407,131]}]}]}]},{\"start\":[521,394],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[566,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[568,394],\"end\":[570,395.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,397],\"end\":[573,399]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[622,530]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[671,399]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,397],\"end\":[674,395.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[676,394],\"end\":[679,394]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[723,394]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,394],\"end\":[728.5,396.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[731,399],\"end\":[731,402]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[731,622]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[731,625],\"end\":[728.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[726,630],\"end\":[723,630]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[694,630]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[691,630],\"end\":[688.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,625],\"end\":[686,622]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[686,471]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[644,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[644,585],\"end\":[642,586.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[640,588],\"end\":[637,588]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[607,588]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[605,588],\"end\":[603,586.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[601,585],\"end\":[600,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[558,471]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[558,622]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[558,625],\"end\":[555.5,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[553,630],\"end\":[550,630]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[521,630]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[518,630],\"end\":[516,627.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,625],\"end\":[514,622]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[514,402]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[514,399],\"end\":[516,396.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[518,394],\"end\":[521,394]}]}]}]},{\"start\":[697.5,422],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[698,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[698,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[698,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,422],\"end\":[697.5,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[697,422],\"end\":[697,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[697,422],\"end\":[697.5,422]}]}]}]},{\"start\":[715,613],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[715,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,613]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[715,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[715,613]}]}]},{\"start\":[529,615],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[543,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[536,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[529,615]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[529,613]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[529,615]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"copyline-up\",\"codePoint\":59915},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[530,304],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,304],\"end\":[530,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[530,304],\"end\":[530,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[525,304],\"end\":[520,302]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,300],\"end\":[512,296]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[304,296]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[300,300],\"end\":[295.5,302]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[291,304],\"end\":[285,304]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,304],\"end\":[267.5,296.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,289],\"end\":[260,279]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,273],\"end\":[262,268.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,264],\"end\":[268,260]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[390,138]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,135],\"end\":[398,133]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[403,131],\"end\":[408,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,131],\"end\":[417.5,133]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,135],\"end\":[426,138]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[548,260]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[552,264],\"end\":[554,268.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,273],\"end\":[556,278]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,289],\"end\":[548.5,296.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[541,304],\"end\":[530,304]}]}]}]},{\"start\":[408,899],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,899]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,899],\"end\":[408,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,899],\"end\":[408,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,899],\"end\":[388,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,883],\"end\":[380,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,871],\"end\":[380,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,871],\"end\":[380,871]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,192]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,192]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,192],\"end\":[380,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,192],\"end\":[380,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,180],\"end\":[388,172]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,164],\"end\":[408,164]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,164],\"end\":[428,172]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,180],\"end\":[436,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,192],\"end\":[436,192]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,192],\"end\":[436,192]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,871]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,871]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,871],\"end\":[436,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,871],\"end\":[436,871]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,883],\"end\":[428,891]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[420,899],\"end\":[408,899]}]}]}]},{\"start\":[643,393],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,393],\"end\":[665,394.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[676,396],\"end\":[686,399]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,399],\"end\":[686,399]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,399],\"end\":[686,399]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[696,401],\"end\":[705.5,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[715,409],\"end\":[724,415]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[724,415]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[727,416],\"end\":[728.5,418.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,421],\"end\":[730,424]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,454]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,458],\"end\":[726.5,461.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,465],\"end\":[719,465]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,465],\"end\":[715,464.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[713,464],\"end\":[712,462]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,456],\"end\":[696,451.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,447],\"end\":[680,444]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,444]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,444],\"end\":[680,444]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,444],\"end\":[680,444]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,442],\"end\":[663,440.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,439],\"end\":[645,439]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[625,439],\"end\":[612,443.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[599,448],\"end\":[590,457]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[590,457]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,457],\"end\":[590,457]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,457],\"end\":[590,457]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,466],\"end\":[576,479.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[571,493],\"end\":[571,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[571,531],\"end\":[575.5,544.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,558],\"end\":[590,567]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[599,576],\"end\":[612,580.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[625,585],\"end\":[645,585]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,585],\"end\":[663,583.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,582],\"end\":[680,580]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,580]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,580],\"end\":[680,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,580],\"end\":[680,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,580],\"end\":[680,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,580],\"end\":[680,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,577],\"end\":[696,572.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,568],\"end\":[712,562]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,562]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[713,560],\"end\":[715,559.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,559],\"end\":[719,559]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,559],\"end\":[726.5,562.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,566],\"end\":[730,570]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,600]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,600]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,603],\"end\":[728.5,605.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[727,608],\"end\":[724,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[715,615],\"end\":[705.5,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[696,623],\"end\":[686,625]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[675,628],\"end\":[664.5,629.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,631],\"end\":[643,631]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,631],\"end\":[590.5,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[567,615],\"end\":[549,599]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[549,599]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,599],\"end\":[549,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,599],\"end\":[549,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[532,583],\"end\":[523.5,561]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,539],\"end\":[515,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,485],\"end\":[523.5,463]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[532,441],\"end\":[549,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[567,409],\"end\":[590.5,401]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,393],\"end\":[643,393]}]}]}]},{\"start\":[643,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,416],\"end\":[597.5,422.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,429],\"end\":[565,441]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,441]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[565,441],\"end\":[565,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[565,441],\"end\":[565,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,454],\"end\":[544,471.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,489],\"end\":[537,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,535],\"end\":[544,552.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,570],\"end\":[565,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,595],\"end\":[597.5,601.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,608],\"end\":[643,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,608],\"end\":[662,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,606],\"end\":[680,604]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,604]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[687,602],\"end\":[694,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,596],\"end\":[708,592]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,589]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[703,592],\"end\":[698,595.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[693,599],\"end\":[688,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,604],\"end\":[666.5,606]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[656,608],\"end\":[645,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[623,608],\"end\":[605,601.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,595],\"end\":[574,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[574,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,583],\"end\":[574,583]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,583],\"end\":[574,583]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,570],\"end\":[555,552]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,534],\"end\":[549,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,490],\"end\":[555,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,454],\"end\":[574,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,429],\"end\":[605,422.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[623,416],\"end\":[645,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[656,416],\"end\":[666.5,418]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,420],\"end\":[688,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[693,425],\"end\":[698,428.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[703,432],\"end\":[708,435]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,431]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,428],\"end\":[694.5,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,422],\"end\":[680,420]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,420]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,420],\"end\":[680,420]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,420],\"end\":[680,420]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,418],\"end\":[662,417]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,416],\"end\":[643,416]}]}]}]},{\"start\":[635,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[649,609],\"end\":[661.5,607.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[674,606],\"end\":[685,602]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[689,601],\"end\":[692.5,599.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[696,598],\"end\":[701,595]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,592]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,591]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[707,590],\"end\":[706.5,590]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,590],\"end\":[702,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,597],\"end\":[692,598.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[689,600],\"end\":[684,602]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,604],\"end\":[669.5,605.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[662,607],\"end\":[655,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[651,608],\"end\":[644,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[637,608],\"end\":[634,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[622,607],\"end\":[612,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[602,601],\"end\":[594,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,589],\"end\":[567.5,575.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,562],\"end\":[552,543]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,529],\"end\":[549,511.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,494],\"end\":[552,480]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[555,470],\"end\":[559.5,461.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[564,453],\"end\":[570,446]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,443],\"end\":[575.5,440]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[579,437],\"end\":[581,435]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[593,426],\"end\":[607,421.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[621,417],\"end\":[639,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[650,416],\"end\":[661,417.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,419],\"end\":[683,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,423],\"end\":[691.5,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,427],\"end\":[701,431]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[703,432],\"end\":[704.5,433]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,434],\"end\":[707,434]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,435]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,431]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,429]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,428],\"end\":[697.5,427]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,426],\"end\":[694,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,422],\"end\":[676.5,419.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[667,417],\"end\":[656,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,416],\"end\":[643,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[634,416],\"end\":[630,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[612,418],\"end\":[599.5,422]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,426],\"end\":[576,433]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[572,435],\"end\":[569,437.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[566,440],\"end\":[563,443]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,449],\"end\":[553.5,454]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,459],\"end\":[547,465]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[542,475],\"end\":[539.5,486.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,498],\"end\":[537,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,522],\"end\":[538,530.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[539,539],\"end\":[542,547]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[545,557],\"end\":[550,565]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[555,573],\"end\":[562,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[575,593],\"end\":[593.5,600]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[612,607],\"end\":[635,608]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"copyline-down\",\"codePoint\":59916},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":968,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[285,726],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[285,726]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[285,726],\"end\":[285.5,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[286,726],\"end\":[286,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[291,726],\"end\":[295.5,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[300,730],\"end\":[304,733]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[408,837]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,733]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,730],\"end\":[520,728]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[525,726],\"end\":[530,726]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[541,726],\"end\":[548.5,733.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,741],\"end\":[556,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[556,757],\"end\":[553.5,761.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,766],\"end\":[548,770]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,892]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[422,895],\"end\":[417.5,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,899],\"end\":[408,899]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[402,899],\"end\":[397.5,897]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[393,895],\"end\":[390,892]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[268,770]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[264,766],\"end\":[262,761.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,757],\"end\":[260,751]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[260,741],\"end\":[267.5,733.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[275,726],\"end\":[285,726]}]}]}]},{\"start\":[407,131],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[407,131],\"end\":[407.5,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,131],\"end\":[408,131]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[419,131],\"end\":[427.5,139]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,147],\"end\":[436,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,159],\"end\":[436,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,159],\"end\":[436,159]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,837]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[436,837]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,837],\"end\":[436,837.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,838],\"end\":[436,838]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,849],\"end\":[427.5,857.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[419,866],\"end\":[408,866]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,866],\"end\":[388,857.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,849],\"end\":[380,838]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,838],\"end\":[380,837.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,837],\"end\":[380,837]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,159]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[380,159]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,159],\"end\":[380,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,159],\"end\":[380,159]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[380,147],\"end\":[388,139]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[396,131],\"end\":[407,131]}]}]}]},{\"start\":[643,393],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,393],\"end\":[665,394.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[676,396],\"end\":[686,399]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,399],\"end\":[686,399]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[686,399],\"end\":[686,399]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[696,401],\"end\":[705.5,405]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[715,409],\"end\":[724,415]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[724,415]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[727,416],\"end\":[728.5,418.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,421],\"end\":[730,424]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,424],\"end\":[730,424]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,424],\"end\":[730,424]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,454]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,454]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,458],\"end\":[726.5,461.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,465],\"end\":[719,465]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,465],\"end\":[715,464.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[713,464],\"end\":[712,462]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,456],\"end\":[696,451.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,447],\"end\":[680,444]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,444]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,444],\"end\":[680,444]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,444],\"end\":[680,444]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,442],\"end\":[663,440.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,439],\"end\":[645,439]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[625,439],\"end\":[612,443.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[599,448],\"end\":[590,457]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[590,457]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,457],\"end\":[590,457]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[590,457],\"end\":[590,457]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[581,466],\"end\":[576,479.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[571,493],\"end\":[571,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[571,531],\"end\":[575.5,544.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,558],\"end\":[590,567]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[590,567]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[599,576],\"end\":[612,580.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[625,585],\"end\":[645,585]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,585],\"end\":[663,583.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[672,582],\"end\":[680,580]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,580]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,580],\"end\":[680,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,580],\"end\":[680,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,577],\"end\":[696,572.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,568],\"end\":[712,562]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[712,562]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[713,560],\"end\":[715,559.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[717,559],\"end\":[719,559]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,559],\"end\":[726.5,562.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,566],\"end\":[730,570]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,600]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[730,600]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[730,603],\"end\":[728.5,605.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[727,608],\"end\":[724,609]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[715,615],\"end\":[705.5,619]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[696,623],\"end\":[686,625]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[675,628],\"end\":[664.5,629.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[654,631],\"end\":[643,631]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,631],\"end\":[590.5,623]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[567,615],\"end\":[549,599]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[549,599]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,599],\"end\":[549,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,599],\"end\":[549,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[532,583],\"end\":[523.5,561]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,539],\"end\":[515,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[515,485],\"end\":[523.5,463]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[532,441],\"end\":[549,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[567,409],\"end\":[590.5,401]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,393],\"end\":[643,393]}]}]}]},{\"start\":[643,416],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,416],\"end\":[597.5,422.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,429],\"end\":[565,441]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,441]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[565,441],\"end\":[565,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[565,441],\"end\":[565,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,454],\"end\":[544,471.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,489],\"end\":[537,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,535],\"end\":[544,552.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,570],\"end\":[565,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[565,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,595],\"end\":[597.5,601.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[617,608],\"end\":[643,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,608],\"end\":[662,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,606],\"end\":[680,604]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,604]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,604],\"end\":[680,604]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[687,602],\"end\":[694,599]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,596],\"end\":[708,592]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,589]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[703,592],\"end\":[698,595.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[693,599],\"end\":[688,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,604],\"end\":[666.5,606]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[656,608],\"end\":[645,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[623,608],\"end\":[605,601.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,595],\"end\":[574,583]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[574,583]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,583],\"end\":[574,583]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[574,583],\"end\":[574,583]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,570],\"end\":[555,552]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,534],\"end\":[549,512]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,490],\"end\":[555,472]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,454],\"end\":[574,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,429],\"end\":[605,422.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[623,416],\"end\":[645,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[656,416],\"end\":[666.5,418]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,420],\"end\":[688,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[693,425],\"end\":[698,428.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[703,432],\"end\":[708,435]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[708,431]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,428],\"end\":[694.5,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[688,422],\"end\":[680,420]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,420]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,420],\"end\":[680,420]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,420],\"end\":[680,420]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,418],\"end\":[662,417]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,416],\"end\":[643,416]}]}]}]},{\"start\":[637,608],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,609],\"end\":[666.5,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[680,605],\"end\":[691,600]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[694,599],\"end\":[698,597]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[702,595],\"end\":[705,594]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,592]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,591]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[707,590],\"end\":[707,590]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[707,590],\"end\":[707,590]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[707,590],\"end\":[705.5,591]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[704,592],\"end\":[702,593]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,597],\"end\":[692,598.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[689,600],\"end\":[684,602]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[679,603],\"end\":[673,604.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[667,606],\"end\":[662,607]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[657,607],\"end\":[654,607.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[651,608],\"end\":[644,608]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[637,608],\"end\":[633,607.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[629,607],\"end\":[623,606]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,602],\"end\":[577,585.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[559,569],\"end\":[552,543]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[549,531],\"end\":[548.5,516]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[548,501],\"end\":[551,488]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[553,474],\"end\":[559,462]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[565,450],\"end\":[574,441]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[585,431],\"end\":[599.5,424.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,418],\"end\":[633,417]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[637,416],\"end\":[645,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[653,416],\"end\":[657,417]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[665,418],\"end\":[673,419.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[681,421],\"end\":[688,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[690,424],\"end\":[693.5,426]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[697,428],\"end\":[700,430]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[701,431],\"end\":[703,432]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[705,433],\"end\":[705,433]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,435]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[707,431]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[702,429]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[695,425],\"end\":[690,423]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[685,421],\"end\":[678,420]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[673,418],\"end\":[668,417.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[663,417],\"end\":[656,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,416],\"end\":[641.5,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[631,416],\"end\":[627,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,418],\"end\":[604,420.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[594,423],\"end\":[584,428]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[573,434],\"end\":[563.5,443]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[554,452],\"end\":[548,463]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[544,471],\"end\":[541.5,479.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[539,488],\"end\":[538,500]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,503],\"end\":[537,511.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[537,520],\"end\":[537,523]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[539,542],\"end\":[545,555.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[551,569],\"end\":[562,580]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[576,594],\"end\":[594.5,601]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[613,608],\"end\":[637,608]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"acode\",\"codePoint\":59917},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[976,526],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[976,531],\"end\":[974.5,537.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[973,544],\"end\":[967,550]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[965,552],\"end\":[963,554]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[961,556],\"end\":[959,558]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[959,558]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,589],\"end\":[885.5,635]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[841,681],\"end\":[813,709]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,714],\"end\":[803,717]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[798,720],\"end\":[792,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[777,720],\"end\":[750,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,720],\"end\":[708,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[706,720],\"end\":[704,719.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[702,719],\"end\":[701,719]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[705,746],\"end\":[709.5,772]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[714,798],\"end\":[718,822]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[720,833],\"end\":[710,843.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[700,854],\"end\":[690,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[675,854],\"end\":[655,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[635,854],\"end\":[624,854]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[616,854],\"end\":[606.5,846.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[597,839],\"end\":[595,824]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[587,785],\"end\":[578,734]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[568,683],\"end\":[557,625.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[546,568],\"end\":[534,508]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[523,447],\"end\":[512,390]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[501,447],\"end\":[489,508]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,568],\"end\":[467,626]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[456,684],\"end\":[446,735]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[436,786],\"end\":[429,824]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[426,840],\"end\":[417,847.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,855],\"end\":[400,855]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[388,855],\"end\":[368.5,855]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[349,855],\"end\":[334,855]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[324,855],\"end\":[313.5,844.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[303,834],\"end\":[305,823]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[309,799],\"end\":[313.5,773]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[318,747],\"end\":[323,719]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[321,720],\"end\":[319,720.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[317,721],\"end\":[315,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[300,721],\"end\":[273.5,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[247,721],\"end\":[231,721]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[226,721],\"end\":[220.5,718]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,715],\"end\":[210,710]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[183,682],\"end\":[138,636]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[93,590],\"end\":[64,559]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,559]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[62,557],\"end\":[60,555]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[58,553],\"end\":[56,551]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,545],\"end\":[49,538.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[47,532],\"end\":[47,527]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[47,526],\"end\":[47.5,525]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[48,524],\"end\":[48,523]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[48,518],\"end\":[50,512.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[52,507],\"end\":[56,502]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[58,500],\"end\":[60,498]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[62,496],\"end\":[64,494]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[64,494]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[93,463],\"end\":[138,417]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[183,371],\"end\":[210,343]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,338],\"end\":[220.5,335]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[226,332],\"end\":[231,332]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[247,332],\"end\":[273.5,332]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[300,332],\"end\":[315,332]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[324,332],\"end\":[332,338.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[340,345],\"end\":[343,352]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[347,362],\"end\":[346,374]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[345,386],\"end\":[337,395]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[314,418],\"end\":[264.5,469]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,520],\"end\":[209,526]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[215,533],\"end\":[262.5,582]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[310,631],\"end\":[334,656]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[345,595],\"end\":[355,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[366,471],\"end\":[376,412]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[386,353],\"end\":[395,299]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[404,246],\"end\":[411,203]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[413,191],\"end\":[422.5,181]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[432,171],\"end\":[441,171]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[452,171],\"end\":[468.5,171]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[485,171],\"end\":[500,171]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[501,170],\"end\":[502.5,170]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[504,170],\"end\":[506,170]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[520,170],\"end\":[544,170]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[568,170],\"end\":[583,170]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[592,170],\"end\":[601,180]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[610,190],\"end\":[612,202]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[620,245],\"end\":[629,298]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[638,352],\"end\":[648,410.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[658,469],\"end\":[669,532]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[679,594],\"end\":[690,654]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[714,630],\"end\":[761,581]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,532],\"end\":[814,525]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,519],\"end\":[759,468]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[710,417],\"end\":[687,394]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[678,385],\"end\":[677.5,373]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[677,361],\"end\":[681,351]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[683,344],\"end\":[691,337.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[699,331],\"end\":[708,331]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[723,331],\"end\":[750,331]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[777,331],\"end\":[792,331]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[798,331],\"end\":[803,334]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[808,337],\"end\":[813,342]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[841,370],\"end\":[885.5,416]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[930,462],\"end\":[959,493]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[959,493]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[961,495],\"end\":[963,497]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[965,499],\"end\":[967,501]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[972,506],\"end\":[973.5,511.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[975,517],\"end\":[976,522]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[976,523],\"end\":[976,524]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[976,525],\"end\":[976,526]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"patreon\",\"codePoint\":59919},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[799,51],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[799,51]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[866,80],\"end\":[916,130.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[966,181],\"end\":[995,248]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,315],\"end\":[1024,391]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,467],\"end\":[995,534]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[966,601],\"end\":[916,651]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[866,701],\"end\":[799,730]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,758],\"end\":[656,758]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,758],\"end\":[513,730]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[446,701],\"end\":[396,651]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[346,601],\"end\":[317,534]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,467],\"end\":[288,391]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[288,315],\"end\":[317,248]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[346,181],\"end\":[396,130.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[446,80],\"end\":[513,51]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[580,22],\"end\":[656,22]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[732,22],\"end\":[799,51]}]}]}]},{\"start\":[0,22],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[180,22]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[180,1004]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,1004]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,22]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"paypal\",\"codePoint\":59920},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[295,1024],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[295,1023]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[316,1023],\"end\":[334.5,1008.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[353,994],\"end\":[358,973]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[403,777]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[408,756],\"end\":[427,741]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[446,726],\"end\":[467,726]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[505,726]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[566,726],\"end\":[619,720]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[673,714],\"end\":[720.5,701.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[768,689],\"end\":[808,670]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[848,652],\"end\":[882,627]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[950,577],\"end\":[983.5,511.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1017,446],\"end\":[1017,365]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1017,330],\"end\":[1010.5,300]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1004,270],\"end\":[992,246]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[979,222],\"end\":[961,203]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[943,184],\"end\":[919,169]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[913,166]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[913,166],\"end\":[913,166.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[913,167],\"end\":[913,168]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[926,191],\"end\":[932.5,221]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[939,251],\"end\":[939,287]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[939,368],\"end\":[905,433]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[871,498],\"end\":[804,548]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[770,573],\"end\":[730,592]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[689,611],\"end\":[642,623.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[595,636],\"end\":[541,642]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[487,648],\"end\":[427,648]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[389,648]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[368,648],\"end\":[349.5,663]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[331,678],\"end\":[326,699]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[280,894]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,915],\"end\":[257,930]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[238,945],\"end\":[217,945]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[129,945]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[122,974]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[118,994],\"end\":[129.5,1009]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[141,1024],\"end\":[162,1024]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[295,1024]}]}]},{\"start\":[181,909],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[180,907]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[201,907],\"end\":[219.5,892.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[238,878],\"end\":[243,857]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[288,662]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[293,641],\"end\":[311.5,626.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[330,612],\"end\":[352,612]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[389,612]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[450,612],\"end\":[504,606]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[557,600],\"end\":[604.5,587.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[652,575],\"end\":[692,556]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[733,538],\"end\":[766,513]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[834,463],\"end\":[867.5,398]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[901,333],\"end\":[901,251]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[901,216],\"end\":[895,186]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[889,156],\"end\":[876,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[864,108],\"end\":[845.5,88.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[827,69],\"end\":[804,55]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[779,39],\"end\":[750.5,28.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[722,18],\"end\":[689,12]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[656,6],\"end\":[617,3]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[578,0],\"end\":[534,0]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[258,0]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[237,0],\"end\":[218.5,15]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[200,30],\"end\":[195,50]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[8,859]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[3,879],\"end\":[15,894]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[27,909],\"end\":[48,909]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[181,909]}]}]},{\"start\":[498,168],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[497,168]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[535,168],\"end\":[564,174.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[593,181],\"end\":[612,194]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[631,206],\"end\":[640.5,226]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[650,246],\"end\":[650,272]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[650,312],\"end\":[635.5,343.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[621,375],\"end\":[591,396]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[561,418],\"end\":[519.5,429]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[478,440],\"end\":[424,440]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,440]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[371,440],\"end\":[359,425]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[347,410],\"end\":[352,390]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,218]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[397,197],\"end\":[415.5,182.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[434,168],\"end\":[455,168]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[498,168]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"ruby\",\"codePoint\":59921},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[860,4],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[860,3]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[704,90]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[974,310]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1000,332]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[624,356]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[666,523]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,755]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[331,628]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[330,630]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[332,629]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[212,1020]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[70,689]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[0,817]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[2,890],\"end\":[27,931]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[51,971],\"end\":[83.5,991]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[116,1011],\"end\":[151,1015]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[186,1020],\"end\":[209,1021]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[966,969]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1024,206]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[1023,207]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1025,138],\"end\":[991,79]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[957,20],\"end\":[860,4]}]}]}]},{\"start\":[219,216],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[219,217]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[162,273],\"end\":[120,335]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[78,396],\"end\":[56.5,452.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[35,509],\"end\":[36,557]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[36,604],\"end\":[65,633]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[93,661],\"end\":[141,659]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[189,658],\"end\":[246.5,633]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[304,608],\"end\":[366,564]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[428,520],\"end\":[486,464]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[543,407],\"end\":[585,347]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[627,286],\"end\":[649,230]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[671,174],\"end\":[671,128]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[670,81],\"end\":[642,53]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[614,24],\"end\":[566,25]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[517,26],\"end\":[459,49.5]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[401,73],\"end\":[339,117]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[276,160],\"end\":[219,216]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"font_download\",\"codePoint\":59922},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[632,683],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[392,683]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[344,811]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[254,811]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[472,255]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[552,255]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[770,811]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[680,811]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,683]}]}]},{\"start\":[170,107],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,107],\"end\":[111,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,157],\"end\":[86,191]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,875]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,909],\"end\":[111,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[136,959],\"end\":[170,959]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,959]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,959],\"end\":[913,934]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,909],\"end\":[938,875]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,191]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,157],\"end\":[913,132]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[888,107],\"end\":[854,107]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,107]}]}]},{\"start\":[600,597],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,361]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[424,597]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[600,597]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"notes\",\"codePoint\":59923},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[896,575],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,575]}]}]},{\"start\":[128,363],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[896,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,277]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,363]}]}]},{\"start\":[640,789],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[128,789]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,789]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"http\",\"codePoint\":59924},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[832,511],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[918,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[918,511]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,511]}]}]},{\"start\":[768,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[832,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[918,575]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[944,575],\"end\":[963,556]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,537],\"end\":[982,511]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[982,469]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[982,443],\"end\":[963,424]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[944,405],\"end\":[918,405]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[768,405]}]}]},{\"start\":[598,469],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[662,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[726,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[534,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[534,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[598,469]}]}]},{\"start\":[362,469],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[426,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[490,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[298,469]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[362,469]}]}]},{\"start\":[106,491],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[106,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[42,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[106,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[106,555]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,555]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[256,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[192,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[106,491]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"compare_arrows\",\"codePoint\":59925},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[640,447],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,447]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[938,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,363]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,235]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,575]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,447]}]}]},{\"start\":[86,619],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,703]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,831]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,661]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,491]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[86,619]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"home_filled\",\"codePoint\":59926},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[170,405],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[384,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,619]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[854,405]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[170,405]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"height\",\"codePoint\":59927},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[682,319],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,149]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[470,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[342,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,917]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,747]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[554,319]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[682,319]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}},{\"extras\":{\"name\":\"all_inclusive\",\"codePoint\":59928},\"node\":{\"tag\":\"Element\",\"args\":[{\"tagName\":\"svg\",\"attributes\":{\"viewBox\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"ViewBox\",\"args\":[{\"minX\":0,\"minY\":0,\"width\":1024,\"height\":1024}]}]}},\"children\":[{\"tag\":\"Element\",\"args\":[{\"tagName\":\"path\",\"attributes\":{\"d\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paths\",\"args\":[[{\"start\":[632,369],\"endings\":{\"tag\":\"Connected\",\"args\":[]},\"cmds\":[{\"tag\":\"LineTo\",\"args\":[{\"point\":[632,369]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,475]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[474,511],\"end\":[332,635]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,677],\"end\":[230,677]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,677],\"end\":[128,635]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,593],\"end\":[86,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[86,473],\"end\":[128,431]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[170,389],\"end\":[230,389]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[290,389],\"end\":[334,433]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[382,475]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[448,419]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[394,371]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[366,343],\"end\":[319,323]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[272,303],\"end\":[232,303]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[230,303]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,303],\"end\":[67,371]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,439],\"end\":[0,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[0,627],\"end\":[67,695]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[134,763],\"end\":[230,763]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[326,763],\"end\":[392,697]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[512,591]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[550,555],\"end\":[692,431]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,389],\"end\":[794,389]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,389],\"end\":[896,431]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,473],\"end\":[938,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[938,593],\"end\":[896,635]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[854,677],\"end\":[794,677]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[734,677],\"end\":[690,633]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[640,591]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[576,647]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[630,695]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[660,723],\"end\":[707,743]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[754,763],\"end\":[792,763]}]}]},{\"tag\":\"LineTo\",\"args\":[{\"point\":[794,763]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,763],\"end\":[957,695]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,627],\"end\":[1024,533]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[1024,439],\"end\":[957,371]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[890,303],\"end\":[794,303]}]}]},{\"tag\":\"BezierCurveTo\",\"args\":[{\"tag\":\"QParams\",\"args\":[{\"c\":[698,303],\"end\":[632,369]}]}]}]}]]}]},\"fill\":{\"tag\":\"Value\",\"args\":[{\"tag\":\"Paint\",\"args\":[{\"tag\":\"CurrentColor\",\"args\":[]}]}]}},\"children\":[]}]}]}]},\"palettes\":{\"index\":0,\"table\":[[{\"background\":{\"tag\":\"AutomaticColor\",\"args\":[]},\"foreground\":{\"tag\":\"AutomaticColor\",\"args\":[]}},[]]]}}]}"
  },
  {
    "path": "utils/config.js",
    "content": "const path = require(\"node:path\");\nconst fs = require(\"node:fs\");\nconst { promisify } = require(\"node:util\");\nconst exec = promisify(require(\"node:child_process\").exec);\n\n(async () => {\n\tconst AD_APP_ID = \"ca-app-pub-5911839694379275~4255791238\";\n\tconst CONFIG_ID = /id=\"([a-z.]+\")/;\n\tconst HTML_ID = /<title>[a-z.]+<\\/title>/;\n\tconst ID_PAID = \"com.foxdebug.acode\";\n\tconst ID_FREE = \"com.foxdebug.acodefree\";\n\tconst arg = process.argv[2];\n\tconst arg2 = process.argv[3];\n\tconst platformsDir = path.resolve(__dirname, \"../platforms/\");\n\tconst babelrcpath = path.resolve(__dirname, \"../.babelrc\");\n\tconst configpath = path.resolve(__dirname, \"../config.xml\");\n\tconst htmlpath = path.resolve(__dirname, \"../www/index.html\");\n\tconst logopath = path.resolve(\n\t\t__dirname,\n\t\t\"../res/android/values/ic_launcher_background.xml\",\n\t);\n\n\tconst logoTextPaid = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#3a3e54</color>\n    <color name=\"ic_splash_background\">#3a3e54</color>\n</resources>`;\n\tconst logoTextFree = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#ffffff</color>\n    <color name=\"ic_splash_background\">#313131</color>\n</resources>`;\n\n\ttry {\n\t\tlet babelrcText = fs.readFileSync(babelrcpath, \"utf-8\");\n\t\tlet config = fs.readFileSync(configpath, \"utf-8\");\n\t\tlet html = fs.readFileSync(htmlpath, \"utf-8\");\n\t\tlet platforms = fs\n\t\t\t.readdirSync(platformsDir)\n\t\t\t.filter((file) => !file.startsWith(\".\"));\n\t\tlet logo, id, currentId;\n\n\t\tcurrentId = /id=\"([a-z.]+)\"/.exec(config)[1];\n\t\tbabelrc = JSON.parse(babelrcText);\n\n\t\tif (arg === \"d\") {\n\t\t\tbabelrc.compact = false;\n\t\t} else if (arg === \"p\") {\n\t\t\tbabelrc.compact = true;\n\t\t}\n\n\t\tif (arg2 === \"free\") {\n\t\t\tlogo = logoTextFree;\n\t\t\tid = ID_FREE;\n\t\t} else {\n\t\t\tlogo = logoTextPaid;\n\t\t\tid = ID_PAID;\n\t\t}\n\n\t\tfs.writeFileSync(babelrcpath, babelrcText, \"utf8\");\n\n\t\tif (currentId !== id) {\n\t\t\tconst promises = [];\n\n\t\t\thtml = html.replace(HTML_ID, `<title>${id}</title>`);\n\t\t\tconfig = config.replace(CONFIG_ID, `id=\"${id}\"`);\n\t\t\tbabelrcText = JSON.stringify(babelrc, undefined, 2);\n\n\t\t\tfs.writeFileSync(htmlpath, html, \"utf8\");\n\t\t\tfs.writeFileSync(logopath, logo, \"utf8\");\n\t\t\tfs.writeFileSync(configpath, config, \"utf8\");\n\n\t\t\tfor (let platform of platforms) {\n\t\t\t\tif (!platform) continue;\n\n\t\t\t\tpromises.push(\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`|--- Preparing platform ${platform.toUpperCase()} ---|`,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (id === ID_FREE) {\n\t\t\t\t\t\t\tconsole.log(`|--- Installing Admob ---|`);\n\t\t\t\t\t\t\tawait exec(\n\t\t\t\t\t\t\t\t`cordova plugin add cordova-plugin-consent@2.4.0 --save`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tawait exec(\n\t\t\t\t\t\t\t\t`cordova plugin add admob-plus-cordova@2.0.0-alpha.19 --save --variable APP_ID_ANDROID=\"${AD_APP_ID}\" --variable PLAY_SERVICES_VERSION=\"23.2.0\"`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.log(\"DONE! Installing admob-plus-cordova\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconsole.log(`|--- Removing Admob ---|`);\n\t\t\t\t\t\t\tawait exec(`cordova plugin remove cordova-plugin-consent --save`);\n\t\t\t\t\t\t\tawait exec(`cordova plugin remove admob-plus-cordova --save`);\n\t\t\t\t\t\t\tconsole.log(\"DONE! Removing admob-plus-cordova\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconsole.log(`|--- Reinstalling platform ---|`);\n\t\t\t\t\t\tconst { stderr } = await exec(`npm run clean`);\n\t\t\t\t\t\tif (stderr) console.error(stderr);\n\t\t\t\t\t\telse console.log(\"DONE! Reinstalling platform\");\n\t\t\t\t\t})(),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait Promise.all(promises);\n\t\t}\n\t\tprocess.exit(0);\n\t} catch (error) {\n\t\tconsole.error(error);\n\t\tprocess.exit(1);\n\t}\n})();\n"
  },
  {
    "path": "utils/custom-loaders/html-tag-jsx-loader.js",
    "content": "/**\n * Custom loader that transforms JSX to html-tag-js tag() calls\n * This uses Babel's parser/transformer but is lighter than full babel-loader\n */\nconst { parse } = require(\"@babel/parser\");\nconst traverse = require(\"@babel/traverse\").default;\nconst generate = require(\"@babel/generator\").default;\nconst t = require(\"@babel/types\");\n\nmodule.exports = function htmlTagJsxLoader(source) {\n\tconst callback = this.async();\n\n\t// Enable caching for this loader\n\tthis.cacheable && this.cacheable();\n\n\ttry {\n\t\t// Debug logging - verify loader is running\n\t\t// console.log(`🔧 Custom JSX loader processing: ${this.resourcePath}\\n`);\n\n\t\t// Determine file type from extension\n\t\tconst isTypeScript = /\\.tsx?$/.test(this.resourcePath);\n\n\t\t// Quick check: if no JSX syntax at all, pass through unchanged\n\t\t// Look for complete JSX opening tags with proper spacing\n\t\tconst hasJSXLike =\n\t\t\t/<\\/?[A-Z][a-zA-Z0-9]*[^>]*>|<\\/?[a-z][a-z0-9-]*[^>]*>/.test(source);\n\n\t\tif (!hasJSXLike) {\n\t\t\treturn callback(null, source);\n\t\t}\n\n\t\t// Parse with appropriate plugins\n\t\tconst parserPlugins = [\"jsx\"];\n\t\tif (isTypeScript) {\n\t\t\tparserPlugins.push(\"typescript\");\n\t\t}\n\n\t\tconst ast = parse(source, {\n\t\t\tsourceType: \"module\",\n\t\t\tplugins: parserPlugins,\n\t\t});\n\n\t\t// Track if we need to add the import\n\t\tlet needsTagImport = false;\n\t\tlet hasJSX = false;\n\t\tconst hasExistingImport =\n\t\t\t/import\\s+(?:\\{[^}]*\\btag\\b[^}]*\\}|tag(?:\\s+as\\s+\\w+)?)\\s+from\\s+['\"]html-tag-js['\"]/.test(\n\t\t\t\tsource,\n\t\t\t) ||\n\t\t\t/(?:const|let|var)\\s+(?:\\{[^}]*\\btag\\b[^}]*\\}|tag)\\s*=\\s*require\\s*\\(\\s*['\"]html-tag-js['\"]\\s*\\)/.test(\n\t\t\t\tsource,\n\t\t\t);\n\n\t\t// Transform JSX elements\n\t\ttraverse(ast, {\n\t\t\tJSXFragment(path) {\n\t\t\t\thasJSX = true;\n\t\t\t\tneedsTagImport = true;\n\t\t\t\tconst { node } = path;\n\t\t\t\tconst { children: childrenNode } = node;\n\n\t\t\t\tconst children = [];\n\t\t\t\tpopulateChildren(childrenNode, children, t);\n\t\t\t\tconst arrayExpression = t.arrayExpression(children);\n\t\t\t\tpath.replaceWith(arrayExpression);\n\t\t\t},\n\n\t\t\tJSXElement(path) {\n\t\t\t\thasJSX = true;\n\t\t\t\tneedsTagImport = true;\n\t\t\t\tconst { node } = path;\n\t\t\t\tconst { openingElement: el, children: childrenNode } = node;\n\n\t\t\t\tlet { name: tagName } = el.name;\n\t\t\t\tconst { attributes } = el;\n\n\t\t\t\tlet id;\n\t\t\t\tlet className;\n\t\t\t\tconst on = [];\n\t\t\t\tconst args = [];\n\t\t\t\tconst attrs = [];\n\t\t\t\tconst children = [];\n\t\t\t\tconst options = [];\n\t\t\t\tconst events = {};\n\t\t\t\tlet isComponent =\n\t\t\t\t\t/^(?:[A-Z][a-zA-Z0-9_$]*|(?:[a-zA-Z_$][a-zA-Z0-9_$]*\\.)+[a-zA-Z_$][a-zA-Z0-9_$]*)$/.test(\n\t\t\t\t\t\ttagName,\n\t\t\t\t\t);\n\n\t\t\t\tif (el.name.type === \"JSXMemberExpression\") {\n\t\t\t\t\tconst { object, property } = el.name;\n\t\t\t\t\ttagName = `${object.name}.${property.name}`;\n\t\t\t\t\tisComponent = true;\n\t\t\t\t}\n\n\t\t\t\tpopulateChildren(childrenNode, children, t);\n\n\t\t\t\tfor (const attr of attributes) {\n\t\t\t\t\tif (attr.type === \"JSXSpreadAttribute\") {\n\t\t\t\t\t\tif (isComponent) {\n\t\t\t\t\t\t\tattrs.push(t.spreadElement(attr.argument));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toptions.push(t.spreadElement(attr.argument));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet { name, namespace } = attr.name;\n\n\t\t\t\t\tif (!isComponent) {\n\t\t\t\t\t\tif (name === \"id\") {\n\t\t\t\t\t\t\tif (attr.value && attr.value.type === \"StringLiteral\") {\n\t\t\t\t\t\t\t\tid = attr.value;\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\tattr.value &&\n\t\t\t\t\t\t\t\tattr.value.type === \"JSXExpressionContainer\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tid = attr.value.expression;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ([\"class\", \"className\"].includes(name)) {\n\t\t\t\t\t\t\tif (attr.value && attr.value.type === \"StringLiteral\") {\n\t\t\t\t\t\t\t\tclassName = attr.value;\n\t\t\t\t\t\t\t} else if (\n\t\t\t\t\t\t\t\tattr.value &&\n\t\t\t\t\t\t\t\tattr.value.type === \"JSXExpressionContainer\"\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\tclassName = attr.value.expression;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (namespace) {\n\t\t\t\t\t\tnamespace = namespace.name;\n\t\t\t\t\t\tname = name.name;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!attr.value) {\n\t\t\t\t\t\tattrs.push(\n\t\t\t\t\t\t\tt.objectProperty(t.stringLiteral(name), t.stringLiteral(\"\")),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { type } = attr.value;\n\t\t\t\t\tconst isAttr = /-/.test(name);\n\t\t\t\t\tlet value;\n\n\t\t\t\t\tif (type === \"StringLiteral\") {\n\t\t\t\t\t\tvalue = attr.value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvalue = attr.value.expression;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (namespace) {\n\t\t\t\t\t\tif (![\"on\", \"once\", \"off\"].includes(namespace)) {\n\t\t\t\t\t\t\tattrs.push(\n\t\t\t\t\t\t\t\tt.objectProperty(\n\t\t\t\t\t\t\t\t\tt.stringLiteral(\n\t\t\t\t\t\t\t\t\t\tnamespace === \"attr\" ? name : `${namespace}:${name}`,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tvalue,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (namespace === \"off\") continue;\n\n\t\t\t\t\t\tif (!events[name]) {\n\t\t\t\t\t\t\tevents[name] = [];\n\t\t\t\t\t\t\ton.push(\n\t\t\t\t\t\t\t\tt.objectProperty(\n\t\t\t\t\t\t\t\t\tt.stringLiteral(name),\n\t\t\t\t\t\t\t\t\tt.arrayExpression(events[name]),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tevents[name].push(value);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isAttr) {\n\t\t\t\t\t\tconst attrRegex = /^attr-(.+)/;\n\t\t\t\t\t\tif (attrRegex.test(name)) {\n\t\t\t\t\t\t\t[, name] = attrRegex.exec(name);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tattrs.push(t.objectProperty(t.stringLiteral(name), value));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t(isComponent ? attrs : options).unshift(\n\t\t\t\t\t\tt.objectProperty(t.identifier(name), value),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (isComponent) {\n\t\t\t\t\targs.push(t.identifier(tagName));\n\n\t\t\t\t\tif (on.length > 0) {\n\t\t\t\t\t\tattrs.push(\n\t\t\t\t\t\t\tt.objectProperty(t.identifier(\"on\"), t.objectExpression(on)),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (attrs.length > 0) {\n\t\t\t\t\t\targs.push(t.objectExpression(attrs));\n\t\t\t\t\t}\n\n\t\t\t\t\tif (children.length > 0) {\n\t\t\t\t\t\targs.push(t.arrayExpression(children));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\targs.push(t.stringLiteral(tagName));\n\n\t\t\t\t\tif (on.length > 0) {\n\t\t\t\t\t\toptions.push(\n\t\t\t\t\t\t\tt.objectProperty(t.identifier(\"on\"), t.objectExpression(on)),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (attrs.length > 0) {\n\t\t\t\t\t\toptions.push(\n\t\t\t\t\t\t\tt.objectProperty(t.identifier(\"attr\"), t.objectExpression(attrs)),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (id || className) {\n\t\t\t\t\t\tif (className) {\n\t\t\t\t\t\t\targs.push(className);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\targs.push(t.nullLiteral());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (id) {\n\t\t\t\t\t\t\targs.push(id);\n\t\t\t\t\t\t} else if (className) {\n\t\t\t\t\t\t\t// Push null for id when we have className but no id\n\t\t\t\t\t\t\targs.push(t.nullLiteral());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (children.length) {\n\t\t\t\t\t\targs.push(t.arrayExpression(children));\n\t\t\t\t\t}\n\n\t\t\t\t\tif (options.length) {\n\t\t\t\t\t\targs.push(t.objectExpression(options));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst identifier = t.identifier(\"tag\");\n\t\t\t\tconst callExpression = t.callExpression(identifier, args);\n\t\t\t\tpath.replaceWith(callExpression);\n\t\t\t},\n\t\t});\n\n\t\t// If no JSX was found, return original source\n\t\tif (!hasJSX) {\n\t\t\treturn callback(null, source);\n\t\t}\n\n\t\t// Generate the transformed code\n\t\tconst output = generate(\n\t\t\tast,\n\t\t\t{\n\t\t\t\tsourceMaps: true,\n\t\t\t\tsourceFileName: this.resourcePath,\n\t\t\t\tretainLines: false,\n\t\t\t\tcompact: false,\n\t\t\t},\n\t\t\tsource,\n\t\t);\n\n\t\t// Add import if needed\n\t\tif (needsTagImport && !hasExistingImport) {\n\t\t\toutput.code = `import tag from 'html-tag-js';\\n${output.code}`;\n\t\t}\n\n\t\tcallback(null, output.code, output.map);\n\t} catch (error) {\n\t\tconst errorMessage = `html-tag-jsx-loader failed to process ${this.resourcePath}: ${error.message}`;\n\t\tconst enhancedError = new Error(errorMessage);\n\t\tenhancedError.stack = error.stack;\n\t\tcallback(enhancedError);\n\t}\n};\n\n/**\n * Parse node to expression\n */\nfunction parseNode(types, node) {\n\tconst { type } = node;\n\tif (type === \"JSXText\") {\n\t\tconst trimmed = node.value.trim();\n\t\tif (!trimmed) return null;\n\t\t// Preserve original text if it contains non-whitespace\n\t\t// This maintains intentional spacing like \"Hello \" in <span>Hello </span>\n\t\treturn types.stringLiteral(node.value);\n\t}\n\n\tif (type === \"JSXElement\") {\n\t\treturn node;\n\t}\n\n\tconst { expression } = node;\n\tconst invalidExpressions = [\"JSXEmptyExpression\"];\n\n\tif (invalidExpressions.includes(expression.type)) {\n\t\treturn null;\n\t}\n\n\treturn expression;\n}\n\n/**\n * Populate children\n */\nfunction populateChildren(childrenNode, children, t) {\n\tfor (let node of childrenNode) {\n\t\tnode = parseNode(t, node);\n\t\tif (!node) continue;\n\t\tchildren.push(node);\n\t}\n}\n"
  },
  {
    "path": "utils/lang.js",
    "content": "const path = require(\"node:path\");\nconst fs = require(\"node:fs\");\nconst yargs = require(\"yargs\");\nconst { hideBin } = require(\"yargs/helpers\");\nconst readline = require(\"node:readline\");\n\nconst args = yargs(hideBin(process.argv))\n\t.alias(\"a\", \"all\")\n\t.alias(\"b\", \"bulk\").argv;\nconst dir = path.resolve(__dirname, \"../src/lang\");\nconst read = readline.createInterface({\n\tinput: process.stdin,\n\toutput: process.stdout,\n});\nconst enLang = path.join(dir, \"en-us.json\");\nconst list = fs.readdirSync(dir);\nconst len = list.length;\nlet command = \"\";\nlet arg = \"\";\nlet val = \"\";\n\nif (args._.length > 3) {\n\tconsole.error(\"Invalid arguments\", args._);\n\tprocess.exit(0);\n} else {\n\tcommand = args._[0];\n\targ = args._[1];\n\tval = args._[2];\n}\n\nswitch (command) {\n\tcase \"add\":\n\tcase \"remove\":\n\tcase \"update\":\n\tcase \"update-key\":\n\tcase \"search\":\n\tcase \"check\":\n\t\tupdate();\n\t\tbreak;\n\tcase \"add-all\":\n\t\taddToAllFiles();\n\t\tbreak;\n\tcase \"bulk-add\":\n\t\tbulkAddStrings();\n\t\tbreak;\n\tdefault:\n\t\tconsole.error(`Missing/Invalid arguments.\nuse 'add' to add a new string\nuse 'add-all <key> <value>' to add the same string to ALL language files at once\nuse 'bulk-add <json-file>' to add multiple strings from a JSON file to all language files\nuse 'remove' to remove a string\nuse 'search' to search a string\nuse 'update' to update a string\nuse 'update-key' to update a key\nuse 'check' to check a string`);\n\t\tprocess.exit();\n}\n\n/**\n * Adds a key-value pair to ALL language files at once\n * Usage: pnpm lang add-all \"key\" \"value\"\n */\nfunction addToAllFiles() {\n\tif (!arg || !val) {\n\t\tconsole.error('Usage: pnpm lang add-all \"<key>\" \"<value>\"');\n\t\tconsole.error('Example: pnpm lang add-all \"hello world\" \"Hello World\"');\n\t\tprocess.exit(1);\n\t}\n\n\tconst key = arg.toLowerCase();\n\tlet addedCount = 0;\n\tlet skippedCount = 0;\n\n\tfor (const lang of list) {\n\t\tconst file = path.resolve(dir, lang);\n\t\tconst text = fs.readFileSync(file, \"utf8\");\n\t\tconst strings = JSON.parse(text);\n\n\t\tif (key in strings) {\n\t\t\tconsole.log(`${lang}: Skipped (already exists)`);\n\t\t\tskippedCount++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tstrings[key] = val;\n\t\tconst newText = JSON.stringify(strings, undefined, 2);\n\t\tfs.writeFileSync(file, newText, \"utf8\");\n\t\tconsole.log(`${lang}: Added ✓`);\n\t\taddedCount++;\n\t}\n\n\tconsole.log(\n\t\t`\\nDone! Added to ${addedCount} files, skipped ${skippedCount} files.`,\n\t);\n\tprocess.exit(0);\n}\n\n/**\n * Bulk add multiple strings from a JSON file to ALL language files\n * Usage: pnpm lang bulk-add strings.json\n *\n * JSON file format:\n * {\n *   \"key1\": \"value1\",\n *   \"key2\": \"value2\"\n * }\n */\nfunction bulkAddStrings() {\n\tif (!arg) {\n\t\tconsole.error(\"Usage: pnpm lang bulk-add <json-file>\");\n\t\tconsole.error(\"Example: pnpm lang bulk-add new-strings.json\");\n\t\tconsole.error(\"\\nJSON file format:\");\n\t\tconsole.error(\"{\");\n\t\tconsole.error('  \"key1\": \"value1\",');\n\t\tconsole.error('  \"key2\": \"value2\"');\n\t\tconsole.error(\"}\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst jsonFilePath = path.resolve(process.cwd(), arg);\n\n\tif (!fs.existsSync(jsonFilePath)) {\n\t\tconsole.error(`File not found: ${jsonFilePath}`);\n\t\tprocess.exit(1);\n\t}\n\n\tlet newStrings;\n\ttry {\n\t\tconst jsonContent = fs.readFileSync(jsonFilePath, \"utf8\");\n\t\tnewStrings = JSON.parse(jsonContent);\n\t} catch (err) {\n\t\tconsole.error(`Error parsing JSON file: ${err.message}`);\n\t\tprocess.exit(1);\n\t}\n\n\tconst keys = Object.keys(newStrings);\n\tif (keys.length === 0) {\n\t\tconsole.error(\"No strings found in the JSON file.\");\n\t\tprocess.exit(1);\n\t}\n\n\tconsole.log(\n\t\t`Adding ${keys.length} strings to ${list.length} language files...\\n`,\n\t);\n\n\tfor (const lang of list) {\n\t\tconst file = path.resolve(dir, lang);\n\t\tconst text = fs.readFileSync(file, \"utf8\");\n\t\tconst strings = JSON.parse(text);\n\t\tlet addedCount = 0;\n\t\tlet skippedCount = 0;\n\n\t\tfor (const key of keys) {\n\t\t\tconst lowerKey = key.toLowerCase();\n\t\t\tif (lowerKey in strings) {\n\t\t\t\tskippedCount++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstrings[lowerKey] = newStrings[key];\n\t\t\taddedCount++;\n\t\t}\n\n\t\tif (addedCount > 0) {\n\t\t\tconst newText = JSON.stringify(strings, undefined, 2);\n\t\t\tfs.writeFileSync(file, newText, \"utf8\");\n\t\t}\n\n\t\tconsole.log(`${lang}: Added ${addedCount}, Skipped ${skippedCount}`);\n\t}\n\n\tconsole.log(`\\nDone! Added ${keys.length} strings to all language files.`);\n\tprocess.exit(0);\n}\n\nasync function update() {\n\tlet key;\n\n\tif (command === \"check\") {\n\t\tlet total = 0;\n\t\tlet done = 0;\n\n\t\tfs.readFile(enLang, \"utf-8\", (err, data) => {\n\t\t\tif (err) {\n\t\t\t\tconsole.error(err);\n\t\t\t\tprocess.exit(0);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet error = false;\n\n\t\t\tconst fix = arg === \"fix\";\n\t\t\tconst enLangData = JSON.parse(data);\n\n\t\t\tlist.forEach((file, i) => {\n\t\t\t\tif (file === \"en-us.json\") return;\n\n\t\t\t\tlet flagError = false;\n\t\t\t\tlet langFile = path.join(dir, file);\n\t\t\t\tconst exit = (i, len) => {\n\t\t\t\t\tif (i + 1 === len) {\n\t\t\t\t\t\tif (!error) {\n\t\t\t\t\t\t\tconsole.log(\"\\nGOOD NEWS! No Error Found\\n\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprocess.exit(0);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tfs.readFile(langFile, \"utf-8\", (err, data) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\tconsole.error(err);\n\t\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet langError = () => {\n\t\t\t\t\t\tif (!flagError) {\n\t\t\t\t\t\t\terror = true;\n\t\t\t\t\t\t\tflagError = true;\n\t\t\t\t\t\t\tconsole.log(`-------------- ${file}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\tconst langData = JSON.parse(data);\n\t\t\t\t\tflagError = false;\n\n\t\t\t\t\tfor (let enKey in enLangData) {\n\t\t\t\t\t\tconst key = Object.keys(langData).find((k) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (new RegExp(`^${escapeRegExp(k)}$`, \"i\").test(enKey)) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\tconsole.log({ e, k });\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (!key) {\n\t\t\t\t\t\t\tlangError();\n\t\t\t\t\t\t\tif (fix) {\n\t\t\t\t\t\t\t\tlangData[enKey] = enLangData[enKey];\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconsole.log(`Missing: ${enKey} ${fix ? \"✔\" : \"\"}`);\n\t\t\t\t\t\t} else if (key !== enKey) {\n\t\t\t\t\t\t\tlangError();\n\t\t\t\t\t\t\tconsole.log(`Fix: \"${key} --> ${enKey}\" ${fix ? \"✔\" : \"\"}`);\n\n\t\t\t\t\t\t\tif (fix) {\n\t\t\t\t\t\t\t\tconst val = langData[key];\n\t\t\t\t\t\t\t\tdelete langData[key];\n\t\t\t\t\t\t\t\tlangData[enKey] = val;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (flagError) {\n\t\t\t\t\t\tif (fix) {\n\t\t\t\t\t\t\ttotal += 1;\n\t\t\t\t\t\t\tconst langJSONData = JSON.stringify(langData, undefined, 2);\n\t\t\t\t\t\t\tfs.writeFile(langFile, langJSONData, (err) => {\n\t\t\t\t\t\t\t\tif (err) {\n\t\t\t\t\t\t\t\t\tconsole.error(err);\n\t\t\t\t\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tdone += 1;\n\t\t\t\t\t\t\t\texit(done, total);\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.log(\"\\n\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!fix) {\n\t\t\t\t\t\texit(i, len);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t\treturn;\n\t}\n\n\tif (!arg) {\n\t\tgetStr(\"string: \").then((res) => {\n\t\t\tkey = res.toLowerCase();\n\t\t\targ = res;\n\t\t\taskTranslation();\n\t\t});\n\t\treturn;\n\t}\n\n\tkey = arg.toLowerCase();\n\tlet newKey = val;\n\taskTranslation();\n\n\tif (command === \"update-key\" && !newKey) {\n\t\tnewKey = await getStr(\"new key: \");\n\t}\n\n\tfunction askTranslation(i = 0) {\n\t\tconst lang = list[i];\n\t\tconst langName = lang.split(\".\")[0];\n\t\tif (command === \"add\") {\n\t\t\tif (!args.a) {\n\t\t\t\tgetStr(`${langName}: `).then(addString);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\taddString();\n\t\t} else if (command === \"remove\") {\n\t\t\tupdate((strings) => {\n\t\t\t\tif (key in strings) {\n\t\t\t\t\tdelete strings[key];\n\t\t\t\t\tconsole.log(`Removed: ${key}`);\n\t\t\t\t\treturn strings;\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(\"String not exists\");\n\t\t\t\t}\n\t\t\t});\n\t\t} else if (command === \"update-key\") {\n\t\t\tupdate((strings) => {\n\t\t\t\tconst val = strings[key];\n\t\t\t\tdelete strings[key];\n\t\t\t\tstrings[newKey] = val;\n\t\t\t\treturn strings;\n\t\t\t});\n\t\t} else if (command === \"update\") {\n\t\t\tif (val) {\n\t\t\t\tupdate((strings) => {\n\t\t\t\t\tstrings[key] = val;\n\t\t\t\t\treturn strings;\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tgetStr(`${langName}: `).then((res) => {\n\t\t\t\t\tres = res || arg;\n\t\t\t\t\tupdate((strings) => {\n\t\t\t\t\t\tstrings[key] = res;\n\t\t\t\t\t\treturn strings;\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (command === \"search\") {\n\t\t\tupdate((string) => {\n\t\t\t\tif (key in string) console.log(`${key}(${langName}): ${string[key]}`);\n\t\t\t\telse {\n\t\t\t\t\tconsole.log(`${key} not exists`);\n\t\t\t\t\tprocess.exit();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tfunction update(modify) {\n\t\t\tconst file = path.resolve(dir, lang);\n\t\t\tconst text = fs.readFileSync(file, \"utf8\");\n\t\t\tconst strings = modify(JSON.parse(text));\n\t\t\tif (strings) {\n\t\t\t\tconst newText = JSON.stringify(strings, undefined, 2);\n\t\t\t\tfs.writeFile(file, newText, \"utf8\", (err) => {\n\t\t\t\t\tif (err) {\n\t\t\t\t\t\tconsole.error(err);\n\t\t\t\t\t\tprocess.exit(1);\n\t\t\t\t\t}\n\n\t\t\t\t\tnext();\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tnext();\n\t\t\t}\n\n\t\t\tfunction next() {\n\t\t\t\tif (i === list.length - 1) {\n\t\t\t\t\tprocess.exit();\n\t\t\t\t} else {\n\t\t\t\t\taskTranslation(++i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction addString(string) {\n\t\t\tstring = string || arg;\n\t\t\tupdate((strings) => {\n\t\t\t\tif (key in strings) {\n\t\t\t\t\tconsole.error(\"String already exists\");\n\t\t\t\t\tprocess.exit(1);\n\t\t\t\t} else {\n\t\t\t\t\tstrings[key] = string;\n\t\t\t\t\treturn strings;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction getStr(str) {\n\treturn new Promise((resolve, reject) => {\n\t\tif (val) {\n\t\t\tresolve(val);\n\t\t\treturn;\n\t\t}\n\n\t\tread.question(str, (res) => {\n\t\t\tresolve(res);\n\t\t});\n\t});\n}\n\nfunction escapeRegExp(text) {\n\treturn text.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, \"\\\\$&\");\n}\n"
  },
  {
    "path": "utils/loadStyles.js",
    "content": "const fs = require(\"node:fs\");\nconst path = require(\"node:path\");\n\nconst WWW = path.resolve(__dirname, \"../www\");\nconst CSS = path.resolve(WWW, \"css/build\");\nconst CSS_PATH = \"./css/build/\";\n\nconst cssFiles = fs.readdirSync(CSS).filter((file) => file.endsWith(\".css\"));\nconst htmlFiles = fs.readdirSync(WWW).filter((file) => file.endsWith(\".html\"));\n\ntry {\n\tfor (let htmlFile of htmlFiles) {\n\t\tloadStyles(path.resolve(WWW, htmlFile));\n\t}\n} catch (error) {\n\tconsole.error(error);\n\tprocess.exit(1);\n}\n\nconsole.log(\"Styles loaded\");\nprocess.exit(0);\n\n/**\n *\n * @param {String} htmlFile\n */\nfunction loadStyles(htmlFile) {\n\tlet styles = \"<!--styles-->\";\n\n\tfor (let cssFile of cssFiles) {\n\t\tstyles += `\\n<link rel=\"stylesheet\" href=\"${CSS_PATH}${cssFile}\">`;\n\t}\n\n\tstyles += \"\\n<!--styles_end-->\\n\";\n\n\tconst rgx =\n\t\t/<!--styles-->([^<]*(?:<(?!!--styles_end-->)[^<]*)*)<!--styles_end-->\\n*/gim;\n\tlet html = fs.readFileSync(htmlFile, \"utf8\");\n\thtml = html.replace(rgx, \"\");\n\thtml = html.replace(\"</head>\", styles + \"</head>\");\n\tfs.writeFileSync(htmlFile, html);\n}\n"
  },
  {
    "path": "utils/scripts/build.sh",
    "content": "#!/bin/bash\n# Default values\napp=\"paid\"\nmode=\"d\"\nfdroidFlag=\"\"\npackageType=\"apk\"  # New default: apk or aar\nwebpackmode=\"development\"\ncordovamode=\"\"\n\n# Check all arguments for specific values\nfor arg in \"$@\"; do\n    case \"$arg\" in\n        \"free\"|\"paid\")\n            app=\"$arg\"\n            ;;\n        \"p\"|\"prod\"|\"d\"|\"dev\")\n            mode=\"$arg\"\n            ;;\n        \"fdroid\")\n            fdroidFlag=\"fdroid\"\n            ;;\n        \"apk\"|\"bundle\")\n            packageType=\"$arg\"\n            ;;\n        *)\n            echo \"Warning: Unknown argument '$arg' ignored\"\n            ;;\n    esac\ndone\n\nroot=$(npm prefix)\n\nif [ -n \"$TMPDIR\" ] && [ -r \"$TMPDIR\" ] && [ -w \"$TMPDIR\" ]; then\n  tmpdir=\"$TMPDIR\"\nelif [ -r \"/tmp\" ] && [ -w \"/tmp\" ]; then\n  tmpdir=\"/tmp\"\nelse\n  echo \"Warning: No usable temporary directory found (TMPDIR or /tmp not accessible). Skipping fdroid.bool file.\" >&2\n  tmpdir=\"\"\nfi\n\nif [ \"$fdroidFlag\" = \"fdroid\" ]; then\n  if [ -n \"$tmpdir\" ]; then\n    echo \"true\" > \"$tmpdir/fdroid.bool\"\n  fi\n\n  # Remove only if installed\n  if [ -d \"plugins/com.foxdebug.acode.rk.exec.proot\" ]; then\n    cordova plugin remove com.foxdebug.acode.rk.exec.proot\n  fi\nelse\n  if [ -n \"$tmpdir\" ]; then\n    echo \"false\" > \"$tmpdir/fdroid.bool\"\n  fi\n\n  # Add only if the src exists and not already installed\n  if [ -d \"src/plugins/proot\" ] && [ ! -d \"plugins/com.foxdebug.acode.rk.exec.proot\" ]; then\n    cordova plugin add src/plugins/proot/\n  fi\nfi\n\n\n\n# Normalize mode values\nif [ \"$mode\" = \"p\" ] || [ \"$mode\" = \"prod\" ]\nthen\nmode=\"p\"\nwebpackmode=\"production\"\ncordovamode=\"--release\"\nfi\n\n# Set build target based on packageType\nif [ \"$packageType\" = \"bundle\" ]; then\n    echo \"Building AAR library file...\"\nelse\n    echo \"Building APK file...\"\nfi\n\nRED=''\nNC=''\n\nscript1=\"node ./utils/config.js $mode $app\"\nscript2=\"rspack --mode $webpackmode\"\n# script3=\"node ./utils/loadStyles.js\"\n\necho \"type : $packageType\"\n\nscript4=\"cordova build android $cordovamode -- --packageType=$packageType\"\n\neval \"\necho \\\"${RED}$script1${NC}\\\";\n$script1;\necho \\\"${RED}$script2${NC}\\\";\n$script2&&\n# echo \\\"${RED}$script3${NC}\\\";\n# $script3;\necho \\\"${RED}$script4${NC}\\\";\n$script4;\n\"\n"
  },
  {
    "path": "utils/scripts/clean.sh",
    "content": "#! /bin/bash\n\nplatform_rm=$1\nplatform_add=$2\n\nif [ -z \"$platform_rm\" ]\nthen\nplatform_rm=\"android\"\nfi\n\nif [ -z \"$platform_add\" ]\nthen\nplatform_add=\"$platform_rm\"\nfi\n\neval \"\ncordova platform rm $platform_rm;\ncordova platform add $platform_add\n\""
  },
  {
    "path": "utils/scripts/generate-release-notes.js",
    "content": "#!/usr/bin/env node\n/**\n * ✨ @ UnschooledGamer (baked With AI, Modified by @ UnschooledGamer) ~ 2025.\n *\n * GitHub Release Notes Generator\n *\n * Features:\n *  - Auto categorizes commits by type\n *  - Optional compact \"plain\" output to save space\n *  - Option to include only important tags (feat, fix, refactor, perf)\n *  - Option to use only merge commits\n *\n * Usage:\n *  GITHUB_TOKEN=<token> node generate-release-notes.js <owner> <repo> <current_tag> [options]\n *\n * Options:\n *  --plain             Output minimal Markdown (no emojis, compact)\n *  --important-only    Include only features, fixes, refactors, and perf\n *  --merge-only        Include only merge commits\n *  --help              Show usage\n *  --format [md/json]  Output Format\n *  --fromTag v1.11.0   The From/Previous Tag\n *  --quiet             Suppress output to stdout\n */\n\nconst args = process.argv.slice(2);\n\nfunction getArgValue(flag) {\n\tconst idx = args.indexOf(flag);\n\treturn idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith(\"-\")\n\t\t? args[idx + 1]\n\t\t: null;\n}\nif (args.includes(\"--help\") || args.length < 3) {\n\tconsole.log(`\nUsage: GITHUB_TOKEN=<token> node generate-release-notes.js <owner> <repo> <tag> [options]\n✨ @ UnschooledGamer (baked With AI, Modified by @ UnschooledGamer) ~ 2025\n\nOptions:\n  --plain             Compact, no emojis (saves space)\n  --important-only    Only include Features, Fixes, Refactors, Perf\n  --merge-only        Include only merge commits\n  --help              Show this help message\n  --format [md/json]  Output Format\n  --from-tag v1.11.0   The From/Previous Tag\n  --quiet             Suppress output to stdout\n  --stdout-only       Output to stdout only\n  --changelog-only    Output changelog only\n`);\n\tprocess.exit(0);\n}\n\nconst [owner, repo, currentTag, previousTagArg] = args;\nconst token = process.env.GITHUB_TOKEN;\nif (!token) {\n\tconsole.error(\"❌ Missing GITHUB_TOKEN environment variable.\");\n\tprocess.exit(1);\n}\n\nconst flags = {\n\tplain: args.includes(\"--plain\"),\n\timportantOnly: args.includes(\"--important-only\"),\n\tmergeOnly: args.includes(\"--merge-only\"),\n\tquiet: args.includes(\"--quiet\") || args.includes(\"--stdout-only\"),\n\tformat: getArgValue(\"--format\") || \"md\",\n\tfromTag: getArgValue(\"--from-tag\"),\n\tchangelogOnly: args.includes(\"--changelog-only\"),\n};\n\nfunction log(...msg) {\n\tif (!flags.quiet) console.error(...msg);\n}\n\nconst headers = {\n\tAuthorization: `token ${token}`,\n\tAccept: \"application/vnd.github+json\",\n\t\"User-Agent\": \"release-notes-script\",\n};\n\nasync function getPreviousTag() {\n\tconst res = await fetch(\n\t\t`https://api.github.com/repos/${owner}/${repo}/tags`,\n\t\t{ headers },\n\t);\n\tconst tags = await res.json();\n\tif (!Array.isArray(tags) || tags.length < 2) return null;\n\treturn tags[1].name;\n}\n\nasync function getCommits(previousTag, currentTag) {\n\tconst url = `https://api.github.com/repos/${owner}/${repo}/compare/${previousTag}...${currentTag}`;\n\tconst res = await fetch(url, { headers });\n\tif (!res.ok) throw new Error(`Failed to fetch commits: ${res.status}`);\n\tconst data = await res.json();\n\treturn data.commits || [];\n}\n\nfunction categorizeCommits(commits, { mergeOnly, importantOnly }) {\n\tconst sections = {\n\t\tfeat: [],\n\t\tfix: [],\n\t\tperf: [],\n\t\trefactor: [],\n\t\tdocs: [],\n\t\tchore: [],\n\t\ttest: [],\n\t\tadd: [],\n\t\trevert: [],\n\t\tupdate: [],\n\t\tother: [],\n\t};\n\n\tfor (const c of commits) {\n\t\tconst msg = c.commit.message.split(\"\\n\")[0];\n\t\tconst isMerge =\n\t\t\tmsg.startsWith(\"Merge pull request\") || msg.startsWith(\"Merge branch\");\n\n\t\tif (mergeOnly && !isMerge) continue;\n\n\t\tconst type =\n\t\t\tObject.keys(sections).find((k) => {\n\t\t\t\tconst lowerMsg = msg.toLowerCase();\n\t\t\t\treturn (\n\t\t\t\t\tlowerMsg.startsWith(`${k}:`) ||\n\t\t\t\t\tlowerMsg.startsWith(`${k} `) ||\n\t\t\t\t\tlowerMsg.startsWith(`${k}: `) ||\n\t\t\t\t\tlowerMsg.startsWith(`${k}(`) // handles e.g. 'feat(plugin-api): ...'\n\t\t\t\t);\n\t\t\t}) || \"other\";\n\n\t\tif (\n\t\t\timportantOnly &&\n\t\t\t![\"feat\", \"fix\", \"refactor\", \"perf\", \"add\", \"revert\", \"update\"].includes(\n\t\t\t\ttype,\n\t\t\t)\n\t\t)\n\t\t\tcontinue;\n\n\t\tconst author = c.author?.login\n\t\t\t? `[${c.author.login}](https://github.com/${c.author.login})`\n\t\t\t: \"unknown\";\n\n\t\tconst entry = `- ${msg} (${c.sha.slice(0, 7)}) by ${author}`;\n\t\tsections[type].push(entry);\n\t}\n\n\treturn sections;\n}\n\nconst emojis = {\n\tfeat: flags.plain ? \"\" : \"✨ \",\n\tfix: flags.plain ? \"\" : \"🐞 \",\n\tperf: flags.plain ? \"\" : \"⚡ \",\n\trefactor: flags.plain ? \"\" : \"🔧 \",\n\tdocs: flags.plain ? \"\" : \"📝 \",\n\tchore: flags.plain ? \"\" : \"🧹 \",\n\ttest: flags.plain ? \"\" : \"🧪 \",\n\tother: flags.plain ? \"\" : \"📦 \",\n\trevert: flags.plain ? \"\" : \"⏪ \",\n\tadd: flags.plain ? \"\" : \"➕ \",\n\tupdate: flags.plain ? \"\" : \"🔄 \",\n};\n\nfunction formatMarkdown(tag, prevTag, sections, { plain }) {\n\tconst lines = [\n\t\tflags.changelogOnly\n\t\t\t? \"\"\n\t\t\t: `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`,\n\t\t\"\",\n\t];\n\n\tfor (const [type, list] of Object.entries(sections)) {\n\t\tif (list.length === 0) continue;\n\t\tconst header = plain\n\t\t\t? `## ${type}`\n\t\t\t: `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`;\n\t\tlines.push(header, \"\", list.join(\"\\n\"), \"\");\n\t}\n\n\t// Compact single-line mode for super small output\n\t// if (plain) {\n\t//   const compact = Object.entries(sections)\n\t//     .filter(([_, list]) => list.length)\n\t//     .map(([type, list]) => `${type.toUpperCase()}: ${list.length} commits`)\n\t//     .join(\" | \");\n\t//   lines.push(`\\n_Summary: ${compact}_`);\n\t// }\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction formatJSON(tag, prevTag, sections, plain = true) {\n\tconst lines = [\n\t\t\"\",\n\t\tflags.changelogOnly\n\t\t\t? \"\"\n\t\t\t: `Changes since [${prevTag}](https://github.com/${owner}/${repo}/releases/tag/${prevTag})`,\n\t\t\"\",\n\t];\n\n\t// todo: split into function\n\tfor (const [type, list] of Object.entries(sections)) {\n\t\tif (list.length === 0) continue;\n\t\tconst header = plain\n\t\t\t? `## ${type}`\n\t\t\t: `## ${emojis[type]}${type[0].toUpperCase() + type.slice(1)}`;\n\t\tlines.push(header, \"\", list.join(\"\\n\"), \"\");\n\t}\n\treturn JSON.stringify(\n\t\t{\n\t\t\trelease: tag,\n\t\t\tprevious: prevTag,\n\t\t\tsections: Object.fromEntries(\n\t\t\t\tObject.entries(sections).filter(([_, v]) => v.length),\n\t\t\t),\n\t\t\tnotes: lines.join(\"\\n\"),\n\t\t},\n\t\tnull,\n\t\t2,\n\t);\n}\n\nasync function main() {\n\tlog(`🔍 Generating release notes for ${owner}/${repo} @ ${currentTag}...`);\n\n\tconst prevTag = flags.fromTag || (await getPreviousTag());\n\tif (!prevTag) {\n\t\tconsole.error(\"No previous tag found. Use --from-tag to specify one.\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst commits = await getCommits(prevTag, currentTag);\n\tif (!commits.length) {\n\t\tconsole.error(\"No commits found.\");\n\t\tprocess.exit(1);\n\t}\n\tconst categorized = categorizeCommits(commits, flags);\n\tlet output;\n\n\tif (flags.format === \"json\") {\n\t\toutput = formatJSON(currentTag, prevTag, categorized);\n\t} else {\n\t\toutput = formatMarkdown(currentTag, prevTag, categorized, flags);\n\t}\n\n\tprocess.stdout.write(output + \"\\n\");\n}\n\nmain().catch((err) => console.error(err));\n"
  },
  {
    "path": "utils/scripts/plugin.sh",
    "content": "#! /bin/bash\n\nif [ -z \"$2\" ]\nthen\neval \"cordova plugin rm $1; cordova plugin add $1\"\nelse\neval \"cordova plugin rm $1; cordova plugin add $2\"\nfi"
  },
  {
    "path": "utils/scripts/setup.sh",
    "content": "echo \"Setting up the project...\"\n\nnpm install\ncordova platform add android@10\ncordova prepare\nmkdir -p www/css/build www/js/build"
  },
  {
    "path": "utils/scripts/start.sh",
    "content": "#! /bin/bash\n\nplatform=\"$1\"\napp=\"$2\"\nmode=\"$3\"\nwebpackmode=\"development\"\ncordovamode=\"\"\n\nif [ -z \"$platform\" ]\nthen\nplatform=\"android\"\nfi\n\nif [ -z \"$mode\" ]\nthen\nmode=\"d\"\nfi\n\nif [ -z \"$app\" ]\nthen\napp=\"paid\"\nfi\n\nif [ \"$mode\" = \"p\" ]\nthen\nwebpackmode=\"production\"\ncordovamode=\"--release\"\nfi\n\nRED=''\nNC=''\nscript1=\"node ./utils/config.js $mode $app\"\nscript2=\"rspack --mode $webpackmode\"\n# script3=\"node ./utils/loadStyles.js\"\nscript4=\"cordova run $platform $cordovamode\"\neval \"\necho \\\"${RED}$script1${NC}\\\";\n$script1;\necho \\\"${RED}$script2${NC}\\\";\n$script2&&\n# echo \\\"${RED}$script3${NC}\\\";\n# $script3;\necho \\\"${RED}$script4${NC}\\\";\n$script4\n\"\n"
  },
  {
    "path": "utils/setup.js",
    "content": "// setup acode for the first time\n// 1. install dependencies\n// 2. add cordova platform android@10.2\n// 3. install cordova plugins\n// cordova-plugin-buildinfo\n// cordova-plugin-device\n// cordova-plugin-file\n// all the plugins in ./src/plugins\n\nconst { execSync } = require(\"node:child_process\");\nconst fs = require(\"node:fs\");\nconst path = require(\"node:path\");\nconst PLATFORM_FILES = [\".DS_Store\"];\n\nexecSync(\"npm install\", { stdio: \"inherit\" });\ntry {\n\texecSync(\"cordova platform add android\", { stdio: \"inherit\" });\n} catch (error) {\n\t// ignore\n}\n\ntry {\n\texecSync(\"mkdir -p www/css/build www/js/build\", { stdio: \"inherit\" });\n} catch (error) {\n\tconsole.log(\n\t\t\"Failed to create www/css/build & www/js/build directories (You may Try after reading The Error)\",\n\t\terror,\n\t);\n}\n\nexecSync(\"cordova plugin add cordova-plugin-buildinfo\", { stdio: \"inherit\" });\nexecSync(\"cordova plugin add cordova-plugin-device\", { stdio: \"inherit\" });\nexecSync(\"cordova plugin add cordova-plugin-file\", { stdio: \"inherit\" });\n\nconst plugins = fs.readdirSync(path.join(__dirname, \"../src/plugins\"));\nplugins.forEach((plugin) => {\n\tif (PLATFORM_FILES.includes(plugin) || plugin.startsWith(\".\")) return;\n\texecSync(`cordova plugin add ./src/plugins/${plugin}`, { stdio: \"inherit\" });\n});\n"
  },
  {
    "path": "utils/storage_manager.mjs",
    "content": "#!/usr/bin/env node\n\nimport readline from 'node:readline';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\n\nconst npmPrefix = execSync('npm prefix').toString().trim();\nconst pluginXmlPath = join(npmPrefix, 'src/plugins/terminal/plugin.xml');\nconst permissionLine = `        <uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\" />`;\nconst permissionRegex = /^\\s*<uses-permission android:name=\"android\\.permission\\.MANAGE_EXTERNAL_STORAGE\"\\s*\\/>\\s*$/gm;\n\nasync function addPermission() {\n  try {\n    let xml = await readFile(pluginXmlPath, 'utf-8');\n\n    if (xml.includes('android.permission.MANAGE_EXTERNAL_STORAGE')) {\n      console.log('Permission already exists in plugin.xml');\n      return false;\n    }\n\n    const pattern = /<config-file\\s+target=\"AndroidManifest\\.xml\"\\s+parent=\"\\/manifest\">\\s*<\\/config-file>/;\n\n    if (pattern.test(xml)) {\n      xml = xml.replace(pattern, match => {\n        return match.replace(\n          '</config-file>',\n          `\\n${permissionLine}\\n    </config-file>`\n        );\n      });\n\n      await writeFile(pluginXmlPath, xml, 'utf-8');\n      console.log('Permission added inside existing <config-file> block.');\n      return true\n    } else {\n      console.error('Could not find <config-file parent=\"/manifest\"> block.');\n      return false\n    }\n  } catch (err) {\n    console.error('Failed to add permission:', err);\n    return false\n  }\n}\n\nasync function removePermission() {\n  try {\n    let xml = await readFile(pluginXmlPath, 'utf-8');\n\n    if (!xml.includes('android.permission.MANAGE_EXTERNAL_STORAGE')) {\n      console.log('Permission not found — nothing to remove.');\n      return false;\n    }\n\n    const cleanedXml = xml.replace(permissionRegex, '');\n    await writeFile(pluginXmlPath, cleanedXml, 'utf-8');\n    console.log('Permission removed from plugin.xml');\n    return true\n  } catch (err) {\n    console.error('Failed to remove permission:', err);\n    return false\n  }\n}\n\n\nfunction updatePlugin() {\n  try {\n    const prefix = execSync('npm prefix').toString().trim();\n    const pluginPath = join(prefix, 'src/plugins/terminal');\n\n    execSync('cordova plugin remove com.foxdebug.acode.rk.exec.terminal', { stdio: 'inherit' });\n    execSync(`cordova plugin add \"${pluginPath}\"`, { stdio: 'inherit' });\n\n    console.log('✅ Plugin updated successfully.');\n  } catch (err) {\n    console.error(err.message);\n    process.exit(1);\n  }\n}\n\nasync function handleAnswer(answer) {\n  answer = answer.trim().toLowerCase();\n  if (answer === 'yes' || answer === 'y') {\n    if(await addPermission()){\n      updatePlugin()\n    }\n   \n  } else if (answer === 'no' || answer === 'n') {\n    if(await removePermission()){\n      updatePlugin()\n    }\n   \n  } else {\n    console.error(\"Invalid input. Please type 'yes' or 'no'.\");\n    process.exit(1);\n  }\n}\n\nfunction prompt() {\n  const rl = readline.createInterface({ input, output });\n  rl.question(\"Enable 'MANAGE_EXTERNAL_STORAGE' permission? Y/n: \", async (answer) => {\n    rl.close();\n    await handleAnswer(answer);\n  });\n}\n\nconst args = process.argv.slice(2);\nlet answer = null;\n\nif (args.includes('--yes') || args.includes('-y')) {\n  answer = 'yes';\n} else if (args.includes('--no') || args.includes('-n')) {\n  answer = 'no';\n} else if (args[0]) {\n  answer = args[0];\n}\n\nif (answer) {\n  await handleAnswer(answer);\n} else {\n  prompt();\n}\n"
  },
  {
    "path": "utils/updateAce.js",
    "content": "const fs = require(\"node:fs\");\nconst path = require(\"node:path\");\n\nconst sourceDir = \"./ace-builds/src-min\";\nconst destDir = \"./www/js/ace\";\n\nfunction updateAce() {\n\ttry {\n\t\t// Remove existing destination directory if it exists\n\t\tif (fs.existsSync(destDir)) {\n\t\t\tfs.rmSync(destDir, { recursive: true });\n\t\t\tconsole.log(\"Removed existing destination directory\");\n\t\t}\n\n\t\t// Create destination directory\n\t\tfs.mkdirSync(destDir, { recursive: true });\n\t\tconsole.log(\"Created new destination directory\");\n\n\t\t// Read all files from source directory\n\t\tconst files = fs.readdirSync(sourceDir);\n\n\t\tfiles.forEach((file) => {\n\t\t\tconst sourcePath = path.join(sourceDir, file);\n\t\t\tconst destPath = path.join(destDir, file);\n\n\t\t\t// Skip snippets directory and worker files\n\t\t\tif (file === \"snippets\" || file.startsWith(\"worker-\")) {\n\t\t\t\tconsole.log(`Skipping: ${file}`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Copy if it's a file\n\t\t\tif (fs.statSync(sourcePath).isFile()) {\n\t\t\t\tfs.copyFileSync(sourcePath, destPath);\n\t\t\t\tconsole.log(`Copied: ${file}`);\n\t\t\t}\n\t\t});\n\n\t\tconsole.log(\"Ace editor files updated successfully!\");\n\t} catch (error) {\n\t\tconsole.error(\"Error updating Ace editor files:\", error);\n\t}\n}\n\nupdateAce();\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require('path');\r\nconst fs = require('fs');\r\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\r\n\r\nconst WWW = path.resolve(__dirname, 'www');\r\n\r\nmodule.exports = (env, options) => {\r\n  const { mode = 'development' } = options;\r\n  const rules = [\r\n    {\r\n      test: /\\.tsx?$/,\r\n      exclude: /node_modules/,\r\n      use: [\r\n        'html-tag-js/jsx/tag-loader.js',\r\n        {\r\n          loader: 'babel-loader',\r\n          options: {\r\n            presets: ['@babel/preset-env', '@babel/preset-typescript'],\r\n          },\r\n        },\r\n        {\r\n          loader: 'ts-loader',\r\n          options: {\r\n            transpileOnly: true, // Skip type checking for faster builds\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    {\r\n      test: /\\.(hbs|md)$/,\r\n      use: ['raw-loader'],\r\n    },\r\n    {\r\n      test: /\\.m.(sa|sc|c)ss$/,\r\n      use: [\r\n        'raw-loader',\r\n        'postcss-loader',\r\n        'sass-loader',\r\n      ],\r\n    },\r\n    {\r\n      test: /\\.(png|svg|jpg|jpeg|ico|ttf|webp|eot|woff|webm|mp4|webp|wav)(\\?.*)?$/,\r\n      type: \"asset/resource\",\r\n    },\r\n    {\r\n      test: /(?<!\\.m)\\.(sa|sc|c)ss$/,\r\n      use: [\r\n        {\r\n          loader: MiniCssExtractPlugin.loader,\r\n        },\r\n        'css-loader',\r\n        'postcss-loader',\r\n        'sass-loader',\r\n      ],\r\n    },\r\n  ];\r\n\r\n  // if (mode === 'production') {\r\n  rules.push({\r\n    test: /\\.m?js$/,\r\n    exclude: /node_modules\\/(@codemirror|codemirror|marked)/, // Exclude CodeMirror and marked files from html-tag-js loader\r\n    use: [\r\n      'html-tag-js/jsx/tag-loader.js',\r\n      {\r\n        loader: 'babel-loader',\r\n        options: {\r\n          presets: ['@babel/preset-env'],\r\n        },\r\n      },\r\n    ],\r\n  });\r\n\r\n  // Separate rule for CodeMirror files - only babel-loader, no html-tag-js\r\n  rules.push({\r\n    test: /\\.m?js$/,\r\n    include: /node_modules\\/(@codemirror|codemirror)/,\r\n    use: [\r\n      {\r\n        loader: 'babel-loader',\r\n        options: {\r\n          presets: ['@babel/preset-env'],\r\n        },\r\n      },\r\n    ],\r\n  });\r\n\r\n  // Separate rule for CodeMirror files - only babel-loader, no html-tag-js\r\n  rules.push({\r\n    test: /\\.m?js$/,\r\n    include: /node_modules\\/(@codemirror|codemirror)/,\r\n    use: [\r\n      {\r\n        loader: 'babel-loader',\r\n        options: {\r\n          presets: ['@babel/preset-env'],\r\n        },\r\n      },\r\n    ],\r\n  });\r\n  // }\r\n\r\n  const main = {\r\n    mode,\r\n    entry: {\r\n      main: './src/main.js',\r\n      console: './src/lib/console.js',\r\n      searchInFilesWorker: './src/sidebarApps/searchInFiles/worker.js',\r\n    },\r\n    output: {\r\n      path: path.resolve(__dirname, 'www/build/'),\r\n      filename: '[name].js',\r\n      chunkFilename: '[name].chunk.js',\r\n      assetModuleFilename: '[name][ext]',\r\n      publicPath: '/build/',\r\n      clean: true,\r\n    },\r\n    module: {\r\n      rules,\r\n    },\r\n    resolve: {\r\n      extensions: ['.ts', '.tsx', '.js', '.mjs', '.json'],\r\n      fallback: {\r\n        path: require.resolve('path-browserify'),\r\n        crypto: false,\r\n      },\r\n      modules: [\"node_modules\", \"src\"],\r\n    },\r\n    plugins: [\r\n      new MiniCssExtractPlugin({\r\n        filename: '[name].css',\r\n      }),\r\n    ],\r\n  };\r\n\r\n  return [main];\r\n};\r\n"
  },
  {
    "path": "www/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\"\n    />\n\n    <style>\n      html,\n      body {\n        margin: 0;\n        background-color: #20202c; /* fallback dark */\n      }\n\n      :root {\n        --bg-color: #20202c; /* fallback splash color */\n        --test-height: 45px;\n      }\n\n      body.loading {\n        position: relative;\n      }\n\n      body.loading #splash {\n        display: block;\n      }\n\n      #splash {\n        display: none;\n        position: fixed;\n        inset: 0;\n        z-index: 999999;\n\n        background-image: url(\"./logo.svg\");\n        background-position: center;\n        background-repeat: no-repeat;\n        background-size: 180px;\n        background-color: var(--bg-color);\n\n        text-align: center;\n\n        /* Disable any possible fade */\n        animation: none !important;\n        transition: none !important;\n      }\n\n      .splash-version {\n        position: absolute;\n        top: 15px;\n        left: 15px;\n\n        color: rgb(209, 209, 209);\n        font-size: 0.8rem;\n        white-space: pre;\n        font-family: monospace;\n        text-align: left;\n      }\n\n      .splash-message {\n        position: absolute;\n        bottom: 15px;\n        left: 50%;\n        transform: translateX(-50%);\n        color: rgb(209, 209, 209);\n        font-size: 1rem;\n        width: 90vw;\n        text-align: center;\n      }\n    </style>\n\n    <!-- Set theme color BEFORE UI renders -->\n    <script>\n      (function () {\n        var color = localStorage.getItem(\"__primary_color\");\n        var bg = color || \"#20202c\";\n\n        document.documentElement.style.setProperty(\"--bg-color\", bg);\n        document.documentElement.style.backgroundColor = bg;\n        document.body && (document.body.style.backgroundColor = bg);\n      })();\n    </script>\n\n    <!-- Core Scripts -->\n    <script src=\"cordova.js\"></script>\n    \n    <script src=\"./build/main.js\"></script>\n\n\n    <link rel=\"stylesheet\" href=\"./build/main.css\" />\n\n    <title>Acode</title>\n  </head>\n\n  <body class=\"loading\" data-version=\"\" data-small-msg=\"\">\n    <div id=\"splash\">\n      <div class=\"splash-version\"></div>\n      <div class=\"splash-message\"></div>\n    </div>\n\n    <wc-page id=\"root\" class=\"primary\"></wc-page>\n\n    <script>\n      document.addEventListener(\"DOMContentLoaded\", function () {\n        const splash = document.getElementById(\"splash\");\n\n        function updateSplash() {\n          splash.querySelector(\".splash-version\").textContent =\n            document.body.getAttribute(\"data-version\") || \"\";\n\n          splash.querySelector(\".splash-message\").textContent =\n            document.body.getAttribute(\"data-small-msg\") || \"\";\n        }\n\n        updateSplash();\n\n        new MutationObserver(updateSplash).observe(document.body, {\n          attributes: true,\n          attributeFilter: [\"data-version\", \"data-small-msg\"],\n        });\n      });\n    </script>\n  </body>\n</html>\n"
  }
]