diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..258cc99 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/devcontainers/base:focal + +RUN apt update && apt upgrade -y + +RUN apt install -y software-properties-common apt-transport-https ca-certificates curl gnupg + +RUN mkdir -p /etc/apt/keyrings +RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg + +RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list +RUN apt update && apt install -y nodejs + +RUN corepack enable + +RUN echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list +RUN curl -fSsL https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor | sudo tee /usr/share/keyrings/google-chrome.gpg >> /dev/null +RUN echo deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main | sudo tee /etc/apt/sources.list.d/google-chrome.list + +RUN apt update && apt install -y google-chrome-stable firefox diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e89dbc7 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node +{ + "name": "TabsAsideExtension", + "build": { + "dockerfile": "Dockerfile" + }, + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "zardoy.disable-ts-errors", + "github.vscode-github-actions", + "GitHub.vscode-pull-request-github", + "bierner.github-markdown-preview", + "mrmlnc.vscode-scss", + "Gruntfuggly.todo-tree", + "redhat.vscode-yaml" + ] + } + }, + + "postCreateCommand": "yarn install" +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..5cc8cdd --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +* @XFox111 +locales/pt_BR.yml @maisondasilva @XFox111 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 249239a..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -#patreon: # Replace with a single Patreon username -open_collective: TabsAside -#ko_fi: # Replace with a single Ko-fi username -#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -#liberapay: # Replace with a single Liberapay username -#issuehunt: # Replace with a single IssueHunt username -#otechie: # Replace with a single Otechie username -custom: [ "https://buymeacoffee.com/xfox111" ] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 0510750..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve the extension -title: '' -labels: bug -assignees: '' - ---- - -### Description -A clear and concise description of what the bug is. - -### Reproduction steps -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '...' -3. Scroll down to '...' -4. See error - -### Expected behavior -A clear and concise description of what you expected to happen. - -### Screenshots -If applicable, add screenshots to help explain your problem. - -### Environment -Please provide the following information: - - Operating System: [e.g. Windows 10 Pro 1909 (10.0.18363)] - - Browser: [e.g. Microsoft Edge 83.0.478.56] - - Extension version: [e.g. 1.5] - -### Additional context -Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..62570b8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,107 @@ +name: "๐Ÿž Bug Report" +description: Create a report to help us improve the extension +title: "[Bug]: " +labels: ["bug", "needs-triage"] +assignees: + - xfox111 +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + - type: textarea + id: desc + attributes: + label: Description + description: A clear and concise description of what the bug is. + placeholder: e.g. Sometimes when generating a password not all character sets are included + validations: + required: true + + - type: textarea + attributes: + label: Reproduction steps + description: Precisely describe minimal number of steps that make the bug to appear + placeholder: | + 1. Go to '...' + 2. Click on '...' + 3. Scroll down to '...' + 4. See '...' + validations: + required: true + + - type: textarea + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + placeholder: e.g. Generated password should include at least one character from every enabled character set + validations: + required: true + + - type: textarea + attributes: + label: Screenshot + description: If applicable, add screenshots to help explain your problem. + validations: + required: false + + - type: dropdown + id: os + attributes: + label: Operating system + options: + - "Windows 10 and newer" + - "Windows 8/8.1" + - "Windows 7 and older" + - "MacOS" + - "Debian or Debian-based" + - "Other" + validations: + required: true + + - type: input + id: browser + attributes: + label: Browser name and version + placeholder: e.g. Microsoft Edge 119.0.2151.58 + description: Put here your browser's name and version + validations: + required: true + + - type: input + id: version + attributes: + label: Extension version + placeholder: e.g. 3.0.0 + validations: + required: true + + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false + + - type: dropdown + id: requested-help + attributes: + label: Are you willing to submit a PR for this issue? + options: + - "yes" + - "no" + validations: + required: true + + - type: checkboxes + id: checkboxes + attributes: + label: Validations + description: Before submitting the issue, please make sure you do the following + options: + - label: Check that there isn't already an issue that reports the same bug to avoid creating a duplicate. + required: true + - label: The provided reproduction is a minimal reproducible example of the bug. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7a1fa3b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-issue-config.json + +blank_issues_enabled: true +contact_links: + - name: Questions & Discussions + url: https://github.com/XFox111/TabsAsideExtension/discussions + about: Use GitHub discussions for message-board style questions and discussions. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index d85c3dd..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when '...' - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..6318208 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,62 @@ +name: "๐Ÿš€ New feature proposal" +description: Suggest a feature idea for this project +title: "[Feature]: " +labels: ["feature", "needs-triage"] +assignees: + - xfox111 +body: + - type: markdown + attributes: + value: | + Thanks for your interest in the project and taking the time to fill out this feature report! + + - type: textarea + id: proposition + attributes: + label: Proposed solution + description: Describe the solution you'd like + validations: + required: true + + - type: textarea + id: justification + attributes: + label: Justification + description: Is your feature request related to a problem? Please describe. + validations: + required: true + + - type: textarea + id: alts + attributes: + label: Alternatives + description: Describe alternatives you've considered. + validations: + required: true + + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. + validations: + required: false + + - type: dropdown + id: requested-help + attributes: + label: Are you willing to submit a PR for this issue? + options: + - "yes" + - "no" + validations: + required: true + + - type: checkboxes + id: checkboxes + attributes: + label: Validations + description: Before submitting the issue, please make sure you do the following + options: + - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..03e1a8f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,39 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +# yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json + +version: 2 +updates: + + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + target-branch: "next" + assignees: + - "XFox111" + schedule: + interval: monthly + rebase-strategy: disabled + open-pull-requests-limit: 20 + + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "next" + assignees: + - "XFox111" + schedule: + interval: monthly + rebase-strategy: disabled + open-pull-requests-limit: 20 + + - package-ecosystem: "devcontainers" + directory: "/" + target-branch: "next" + assignees: + - "XFox111" + schedule: + interval: monthly + rebase-strategy: disabled + open-pull-requests-limit: 20 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2904fca..a0a4001 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,9 +1,33 @@ -Implements following issues: + + +## Description + + +Resolves: #issue_number + + + + + + + diff --git a/.github/release_description_template.md b/.github/release_description_template.md index cadcc3e..812c9ce 100644 --- a/.github/release_description_template.md +++ b/.github/release_description_template.md @@ -1,13 +1,14 @@ + + ## What's new + -## How to install -1. Download attached archive and unpack it -2. Enable Developers mode on your browser extensions page -3. Click "Load unpacked" button and navigate to the extension root folder (contains `manifest.json`) -4. Done! + -*On Firefox you should open manifest file instead of extension's folder - -**Note:** If you delete extension folder it will disappear from your browser - -_Sideloaded extensions don't replace officially installed ones_ \ No newline at end of file +Refer to [Download section of the README.md](https://github.com/XFox111/TabsAsideExtension#download) for sideloading instructions and download links diff --git a/.github/workflows/cd_pipeline.yml b/.github/workflows/cd_pipeline.yml new file mode 100644 index 0000000..e95e6da --- /dev/null +++ b/.github/workflows/cd_pipeline.yml @@ -0,0 +1,156 @@ +name: Release pipeline + +on: + release: + types: [ released ] + workflow_dispatch: + inputs: + bypass_audit: + description: Bypass npm audit + type: boolean + default: false + targets: + description: Targets + required: true + default: '["chrome","firefox"]' + type: choice + options: + - '["chrome","firefox"]' + - '["chrome"]' + - '["firefox"]' + firefox: + description: Deploy Firefox + type: boolean + default: true + chrome: + description: Deploy Chrome + type: boolean + default: true + edge: + description: Deploy Edge + type: boolean + default: true + gh-release: + description: Attach to GitHub release + type: boolean + default: true + +jobs: + build: + runs-on: ubuntu-latest + container: node:24 + strategy: + fail-fast: false + matrix: + target: ${{ fromJSON(github.event.inputs.targets || '["chrome","firefox"]') }} + + steps: + - uses: actions/checkout@main + + - run: | + echo "WXT_GA4_API_SECRET=${{ secrets.GA4_SECRET }}" >> .env + echo "WXT_GA4_MEASUREMENT_ID=${{ secrets.GA4_MEASUREMENT_ID }}" >> .env + + - run: corepack enable + - run: yarn install + + # Patch for firefox dnd popup (see https://github.com/clauderic/dnd-kit/issues/1043) + - run: grep -v "this.windowListeners.add(EventName.Resize, this.handleCancel);" core.esm.js > core.esm.js.tmp && mv core.esm.js.tmp core.esm.js + working-directory: ./node_modules/@dnd-kit/core/dist + if: ${{ matrix.target == 'firefox' }} + + - run: yarn zip -b ${{ matrix.target }} + + - name: Drop build artifacts (${{ matrix.target }}) + uses: actions/upload-artifact@main + with: + name: ${{ matrix.target }} + path: ./.output/tabs-aside-*.zip + include-hidden-files: true + + - name: web-ext lint + if: ${{ matrix.target == 'firefox' }} + uses: freaktechnik/web-ext-lint@main + with: + extension-root: ./.output/firefox-mv3 + self-hosted: false + + - run: yarn npm audit + continue-on-error: ${{ github.event_name != 'release' && github.event.inputs.bypass_audit == 'true' }} + + publish-github: + needs: build + if: ${{ github.event_name == 'release' || github.event.inputs.gh-release == 'true' }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: ${{ fromJSON(github.event.inputs.targets || '["chrome","firefox"]') }} + + steps: + - uses: actions/download-artifact@main + with: + name: ${{ matrix.target }} + + - name: Attach build to release + uses: xresloader/upload-to-github-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: tabs-aside-*-${{ matrix.target }}.zip + draft: false + overwrite: true + update_latest_release: true + + publish-chrome: + needs: build + if: ${{ github.event_name == 'release' || (github.event.inputs.chrome == 'true' && contains(github.event.inputs.targets, 'chrome')) }} + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@main + with: + name: chrome + + - uses: wdzeng/chrome-extension@v1.3.0 + with: + extension-id: ${{ secrets.CHROME_EXT_ID }} + zip-path: tabs-aside-*-chrome.zip + client-id: ${{ secrets.CHROME_CLIENT_ID }} + client-secret: ${{ secrets.CHROME_CLIENT_SECRET }} + refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }} + + publish-edge: + needs: build + if: ${{ github.event_name == 'release' || (github.event.inputs.edge == 'true' && contains(github.event.inputs.targets, 'chrome')) }} + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@main + with: + name: chrome + + - uses: wdzeng/edge-addon@v2.1.0 + with: + product-id: ${{ secrets.EDGE_PRODUCT_ID }} + zip-path: tabs-aside-*-chrome.zip + client-id: ${{ secrets.EDGE_CLIENT_ID }} + api-key: ${{ secrets.EDGE_API_KEY }} + + publish-firefox: + needs: build + if: ${{ github.event_name == 'release' || (github.event.inputs.firefox == 'true' && contains(github.event.inputs.targets, 'firefox')) }} + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@main + with: + name: firefox + + - uses: wdzeng/firefox-addon@v1.2.0 + with: + addon-guid: ${{ secrets.FIREFOX_EXT_UUID }} + xpi-path: tabs-aside-*-firefox.zip + source-file-path: tabs-aside-*-sources.zip + jwt-issuer: ${{ secrets.FIREFOX_API_KEY }} + jwt-secret: ${{ secrets.FIREFOX_CLIENT_SECRET }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index f669799..0000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,70 +0,0 @@ -name: CI - -on: - release: - types: [published] - -jobs: - Firefox: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Build Extension for Firefox - id: web-ext-build - uses: kewisch/action-web-ext@v1 - with: - cmd: build - - - name: 'Sign & publish' - id: web-ext-sign - uses: kewisch/action-web-ext@v1 - with: - cmd: sign - channel: listed - source: ${{ steps.web-ext-build.outputs.target }} - apiKey: ${{ secrets.FIREFOX_API_KEY }} - apiSecret: ${{ secrets.FIREFOX_CLIENT_SECRET }} - - - name: Drop artifacts - uses: actions/upload-artifact@v2 - with: - name: 'Firefox Artefacts' - path: ${{ steps.web-ext-build.outputs.target }} - - Chrome: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Pack extension - uses: TheDoctor0/zip-release@0.4.1 - with: - filename: ./TabsAside.zip - exclusions: '.git/* .vscode/* .github/* *.md' - - - name: Publish to Chrome Webstore - uses: trmcnvn/chrome-addon@v2 - with: - extension: mgmjbodjgijnebfgohlnjkegdpbdjgin - zip: ./TabsAside.zip - client-id: ${{ secrets.CHROME_CLIENT_ID }} - client-secret: ${{ secrets.CHROME_CLIENT_SECRET }} - refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }} - - - name: Upload artifact - uses: xresloader/upload-to-github-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - file: ./TabsAside.zip - tags: true - draft: false - - - name: Drop artifacts - uses: actions/upload-artifact@v2 - with: - name: 'Chrome Artifacts' - path: ./TabsAside.zip \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..782a458 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,86 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main", "next" ] + paths-ignore: + - '**.md' + - 'LICENSE' + - '**/cd_pipeline.yaml' + - '**/dependabot.yml' + - '**/pr_pipeline.yaml' + - '.vscode/*' + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main", "next" ] + paths-ignore: + - '**.md' + - 'LICENSE' + - '**/cd_pipeline.yaml' + - '**/dependabot.yml' + - '**/pr_pipeline.yaml' + - '.vscode/*' + schedule: + - cron: '24 7 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'typescript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # โ„น๏ธ Command-line programs to run using the OS shell. + # ๐Ÿ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr_next.yml b/.github/workflows/pr_next.yml new file mode 100644 index 0000000..7a30dc6 --- /dev/null +++ b/.github/workflows/pr_next.yml @@ -0,0 +1,31 @@ +name: PR next workflow + +on: + push: + branches: [ main ] + paths: + - 'package.json' + workflow_dispatch: + +permissions: + contents: write + +jobs: + create-release-draft: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + + - name: Get version from package.json + id: get_version + run: | + extver=`jq -r ".version" package.json` + echo "version=$extver" >> "$GITHUB_OUTPUT" + + - uses: dev-build-deploy/release-me@v0.18.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + prefix: v + draft: true + version: v${{ steps.get_version.outputs.version }} + release-notes: .github/release_description_template.md diff --git a/.github/workflows/pr_pipeline.yml b/.github/workflows/pr_pipeline.yml new file mode 100644 index 0000000..497a45e --- /dev/null +++ b/.github/workflows/pr_pipeline.yml @@ -0,0 +1,70 @@ +name: PR check pipeline + +on: + pull_request: + branches: [ "main", "next" ] + paths-ignore: + - '**.md' + - '**.txt' + - "locales/*" + - 'LICENSE' + - 'PRIVACY' + - '**/cd_pipeline.yml' + - '**/dependabot.yml' + - '**/codeql-analysis.yml' + - '**/pr_next.yaml' + - '.vscode/*' + - '.devcontainer/*' + workflow_dispatch: + inputs: + targets: + description: Targets + required: true + default: '["chrome","firefox"]' + type: choice + options: + - '["chrome","firefox"]' + - '["chrome"]' + - '["firefox"]' + +jobs: + build: + runs-on: ubuntu-latest + container: node:24 + strategy: + fail-fast: false + matrix: + target: ${{ fromJSON(github.event.inputs.targets || '["chrome","firefox"]') }} + + steps: + - uses: actions/checkout@main + + - run: | + echo "WXT_GA4_API_SECRET=${{ secrets.GA4_SECRET }}" >> .env + echo "WXT_GA4_MEASUREMENT_ID=${{ secrets.GA4_MEASUREMENT_ID }}" >> .env + + - run: corepack enable + - run: yarn install + + # Patch for firefox dnd popup (see https://github.com/clauderic/dnd-kit/issues/1043) + - run: grep -v "this.windowListeners.add(EventName.Resize, this.handleCancel);" core.esm.js > core.esm.js.tmp && mv core.esm.js.tmp core.esm.js + working-directory: ./node_modules/@dnd-kit/core/dist + if: ${{ matrix.target == 'firefox' }} + + - run: yarn zip -b ${{ matrix.target }} + + - name: Drop artifacts (${{ matrix.target }}) + uses: actions/upload-artifact@main + with: + name: ${{ matrix.target }} + path: ./.output/tabs-aside-*-${{ matrix.target }}.zip + include-hidden-files: true + + - name: web-ext lint + if: ${{ matrix.target == 'firefox' }} + uses: freaktechnik/web-ext-lint@main + with: + extension-root: ./.output/firefox-mv3 + self-hosted: false + + - run: yarn npm audit diff --git a/.gitignore b/.gitignore index ca86803..f0666ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,31 @@ -\.vscode \ No newline at end of file +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.yarn/ +.output +stats.html +stats-*.json +.wxt +web-ext.config.ts + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +web-ext.config.js + +.env* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..8632321 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "bierner.github-markdown-preview", + "dbaeumer.vscode-eslint", + "github.vscode-github-actions", + "Gruntfuggly.todo-tree", + "jock.svg", + "ms-azuretools.vscode-docker", + "saeris.markdown-github-alerts", + "zardoy.disable-ts-errors" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 3d73cd3..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "0.2.0", - "configurations": - [ - { - "name": "Debug", - "type": "firefox", - "request": "launch", - "reAttach": true, - "addonPath": "${workspaceFolder}" - } - ] -} \ No newline at end of file diff --git a/.whitesource b/.whitesource deleted file mode 100644 index 55b922e..0000000 --- a/.whitesource +++ /dev/null @@ -1,12 +0,0 @@ -{ - "scanSettings": { - "baseBranches": [] - }, - "checkRunSettings": { - "vulnerableCheckRunConclusionLevel": "failure", - "displayMode": "diff" - }, - "issueSettings": { - "minSeverityLevel": "LOW" - } -} \ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..85c4b82 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,117 @@ +nodeLinker: node-modules + +packageExtensions: + "@wxt-dev/module-react@*": + peerDependencies: + vite: "*" + "@fluentui/react-accordion@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-avatar@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-carousel@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-color-picker@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-combobox@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-dialog@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-field@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-list@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-menu@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-nav@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-overflow@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-popover@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-swatch-picker@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-table@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-tabs@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-tag-picker@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-teaching-popover@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-toolbar@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-tree@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-alert@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-checkbox@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-components@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-drawer@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-infobutton@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-infolabel@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-input@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-persona@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-progress@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-radio@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-select@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-skeleton@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-slider@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-spinbutton@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-switch@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-tags@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-textarea@*": + peerDependencies: + scheduler: "0.23.0" + "@fluentui/react-search@*": + peerDependencies: + scheduler: "0.23.0" diff --git a/App.tsx b/App.tsx new file mode 100644 index 0000000..1db5711 --- /dev/null +++ b/App.tsx @@ -0,0 +1,11 @@ +import DialogProvider from "@/contexts/DialogProvider"; +import ThemeProvider from "@/contexts/ThemeProvider"; + +const App: React.FC = ({ children }: React.PropsWithChildren) => + + + { children } + + ; + +export default App; diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 62ba1d4..ad52cd6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,75 +2,133 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to creating a positive environment -include: +Examples of behavior that contributes to a positive environment for our +community include: -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community -Examples of unacceptable behavior by participants include: +Examples of unacceptable behavior include: -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting -## Our Responsibilities +## Enforcement Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. ## Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at opensource@xfox111.net. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +reported to the community leaders responsible for enforcement at +[opensource@xfox111.net](mailto:opensource@xfox111.net). +All complaints will be reviewed and investigated promptly and fairly. -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +> Contributor Covenant is released under the [Creative Commons Attribution 4.0 International Public License](https://github.com/EthicalSource/contributor_covenant/blob/release/LICENSE.md). [homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see - +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5684bc5..60a9362 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,214 +1,4 @@ # Contribution Guidelines -Welcome, and thank you for your interest in contributing to my project! -There are many ways in which you can contribute, beyond writing code. The goal of this document is to provide a high-level overview of how you can get involved. - -## Table of Contents -- [Contribution Guidelines](#contribution-guidelines) - - [Table of Contents](#table-of-contents) - - [Asking Questions](#asking-questions) - - [Providing Feedback](#providing-feedback) - - [Reporting Issues](#reporting-issues) - - [Look For an Existing Issue](#look-for-an-existing-issue) - - [Writing Good Bug Reports and Feature Requests](#writing-good-bug-reports-and-feature-requests) - - [Final Checklist](#final-checklist) - - [Follow Your Issue](#follow-your-issue) - - [Contributing to the codebase](#contributing-to-the-codebase) - - [Deploy test version on your browser](#deploy-test-version-on-your-browser) - - [Development workflow](#development-workflow) - - [Release](#release) - - [Coding guidelines](#coding-guidelines) - - [Indentation](#indentation) - - [Names](#names) - - [Comments](#comments) - - [Strings](#strings) - - [Style](#style) - - [Finding an issue to work on](#finding-an-issue-to-work-on) - - [Contributing to translations](#contributing-to-translations) - - [Submitting pull requests](#submitting-pull-requests) - - [Spell check errors](#spell-check-errors) - - [Thank You!](#thank-you) - - [Attribution](#attribution) - -## Asking Questions -Have a question? Rather than opening an issue, please ask me directly on opensource@xfox111.net. - -## Providing Feedback -Your comments and feedback are welcome. -You can leave your feedbak on feedback@xfox111.net or do it on [Microsoft Edge Add-ons Webstore](https://microsoftedge.microsoft.com/addons/detail/tabs-aside/kmnblllmalkiapkfknnlpobmjjdnlhnd), [Chrome Extensions Webstore](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin) or [Mozilla Add-ons Webstore](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/) - -## Reporting Issues -Have you identified a reproducible problem in the extension? Have a feature request? I'd like to hear it! Here's how you can make reporting your issue as effective as possible. - -### Look For an Existing Issue -Before you create a new issue, please do a search in [open issues](https://github.com/xfox111/TabsAsideExtension/issues) to see if the issue or feature request has already been filed. - -Be sure to scan through the [feature requests](https://github.com/XFox111/TabsAsideExtension/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement). - -If you find your issue already exists, make relevant comments and add your [reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments). Use a reaction in place of a "+1" comment: - -- ๐Ÿ‘ - upvote -- ๐Ÿ‘Ž - downvote - -If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below. - -### Writing Good Bug Reports and Feature Requests -File a single issue per problem and feature request. Do not enumerate multiple bugs or feature requests in the same issue. - -Do not add your issue as a comment to an existing issue unless they are the same ones. Many issues look similar, but have different causes. - -The more information you can provide, the more likely someone will be successful at reproducing the issue and finding a solution. - -Please include the following with each issue: -- Current version of the extension -- Your current browser and OS name -- Reproducible steps (1... 2... 3...) that cause the issue -- What you expected to see, versus what you actually saw -- Images, animations, or a link to a video showing the issue occurring - -### Final Checklist -Please remember to do the following: - -- [ ] Search the issue repository to ensure your report is a new issue -- [ ] Separate issues reports -- [ ] Include as much information as you can to your report - -Don't feel bad if the developers can't reproduce the issue right away. They will simply ask for more information! - -### Follow Your Issue -Once your report is submitted, be sure to stay in touch with the devs in case they need more help from you. - -## Contributing to the codebase -If you are interested in writing code to fix issues or implement new awesome features you can follow these guidelines to get a better result - -### Deploy test version on your browser -1. Clone repository to local storage using [Git](https://guides.github.com/introduction/git-handbook/) - - ```bash - git clone https://github.com/xfox111/TabsAsideExtension.git - ``` -2. Enable Developers mode on your browser extensions page -3. Click "Load unpacked" button and navigate to the extension root folder (contains `manifest.json`) -4. Done! - -Next time you make any changes to the codebase, reload the extension by toggling it off and on or by pressing "Reload" button on the extensions list page - -> **Note:** You can also check [this article](https://xfox111.net/46hsgv) to get more information about debugging web extensions - -### Development workflow -This section represents how contributors should interact with codebase implementing features and fixing bugs - -1. Getting assigned to the issue -2. Creating a repository fork -3. Making changes to the codebase -4. Creating a pull request to `master` -5. Reviewing & completing PR -6. Done - -#### Release -The next stage is the release. Release performs on every push to master (which makes functional changes to the source code). Release performs manually by @XFox111 into: Chrome webstore, Edge webstore and GitHub releases - -### Coding guidelines -#### Indentation -We use tabs, not spaces. - -#### Names -The project naming rules inherit [.NET Naming Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines). Nevertheless there'is some distinction with the guidelines as well as additions to the one: - -- Use `camelCase` for variables instead of `CamelCase` stated in [Capitalization Conventions](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/capitalization-conventions#capitalization-rules-for-identifiers) -- Use `snake_case` for file names - -#### Comments -Leave as more comments as you can. Remember: the more detailed documentation your code has the less programmers will curse you in the future - -#### Strings -Use "double quotes" wherever it's possible - -#### Style -- Prefer to use lambda functions -- Put curly braces on new lines - - Wrong: - - ```javascript - if (condition) { - ... - } - ``` - - - Correct: - - ```javascript - if (condition) - { - ... - } - ``` -- Put spaces between operators and before braces in methods declarations, conditionals and loops - - Wrong: - - `y=k*x+b` - - `function FunctionName()` - - Correct: - - `y = k * x + b` - - `function FunctionName ()` -- Use ternary conditionals wherever it's possible - - Wrong: - ```javascript - var s; - if (condition) - s = "Life"; - else - s = "Death"; - ``` - - Correct: - - ```javascript - var s = condition ? "Life" : "Death"; - ``` -- Do not surround loop and conditional bodies with curly braces if they can be avoided - - Wrong: - ```javascript - if (condition) - { - console.log("Hello, World!"); - } - else - { - return; - } - ``` - - Correct - - ```javascript - if (condition) - console.log("Hello, World!"); - else - return; - ``` - -### Finding an issue to work on -Check out the [full issues list](https://github.com/XFox111/TabsAsideExtension/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue) for a list of all potential areas for contributions. **Note** that just because an issue exists in the repository does not mean we will accept a contribution. There are several reasons we may not accept a pull request like: - -- Performance - One of Tabs Aside core values is to deliver a lightweight extension, that means it should perform well in both real and test environments. -- User experience - Since we want to deliver a lightweight extension, the UX should feel lightweight as well and not be cluttered. Most changes to the UI should go through the issue owner and project owner (@XFox111). -- Architectural - Project owner needs to agree with any architectural impact a change may make. Such things must be discussed with and agreed upon by the project owner. - -To improve the chances to get a pull request merged you should select an issue that is labelled with the `help-wanted` or `bug` labels. If the issue you want to work on is not labelled with `help-wanted` or `bug`, you can start a conversation with the project owner asking whether an external contribution will be considered. - -To avoid multiple pull requests resolving the same issue, let others know you are working on it by saying so in a comment. - -### Contributing to translations -If you want to help us to translate this extension into other languages, please read [this article](https://developer.chrome.com/extensions/i18n) - -**Note** that whatever you want to contribute to the codebase, you should do it only after you got assigned on an issue - -### Submitting pull requests -To enable us to quickly review and accept your pull requests, always create one pull request per issue and [link the issue in the pull request](https://github.com/blog/957-introducing-issue-mentions). Never merge multiple requests in one unless they have the same root cause. Be sure to follow our [Coding Guidelines](#coding-guidelines) and keep code changes as small as possible. Avoid pure formatting changes to code that has not been modified otherwise. Pull requests should contain tests whenever possible. Fill pull request content according to its template. Deviations from template are not recommended - -#### Spell check errors -Pull requests that fix typos are welcomed but please make sure it doesn't touch multiple feature areas, otherwise it will be difficult to review. Pull requests only fixing spell check errors in source code are not recommended. - -## Thank You! -Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute. - -## Attribution -These Contribution Guidelines are adapted from the [Contributing to VS Code](https://github.com/microsoft/vscode/blob/master/CONTRIBUTING.md) +> [!IMPORTANT] +> This article has been moved to the [project's Wiki section](https://github.com/XFox111/TabsAsideExtension/wiki/Contribution-Guidelines) diff --git a/LICENSE b/LICENSE index 8c0785d..db0177c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Michael "XFox" Gordeev +Copyright (c) 2025 Eugene Fox Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PRIVACY b/PRIVACY deleted file mode 100644 index cb20fee..0000000 --- a/PRIVACY +++ /dev/null @@ -1,7 +0,0 @@ -Tabs Aside Extension Privacy Policy -1. Developers of the extension don't affiliate with Google LLC. or Microsoft Corporation in any way -2. This extension doesn't transfer any personal data (data which may be used to track your location or reveal your identity) to any remote or local server -3. This extension doesn't share any personal data with third parties -4. This extension stores following personal data: - - Browser tabs which have been saved for later by user via this extension (if user click "Set current tabs aside" button). This includes titles, favicons and web links -5. User can delete all saved personal data by removing this extension from his browser diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..6f3bdee --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,20 @@ +# Tabs aside extension Privacy policy +1. Developers of the extension don't affiliate with Google LLC, Mozilla Foundation or Microsoft Corporation in any way. +2. This extension stores user data only related to its core functionality. This includes: + - User settings + - User saved collections of tabs + - Thumbnails of saved tabs +3. This extension uses Google Analytics to collect usage statistics and improve the extension. +4. This extension uses analytics to collect following data: + - Random UUID to identify the user + - Browser name and version + - Operating system name and version + - System architecture + - Screen resolution + - Extension language + - User settings + - Number of saved collections + - Action identifiers (e.g. "page_view", "extension_installed", "item_created", etc.) +4. This extension does not collect or use any personally identifiable information (PII) or sensitive data for purposes other than its core functionality. +5. This extension uses cloud storage built into your browser to store its data. +6. Refer to your browser's developer regarding the privacy policy of the cloud storage used by your browser. diff --git a/README.md b/README.md index e615bab..c851e2e 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,100 @@ -# Tabs aside [![GitHub release (latest by date)](https://img.shields.io/github/v/release/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/releases/latest) -![CI](https://github.com/XFox111/TabsAsideExtension/workflows/CI/badge.svg) -[![Mozilla Add-on](https://img.shields.io/amo/rating/ms-edge-tabs-aside?label=Firefox%20rating)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/) +[![GitHub last commit](https://img.shields.io/github/last-commit/xfox111/TabsAsideExtension?label=Last+update)](https://github.com/XFox111/TabsAsideExtension/commits/main) -[![Chrome Web Store](https://img.shields.io/chrome-web-store/users/mgmjbodjgijnebfgohlnjkegdpbdjgin?label=Chrome%20Webstore%20downloads)](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin) -[![Mozilla Add-on](https://img.shields.io/amo/users/ms-edge-tabs-aside?label=Firefox%20Webstore%20downloads)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/) + + + + Password generator + -[![GitHub issues](https://img.shields.io/github/issues/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/issues) -[![GitHub last commit](https://img.shields.io/github/last-commit/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/commits/master) -[![GitHub repo size](https://img.shields.io/github/repo-size/xfox111/TabsAsideExtension?label=repo%20size)](https://github.com/xfox111/TabsAsideExtension) -[![MIT License](https://img.shields.io/github/license/xfox111/TabsAsideExtension)](https://opensource.org/licenses/MIT) +Stemming its roots from the original Microsoft Edge browser feature, this extension has grown much bigger than just a temporary storage for tabs. -[![Twitter Follow](https://img.shields.io/twitter/follow/xfox111?style=social)](https://twitter.com/xfox111) -[![GitHub followers](https://img.shields.io/github/followers/xfox111?label=Follow%20@xfox111&style=social)](https://github.com/xfox111) -[![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-%40xfox111-orange)](https://buymeacoffee.com/xfox111) - -![Tabs aside](https://xfox111.net/y7xk3z) - -If youโ€™re like me, you often find yourself with a bunch of open tabs. Youโ€™d like to get those tabs out of the way sometimes, but theyโ€™re maybe not worth saving as actual bookmarks. - -In the Edge browser, Microsoft has introduced a new feature called "Tabs aside" (or Tab groups) which lets you set tabs aside in a sort of temporary workspace so that you can call them back up later. - -![Tabs aside demo](https://xfox111.net/knrp7b) - -Unfortunately, in new Chromium-based Microsoft Edge, the devs decided not to implement this must-have-feature. So I've decided to create a browser extension which replicates this awesome feature +It allows you to save and manage your tabs in a convenient way, providing a range of features that make it easy to organize and access your saved tabs. ## Features -- Familiar UI inherited from legacy Microsoft Edge with some improvements -- Auto Dark mode -- Now you can restore one tab from collection without removing -- Now you can choose if you want to load restored tabs only when you're navigating onto them -- Set tabs you've selected aside -- Sync your saved tabs between different PCs -- **Now available for Firefox!** +- **Save tabs**: Save all your open tabs in a single click, and restore them later +- **Organize tabs**: Create collections and subgroups to organize your saved tabs +- **Search tabs**: Quickly find the tabs you need using the search feature +- **Sync across devices**: Access your saved tabs from any device with your account +- **Go dark**: Dark mode support for a more comfortable browsing experience +- **Personalize**: Change the appearance and behavior of the extension to suit your needs + +Check out our [latest blog post](https://at.xfox111.net/tabs-aside-3-0) regarding all the new features and improvements in Tabs aside 3.0 + +## Languages +- Chinese (Simplified) +- English +- Italian +- Polish +- Portuguese (Brazil) by [@maisondasilva](https://github.com/maisondasilva) +- Russian +- Spanish +- Ukrainian ## Download -- [Google Chrome Webstore](https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin) +[![Chrome Web Store](https://img.shields.io/chrome-web-store/users/mgmjbodjgijnebfgohlnjkegdpbdjgin?label=Chrome%20Webstore%20downloads)](https://chrome.google.com/webstore/detail/mgmjbodjgijnebfgohlnjkegdpbdjgin) +[![Mozilla Add-on](https://img.shields.io/amo/users/ms-edge-tabs-aside?label=Firefox%20Webstore%20downloads)](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/) + +- [Google Chrome Webstore](https://chrome.google.com/webstore/detail/mgmjbodjgijnebfgohlnjkegdpbdjgin) - [Microsoft Edge Add-ons Webstore](https://microsoftedge.microsoft.com/addons/detail/kmnblllmalkiapkfknnlpobmjjdnlhnd) -- [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/ms-edge-tabs-aside/) +- [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/ms-edge-tabs-aside/) - [GitHub Releases](https://github.com/xfox111/TabsAsideExtension/releases/latest) -## Project roadmap -You can go to the project's [roadmap kanban board](https://github.com/XFox111/TabsAsideExtension/projects/1) and see what have we planned and watch our progress in realtime +### Sideloading (for testing purposes only) + +
+ Click to expand + +--- + +
+ Chromium-based browsers (Edge, Chrome, etc.) + +> 1. Go to [Releases](https://github.com/XFox111/TabsAsideExtension/releases) and select a release to download +> 2. Download attached archive for Chromium and unpack it +> 3. Go to `chrome://extensions` +> 4. Enable "Developer mode" +> 5. Click the "Load unpacked" button and navigate to the extension's root folder (contains `manifest.json`) +> 6. Done! + +
+ +
+ Firefox + +> 1. Go to [Releases](https://github.com/XFox111/TabsAsideExtension/releases) and select a release to download +> 2. Download attached archive for Firefox and unpack it +> 3. Go to `about:debugging#/runtime/this-firefox` +> 4. Click the "Load Temporary Add-on..." button and select `manifest.json` file in the root folder +> 5. Done! + +> **Important!** +This will _replace_ officialy installed version if you have one. +If you want to sideload it without replacing to run both versions at the same time - before loading add-on, open `manifest.json` in a text editor and change `id` key (it's `tabsaside@xfox111.net` by default) to something else + +
+ +> **Note:** If you delete the extension folder it will disappear from your browser +--- + +
## Contributing +[![GitHub issues](https://img.shields.io/github/issues/xfox111/TabsAsideExtension)](https://github.com/xfox111/TabsAsideExtension/issues) +[![CI](https://github.com/XFox111/TabsAsideExtension/actions/workflows/cd_pipeline.yaml/badge.svg)](https://github.com/XFox111/TabsAsideExtension/actions/workflows/cd_pipeline.yaml) +[![GitHub repo size](https://img.shields.io/github/repo-size/xfox111/TabsAsideExtension?label=repo%20size)](https://github.com/xfox111/TabsAsideExtension) + There are many ways in which you can participate in the project, for example: - [Submit bugs and feature requests](https://github.com/xfox111/TabsAsideExtension/issues), and help us verify as they are checked in - Review [source code changes](https://github.com/xfox111/TabsAsideExtension/pulls) - Review documentation and make pull requests for anything from typos to new content -If you are interested in fixing issues and contributing directly to the code base, please see the [Contribution Guidelines](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md), which covers the following: -- [How to deploy the extension on your browser](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#deploy-test-version-on-your-browser) -- [The development workflow](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#development-workflow), including debugging and running tests -- [Coding guidelines](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#coding-guidelines) -- [Submitting pull requests](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#submitting-pull-requests) -- [Finding an issue to work on](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#finding-an-issue-to-work-on) -- [Contributing to translations](https://github.com/XFox111/TabsAsideExtension/blob/master/CONTRIBUTING.md#contributing-to-translations) +If you are interested in fixing issues and contributing directly to the code base, please refer to the [Contribution Guidelines](https://github.com/XFox111/TabsAsideExtension/wiki/Contribution-Guidelines) -## Code of Conduct -This project has adopted the Contributor Covenant. For more information see the [Code of Conduct](https://github.com/XFox111/TabsAsideExtension/blob/master/CODE_OF_CONDUCT.md) +--- -## Copyrights -> ยฉ2021 Michael "XFox" Gordeev +[![Bluesky](https://img.shields.io/badge/%40xfox111.net-BSky?logo=bluesky&logoColor=%230285FF&label=Bluesky&labelColor=white&color=%230285FF)](https://bsky.app/profile/xfox111.net) +[![GitHub](https://img.shields.io/badge/%40xfox111-GitHub?logo=github&logoColor=%23181717&label=GitHub&labelColor=white&color=%23181717)](https://github.com/xfox111) +[![Buy Me a Coffee](https://img.shields.io/badge/%40xfox111-BMC?logo=buymeacoffee&logoColor=black&label=Buy%20me%20a%20coffee&labelColor=white&color=%23FFDD00)](https://buymeacoffee.com/xfox111) -Font copyrights: Microsoft Corportation ยฉ2021 (Additional ELUA applied) - -Licensed under [MIT License](https://opensource.org/licenses/MIT) \ No newline at end of file +> ยฉ2025 Eugene Fox. Licensed under [MIT license](https://github.com/XFox111/TabsAsideExtension/blob/main/LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5b2b0a1 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +## Supported Versions +Tabs aside extension has a linear versioning system. The latest version is always the most secure one. This is applied to major versions as well. If you are using an older version, please update it to the latest one. + +We regularly run security audits and fix any security issues that are found. If you find a security issue, please report it to us as described below. + +If you are willing to continue using an older version, you can sideload it from the [releases page](https://github.com/XFox111/TabsAsideExtension/releases). Use outdated versions at your own risk. + +## Reporting a Vulnerability +You can report a security issue by clicking "Report a vulnerability" button at the top-right of this page, or by going through [this link](https://github.com/XFox111/TabsAsideExtension/security/advisories/new) diff --git a/TabsAside.html b/TabsAside.html deleted file mode 100644 index 19700f4..0000000 --- a/TabsAside.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - Tabs aside - - - - - - - - - - - -
-
- -
- - - - - diff --git a/_locales/en/messages.json b/_locales/en/messages.json deleted file mode 100644 index 5ddfcda..0000000 --- a/_locales/en/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "Tabs aside", - "description": "Extension name. Displayed in the manifest and pane header" - }, - "description": - { - "message": "Classic Microsoft Edge \"Tabs Aside\" feature", - "description": "Extension description" - }, - "author": - { - "message": "Michael \"XFox\" Gordeev", - "description": "Author name" - }, - "options": - { - "message": "Options", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "Close", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "Load tabs on restore", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "Show confirmation dialog before deleting an item", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "Set tabs aside on extension icon click (%TOGGLE_SHORTCUT% or right-click to open the pane)", - "description": "Label for option" - }, - "github": - { - "message": "Visit GitHub page", - "description": "Link title" - }, - "changelog": - { - "message": "Changelog", - "description": "Link title" - }, - "feedback": - { - "message": "Leave feedback", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "Buy me a coffee!", - "description": "Link title" - }, - "credits": - { - "message": "Developed by Michael 'XFox' Gordeev", - "description": "Options menu credits" - }, - "setAside": - { - "message": "Set current tabs aside", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "Tip : You can set aside specific tabs by selecting them (by holding CTRL or SHIFT and clicking on the tabs) before clicking on the TabsAside extension", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "You have no aside tabs", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "Remove tab from collection", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "Restore tabs", - "description": "Collection restore action link name" - }, - "more": - { - "message": "More...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "Restore without removing", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "Remove collection", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "Are you sure you want to delete this collection?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "Are you sure you want to delete this tab?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "Toggle tabs aside pane", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "No tabs available to save", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "Tabs could not be set aside. You may be trying to set too many tabs aside at once, or have too many tabs already set aside.", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "Some tabs set aside from a previous version could not be migrated. They have been backed up as browser bookmarks.", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "items", - "description": "Collection tabs counter label (e.g. 8 items)" - } -} diff --git a/_locales/es/messages.json b/_locales/es/messages.json deleted file mode 100644 index 91a6669..0000000 --- a/_locales/es/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "Pestaรฑas que has reservado", - "description": "Extension name. Displayed in the manifest and pane header" - }, - "description": - { - "message": "Funciรณn \"Reservar pestaรฑas\" de Microsoft Edge clรกsico", - "description": "Extension description" - }, - "author": - { - "message": "Michael \"XFox\" Gordeev", - "description": "Author name" - }, - "options": - { - "message": "Opciones", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "Cerrar", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "Cargar pestaรฑas al restaurar", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "Mostrar diรกlogo de confirmacion antes de eliminar un item", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "Reservar pestaรฑas al apretar el icono de la extensiรณn (%TOGGLE_SHORTCUT% o click-derecho para abrir el panel)", - "description": "Label for option" - }, - "github": - { - "message": "Visitar la pagina de GitHub", - "description": "Link title" - }, - "changelog": - { - "message": "Registro de cambios", - "description": "Link title" - }, - "feedback": - { - "message": "Deja un comentario", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "ยกComprame un cafe!", - "description": "Link title" - }, - "credits": - { - "message": "Desarrollado por Michael 'XFox' Gordeev", - "description": "Options menu credits" - }, - "setAside": - { - "message": "Reservar las pestaรฑas actuales", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "Consejo: puede dejar de lado pestaรฑas especรญficas seleccionรกndolas (manteniendo presionada la tecla CTRL o SHIFT y haciendo clic en las pestaรฑas) antes de hacer clic en la extensiรณn TabsAside", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "No tienes pestaรฑas reservadas", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "Eliminar pestaรฑas de la colecciรณn", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "Restaurar pestaรฑas", - "description": "Collection restore action link name" - }, - "more": - { - "message": "Mรกs...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "Restaurar sin eliminar", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "Eliminar la colecciรณn", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "ยฟEstรก seguro que quiere eliminar esta colecciรณn?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "ยฟEstรก seguro que quiere eliminar esta pestaรฑa?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "Abrir panel de pestaรฑas", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "No hay pestaรฑas disponibles para reservar", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "Las pestaรฑas no se pueden dejar de lado. Es posible que estรฉ intentando dejar demasiadas pestaรฑas a un lado a la vez o que ya haya reservado demasiadas pestaรฑas.", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "Algunas pestaรฑas apartadas de una versiรณn anterior no se pudieron migrar. Se han respaldado como marcadores del navegador.", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "items", - "description": "Collection tabs counter label (e.g. 8 items)" - } -} \ No newline at end of file diff --git a/_locales/it/messages.json b/_locales/it/messages.json deleted file mode 100644 index b90a5c6..0000000 --- a/_locales/it/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "Schede a parte", - "description": "name of the extension. Displayed in the manifest and pane header" - }, - "description": - { - "message": "La funzione del classico Microsoft Edge \"Tabs Aside\"", - "description": "Extension description" - }, - "author": - { - "message": "Michael \"XFox\" Gordeev", - "description": "Author name" - }, - "options": - { - "message": "Opzioni", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "Chiudi", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "Riapri le schede al ripristino", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "Mostra una finestra di conferma prima di eliminare qualcosa", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "Metti da parte le schede in Tabs Aside al click sull'estensione (%TOGGLE_SHORTCUT% o click con il tasto destro sull'icona dell'estensione per aprire il pannello)", - "description": "Label for option" - }, - "github": - { - "message": "Visita la pagina di GitHub", - "description": "Link title" - }, - "changelog": - { - "message": "Registro dei cambiamenti", - "description": "Link title" - }, - "feedback": - { - "message": "Lascia un feedback", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "Comprami un caffรจ!", - "description": "Link title" - }, - "credits": - { - "message": "Sviluppato da Michael 'XFox' Gordeev", - "description": "Options menu credits" - }, - "setAside": - { - "message": "Sposta le schede correnti in Tabs Aside", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "Suggerimento: puoi mettere da parte schede specifiche selezionandole (tenendo premuto CTRL o MAIUSC e facendo clic sulle schede) e cliccando sull'estensione Tabs Aside.", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "Non hai schede da parte qua", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "Rimuovi la scheda da questo gruppo di schede", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "Ripristina schede", - "description": "Collection restore action link name" - }, - "more": - { - "message": "Altro...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "Ripristina senza rimuovere le schede da da Tabs Aside", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "Rimuovi gruppo di schede", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "Sei sicuro di voler eliminare questo gruppo di schede?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "Sei sucuro di voler rimuovere questa scheda?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "Mostra/nascondi il pannello di Tabs Aside", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "Nessuna scheda da salvare", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "Queste schede non possono essere messe da parte. Forse perchรฉ stai cercando di mettere da parte troppe schede o perchรฉ ce ne sono giร  troppe messe da parte.", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "Alcune schede messe da parte in una versione precedente non potevano essere mantenute. Sono state salvate come preferiti del browser.", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "schede", - "description": "Collection tabs counter label (e.g. 8 items)" - } - } diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json deleted file mode 100644 index 506deee..0000000 --- a/_locales/pl/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "Kolekcje zakล‚adek", - "description": "Extension name. Displayed in the manifest and pane header" - }, - "description": - { - "message": "Klasyczna funkcjonalnoล›ฤ‡ Microsoft Edge \"Kolekcje zakล‚adek\"", - "description": "Extension description" - }, - "author": - { - "message": "Michael \"XFox\" Gordeev", - "description": "Author name" - }, - "options": - { - "message": "Opcje", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "Zamknij", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "Przeล‚aduj zawartoล›ฤ‡ kart podczas przywracania", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "Pokaลผ potwierdzenie podczas kasowania elementu", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "Klikniฤ™cie na ikonฤ™ zapisuje aktualne karty (%TOGGLE_SHORTCUT% lub prawy przycisk myszki, aby otworzyฤ‡ panel)", - "description": "Label for option" - }, - "github": - { - "message": "Strona GitHub", - "description": "Link title" - }, - "changelog": - { - "message": "Historia zmian", - "description": "Link title" - }, - "feedback": - { - "message": "Wyล›lij opiniฤ™", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "Kup mi kawฤ™!", - "description": "Link title" - }, - "credits": - { - "message": "Opracowane przez Michael 'XFox' Gordeev", - "description": "Options menu credits" - }, - "setAside": - { - "message": "Zapisz aktualne karty", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "Wskazรณwka : moลผesz zaznaczyฤ‡ interesujฤ…ce karty uลผywajฤ…c CTRL lub SHIFT na karcie, aby tylko te pรณลบniej zapisaฤ‡", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "Nie posiadasz ลผadnej kolekcji zakล‚adek", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "Usuniฤ™cie karty z kolekcji", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "Przywrรณcenie kart", - "description": "Collection restore action link name" - }, - "more": - { - "message": "Wiฤ™cej...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "Przywrรณcenie kart bez usuniฤ™cia kolekcji", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "Usuniฤ™cie kolekcji", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "Jesteล› pewny, ลผe chcesz skasowaฤ‡ kolekcjฤ™?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "Jesteล› pewny, ลผe chcesz skasowaฤ‡ zakล‚adkฤ™?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "Przeล‚ฤ…cz panel kolekcji", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "Brak zakล‚adek do zapisu", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "Zakล‚adki nie mogฤ… zostaฤ‡ zapisane. Przypuszczalnie zbyt wiele zakล‚adek lub aktualnie posiadanych kolekcji.", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "Niektรณre kolekcje z poprzedniej wersji nie mogล‚y zostaฤ‡ przekonwertowane, w zwiฤ…zku z czym zostaล‚y zapisane jako zwykล‚e zakล‚adki w przeglฤ…darce", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "elementรณw", - "description": "Collection tabs counter label (e.g. 8 items)" - } -} diff --git a/_locales/pt_BR/messages.json b/_locales/pt_BR/messages.json deleted file mode 100644 index f06aa77..0000000 --- a/_locales/pt_BR/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "Reservar abas", - "description": "Extension name. Displayed in the manifest and pane header" - }, - "description": - { - "message": "Funรงรฃo \"Reservar abas\" do Microsoft Edge Clรกssico", - "description": "Extension description" - }, - "author": - { - "message": "Michael \"XFox\" Gordeev", - "description": "Author name" - }, - "options": - { - "message": "Opรงรตes", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "Fechar", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "Carregar abas ao restaurar", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "Mostrar diรกlogo de confirmaรงรฃo antes de deletar um item", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "Reservar abas ao apertar o รญcone da extensรฃo (%TOGGLE_SHORTCUT% ou clique-direito para abrir o painel)", - "description": "Label for option" - }, - "github": - { - "message": "Visite a pรกgina do GitHub", - "description": "Link title" - }, - "changelog": - { - "message": "Registro de mudanรงas", - "description": "Link title" - }, - "feedback": - { - "message": "Deixe um comentรกrio", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "Me compre um cafรฉ!", - "description": "Link title" - }, - "credits": - { - "message": "Desenvolvido por Michael 'XFox' Gordeev", - "description": "Options menu credits" - }, - "setAside": - { - "message": "Reservas as abas atuais", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "Dica: Vocรช pode reservar abas especรญficas ao selecionรก-las (ao apertar CTRL ou SHIFT e clicando nas abas) antes de clicar na extensรฃo Reservar Abas", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "Vocรช nรฃo tem abas reservadas", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "Remover aba da coleรงรฃo", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "Restaurar abas", - "description": "Collection restore action link name" - }, - "more": - { - "message": "Mais...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "Restaurar sem remover", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "Remover coleรงรฃo", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "Vocรช tem certeza que quer deletar esta coleรงรฃo?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "Vocรช tem certeza que quer deletar esta aba?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "Alternar painel de abas", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "Nรฃo hรก abas disponรญveis para salvar", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "As abas nรฃo puderam ser reservadas. Vocรช pode ter tentado reservar muitas abas de uma vez sรณ, ou pode ter abas demais jรก reservadas.", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "Algumas abas reservadas de uma versรฃo antiga nรฃo puderam ser migradas. Elas foram salvas como marcadores do navegador.", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "itens", - "description": "Collection tabs counter label (e.g. 8 items)" - } -} \ No newline at end of file diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json deleted file mode 100644 index 0e2fd9a..0000000 --- a/_locales/ru/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "ะžั‚ะปะพะถะตะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ", - "description": "Extension name. Displayed in the manifest and pane header" - }, - "description": - { - "message": "ะคัƒะฝะบะธั†ั ะพั‚ะปะพะถะตะฝะฝั‹ั… ะฒะบะปะฐะดะพะบ ะบะปะฐััะธั‡ะตัะบะพะณะพ Microsoft Edge", - "description": "Extension description" - }, - "author": - { - "message": "ะœะธั…ะฐะธะป \"XFox\" ะ“ะพั€ะดะตะตะฒ", - "description": "Author name" - }, - "options": - { - "message": "ะะฐัั‚ั€ะพะนะบะธ", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "ะ—ะฐะบั€ั‹ั‚ัŒ", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "ะ—ะฐะณั€ัƒะถะฐั‚ัŒ ะฒะบะปะฐะดะบะธ ะฟะพัะปะต ะพั‚ะบั€ั‹ั‚ะธั", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "ะŸะพะบะฐะทั‹ะฒะฐั‚ัŒ ะพะบะฝะพ ะฟะพะดั‚ะฒะตั€ะถะดะตะฝะธั ะฟะตั€ะตะด ัƒะดะฐะปะตะฝะธะตะผ ัะปะตะผะตะฝั‚ะฐ", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "ะžั‚ะบะปะฐะดั‹ะฒะฐั‚ัŒ ะฒะบะปะฐะดะบะธ ะฟั€ะธ ะฝะฐะถะฐั‚ะธะธ ะฝะฐ ะธะบะพะฝะบัƒ ั€ะฐััˆะธั€ะตะฝะธั (%TOGGLE_SHORTCUT% ะธะปะธ ะฟั€ะฐะฒะฐั ะบะฝะพะฟะบะฐ ะผั‹ัˆะธ ะดะปั ะพั‚ะบั€ั‹ั‚ะธั ะฟะฐะฝะตะปะธ)", - "description": "Label for option" - }, - "github": - { - "message": "ะกั‚ั€ะฐะฝะธั†ะฐ GitHub", - "description": "Link title" - }, - "changelog": - { - "message": "ะกะฟะธัะพะบ ะธะทะผะตะฝะตะฝะธะน", - "description": "Link title" - }, - "feedback": - { - "message": "ะžัั‚ะฐะฒะธั‚ัŒ ะพั‚ะทั‹ะฒ", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "ะšัƒะฟะธั‚ัŒ ะผะฝะต ะบะพั„ะต!", - "description": "Link title" - }, - "credits": - { - "message": "ะ ะฐะทั€ะฐะฑะพั‚ะฐะฝะพ: ะœะธั…ะฐะธะป 'XFox' ะ“ะพั€ะดะตะตะฒ", - "description": "Options menu credits" - }, - "setAside": - { - "message": "ะžั‚ะปะพะถะธั‚ัŒ ะพั‚ะบั€ั‹ั‚ั‹ะต ะฒะบะปะฐะดะบะธ", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "ะŸะพะดัะบะฐะทะบะฐ: ั‚ะตะฟะตั€ัŒ ะฒั‹ ะผะพะถะตั‚ะต ะพั‚ะบะปะฐะดั‹ะฒะฐั‚ัŒ ั‚ะพะปัŒะบะพ ะฝะตะบะพั‚ะพั€ั‹ะต ะฒะบะปะฐะดะบะธ. ะŸั€ะพัั‚ะพ ะฒั‹ะดะตะปะธั‚ะต ะฝัƒะถะฝั‹ะต ะฒะบะปะฐะดะบะธ ั ะฟะพะผะพั‰ัŒัŽ Shift ะธะปะธ Ctrl ะฟะตั€ะตะด ะธัะฟะพะปัŒะทะพะฒะฐะฝะธะตะผ ั€ะฐััˆะธั€ะตะฝะธั", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "ะฃ ะฒะฐั ะฝะตั‚ ะพั‚ะปะพะถะตะฝะฝั‹ั… ะฒะบะปะฐะดะพะบ", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "ะฃะดะฐะปะธั‚ัŒ ะฒะบะปะฐะดะบัƒ ะธะท ะบะพะปะปะตะบั†ะธะธ", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "ะ’ะพััั‚ะฐะฝะพะฒะธั‚ัŒ ะฒะบะปะฐะดะบะธ", - "description": "Collection restore action link name" - }, - "more": - { - "message": "ะ‘ะพะปัŒัˆะต...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "ะ’ะพััั‚ะฐะฝะพะฒะธั‚ัŒ ะฑะตะท ัƒะดะฐะปะตะฝะธั", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "ะฃะดะฐะปะธั‚ัŒ ะบะพะปะปะตะบั†ะธัŽ", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "ะ’ั‹ ัƒะฒะตั€ะตะฝั‹, ั‡ั‚ะพ ั…ะพั‚ะธั‚ะต ัƒะดะฐะปะธั‚ัŒ ัั‚ัƒ ะบะพะปะปะตะบั†ะธัŽ?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "ะ’ั‹ ัƒะฒะตั€ะตะฝั‹, ั‡ั‚ะพ ั…ะพั‚ะธั‚ะต ัƒะดะฐะปะธั‚ัŒ ัั‚ัƒ ะฒะบะปะฐะดะบัƒ ะธะท ะบะพะปะปะตะบั†ะธะธ?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "ะžั‚ะบั€ั‹ั‚ัŒ/ะทะฐะบั€ั‹ั‚ัŒ ะฟะฐะฝะตะปัŒ", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "ะะตั‚ ะดะพัั‚ัƒะฟะฝั‹ั… ะดะปั ัะพั…ั€ะฐะฝะตะฝะธั ะฒะบะปะฐะดะพะบ", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "ะะต ัƒะดะฐะตั‚ัั ะพั‚ะปะพะถะธั‚ัŒ ะฒะบะปะฐะดะบะธ. ะ’ะพะทะผะพะถะฝะพ, ะฒั‹ ะฟั‹ั‚ะฐะตั‚ะตััŒ ะพั‚ะปะพะถะธั‚ัŒ ัะปะธัˆะบะพะผ ะผะฝะพะณะพ ะฒะบะปะฐะดะพะบ ะทะฐ ั€ะฐะท ะธะปะธ ัƒะถะต ะพั‚ะปะพะถะธะปะธ ัะปะธัˆะบะพะผ ะผะฝะพะณะพ ะฒะบะปะฐะดะพะบ", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "ะะต ัƒะดะฐะปะพััŒ ะฟะตั€ะตะฝะตัั‚ะธ ะพั‚ะปะพะถะตะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ, ัะพั…ั€ะฐะฝะตะฝะฝั‹ะต ะฒ ัั‚ะฐั€ะพะน ะฒะตั€ัะธะธ ั€ะฐััˆะธั€ะตะฝะธั. ะžะฝะธ ะฑั‹ะปะธ ัะพั…ั€ะฐะฝะตะฝั‹ ะฒ ะทะฐะบะปะฐะดะบะฐั…", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "ะฒะบะปะฐะดะพะบ", - "description": "Collection tabs counter label (e.g. 8 items)" - } -} \ No newline at end of file diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json deleted file mode 100644 index 38183fd..0000000 --- a/_locales/zh_CN/messages.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "name": - { - "message": "ๆ็ฝฎ็š„ๆ ‡็ญพ้กต", - "description": "Extension name. Displayed in the manifest and pane header" - }, - "description": - { - "message": "ไธบChromiumๆต่งˆๅ™จๆไพ›ๆ—ง็‰ˆ Microsoft Edge ไธญ็š„\"ๆ็ฝฎๆ ‡็ญพ้กต\" ๅŠŸ่ƒฝ", - "description": "Extension description" - }, - "author": - { - "message": "Michael \"XFox\" Gordeev", - "description": "Author name" - }, - "options": - { - "message": "่ฎพ็ฝฎ", - "description": "Alternative text for options button in the pane" - }, - "closePanel": - { - "message": "ๅ…ณ้—ญ", - "description": "Alternative text for close panel button" - }, - "loadOnRestore": - { - "message": "ๅœจ้‡ๆ–ฐๆ‰“ๅผ€ๆ—ถๅŠ ่ฝฝ้กต้ข", - "description": "Label for option" - }, - "showDeleteDialog": - { - "message": "ๅœจๅˆ ้™ค้กน็›ฎไน‹ๅ‰ๆ˜พ็คบ็กฎ่ฎคๅฏน่ฏๆก†", - "description": "Label for option" - }, - "swapIconAction": - { - "message": "็‚นๅ‡ปๆ‹“ๅฑ•ๅ›พๆ ‡ๆฅๆ็ฝฎๆ‰€ๆœ‰ๆ ‡็ญพ้กต (ๆŒ‰%TOGGLE_SHORTCUT%ๆˆ–ๅณ้”ฎๆฅๆ‰“ๅผ€ไพงๆ )", - "description": "Label for option" - }, - "github": - { - "message": "ๆŸฅ็œ‹GitHub้กต้ข", - "description": "Link title" - }, - "changelog": - { - "message": "ๅ˜ๆ›ดๆ—ฅๅฟ—", - "description": "Link title" - }, - "feedback": - { - "message": "็ป™ๆˆ‘ไปฌๅ้ฆˆ", - "description": "Link title" - }, - "buyMeACoffee": - { - "message": "็ป™ๆˆ‘ไนฐๆฏๅ’–ๅ•ก!", - "description": "Link title" - }, - "credits": - { - "message": "็”ฑMichael 'XFox' Gordeevๅผ€ๅ‘", - "description": "Options menu credits" - }, - "setAside": - { - "message": "ๆ็ฝฎๅฝ“ๅ‰็š„ๆ‰€ๆœ‰ๆ ‡็ญพ้กต", - "description": "Save collection action name. Used in the pane, extension context menu and manifest shortcuts" - }, - "setMultipleTabsAsideTooltip": - { - "message": "ๆ็คบ๏ผšๅœจๅ•ๅ‡ปTabsAsideๆ‰ฉๅฑ•ๅไน‹ๅ‰๏ผŒๅฏไปฅ้€š่ฟ‡้€‰ๆ‹ฉ็‰นๅฎš้€‰้กนๅก๏ผˆๆŒ‰ไฝCTRLๆˆ–SHIFTๅนถๅ•ๅ‡ป้€‰้กนๅก๏ผ‰ๆฅๆ็ฝฎๅฎƒไปฌ", - "description": "Tooltip displayed on hover in the pane to explain how to set specific tabs aside" - }, - "nothingSaved": - { - "message": "ไฝ ็›ฎๅ‰ๆฒกๆœ‰ๆ็ฝฎ็š„ๆ ‡็ญพ้กต", - "description": "Placeholder for empty pane" - }, - "removeTab": - { - "message": "ไปŽๆ ‡็ญพ้กต้›†ไธญ็งป้™คๆ ‡็ญพ้กต", - "description": "Button hint on a tab card" - }, - "restoreTabs": - { - "message": "ๆขๅคๆ ‡็ญพ้กต", - "description": "Collection restore action link name" - }, - "more": - { - "message": "ๆ›ดๅคš...", - "description": "Collections' more button title" - }, - "restoreNoRemove": - { - "message": "ๆขๅคไฝ†ไธ็งป้™คๆ ‡็ญพ้กต", - "description": "Context action item name" - }, - "removeCollection": - { - "message": "็งป้™คๆ ‡็ญพ้กต้›†", - "description": "Collection remove action name" - }, - "removeCollectionConfirm": - { - "message": "ไฝ ็กฎๅฎš่ฆ็งป้™ค่ฟ™ไธชๆ ‡็ญพ้กต้›†ๅ—?", - "description": "Prompt dialog content on collection deletion" - }, - "removeTabConfirm": - { - "message": "ไฝ ็กฎๅฎš่ฆ็งป้™ค่ฟ™ไธชๆ ‡็ญพ้กตๅ—?", - "description": "Prompt dialog content on one tab deletion" - }, - "togglePaneContext": - { - "message": "ๆ‰“ๅผ€ๆˆ–ๅ…ณ้—ญไพงๆ ", - "description": "Context action name. Used in extension context menu and manifest shortcuts" - }, - "noTabsToSave": - { - "message": "ๆฒกๆœ‰ๅฏไปฅๆ็ฝฎ็š„ๆ ‡็ญพ้กต", - "description": "Alert dialog message when there's no tabs to save" - }, - "errorSavingTabs": - { - "message": "ๆ ‡็ญพ้กตๆ— ๆณ•่ขซๆ็ฝฎ. ๅฏ่ƒฝๆ˜ฏๅ› ไธบๆ‚จๅฐ่ฏ•ไธ€ๆฌกๆ€งๆ็ฝฎ่ฟ‡ๅคš็š„ๆ ‡็ญพ้กต, ๆˆ–ๆ˜ฏๅ…ˆๅ‰ๆ็ฝฎ็š„ๆ ‡็ญพ้กต่ฟ‡ๅคš.", - "description": "Alert dialog message when there is an issue saving tabs" - }, - "olderDataMigrationFailed": - { - "message": "ไธ€้ƒจๅˆ†ๆ็ฝฎ็š„ๆ ‡็ญพ้กตๆ— ๆณ•ไปŽไธŠไธ€ไธช็‰ˆๆœฌไธญ่ฟ็งป. ่ฟ™ไบ›ๆ ‡็ญพ้กตๅทฒ็ปไฝœไธบๆต่งˆๅ™จไนฆ็ญพ่ขซๅค‡ไปฝ.", - "description": "Alert dialog message when there is an issue migrating previous versions data" - }, - "tabs": - { - "message": "้กน", - "description": "Collection tabs counter label (e.g. 8 items)" - } -} diff --git a/app.config.ts b/app.config.ts new file mode 100644 index 0000000..62f49f1 --- /dev/null +++ b/app.config.ts @@ -0,0 +1,24 @@ +import { googleAnalytics4 } from "@wxt-dev/analytics/providers/google-analytics-4"; +import { WxtAppConfig } from "wxt/sandbox"; +import { userPropertiesStorage } from "./features/analytics"; + +export default defineAppConfig({ + analytics: + { + debug: import.meta.env.DEV, + enabled: storage.defineItem("local:analytics", { + fallback: true + }), + userId: storage.defineItem("local:userId", { + init: () => crypto.randomUUID() + }), + userProperties: userPropertiesStorage, + providers: + [ + googleAnalytics4({ + apiSecret: import.meta.env.WXT_GA4_API_SECRET, + measurementId: import.meta.env.WXT_GA4_MEASUREMENT_ID + }) + ] + } +} as WxtAppConfig); diff --git a/assets/BuyMeACoffee20.tsx b/assets/BuyMeACoffee20.tsx new file mode 100644 index 0000000..4c665c8 --- /dev/null +++ b/assets/BuyMeACoffee20.tsx @@ -0,0 +1,64 @@ +import { makeStyles } from "@fluentui/react-components"; +import { FluentIcon } from "@fluentui/react-icons"; + +export const BuyMeACoffee20Regular: FluentIcon = (props) => +{ + const cls = useStyles(); + return ( + + + + + + + + + + + + + + + + + + ); +}; + +export const BuyMeACoffee20Filled: FluentIcon = (props) => +{ + const cls = useStyles(); + return ( + + + + + + + + + + + + + + + + + + + ); +}; + +const useStyles = makeStyles({ + outline: + { + fill: "currentColor" + }, + coffee: + { + fill: "#FFDD00" + } +}); diff --git a/assets/FaviconPlaceholder.svg b/assets/FaviconPlaceholder.svg new file mode 100644 index 0000000..e87674c --- /dev/null +++ b/assets/FaviconPlaceholder.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/assets/PagePlaceholder.svg b/assets/PagePlaceholder.svg new file mode 100644 index 0000000..7ea3430 --- /dev/null +++ b/assets/PagePlaceholder.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + diff --git a/assets/global.css b/assets/global.css new file mode 100644 index 0000000..4f536dc --- /dev/null +++ b/assets/global.css @@ -0,0 +1,57 @@ +html, +body +{ + padding: 0; + margin: 0; + + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; + + overflow: hidden; +} + +* +{ + margin: 0; + box-sizing: border-box; +} + +#root > .fui-FluentProvider +{ + height: 100vh; + overflow: hidden; +} + +/* width */ +::-webkit-scrollbar +{ + width: 8px; + height: 8px; +} + +/* Track */ +::-webkit-scrollbar-track +{ + background: transparent; +} + +/* Handle */ +::-webkit-scrollbar-thumb +{ + /* eslint-disable-next-line css/no-invalid-properties */ + background-color: var(--colorNeutralStroke1); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover +{ + /* eslint-disable-next-line css/no-invalid-properties */ + background-color: var(--colorNeutralStroke1Hover); +} + +::-webkit-scrollbar-thumb:hover:active +{ + /* eslint-disable-next-line css/no-invalid-properties */ + background-color: var(--colorNeutralStroke1Pressed); +} diff --git a/components/PromptDialog.tsx b/components/PromptDialog.tsx new file mode 100644 index 0000000..656a283 --- /dev/null +++ b/components/PromptDialog.tsx @@ -0,0 +1,48 @@ +import { useDangerStyles } from "@/hooks/useDangerStyles"; +import { Button, DialogActions, DialogBody, DialogContent, DialogSurface, DialogTitle, DialogTrigger } from "@fluentui/react-components"; + +export default function PromptDialog(props: PromptDialogProps): React.ReactElement +{ + const dangerCls = useDangerStyles(); + + return ( + + + + { props.title } + + + { props.content } + + + + + + + + + + + + + + ); +} + +export type PromptDialogProps = + { + title: string; + content: React.ReactNode; + confirmText: string; + cancelText?: string; + onConfirm: () => void; + destructive?: boolean; + }; diff --git a/contexts/DialogProvider.tsx b/contexts/DialogProvider.tsx new file mode 100644 index 0000000..3b920aa --- /dev/null +++ b/contexts/DialogProvider.tsx @@ -0,0 +1,51 @@ +import { Dialog, DialogModalType } from "@fluentui/react-components"; +import { createContext, PropsWithChildren, ReactElement } from "react"; +import PromptDialog, { PromptDialogProps } from "@/components/PromptDialog"; + +const DialogContext = createContext(null!); + +export default function DialogProvider(props: PropsWithChildren): ReactElement +{ + const [dialog, setDialog] = useState(null); + const [modalType, setModalType] = useState(undefined); + const [onDismiss, setOnDismiss] = useState<(() => void) | undefined>(undefined); + + const pushPrompt = (props: PromptDialogProps): void => + setDialog( + + ); + + const pushCustom = (dialogSurface: ReactElement, modalType?: DialogModalType, onDismiss?: () => void): void => + { + setDialog(dialogSurface); + setModalType(modalType); + setOnDismiss(() => onDismiss); + }; + + const handleOpenChange = () => + { + onDismiss?.(); + setOnDismiss(undefined); + setTimeout(() => setDialog(null), 200); + }; + + return ( + + { props.children } + + { dialog && + + { dialog } + + } + + ); +} + +export const useDialog = () => useContext(DialogContext); + +export type DialogContextType = + { + pushPrompt(props: PromptDialogProps): void; + pushCustom(dialogSurface: ReactElement, modalType?: DialogModalType, onDismiss?: () => void): void; + }; diff --git a/contexts/ThemeProvider.tsx b/contexts/ThemeProvider.tsx new file mode 100644 index 0000000..480715b --- /dev/null +++ b/contexts/ThemeProvider.tsx @@ -0,0 +1,35 @@ +import { FluentProvider, Theme, webDarkTheme, webLightTheme } from "@fluentui/react-components"; +import { createContext } from "react"; + +const ThemeContext = createContext({ theme: webLightTheme, isDark: false }); +const media: MediaQueryList = window.matchMedia("(prefers-color-scheme: dark)"); + +export default function ThemeProvider(props: React.PropsWithChildren): React.ReactElement +{ + const [isDark, setIsDark] = useState(media.matches); + const theme = useMemo(() => isDark ? webDarkTheme : webLightTheme, [isDark]); + + useEffect(() => + { + const updateTheme = (args: MediaQueryListEvent) => setIsDark(args.matches); + media.addEventListener("change", updateTheme); + + return () => media.removeEventListener("change", updateTheme); + }, []); + + return ( + + + { props.children } + + + ); +} + +export const useTheme = (): ThemeContextType => useContext(ThemeContext); + +export type ThemeContextType = + { + theme: Theme; + isDark: boolean; + }; diff --git a/css/style.css b/css/style.css deleted file mode 100644 index ff7acee..0000000 --- a/css/style.css +++ /dev/null @@ -1,310 +0,0 @@ -.tabsAside.background -{ - background-color: rgba(255, 255, 255, .5); - position: fixed; - top: 0; - bottom: 0; - right: 0; - left: 0; - transition: .2s; - color: black; -} - -.tabsAside.closeArea -{ - position: fixed; - top: 0; - bottom: 0; - right: 0; - left: 0; - - background-color: transparent; -} - -.tabsAside.pane -{ - user-select: none; - position: fixed; - - right: 0px; - top: 0px; - bottom: 0px; - overflow: hidden; - display: grid; - grid-template-rows: auto 1fr; - - width: 100%; - min-width: 500px; - - background-color: #f7f7f7; - border: 1px solid rgba(100, 100, 100, .5); - border-width: 0px 0px 0px 1px; - box-shadow: 6px 0px 12px black; - - transform: translateX(110%); /* pane is hidded */ - transition: .2s; -} - - aside[embedded] - { - width: 500px !important; - } - - .tabsAside.pane[opened] - { - transform: translateX(0px); - } - - /* Pane header*/ - .tabsAside.pane > header - { - z-index: 1; - padding: 14px 20px 16px 20px; - box-shadow: 0px 0px 5px rgba(0,0,0,.5); - background-color: white; - display: grid; - grid-template-columns: 1fr auto auto; - grid-column-gap: 10px; - grid-row-gap: 20px; - } - - .tabsAside.pane > header > h1 - { - margin: 0px 5px; - font-weight: 400; - font-size: 24px; - } - - .tabsAside.pane > header > button .updateBadge - { - position: absolute; - bottom: 2px; - right: 2px; - } - - .tabsAside.pane > header > nav - { - top: 45px; - right: 55px; - } - - .tabsAside.pane > header nav > p - { - margin: 10px; - } - - .tabsAside.pane > header nav > p > a - { - text-decoration: none; - } - - .tabsAside.pane > header nav > p > a:hover - { - text-decoration: underline; - } - - .saveTabs - { - display: inline-grid; - grid-template-columns: 16px auto 16px; - grid-column-gap: 15px; - - margin-right: auto; - } - - .saveTabs:hover - { - text-decoration: none !important; - } - - .saveTabs:hover span:nth-child(2) - { - text-decoration: underline; - } - - .iconArrowRight,.iconQuestionCircle - { - width: 16px; - height: 16px; - display: inline-block; - font-family: "SegoeMDL2Assets"; - margin: 2px; - } - .iconQuestionCircle - { - font-size: small; - padding-top: 2px; - margin-bottom: 0; - } - - .tabsAside.pane section - { - overflow: auto; - } - - .tabsAside.pane > section > h2 - { - margin: 20px; - font-weight: normal; - } - - /* Collection header */ - .tabsAside.pane > section > div - { - transition: .2s; - } - - .collectionSet - { - background-color: white; - margin: 10px; - border-radius: 5px; - border: 1px solid #eee; - } - - .collectionSet:hover - { - box-shadow: 0px 0px 5px rgba(0, 0, 0, .25); - } - - .collectionSet .header > * - { - visibility: hidden; - } - - .collectionSet:hover .header > * - { - visibility: visible; - } - - .collectionSet > .header - { - margin: 10px 10px 0px 20px; - display: grid; - grid-template-columns: 1fr auto auto auto; - grid-column-gap: 10px; - align-items: center; - } - - .collectionSet > .header > small - { - color: gray; - visibility: visible !important; - } - - .collectionSet > .header > input - { - margin: 0px; - visibility: visible !important; - font-weight: 600; - border: none; - background: transparent; - } - - .collectionSet > .header > input:hover - { - border: 1px solid black; - } - - .collectionSet > .header > div > nav - { - width: 250px; - right: 0px; - top: 35px; - } - - /* Tabs collection */ - .collectionSet > .set - { - padding: 5px 10px; - white-space: nowrap; - overflow: auto; - } - - .collectionSet > .set::-webkit-scrollbar-thumb - { - visibility: hidden; - } - - .collectionSet > .set:hover::-webkit-scrollbar-thumb - { - visibility: visible; - } - - .collectionSet > .set > div - { - width: 175px; - height: 148px; - margin: 5px; - - background-color: #c2c2c2; - background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail.png"), - url("moz-extension://__MSG_@@extension_id__/images/tab_thumbnail.png"); - background-size: cover; - background-position-x: center; - - display: inline-grid; - grid-template-rows: 1fr auto; - - transition: .25s; - cursor: pointer; - - border: 1px solid #eee; - border-radius: 5px; - } - .collectionSet > .set > div:hover - { - box-shadow: 0px 0px 5px rgba(100, 100, 100, .5); - } - - .collectionSet > .set > div > div - { - background-color: rgba(233, 233, 233, .75); - grid-row: 2; - display: grid; - grid-template-columns: auto 1fr auto; - } - - .collectionSet > .set > div > div > button - { - margin: auto; - margin-right: 5px; - display: none; - } - - .collectionSet > .set > div:hover > div > button - { - display: initial; - } - - .collectionSet > .set > div > div > div - { - width: 20px; - height: 20px; - margin: 8px; - - background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon.png"), - url("moz-extension://__MSG_@@extension_id__/images/tab_icon.png"); - background-size: 20px; - } - - .collectionSet > .set > div > div > span - { - overflow: hidden; - margin: auto 0px; - margin-right: 10px; - font-size: 12px; - } - .collectionSet > .set > div:hover > div > span - { - margin-right: 5px; - } - -@media only screen and (max-width: 500px) -{ - .tabsAside.pane - { - width: 100% !important; - min-width: initial; - } -} diff --git a/css/style.dark.css b/css/style.dark.css deleted file mode 100644 index 71d29d8..0000000 --- a/css/style.dark.css +++ /dev/null @@ -1,136 +0,0 @@ -.tabsAside[darkmode].background -{ - background-color: rgba(0, 0, 0, .5); -} - -.tabsAside[darkmode] .pane -{ - background-color: #333333; - color: white; -} - - .tabsAside[darkmode] .pane header - { - background-color: #3b3b3b; - } - - .tabsAside[darkmode] nav hr - { - filter: invert(1); - } - - .tabsAside[darkmode] .saveTabs > div - { - filter: invert(); - } - - /* Button style */ - .tabsAside[darkmode] button - { - color: white; - } - .tabsAside[darkmode] .pane button:hover, - .tabsAside[darkmode] .pane .control.checkbox:hover - { - background-color: gray; - } - .tabsAside[darkmode] .pane button img, - .tabsAside[darkmode] .pane label > input + span - { - filter: invert(); - } - .tabsAside[darkmode] .pane button:active - { - background-color: dimgray; - } - - .tabsAside[darkmode] .collectionSet > .header > input - { - color: white; - } - - .tabsAside[darkmode] .collectionSet > .header > input:hover - { - border: 1px solid dimgray; - } - - .tabsAside[darkmode] .collectionSet > .header > input:focus - { - background: white; - color: black; - } - - .tabsAside[darkmode] a - { - color: #48adff; - } - - /* Timestamp label */ - .tabsAside[darkmode] > .pane > section small - { - color: lightgray !important; - } - - /* Scrollbar style */ - .tabsAside[darkmode] .pane ::-webkit-scrollbar-thumb - { - background: gray; - border-radius: 3px; - } - .tabsAside[darkmode] .pane ::-webkit-scrollbar-thumb:hover - { - background: dimgray; - } - - .tabsAside[darkmode] .pane .collectionSet - { - background-color: #3b3b3b; - border-color: #444; - } - - /* Tab style */ - .tabsAside[darkmode] .pane .collectionSet > .set > div - { - background-color: #0c0c0c; - background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_thumbnail_dark.png"), - url("moz-extension://__MSG_@@extension_id__/images/tab_thumbnail_dark.png"); - border-color: #444; - } - - .tabsAside[darkmode] .pane .collectionSet > .set > div > div - { - background-color: rgba(50, 50, 50, .75); - } - - .tabsAside[darkmode] .pane .collectionSet > .set > div > div > div - { - background-image: url("chrome-extension://__MSG_@@extension_id__/images/tab_icon_dark.png"), - url("moz-extension://__MSG_@@extension_id__/images/tab_icon_dark.png"); - } - - /* Context menu style */ - .tabsAside[darkmode] .pane nav - { - background-color: #4a4a4a; - } - -.tabsAside[darkmode] .listviewSwitch > div -{ - border-radius: 1px; - background-color: gray; -} - -.tabsAside[darkmode] .tabsAside .listviewSwitch.tile > div -{ - background-color: #c2c2c2; -} - -.tabsAside[darkmode] .tabsAside[listview] .listviewSwitch.tile > div -{ - background-color: gray; -} - -.tabsAside[darkmode] .tabsAside[listview] .listviewSwitch.list > div -{ - background-color: #c2c2c2; -} \ No newline at end of file diff --git a/css/style.generic.css b/css/style.generic.css deleted file mode 100644 index 29512fb..0000000 --- a/css/style.generic.css +++ /dev/null @@ -1,231 +0,0 @@ -.updateBadge -{ - display: none; -} -.tabsAside[updated] .updateBadge -{ - width: 10px; - height: 10px; - background-color: #0078d7; - border-radius: 5px; - display: block; -} - -/* Custom scrollbar */ -.tabsAside ::-webkit-scrollbar -{ - height: 6px; - width: 6px; -} - -.tabsAside ::-webkit-scrollbar-thumb -{ - background: darkgray; - border-radius: 3px; -} - .tabsAside ::-webkit-scrollbar-thumb:hover - { - background: gray; - } - -.tabsAside -{ - font-family: "SegoeUI", "SegoeMDL2Assets" !important; - font-size: 14px; - user-select: none; -} - -/* Links style */ -.tabsAside a -{ - color: #0078d7; -} - .tabsAside a:hover - { - text-decoration: underline; - cursor: pointer; - } - - .tabsAside a:visited - { - color: #0078d7; - } - -/* Buttons style */ -.tabsAside button -{ - width: 28px; - height: 28px; - font-size: 16px; - background-color: transparent; - border: none; - cursor: pointer; - outline: none !important; - position: relative; -} - .tabsAside button:hover - { - background-color: #f2f2f2; - } - .tabsAside button:active - { - background-color: gray; - } - -/* Context menus style */ -.tabsAside .contextContainer -{ - position: relative; -} - -.tabsAside nav -{ - user-select: none; - - position: absolute; - width: 390px; - font-size: 12px; - - box-shadow: 0px 4px 10px rgba(0,0,0,.25); - background-color: white; - border-radius: 5px; - - z-index: 10; - - visibility: hidden; - padding: 4px 0px; -} - - .tabsAside nav hr - { - border: none; - height: 1px; - background-color: rgba(0, 0, 0, .1); - margin: 4px 0px - } - - .tabsAside nav button - { - align-content: center; - text-align: start; - - padding: 0px 12px; - width: 100%; - - height: 32px; - font-family: "SegoeUI"; - font-size: 12px; - - display: grid; - grid-template-columns: auto 1fr auto; - grid-column-gap: 14px; - } - - .tabsAside nav button:hover - { - background-color: #eeee; - } - - .tabsAside nav button img:first-child - { - width: 16px; - height: 16px; - } - - .tabsAside nav button img:nth-child(3) - { - width: 12px; - height: 12px; - } - - .tabsAside nav button *:nth-child(3) - { - align-self: center; - } - - .tabsAside button + nav:active, - .tabsAside button:focus + nav - { - visibility: visible; - } - -/* Icon buttons style */ -.btn -{ - background-repeat: no-repeat; - background-size: 12px; - background-position: center; - font-family: "SegoeUI", "SegoeMDL2Assets"; -} - -.control.checkbox -{ - display: block; - position: relative; - padding: 8px 12px 8px 42px; - box-sizing: border-box; - cursor: pointer; - min-height: 32px; -} - .control.checkbox:hover - { - background-color: #eeeeee; - } - .control.checkbox input - { - position: absolute; - opacity: 0; - width: 0; - height: 0; - } - .control.checkbox input + span - { - position: absolute; - left: 12px; - height: 16px; - width: 16px; - - transition-property: background, border, border-color; - transition-duration: 200ms; - transition-timing-function: cubic-bezier(0.4, 0, 0.23, 1); - } - .control.checkbox input + span::after - { - position: absolute; - color: rgba(0, 0, 0, .4); - - font-family: "SegoeMDL2Assets"; - content: "\E73E"; - - width: 16px; - height: 16px; - font-size: 16px; - text-align: center; - - display: none; - } - .control.checkbox input:checked + span::after - { - display: block; - color: black; - } - .control.checkbox:hover input + span::after - { - display: block; - } - -@font-face -{ - font-family: "SegoeUI"; - src: local("Segoe UI"), - url("../fonts/segoeui.ttf") format("truetype"), - url("../fonts/segoeui.woff") format("woff") -} - -@font-face -{ - font-family: "SegoeMDL2Assets"; - src: local("Segoe MDL2 Assets"), - url("../fonts/segoemdl2.ttf") format("truetype"), - url("../fonts/segoemdl2.woff") format("woff") -} \ No newline at end of file diff --git a/css/style.listview.css b/css/style.listview.css deleted file mode 100644 index 0d397be..0000000 --- a/css/style.listview.css +++ /dev/null @@ -1,59 +0,0 @@ -.tabsAside[listview] .collectionSet > .header -{ - margin-bottom: 5px; -} - -.tabsAside[listview] .collectionSet > .set -{ - max-height: 250px; -} - -.tabsAside[listview] .collectionSet > .set > div -{ - width: initial; - height: initial; - background-image: none !important; - display: block; -} - -.listviewSwitch -{ - width: 20px; - height: 20px; - display: grid; - grid-row-gap: 2px; - grid-column-gap: 2px; - cursor: pointer; - margin: 0px auto; -} - -.listviewSwitch.tile -{ - grid-template-columns: 1fr 1fr; -} - -.listviewSwitch > div -{ - border-radius: 1px; - background-color: #c2c2c2; -} - -.listviewSwitch:hover > div -{ - background-color: #a0a0aa; -} - -.tabsAside .listviewSwitch.tile > div -{ - background-color: gray; -} - -.tabsAside[listview] .listviewSwitch.tile > div -{ - background-color: #c2c2c2; -} - -.tabsAside[listview] .listviewSwitch.list > div -{ - background-color: gray; -} \ No newline at end of file diff --git a/data/links.ts b/data/links.ts new file mode 100644 index 0000000..ba2b075 --- /dev/null +++ b/data/links.ts @@ -0,0 +1,24 @@ +import Package from "@/package.json"; + +export const buyMeACoffeeLink: string = "https://buymeacoffee.com/xfox111"; +export const bskyLink: string = "https://bsky.app/profile/xfox111.net"; +export const websiteLink: string = "https://xfox111.net"; +export const v3blogPost: string = "https://at.xfox111.net/tabs-aside-3-0"; + +const githubLink = (path: string = "."): string => + new URL(path, browser.runtime.getManifest().homepage_url).href; + +export const githubLinks = +{ + repo: githubLink(), + release: githubLink(`releases/tag/v${Package.version}`), + license: githubLink("blob/main/LICENSE"), + translationGuide: githubLink("wiki/Contribution-Guidelines#contributing-to-translations") +}; + +export const storeLink: string = + import.meta.env.FIREFOX + ? "https://addons.mozilla.org/en-US/firefox/addon/ms-edge-tabs-aside/" : + chrome.runtime.getManifest().update_url?.startsWith("https://edge.microsoft.com/") ? + "https://microsoftedge.microsoft.com/addons/detail/tabs-aside/kmnblllmalkiapkfknnlpobmjjdnlhnd" : + "https://chromewebstore.google.com/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin"; diff --git a/entrypoints/background.ts b/entrypoints/background.ts new file mode 100644 index 0000000..151156b --- /dev/null +++ b/entrypoints/background.ts @@ -0,0 +1,422 @@ +import { track, trackError } from "@/features/analytics"; +import { collectionCount, getCollections, saveCollections } from "@/features/collectionStorage"; +import { migrateStorage } from "@/features/migration"; +import { showWelcomeDialog } from "@/features/v3welcome/utils/showWelcomeDialog"; +import { SettingsValue } from "@/hooks/useSettings"; +import { CollectionItem, GraphicsStorage } from "@/models/CollectionModels"; +import getLogger from "@/utils/getLogger"; +import { onMessage, sendMessage } from "@/utils/messaging"; +import saveTabsToCollection from "@/utils/saveTabsToCollection"; +import sendNotification from "@/utils/sendNotification"; +import { settings } from "@/utils/settings"; +import watchTabSelection from "@/utils/watchTabSelection"; +import { Tabs, Windows } from "wxt/browser"; +import { Unwatch } from "wxt/storage"; +import { openCollection, openGroup } from "./sidepanel/utils/opener"; + +export default defineBackground(() => +{ + try + { + const logger = getLogger("background"); + const graphicsCache: GraphicsStorage = {}; + let listLocation: SettingsValue<"listLocation"> = "sidebar"; + + logger("Background script started"); + + // Little workaround for opening side panel + // See: https://stackoverflow.com/questions/77213045/error-sidepanel-open-may-only-be-called-in-response-to-a-user-gesture-re + settings.listLocation.getValue().then(location => listLocation = location); + settings.listLocation.watch(newLocation => listLocation = newLocation); + + browser.runtime.onInstalled.addListener(async ({ reason, previousVersion }) => + { + logger("onInstalled", reason, previousVersion); + track("extension_installed", { reason, previousVersion: previousVersion ?? "none" }); + + const previousMajor: number = previousVersion ? parseInt(previousVersion.split(".")[0]) : 0; + + if (reason === "update" && previousMajor < 3) + { + await migrateStorage(); + await showWelcomeDialog.setValue(true); + browser.runtime.reload(); + } + }); + + browser.tabs.onUpdated.addListener((_, __, tab) => + { + if (!tab.url) + return; + + graphicsCache[tab.url] = { + preview: graphicsCache[tab.url]?.preview, + capture: graphicsCache[tab.url]?.capture, + icon: tab.favIconUrl ?? graphicsCache[tab.url]?.icon + }; + }); + + browser.commands.onCommand.addListener( + (command, tab) => performContextAction(command, tab!.windowId!) + ); + + onMessage("getGraphicsCache", () => graphicsCache); + onMessage("addThumbnail", ({ data }) => + { + graphicsCache[data.url] = { + preview: data.thumbnail, + capture: graphicsCache[data.url]?.capture, + icon: graphicsCache[data.url]?.icon + }; + }); + onMessage("refreshCollections", () => { }); + + if (import.meta.env.FIREFOX) + { + onMessage("openCollection", ({ data }) => openCollection(data.collection, data.targetWindow)); + onMessage("openGroup", ({ data }) => openGroup(data.group, data.newWindow)); + } + + setupTabCaputre(); + async function setupTabCaputre(): Promise + { + const tryCaptureTab = async (tab: Tabs.Tab): Promise => + { + if (!tab.url || tab.status !== "complete" || !tab.active) + return; + + if (graphicsCache[tab.url]?.capture || graphicsCache[tab.url]?.capture === null) + return; + + try + { + // We use chrome here because polyfill throws uncatchable errors for some reason + // It's a compatible API anyway + const capture: string = await chrome.tabs.captureVisibleTab(tab.windowId!, { format: "jpeg", quality: 1 }); + + if (capture) + { + graphicsCache[tab.url] = { + capture, + preview: graphicsCache[tab.url]?.preview, + icon: graphicsCache[tab.url]?.icon + }; + } + } + catch + { + graphicsCache[tab.url] = { + capture: null!, + preview: graphicsCache[tab.url]?.preview, + icon: graphicsCache[tab.url]?.icon + }; + } + }; + + setInterval(() => + { + browser.tabs.query({ active: true }) + .then(tabs => tabs.forEach(tab => tryCaptureTab(tab))); + }, 1000); + } + + setupContextMenu(); + async function setupContextMenu(): Promise + { + await browser.contextMenus.removeAll(); + + const items: Record = + { + "show_collections": i18n.t("actions.show_collections"), + "set_aside": i18n.t("actions.set_aside.all"), + "save": i18n.t("actions.save.all") + }; + + Object.entries(items).forEach(([id, title]) => browser.contextMenus.create({ + id, title, + visible: true, + contexts: ["action"] + })); + + watchTabSelection(async selection => + { + await browser.contextMenus.update("set_aside", { + title: i18n.t(`actions.set_aside.${selection}`) + }); + await browser.contextMenus.update("save", { + title: i18n.t(`actions.save.${selection}`) + }); + }); + + browser.contextMenus.onClicked.addListener( + ({ menuItemId }, tab) => performContextAction((menuItemId as string), tab!.windowId!) + ); + } + + setupBadge(); + async function setupBadge(): Promise + { + let unwatchBadge: Unwatch | null = null; + const updateBadge = async (count: number | null) => + await browser.action.setBadgeText({ text: count && count > 0 ? count.toString() : "" }); + + if (await settings.showBadge.getValue()) + { + updateBadge(await collectionCount.getValue()); + unwatchBadge = collectionCount.watch(updateBadge); + } + + if (import.meta.env.FIREFOX) + { + await browser.action.setBadgeBackgroundColor({ color: "#0f6cbd" }); + await browser.action.setBadgeTextColor({ color: "white" }); + } + + settings.showBadge.watch(async showBadge => + { + if (showBadge) + { + updateBadge(await collectionCount.getValue()); + unwatchBadge = collectionCount.watch(updateBadge); + } + else + { + unwatchBadge?.(); + await browser.action.setBadgeText({ text: "" }); + } + }); + } + + setupActionButton(); + async function setupActionButton(): Promise + { + let unwatchActionTitle: Unwatch | null = null; + + const onClickAction = async (): Promise => + { + logger("action.onClicked"); + const defaultAction = await settings.defaultSaveAction.getValue(); + await saveTabs(defaultAction === "set_aside"); + }; + + const updateTitle = async (selection: "all" | "selected"): Promise => + { + const defaultAction = await settings.defaultSaveAction.getValue(); + await browser.action.setTitle({ title: i18n.t(`actions.${defaultAction}.${selection}`) }); + }; + + const toggleSidebarFirefox = async (): Promise => + await browser.sidebarAction.toggle(); + + const updateButton = async (action: SettingsValue<"contextAction">): Promise => + { + logger("updateButton", action); + + // Cleanup any existing behavior + browser.action.onClicked.removeListener(onClickAction); + browser.action.onClicked.removeListener(toggleSidebarFirefox); + browser.action.onClicked.removeListener(openCollectionsInTab); + + await browser.action.disable(); + await browser.action.setTitle({ title: i18n.t("manifest.name") }); + unwatchActionTitle?.(); + + if (!import.meta.env.FIREFOX) + await chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }); + + // Setup new behavior + if (action === "action") + { + browser.action.onClicked.addListener(onClickAction); + unwatchActionTitle = watchTabSelection(updateTitle); + await browser.action.enable(); + } + else if (action === "open") + { + await browser.action.enable(); + const location = await settings.listLocation.getValue(); + + if (location === "sidebar") + { + if (import.meta.env.FIREFOX) + browser.action.onClicked.addListener(toggleSidebarFirefox); + else + chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }); + } + else if (location !== "popup") + browser.action.onClicked.addListener(openCollectionsInTab); + } + }; + + updateButton(await settings.contextAction.getValue()); + settings.contextAction.watch(updateButton); + settings.listLocation.watch(async () => updateButton(await settings.contextAction.getValue())); + } + + setupCollectionView(); + async function setupCollectionView(): Promise + { + const enforcePinnedTab = async (): Promise => + { + logger("enforcePinnedTab"); + + const openWindows: Windows.Window[] = await browser.windows.getAll({ populate: true }); + + for (const openWindow of openWindows) + { + if (openWindow.incognito || openWindow.type !== "normal") + continue; + + const activeTabs: Tabs.Tab[] = openWindow.tabs!.filter(tab => + tab.url === browser.runtime.getURL("/sidepanel.html")); + + const targetTab: Tabs.Tab | undefined = activeTabs.find(tab => tab.pinned); + + if (!targetTab) + await browser.tabs.create({ + url: browser.runtime.getURL("/sidepanel.html"), + windowId: openWindow.id, + active: false, + pinned: true + }); + + const tabsToClose: Tabs.Tab[] = activeTabs.filter(tab => tab.id !== targetTab?.id); + + if (tabsToClose.length > 0) + await browser.tabs.remove(tabsToClose.map(tab => tab.id!)); + } + }; + + const updateView = async (viewLocation: SettingsValue<"listLocation">): Promise => + { + logger("updateView", viewLocation); + + browser.tabs.onHighlighted.removeListener(enforcePinnedTab); + const tabs: Tabs.Tab[] = await browser.tabs.query({ + url: browser.runtime.getURL("/sidepanel.html") + }); + await browser.tabs.remove(tabs.map(tab => tab.id!)); + + await browser.action.setPopup({ + popup: viewLocation === "popup" ? browser.runtime.getURL("/popup.html") : "" + }); + + if (import.meta.env.FIREFOX) + await browser.sidebarAction.setPanel({ + panel: viewLocation === "sidebar" ? browser.runtime.getURL("/sidepanel.html") : "" + }); + else + await chrome.sidePanel.setOptions({ enabled: viewLocation === "sidebar" }); + + if (viewLocation === "pinned") + { + enforcePinnedTab(); + browser.tabs.onHighlighted.addListener(enforcePinnedTab); + } + }; + + updateView(await settings.listLocation.getValue()); + settings.listLocation.watch(updateView); + } + + function performContextAction(action: string, windowId: number): void + { + if (action === "show_collections") + { + if (listLocation === "sidebar" || listLocation === "popup") + openCollectionsInView(listLocation, windowId); + else + openCollectionsInTab(); + } + else + saveTabs(action === "set_aside"); + } + + function openCollectionsInView(view: "sidebar" | "popup", windowId: number): void + { + if (view === "sidebar") + { + if (import.meta.env.FIREFOX) + browser.sidebarAction.open(); + else + chrome.sidePanel.open({ windowId }); + } + else + browser.action.openPopup(); + } + + async function openCollectionsInTab(): Promise + { + logger("openCollectionsInTab"); + + const currentWindow: Windows.Window = await browser.windows.getCurrent({ populate: true }); + + if (currentWindow.incognito) + { + let availableWindows: Windows.Window[] = await browser.windows.getAll({ populate: true }); + + availableWindows = availableWindows.filter(window => + !window.incognito && + window.tabs?.some(i => i.url === browser.runtime.getURL("/sidepanel.html")) + ); + + if (availableWindows.length > 0) + { + const availableTab: Tabs.Tab = availableWindows[0].tabs!.find( + tab => tab.url === browser.runtime.getURL("/sidepanel.html") + )!; + + await browser.tabs.update(availableTab.id, { active: true }); + await browser.windows.update(availableWindows[0].id!, { focused: true }); + + return; + } + + await browser.windows.create({ + url: browser.runtime.getURL("/sidepanel.html"), + focused: true + }); + } + else + { + const collectionTab: Tabs.Tab | undefined = currentWindow.tabs!.find( + tab => tab.url === browser.runtime.getURL("/sidepanel.html") + ); + + if (collectionTab) + await browser.tabs.update(collectionTab.id, { active: true }); + else + await browser.tabs.create({ + url: browser.runtime.getURL("/sidepanel.html"), + active: true, + windowId: currentWindow.id + }); + } + } + + async function saveTabs(closeAfterSave: boolean): Promise + { + logger("saveTabs", closeAfterSave); + + const collection: CollectionItem = await saveTabsToCollection(closeAfterSave); + const [savedCollections, cloudIssue] = await getCollections(); + const newList = [collection, ...savedCollections]; + + await saveCollections(newList, cloudIssue === null, graphicsCache); + + sendMessage("refreshCollections", undefined); + + if (await settings.notifyOnSave.getValue()) + await sendNotification({ + title: i18n.t("notifications.tabs_saved.title"), + message: i18n.t("notifications.tabs_saved.message"), + icon: "/notification_icons/cloud_checkmark.png" + }); + } + } + catch (ex) + { + console.error(ex); + trackError("background_error", ex as Error); + } +}); diff --git a/entrypoints/content.ts b/entrypoints/content.ts new file mode 100644 index 0000000..3145970 --- /dev/null +++ b/entrypoints/content.ts @@ -0,0 +1,46 @@ +import getLogger from "@/utils/getLogger"; +import { sendMessage } from "@/utils/messaging"; + +// This content script is injected into each browser tab. +// It's purpose is to retrive an OpenGraph thumbnail URL from the metadata + +export default defineContentScript({ + matches: [""], + runAt: "document_idle", + main +}); + +const logger = getLogger("contentScript"); + +async function main(): Promise +{ + logger("init"); + + // This method tries to sequentially retrieve thumbnails from all know meta tags. + // It stops on the first thumbnail found. + + // The order of search is: + // 1. + // 2. + // 3. + // 4. + + const thumbnailUrl: string | undefined = + document.querySelector("head meta[property='og:image']")?.content ?? + document.querySelector("head meta[name='twitter:image']")?.content ?? + document.querySelector("head link[rel=thumbnail]")?.href ?? + document.querySelector("head link[rel=image_src]")?.href; + + if (thumbnailUrl) + { + logger(`Found thumbnail for "${document.location.href}"`, thumbnailUrl); + await sendMessage("addThumbnail", { + url: document.location.href, + thumbnail: thumbnailUrl + }); + } + else + logger(`No thumbnail found for "${document.location.href}"`); + + logger("done"); +} diff --git a/entrypoints/options/hooks/useOptionsStyles.ts b/entrypoints/options/hooks/useOptionsStyles.ts new file mode 100644 index 0000000..6c00652 --- /dev/null +++ b/entrypoints/options/hooks/useOptionsStyles.ts @@ -0,0 +1,38 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useOptionsStyles = makeStyles({ + main: + { + display: "grid", + gridTemplateRows: "auto 1fr", + height: "100%" + }, + tabList: + { + flexWrap: "wrap" + }, + article: + { + display: "flex", + flexFlow: "column", + gap: tokens.spacingVerticalMNudge, + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`, + overflowY: "auto" + }, + section: + { + display: "flex", + flexFlow: "column", + alignItems: "flex-start" + }, + buttonFix: + { + minHeight: "32px" + }, + horizontalButtons: + { + display: "flex", + flexWrap: "wrap", + gap: tokens.spacingHorizontalS + } +}); diff --git a/entrypoints/options/index.html b/entrypoints/options/index.html new file mode 100644 index 0000000..423ea30 --- /dev/null +++ b/entrypoints/options/index.html @@ -0,0 +1,24 @@ + + + + + + + Tabs aside | Settings + + + + + + + +
+ + + + diff --git a/entrypoints/options/layouts/AboutSection.tsx b/entrypoints/options/layouts/AboutSection.tsx new file mode 100644 index 0000000..4153502 --- /dev/null +++ b/entrypoints/options/layouts/AboutSection.tsx @@ -0,0 +1,58 @@ +import { BuyMeACoffee20Regular } from "@/assets/BuyMeACoffee20"; +import { bskyLink, buyMeACoffeeLink, githubLinks, storeLink, websiteLink } from "@/data/links"; +import { useBmcStyles } from "@/hooks/useBmcStyles"; +import extLink from "@/utils/extLink"; +import { Body1, Button, Caption1, Link, Subtitle1, Text } from "@fluentui/react-components"; +import { PersonFeedback20Regular } from "@fluentui/react-icons"; +import { useOptionsStyles } from "../hooks/useOptionsStyles"; +import Package from "@/package.json"; + +export default function AboutSection(): React.ReactElement +{ + const cls = useOptionsStyles(); + const bmcCls = useBmcStyles(); + + return ( + <> + + { i18n.t("manifest.name") } + v{ Package.version } + + + + { i18n.t("options_page.about.developed_by") } (@xfox111.net)
+ { i18n.t("options_page.about.licensed_under") } { i18n.t("options_page.about.mit_license") } +
+ + + { i18n.t("options_page.about.translation_cta.text") }
+ + { i18n.t("options_page.about.translation_cta.button") } + +
+ + + { i18n.t("options_page.about.links.website") }
+ { i18n.t("options_page.about.links.source") }
+ { i18n.t("options_page.about.links.changelog") } +
+ +
+ + +
+ + ); +} diff --git a/entrypoints/options/layouts/ActionsSection.tsx b/entrypoints/options/layouts/ActionsSection.tsx new file mode 100644 index 0000000..dcc6832 --- /dev/null +++ b/entrypoints/options/layouts/ActionsSection.tsx @@ -0,0 +1,55 @@ +import useSettings, { SettingsValue } from "@/hooks/useSettings"; +import { Dropdown, Field, Option } from "@fluentui/react-components"; + +export default function ActionsSection(): React.ReactElement +{ + const [saveAction, setSaveAction] = useSettings("defaultSaveAction"); + const [restoreAction, setRestoreAction] = useSettings("defaultRestoreAction"); + + return ( + <> + + setSaveAction(e.optionValue as SaveActionType) } + > + { Object.entries(saveActionOptions).map(([value, label]) => + + ) } + + + + + setRestoreAction(e.optionValue as RestoreActionType) } + > + { Object.entries(restoreActionOptions).map(([value, label]) => + + ) } + + + + ); +} + +type SaveActionType = SettingsValue<"defaultSaveAction">; +type RestoreActionType = SettingsValue<"defaultRestoreAction">; + +const restoreActionOptions: Record = +{ + "open": i18n.t("options_page.actions.options.restore_actions.options.open"), + "restore": i18n.t("options_page.actions.options.restore_actions.options.restore") +}; + +const saveActionOptions: Record = +{ + "set_aside": i18n.t("options_page.actions.options.save_actions.options.set_aside"), + "save": i18n.t("options_page.actions.options.save_actions.options.save") +}; diff --git a/entrypoints/options/layouts/GeneralSection.tsx b/entrypoints/options/layouts/GeneralSection.tsx new file mode 100644 index 0000000..ab1a0fd --- /dev/null +++ b/entrypoints/options/layouts/GeneralSection.tsx @@ -0,0 +1,121 @@ +import useSettings, { SettingsValue } from "@/hooks/useSettings"; +import { Button, Checkbox, Dropdown, Field, Option, OptionOnSelectData } from "@fluentui/react-components"; +import { KeyCommand20Regular } from "@fluentui/react-icons"; +import { useOptionsStyles } from "../hooks/useOptionsStyles"; + +export default function GeneralSection(): React.ReactElement +{ + const [alwaysShowToolbars, setAlwaysShowToolbars] = useSettings("alwaysShowToolbars"); + const [ignorePinned, setIgnorePinned] = useSettings("ignorePinned"); + const [deletePrompt, setDeletePrompt] = useSettings("deletePrompt"); + const [showBadge, setShowBadge] = useSettings("showBadge"); + const [notifyOnSave, setNotifyOnSave] = useSettings("notifyOnSave"); + const [dismissOnLoad, setDismissOnLoad] = useSettings("dismissOnLoad"); + const [listLocation, setListLocation] = useSettings("listLocation"); + const [contextAction, setContextAction] = useSettings("contextAction"); + + const cls = useOptionsStyles(); + + const openShortcutsPage = (): Promise => + browser.tabs.create({ + url: "chrome://extensions/shortcuts", + active: true + }); + + const handleListLocationChange = (_: any, e: OptionOnSelectData): void => + { + if (e.optionValue === "popup" && contextAction !== "open") + setContextAction("open"); + + if (import.meta.env.FIREFOX && e.optionValue !== "sidebar") + browser.sidebarAction.close(); + + setListLocation(e.optionValue as ListLocationType); + }; + + return ( + <> +
+ setAlwaysShowToolbars(e.checked as boolean) } /> + setIgnorePinned(!e.checked) } /> + setDeletePrompt(e.checked as boolean) } /> + setShowBadge(e.checked as boolean) } /> + setNotifyOnSave(e.checked as boolean) } /> + setDismissOnLoad(e.checked as boolean) } /> +
+ + + + { Object.entries(listLocationOptions).map(([key, value]) => + + ) } + + + + + setContextAction(e.optionValue as ContextActionType) } + disabled={ listLocation === "popup" } + > + { Object.entries(contextActionOptions).map(([key, value]) => + key === "context" && import.meta.env.FIREFOX + ? <> : + + ) } + + + + { !import.meta.env.FIREFOX && + + } + + ); +} + +type ListLocationType = SettingsValue<"listLocation">; +type ContextActionType = SettingsValue<"contextAction">; + +const listLocationOptions: Record = +{ + "sidebar": i18n.t("options_page.general.options.list_locations.options.sidebar"), + "popup": i18n.t("options_page.general.options.list_locations.options.popup"), + "tab": i18n.t("options_page.general.options.list_locations.options.tab"), + "pinned": i18n.t("options_page.general.options.list_locations.options.pinned") +}; + +const contextActionOptions: Record = +{ + "action": i18n.t("options_page.general.options.icon_action.options.action"), + "context": i18n.t("options_page.general.options.icon_action.options.context"), + "open": i18n.t("options_page.general.options.icon_action.options.open") +}; diff --git a/entrypoints/options/layouts/StorageSection.tsx b/entrypoints/options/layouts/StorageSection.tsx new file mode 100644 index 0000000..7e97037 --- /dev/null +++ b/entrypoints/options/layouts/StorageSection.tsx @@ -0,0 +1,102 @@ +import { useDialog } from "@/contexts/DialogProvider"; +import { cloudDisabled, setCloudStorage } from "@/features/collectionStorage"; +import { useDangerStyles } from "@/hooks/useDangerStyles"; +import useStorageInfo from "@/hooks/useStorageInfo"; +import { Button, Field, MessageBar, MessageBarBody, MessageBarTitle, ProgressBar } from "@fluentui/react-components"; +import { ArrowDownload20Regular, ArrowUpload20Regular } from "@fluentui/react-icons"; +import { useOptionsStyles } from "../hooks/useOptionsStyles"; +import exportData from "../utils/exportData"; +import importData from "../utils/importData"; + +export default function StorageSection(): React.ReactElement +{ + const { bytesInUse, storageQuota, usedStorageRatio } = useStorageInfo(); + const [importResult, setImportResult] = useState(null); + const [isCloudDisabled, setCloudDisabled] = useState(null!); + + const dialog = useDialog(); + const cls = useOptionsStyles(); + const dangerCls = useDangerStyles(); + + useEffect(() => + { + cloudDisabled.getValue().then(setCloudDisabled); + return cloudDisabled.watch(setCloudDisabled); + }, []); + + const handleImport = (): void => + dialog.pushPrompt({ + title: i18n.t("options_page.storage.import_prompt.title"), + confirmText: i18n.t("options_page.storage.import_prompt.proceed"), + onConfirm: () => importData().then(setImportResult), + content: ( + + + { i18n.t("options_page.storage.import_prompt.warning_title") } + + { i18n.t("options_page.storage.import_prompt.warning_text") } + + + ) + }); + + const handleDisableCloud = (): void => + dialog.pushPrompt({ + title: i18n.t("options_page.storage.disable"), + content: i18n.t("options_page.storage.disable_prompt.text"), + confirmText: i18n.t("options_page.storage.disable_prompt.action"), + destructive: true, + onConfirm: () => setCloudStorage(false) + }); + + return ( + <> + { isCloudDisabled === false && + = 0.8 ? "error" : undefined } + > + + + } + + { isCloudDisabled === true && + + } + +
+ + +
+ + { importResult !== null && + + + { importResult === true ? + i18n.t("options_page.storage.import_results.success") : + i18n.t("options_page.storage.import_results.error") + } + + + } + + { isCloudDisabled === false && +
+ +
+ } + + ); +} diff --git a/entrypoints/options/main.tsx b/entrypoints/options/main.tsx new file mode 100644 index 0000000..9c34224 --- /dev/null +++ b/entrypoints/options/main.tsx @@ -0,0 +1,47 @@ +import App from "@/App.tsx"; +import "@/assets/global.css"; +import { Tab, TabList } from "@fluentui/react-components"; +import ReactDOM from "react-dom/client"; +import { useOptionsStyles } from "./hooks/useOptionsStyles.ts"; +import AboutSection from "./layouts/AboutSection.tsx"; +import ActionsSection from "./layouts/ActionsSection.tsx"; +import GeneralSection from "./layouts/GeneralSection.tsx"; +import StorageSection from "./layouts/StorageSection.tsx"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + +); + +analytics.page("options_page"); + +function OptionsPage(): React.ReactElement +{ + const [selection, setSelection] = useState("general"); + const cls = useOptionsStyles(); + + return ( +
+ setSelection(data.value as SelectionType) } + > + { i18n.t("options_page.general.title") } + { i18n.t("options_page.actions.title") } + { i18n.t("options_page.storage.title") } + { i18n.t("options_page.about.title") } + + +
+ { selection === "general" && } + { selection === "actions" && } + { selection === "storage" && } + { selection === "about" && } +
+
+ ); +} + +type SelectionType = "general" | "actions" | "storage" | "about"; diff --git a/entrypoints/options/utils/exportData.ts b/entrypoints/options/utils/exportData.ts new file mode 100644 index 0000000..79db01e --- /dev/null +++ b/entrypoints/options/utils/exportData.ts @@ -0,0 +1,19 @@ +export default async function exportData(): Promise +{ + const data: string = JSON.stringify({ + local: await browser.storage.local.get(null), + sync: await browser.storage.sync.get(null) + }); + const blob: Blob = new Blob([data], { type: "application/json" }); + + const element: HTMLAnchorElement = document.createElement("a"); + element.style.display = "none"; + element.href = URL.createObjectURL(blob); + element.setAttribute("download", "tabs-aside_data.json"); + + document.body.appendChild(element); + element.click(); + + URL.revokeObjectURL(element.href); + document.body.removeChild(element); +}; diff --git a/entrypoints/options/utils/importData.ts b/entrypoints/options/utils/importData.ts new file mode 100644 index 0000000..5f3e3f2 --- /dev/null +++ b/entrypoints/options/utils/importData.ts @@ -0,0 +1,56 @@ +import { sendMessage } from "@/utils/messaging"; + +export default async function importData(): Promise +{ + const element: HTMLInputElement = document.createElement("input"); + element.style.display = "none"; + element.hidden = true; + element.type = "file"; + element.accept = ".json"; + + document.body.appendChild(element); + element.click(); + + await new Promise(resolve => + { + const listener = () => + { + element.removeEventListener("input", listener); + resolve(null); + }; + element.addEventListener("input", listener); + }); + + if (!element.files || element.files.length < 1) + return null; + + const file: File = element.files[0]; + const content: string = await file.text(); + + document.body.removeChild(element); + + try + { + const data: any = JSON.parse(content); + + if (data.local) + await browser.storage.local.set(data.local); + + if (data.sync) + { + if (import.meta.env.FIREFOX && data.sync.contextAction === "context") + data.sync.contextAction = "open"; + + await browser.storage.sync.set(data.sync); + } + } + catch (error) + { + console.error("Failed to parse JSON", error); + return false; + } + + sendMessage("refreshCollections", undefined); + + return true; +} diff --git a/entrypoints/popup.html b/entrypoints/popup.html new file mode 100644 index 0000000..4318975 --- /dev/null +++ b/entrypoints/popup.html @@ -0,0 +1,22 @@ + + + + + + + Tabs aside + + + + +
+ + + + diff --git a/entrypoints/sidepanel/components/CollectionView.styles.ts b/entrypoints/sidepanel/components/CollectionView.styles.ts new file mode 100644 index 0000000..c214b74 --- /dev/null +++ b/entrypoints/sidepanel/components/CollectionView.styles.ts @@ -0,0 +1,102 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useStyles_CollectionView = makeStyles({ + root: + { + backgroundColor: tokens.colorNeutralBackground1, + border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`, + borderRadius: tokens.borderRadiusLarge, + display: "flex", + flexFlow: "column", + + "--border": tokens.colorNeutralForeground1, + + "&:hover .CollectionView__toolbar, &:focus-within .CollectionView__toolbar": + { + display: "flex" + }, + + "&:hover": + { + boxShadow: tokens.shadow4 + } + }, + color: + { + border: `${tokens.strokeWidthThick} solid var(--border)` + }, + verticalRoot: + { + maxHeight: "560px" + }, + empty: + { + display: "flex", + flexFlow: "column", + flexGrow: 1, + margin: `${tokens.spacingVerticalNone} ${tokens.spacingHorizontalSNudge}`, + marginBottom: tokens.spacingVerticalSNudge, + alignItems: "center", + justifyContent: "center", + gap: tokens.spacingVerticalS, + padding: `${tokens.spacingVerticalXL} ${tokens.spacingHorizontalL}`, + color: tokens.colorNeutralForeground3, + height: "144px" + }, + emptyText: + { + display: "flex", + flexFlow: "column", + alignItems: "center", + gap: tokens.spacingVerticalXS + }, + emptyCaption: + { + display: "flex", + flexWrap: "wrap", + alignItems: "center", + justifyContent: "center", + columnGap: tokens.spacingHorizontalXS + }, + list: + { + display: "grid", + padding: `${tokens.spacingVerticalXS} ${tokens.spacingHorizontalS}`, + columnGap: tokens.spacingHorizontalS, + rowGap: tokens.spacingHorizontalSNudge, + overflowX: "auto", + alignItems: "flex-end", + alignSelf: "flex-start", + maxWidth: "100%", + gridAutoFlow: "column" + }, + verticalList: + { + gridAutoFlow: "row", + width: "100%", + paddingBottom: tokens.spacingVerticalS, + gridAutoRows: import.meta.env.FIREFOX ? "min-content" : undefined + }, + dragOverlay: + { + cursor: "grabbing !important", + transform: "scale(1.05)", + boxShadow: `${tokens.shadow16} !important`, + "& > div": + { + pointerEvents: "none" + } + }, + sorting: + { + pointerEvents: "none" + }, + dragging: + { + visibility: "hidden" + }, + draggingOver: + { + backgroundColor: tokens.colorBrandBackground2 + } +}); diff --git a/entrypoints/sidepanel/components/CollectionView.tsx b/entrypoints/sidepanel/components/CollectionView.tsx new file mode 100644 index 0000000..40a5b69 --- /dev/null +++ b/entrypoints/sidepanel/components/CollectionView.tsx @@ -0,0 +1,88 @@ +import CollectionHeader from "@/entrypoints/sidepanel/components/collections/CollectionHeader"; +import useDndItem from "@/entrypoints/sidepanel/hooks/useDndItem"; +import { useGroupColors } from "@/hooks/useGroupColors"; +import { CollectionItem } from "@/models/CollectionModels"; +import { horizontalListSortingStrategy, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"; +import { Body1Strong, mergeClasses } from "@fluentui/react-components"; +import { CollectionsRegular } from "@fluentui/react-icons"; +import { ReactElement } from "react"; +import CollectionContext from "../contexts/CollectionContext"; +import { useCollections } from "../contexts/CollectionsProvider"; +import { useStyles_CollectionView } from "./CollectionView.styles"; +import GroupView from "./GroupView"; +import TabView from "./TabView"; + +export default function CollectionView({ collection, index: collectionIndex, dragOverlay }: CollectionViewProps): ReactElement +{ + const { tilesView } = useCollections(); + const { + setNodeRef, + nodeProps, + setActivatorNodeRef, + activatorProps, + activeItem, isCurrentlySorting, isBeingDragged, isActiveOverThis: isOver + } = useDndItem({ id: collectionIndex.toString(), data: { indices: [collectionIndex], item: collection } }); + + const isActiveOverThis: boolean = isOver && activeItem?.item.type !== "collection"; + + const tabCount: number = useMemo(() => collection.items.flatMap(i => i.type === "group" ? i.items : i).length, [collection.items]); + const hasPinnedGroup: boolean = useMemo(() => collection.items.length > 0 && + (collection.items[0].type === "group" && collection.items[0].pinned === true), [collection.items]); + + const cls = useStyles_CollectionView(); + const colorCls = useGroupColors(); + + return ( + +
+ + + + { (!activeItem || activeItem.item.type !== "collection") && !dragOverlay && + <> + { collection.items.length < 1 ? +
+ + { i18n.t("collections.empty") } +
+ : +
+ [collectionIndex, index].join("/")) } + strategy={ tilesView ? horizontalListSortingStrategy : verticalListSortingStrategy } + > + { collection.items.map((i, index) => + i.type === "group" ? + + : + + ) } + +
+ } + + } +
+
+ ); +} + +export type CollectionViewProps = + { + collection: CollectionItem; + index: number; + dragOverlay?: boolean; + }; diff --git a/entrypoints/sidepanel/components/EditDialog.styles.tsx b/entrypoints/sidepanel/components/EditDialog.styles.tsx new file mode 100644 index 0000000..051efef --- /dev/null +++ b/entrypoints/sidepanel/components/EditDialog.styles.tsx @@ -0,0 +1,40 @@ +import { makeStyles, shorthands, tokens } from "@fluentui/react-components"; + +export const useStyles_EditDialog = makeStyles({ + surface: + { + "--border": tokens.colorTransparentStroke, + ...shorthands.borderWidth(tokens.strokeWidthThick), + ...shorthands.borderColor("var(--border)") + }, + content: + { + display: "flex", + flexFlow: "column", + gap: tokens.spacingVerticalS + }, + colorPicker: + { + display: "flex", + flexWrap: "wrap", + rowGap: tokens.spacingVerticalS, + columnGap: tokens.spacingVerticalS + }, + colorButton: + { + "&[aria-pressed=true]": + { + color: "var(--text) !important", + backgroundColor: "var(--border) !important", + + "& .fui-Button__icon": + { + color: "var(--text)" + } + } + }, + colorButton_icon: + { + color: "var(--border)" + } +}); diff --git a/entrypoints/sidepanel/components/EditDialog.tsx b/entrypoints/sidepanel/components/EditDialog.tsx new file mode 100644 index 0000000..c80355e --- /dev/null +++ b/entrypoints/sidepanel/components/EditDialog.tsx @@ -0,0 +1,157 @@ +import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionTitle"; +import { track } from "@/features/analytics"; +import { useGroupColors } from "@/hooks/useGroupColors"; +import { CollectionItem, GroupItem } from "@/models/CollectionModels"; +import * as fui from "@fluentui/react-components"; +import { Circle20Filled, CircleOff20Regular, Pin20Filled, Rename20Regular } from "@fluentui/react-icons"; +import { ReactElement } from "react"; +import { useStyles_EditDialog } from "./EditDialog.styles"; + +export default function EditDialog(props: GroupEditDialogProps): ReactElement +{ + const [title, setTitle] = useState( + (props.type === "collection" + ? props.collection?.title : + (props.group?.pinned !== true ? props.group?.title : "")) + ?? "" + ); + + const [color, setColor] = useState( + props.type === "collection" + ? props.collection?.color : + props.group?.pinned === true ? "pinned" : (props.group?.color ?? "blue") + ); + + const cls = useStyles_EditDialog(); + const colorCls = useGroupColors(); + const horizontalNavigationAttributes = fui.useArrowNavigationGroup({ axis: "horizontal" }); + + const onSubmit = (e: React.FormEvent) => + { + e.preventDefault(); + handleSave(); + }; + + const handleSave = () => + { + if (props.type === "collection" ? props.collection !== null : props.group !== null) + track("item_edited", { type: props.type }); + else + track("item_created", { type: props.type }); + + if (props.type === "collection") + props.onSave({ + type: "collection", + timestamp: props.collection?.timestamp ?? Date.now(), + color: (color === "pinned") ? undefined : color!, + title: title ? title : undefined, + items: props.collection?.items ?? [] + }); + else if (color === "pinned") + props.onSave({ + type: "group", + pinned: true, + items: props.group?.items ?? [] + }); + else + props.onSave({ + type: "group", + pinned: false, + color: color!, + title: title ? title : undefined, + items: props.group?.items ?? [] + }); + }; + + return ( + +
+ + + { + props.type === "collection" ? + i18n.t(`dialogs.edit.title.${props.collection ? "edit" : "new"}_collection`) : + i18n.t(`dialogs.edit.title.${props.group ? "edit" : "new"}_group`) + } + + + +
+ + } + disabled={ color === "pinned" } + placeholder={ + props.type === "collection" ? getCollectionTitle(props.collection, true) : "" + } + value={ color === "pinned" ? i18n.t("groups.pinned") : title } + onChange={ (_, e) => setTitle(e.value) } /> + + +
+ { (props.type === "group" && (!props.hidePinned || props.group?.pinned)) && + setColor("pinned") } + icon={ } + shape="circular" + > + { i18n.t("groups.pinned") } + + } + { props.type === "collection" && + setColor(undefined) } + icon={ } + shape="circular" + > + { i18n.t("colors.none") } + + } + { Object.keys(colorCls).map(i => + setColor(i as chrome.tabGroups.ColorEnum) } + className={ fui.mergeClasses(cls.colorButton, colorCls[i as chrome.tabGroups.ColorEnum]) } + icon={ { + className: cls.colorButton_icon, + children: + } } + key={ i } + shape="circular" + > + { i18n.t(`colors.${i as chrome.tabGroups.ColorEnum}`) } + + ) } +
+
+
+
+ + + + { i18n.t("common.actions.save") } + + + { i18n.t("common.actions.cancel") } + + +
+
+
+ ); +} + +export type GroupEditDialogProps = + { + type: "collection"; + collection?: CollectionItem; + onSave: (item: CollectionItem) => void; + } | + { + type: "group"; + hidePinned?: boolean; + group?: GroupItem; + onSave: (item: GroupItem) => void; + }; diff --git a/entrypoints/sidepanel/components/GroupView.styles.ts b/entrypoints/sidepanel/components/GroupView.styles.ts new file mode 100644 index 0000000..b19cff4 --- /dev/null +++ b/entrypoints/sidepanel/components/GroupView.styles.ts @@ -0,0 +1,154 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useStyles_GroupView = makeStyles({ + root: + { + display: "flex", + flexFlow: "column", + alignSelf: "normal", + + padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalS}`, + paddingBottom: tokens.spacingVerticalNone, + borderRadius: tokens.borderRadiusLarge, + + "&:hover .GroupView-toolbar, &:focus-within .GroupView-toolbar": + { + visibility: "visible" + }, + + "&:hover": + { + backgroundColor: tokens.colorNeutralBackground1Hover + } + }, + header: + { + display: "flex", + justifyContent: "space-between", + alignItems: "flex-end", + gap: tokens.spacingHorizontalM, + + borderBottom: `${tokens.strokeWidthThick} solid var(--border)`, + borderBottomLeftRadius: tokens.borderRadiusLarge + }, + verticalHeader: + { + borderBottomLeftRadius: tokens.borderRadiusNone + }, + title: + { + display: "grid", + gridAutoFlow: "column", + alignItems: "center", + minHeight: "12px", + minWidth: "24px", + gap: tokens.spacingHorizontalXS, + width: "max-content", + maxWidth: "160px", + + padding: `${tokens.spacingVerticalXXS} ${tokens.spacingHorizontalS}`, + paddingBottom: tokens.spacingVerticalXS, + marginBottom: "-2px", + + border: `${tokens.strokeWidthThick} solid var(--border)`, + borderRadius: `${tokens.borderRadiusLarge} ${tokens.borderRadiusLarge} ${tokens.borderRadiusNone} ${tokens.borderRadiusLarge}`, + borderBottom: "none", + backgroundColor: "var(--border)", + color: "var(--text)" + }, + verticalTitle: + { + borderBottomLeftRadius: tokens.borderRadiusNone + }, + pinned: + { + backgroundColor: "transparent" + }, + toolbar: + { + display: "flex", + gap: tokens.spacingHorizontalS, + visibility: "hidden" + }, + showToolbar: + { + visibility: "visible" + }, + openAllLink: + { + whiteSpace: "nowrap" + }, + empty: + { + display: "flex", + flexFlow: "column", + alignItems: "center", + justifyContent: "center", + color: tokens.colorNeutralForeground3, + minWidth: "160px", + height: "120px", + marginBottom: tokens.spacingVerticalSNudge + }, + verticalEmpty: + { + height: "auto", + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}` + }, + list: + { + display: "flex", + columnGap: tokens.spacingHorizontalS, + rowGap: tokens.spacingHorizontalSNudge, + height: "100%", + position: "relative" + }, + verticalList: + { + flexFlow: "column" + }, + verticalListCollapsed: + { + maxHeight: "136px", + overflow: "clip" + }, + horizontalListCollapsed: + { + maxWidth: "400px", + overflow: "clip" + }, + listContainer: + { + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalXS}`, + paddingBottom: tokens.spacingVerticalNone, + height: "100%" + }, + verticalListContainer: + { + borderLeft: `${tokens.strokeWidthThick} solid var(--border)`, + padding: tokens.spacingVerticalSNudge, + marginBottom: tokens.spacingVerticalSNudge, + borderTopLeftRadius: tokens.borderRadiusNone, + borderBottomLeftRadius: tokens.borderRadiusNone, + borderTop: "none" + }, + pinnedColor: + { + "--border": tokens.colorNeutralStrokeAccessible, + "--text": tokens.colorNeutralForeground1 + }, + dragOverlay: + { + backgroundColor: tokens.colorNeutralBackground1Hover, + transform: "scale(1.05)", + cursor: "grabbing !important", + boxShadow: `${tokens.shadow16} !important`, + "& > div": + { + pointerEvents: "none" + } + }, + dragging: + { + visibility: "hidden" + } +}); diff --git a/entrypoints/sidepanel/components/GroupView.tsx b/entrypoints/sidepanel/components/GroupView.tsx new file mode 100644 index 0000000..c1d5752 --- /dev/null +++ b/entrypoints/sidepanel/components/GroupView.tsx @@ -0,0 +1,120 @@ +import GroupContext from "@/entrypoints/sidepanel/contexts/GroupContext"; +import useDndItem from "@/entrypoints/sidepanel/hooks/useDndItem"; +import { openGroup } from "@/entrypoints/sidepanel/utils/opener"; +import { useGroupColors } from "@/hooks/useGroupColors"; +import useSettings from "@/hooks/useSettings"; +import { GroupItem } from "@/models/CollectionModels"; +import { horizontalListSortingStrategy, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"; +import { Caption1Strong, Link, mergeClasses, Tooltip } from "@fluentui/react-components"; +import { Pin16Filled, WebAssetRegular } from "@fluentui/react-icons"; +import { ReactElement } from "react"; +import { useCollections } from "../contexts/CollectionsProvider"; +import GroupDropZone from "./collections/GroupDropZone"; +import GroupMoreMenu from "./collections/GroupMoreMenu"; +import { useStyles_GroupView } from "./GroupView.styles"; +import TabView from "./TabView"; + +export default function GroupView({ group, indices, dragOverlay }: GroupViewProps): ReactElement +{ + const [alwaysShowToolbars] = useSettings("alwaysShowToolbars"); + const { tilesView } = useCollections(); + + const groupId: string = useMemo(() => indices.join("/"), [indices]); + + const { + setNodeRef, nodeProps, + setActivatorNodeRef, activatorProps, + activeItem: active, isBeingDragged + } = useDndItem({ id: groupId, data: { indices, item: group }, disabled: group.pinned }); + + const disableDropZone: boolean = useMemo( + () => active !== null && + (active.item.type !== "tab" || (active.indices[0] === indices[0] && active.indices[1] === indices[1])), + [active, indices]); + const disableSorting: boolean = useMemo( + () => active !== null && (active.item.type !== "tab" || active.indices[0] !== indices[0]), + [active, indices]); + + const cls = useStyles_GroupView(); + const colorCls = useGroupColors(); + + return ( + +
+
+ +
+ { group.pinned === true ? + <> + + { i18n.t("groups.pinned") } + + : + + { group.title } + + } +
+ +
+ { group.items.length > 0 && + openGroup(group, false) }> + { i18n.t("groups.open") } + + } + + +
+
+ + + { group.items.length < 1 ? +
+ + { i18n.t("groups.empty") } +
+ : +
+ [...indices, index].join("/")) } + disabled={ disableSorting } + strategy={ !tilesView ? verticalListSortingStrategy : horizontalListSortingStrategy } + > + { group.items.map((i, index) => + + ) } + +
+ } +
+
+
+ ); +} + +export type GroupViewProps = + { + group: GroupItem; + indices: number[]; + dragOverlay?: boolean; + }; diff --git a/entrypoints/sidepanel/components/TabView.styles.ts b/entrypoints/sidepanel/components/TabView.styles.ts new file mode 100644 index 0000000..2b85a56 --- /dev/null +++ b/entrypoints/sidepanel/components/TabView.styles.ts @@ -0,0 +1,111 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useStyles_TabView = makeStyles({ + root: + { + display: "grid", + position: "relative", + + width: "160px", + height: "120px", + flexShrink: 0, + marginBottom: tokens.spacingVerticalSNudge, + + border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke3}`, + borderRadius: tokens.borderRadiusMedium, + backgroundColor: tokens.colorNeutralBackground1, + + cursor: "pointer", + textDecoration: "none !important", + userSelect: "none", + + "&:hover button, &:focus-within button": + { + display: "inline-flex" + }, + + "&:hover": + { + boxShadow: tokens.shadow4 + }, + + "&:focus-visible": + { + outline: `2px solid ${tokens.colorStrokeFocus2}` + } + }, + listView: + { + width: "100%", + height: "min-content", + marginBottom: tokens.spacingVerticalNone + }, + image: + { + zIndex: 0, + position: "absolute", + height: "100%", + width: "100%", + + borderRadius: tokens.borderRadiusMedium, + objectFit: "cover" + }, + header: + { + zIndex: 1, + alignSelf: "end", + minHeight: "32px", + + display: "grid", + gridTemplateColumns: "auto 1fr auto", + alignItems: "center", + + borderBottomLeftRadius: tokens.borderRadiusMedium, + borderBottomRightRadius: tokens.borderRadiusMedium, + + backgroundColor: tokens.colorSubtleBackgroundLightAlphaHover, + color: tokens.colorNeutralForeground1, + "-webkit-backdrop-filer": "blur(4px)", + backdropFilter: "blur(4px)" + }, + icon: + { + cursor: "grab", + padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalSNudge}`, + height: "32px", + boxSizing: "border-box", + + "&:active": + { + cursor: "grabbing" + } + }, + title: + { + overflowX: "hidden", + justifySelf: "start", + maxWidth: "100%" + }, + deleteButton: + { + display: "none" + }, + showDeleteButton: + { + display: "inline-flex" + }, + dragOverlay: + { + cursor: "grabbing !important", + transform: "scale(1.05)", + boxShadow: `${tokens.shadow16} !important`, + "& > div": + { + pointerEvents: "none" + } + }, + dragging: + { + visibility: "hidden" + } +}); diff --git a/entrypoints/sidepanel/components/TabView.tsx b/entrypoints/sidepanel/components/TabView.tsx new file mode 100644 index 0000000..9937b63 --- /dev/null +++ b/entrypoints/sidepanel/components/TabView.tsx @@ -0,0 +1,110 @@ +import faviconPlaceholder from "@/assets/FaviconPlaceholder.svg"; +import pagePlaceholder from "@/assets/PagePlaceholder.svg"; +import { useDialog } from "@/contexts/DialogProvider"; +import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider"; +import useDndItem from "@/entrypoints/sidepanel/hooks/useDndItem"; +import useSettings from "@/hooks/useSettings"; +import { TabItem } from "@/models/CollectionModels"; +import { Button, Caption1, Link, mergeClasses, Tooltip } from "@fluentui/react-components"; +import { Dismiss20Regular } from "@fluentui/react-icons"; +import { MouseEventHandler, ReactElement } from "react"; +import { useStyles_TabView } from "./TabView.styles"; +import CollectionContext, { CollectionContextType } from "../contexts/CollectionContext"; + +export default function TabView({ tab, indices, dragOverlay }: TabViewProps): ReactElement +{ + const { removeItem, graphics, tilesView } = useCollections(); + const { collection } = useContext(CollectionContext); + const { + setNodeRef, setActivatorNodeRef, + nodeProps, activatorProps, isBeingDragged + } = useDndItem({ id: indices.join("/"), data: { indices, item: tab } }); + const dialog = useDialog(); + + const [deletePrompt] = useSettings("deletePrompt"); + const [showToolbar] = useSettings("alwaysShowToolbars"); + + const cls = useStyles_TabView(); + + const handleDelete: MouseEventHandler = (args) => + { + args.preventDefault(); + args.stopPropagation(); + + const removeIndex: number[] = [collection.timestamp, ...indices.slice(1)]; + + if (deletePrompt) + dialog.pushPrompt({ + title: i18n.t("tabs.delete"), + content: i18n.t("common.delete_prompt"), + destructive: true, + confirmText: i18n.t("common.actions.delete"), + onConfirm: () => removeItem(...removeIndex) + }); + else + removeItem(...removeIndex); + }; + + const handleClick: MouseEventHandler = (args) => + { + args.preventDefault(); + browser.tabs.create({ url: tab.url, active: true }); + }; + + const handleAuxClick: MouseEventHandler = (args) => + { + args.preventDefault(); + + if (args.button === 1) + browser.tabs.create({ url: tab.url, active: false }); + }; + + return ( + + { tilesView && + e.currentTarget.src = pagePlaceholder } + className={ cls.image } draggable={ false } /> + } + +
+ e.currentTarget.src = faviconPlaceholder } + className={ cls.icon } draggable={ false } /> + + + + { tab.title ?? tab.url } + + + + +
+ + ); +} + +export type TabViewProps = + { + tab: TabItem; + indices: number[]; + dragOverlay?: boolean; + }; diff --git a/entrypoints/sidepanel/components/collections/CollectionHeader.tsx b/entrypoints/sidepanel/components/collections/CollectionHeader.tsx new file mode 100644 index 0000000..e1364a0 --- /dev/null +++ b/entrypoints/sidepanel/components/collections/CollectionHeader.tsx @@ -0,0 +1,110 @@ +import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionTitle"; +import getSelectedTabs from "@/entrypoints/sidepanel/utils/getSelectedTabs"; +import useSettings from "@/hooks/useSettings"; +import { GroupItem, TabItem } from "@/models/CollectionModels"; +import { Button, Caption1, makeStyles, mergeClasses, Subtitle2, tokens, Tooltip } from "@fluentui/react-components"; +import { Add20Filled, Add20Regular, bundleIcon } from "@fluentui/react-icons"; +import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext"; +import { useCollections } from "../../contexts/CollectionsProvider"; +import CollectionMoreButton from "./CollectionMoreButton"; +import OpenCollectionButton from "./OpenCollectionButton"; +import saveTabsToCollection from "@/utils/saveTabsToCollection"; + +export default function CollectionHeader({ dragHandleRef, dragHandleProps }: CollectionHeaderProps): React.ReactElement +{ + const [contextOpen, setContextOpen] = useState(false); + const [listLocation] = useSettings("listLocation"); + const isTab: boolean = listLocation === "tab" || listLocation === "pinned"; + const { updateCollection } = useCollections(); + const { tabCount, collection } = useContext(CollectionContext); + const [alwaysShowToolbars] = useSettings("alwaysShowToolbars"); + + const AddIcon = bundleIcon(Add20Filled, Add20Regular); + + const handleAddSelected = async () => + { + const newTabs: (TabItem | GroupItem)[] = isTab ? + (await saveTabsToCollection(false)).items : + await getSelectedTabs(); + updateCollection({ ...collection, items: [...collection.items, ...newTabs] }, collection.timestamp); + }; + + const cls = useStyles(); + + return ( +
+
+ + + { getCollectionTitle(collection) } + + + + + { i18n.t("collections.tabs_count", [tabCount]) } + +
+ +
+ { tabCount < 1 ? + + : + setContextOpen(e.open) } /> + } + + setContextOpen(e.open) } /> +
+
+ ); +} + +export type CollectionHeaderProps = + { + dragHandleRef?: React.LegacyRef; + dragHandleProps?: React.HTMLAttributes; + }; + +const useStyles = makeStyles({ + header: + { + color: "var(--border)", + display: "grid", + gridTemplateColumns: "1fr auto", + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalS}`, + paddingBottom: tokens.spacingVerticalS + }, + title: + { + display: "flex", + flexFlow: "column", + alignItems: "flex-start", + overflow: "hidden" + }, + titleText: + { + maxWidth: "100%" + }, + toolbar: + { + display: "none", + gap: tokens.spacingHorizontalS, + alignItems: "flex-start" + }, + showToolbar: + { + display: "flex" + } +}); diff --git a/entrypoints/sidepanel/components/collections/CollectionMoreButton.tsx b/entrypoints/sidepanel/components/collections/CollectionMoreButton.tsx new file mode 100644 index 0000000..db0fc24 --- /dev/null +++ b/entrypoints/sidepanel/components/collections/CollectionMoreButton.tsx @@ -0,0 +1,98 @@ +import { useDialog } from "@/contexts/DialogProvider"; +import { useDangerStyles } from "@/hooks/useDangerStyles"; +import useSettings from "@/hooks/useSettings"; +import { Button, Menu, MenuDivider, MenuItem, MenuList, MenuOpenChangeData, MenuOpenEvent, MenuPopover, MenuTrigger, Tooltip } from "@fluentui/react-components"; +import * as ic from "@fluentui/react-icons"; +import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext"; +import { useCollections } from "../../contexts/CollectionsProvider"; +import exportCollectionToBookmarks from "../../utils/exportCollectionToBookmarks"; +import EditDialog from "../EditDialog"; + +export default function CollectionMoreButton({ onAddSelected, onOpenChange }: CollectionMoreButtonProps): React.ReactElement +{ + const [listLocation] = useSettings("listLocation"); + const isTab: boolean = listLocation === "tab" || listLocation === "pinned"; + const { removeItem, updateCollection } = useCollections(); + const { tabCount, hasPinnedGroup, collection } = useContext(CollectionContext); + const dialog = useDialog(); + const [deletePrompt] = useSettings("deletePrompt"); + + const AddIcon = ic.bundleIcon(ic.Add20Filled, ic.Add20Regular); + const GroupIcon = ic.bundleIcon(ic.GroupList20Filled, ic.GroupList20Regular); + const EditIcon = ic.bundleIcon(ic.Edit20Filled, ic.Edit20Regular); + const DeleteIcon = ic.bundleIcon(ic.Delete20Filled, ic.Delete20Regular); + const BookmarkIcon = ic.bundleIcon(ic.BookmarkAdd20Filled, ic.BookmarkAdd20Regular); + + const dangerCls = useDangerStyles(); + + const handleDelete = () => + { + if (deletePrompt) + dialog.pushPrompt({ + title: i18n.t("collections.menu.delete"), + content: i18n.t("common.delete_prompt"), + destructive: true, + confirmText: i18n.t("common.actions.delete"), + onConfirm: () => removeItem(collection.timestamp) + }); + else + removeItem(collection.timestamp); + }; + + const handleEdit = () => + dialog.pushCustom( + updateCollection(item, collection.timestamp) } /> + ); + + const handleCreateGroup = () => + dialog.pushCustom( + updateCollection({ ...collection, items: [...collection.items, group] }, collection.timestamp) } /> + ); + + return ( + + + + + ); +} + +export type CollectionMoreButtonProps = + { + onAddSelected?: () => void; + onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; + }; diff --git a/entrypoints/sidepanel/components/collections/GroupDropZone.tsx b/entrypoints/sidepanel/components/collections/GroupDropZone.tsx new file mode 100644 index 0000000..0a8bbab --- /dev/null +++ b/entrypoints/sidepanel/components/collections/GroupDropZone.tsx @@ -0,0 +1,45 @@ +import { useDroppable } from "@dnd-kit/core"; +import { makeStyles, mergeClasses, tokens } from "@fluentui/react-components"; +import GroupContext, { GroupContextType } from "../../contexts/GroupContext"; + +export default function GroupDropZone({ disabled, ...props }: DropZoneProps): React.ReactElement +{ + const { group, indices } = useContext(GroupContext); + const id: string = indices.join("/") + "_dropzone"; + const { isOver, setNodeRef, active } = useDroppable({ id, data: { indices, item: group }, disabled }); + + const isDragging = !disabled && active !== null; + const cls = useStyles(); + + return ( +
+ { props.children } +
+ ); +} + +export type DropZoneProps = React.DetailedHTMLProps, HTMLDivElement> + & { + disabled?: boolean; + }; + +const useStyles = makeStyles({ + root: + { + borderRadius: tokens.borderRadiusLarge, + borderTopRightRadius: tokens.borderRadiusNone, + border: `${tokens.strokeWidthThin} solid transparent` + }, + over: + { + backgroundColor: tokens.colorBrandBackground2, + border: `${tokens.strokeWidthThin} solid ${tokens.colorBrandStroke1}` + }, + dragging: + { + border: `${tokens.strokeWidthThin} dashed ${tokens.colorNeutralStroke1}` + } +}); diff --git a/entrypoints/sidepanel/components/collections/GroupMoreMenu.tsx b/entrypoints/sidepanel/components/collections/GroupMoreMenu.tsx new file mode 100644 index 0000000..e734eba --- /dev/null +++ b/entrypoints/sidepanel/components/collections/GroupMoreMenu.tsx @@ -0,0 +1,115 @@ +import { useDialog } from "@/contexts/DialogProvider"; +import EditDialog from "@/entrypoints/sidepanel/components/EditDialog"; +import CollectionContext, { CollectionContextType } from "@/entrypoints/sidepanel/contexts/CollectionContext"; +import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider"; +import GroupContext, { GroupContextType } from "@/entrypoints/sidepanel/contexts/GroupContext"; +import getSelectedTabs from "@/entrypoints/sidepanel/utils/getSelectedTabs"; +import { useDangerStyles } from "@/hooks/useDangerStyles"; +import useSettings from "@/hooks/useSettings"; +import { TabItem } from "@/models/CollectionModels"; +import { sendMessage } from "@/utils/messaging"; +import saveTabsToCollection from "@/utils/saveTabsToCollection"; +import { Button, Menu, MenuItem, MenuList, MenuPopover, MenuTrigger, Tooltip } from "@fluentui/react-components"; +import * as ic from "@fluentui/react-icons"; +import { ReactElement } from "react"; +import { openGroup } from "../../utils/opener"; + +export default function GroupMoreMenu(): ReactElement +{ + const [listLocation] = useSettings("listLocation"); + const isTab: boolean = listLocation === "tab" || listLocation === "pinned"; + const { group, indices } = useContext(GroupContext); + const { hasPinnedGroup, collection } = useContext(CollectionContext); + const [deletePrompt] = useSettings("deletePrompt"); + const dialog = useDialog(); + const { updateGroup, removeItem, ungroup } = useCollections(); + + const dangerCls = useDangerStyles(); + + const AddIcon = ic.bundleIcon(ic.Add20Filled, ic.Add20Regular); + const UngroupIcon = ic.bundleIcon(ic.FullScreenMaximize20Filled, ic.FullScreenMaximize20Regular); + const EditIcon = ic.bundleIcon(ic.Edit20Filled, ic.Edit20Regular); + const NewWindowIcon = ic.bundleIcon(ic.WindowNew20Filled, ic.WindowNew20Regular); + const DeleteIcon = ic.bundleIcon(ic.Delete20Filled, ic.Delete20Regular); + + const handleDelete = () => + { + const removeIndex: number[] = [collection.timestamp, ...indices.slice(1)]; + + if (deletePrompt) + dialog.pushPrompt({ + title: i18n.t("groups.menu.delete"), + content: i18n.t("common.delete_prompt"), + confirmText: i18n.t("common.actions.delete"), + destructive: true, + onConfirm: () => removeItem(...removeIndex) + }); + else + removeItem(...removeIndex); + }; + + const handleEdit = () => + dialog.pushCustom( + updateGroup(item, collection.timestamp, indices[1]) } /> + ); + + const openGroupInNewWindow = () => + { + if (import.meta.env.FIREFOX && listLocation === "popup") + sendMessage("openGroup", { group, newWindow: true }); + else + openGroup(group, true); + }; + + const handleAddSelected = async () => + { + const newTabs: TabItem[] = isTab ? + (await saveTabsToCollection(false)).items.flatMap(i => i.type === "tab" ? i : i.items) : + await getSelectedTabs(); + updateGroup({ ...group, items: [...group.items, ...newTabs] }, collection.timestamp, indices[1]); + }; + + return ( + + + + + ); +} diff --git a/entrypoints/sidepanel/components/collections/OpenCollectionButton.tsx b/entrypoints/sidepanel/components/collections/OpenCollectionButton.tsx new file mode 100644 index 0000000..58e225b --- /dev/null +++ b/entrypoints/sidepanel/components/collections/OpenCollectionButton.tsx @@ -0,0 +1,111 @@ +import { useDialog } from "@/contexts/DialogProvider"; +import useSettings from "@/hooks/useSettings"; +import browserLocaleKey from "@/utils/browserLocaleKey"; +import { sendMessage } from "@/utils/messaging"; +import { Menu, MenuButtonProps, MenuItem, MenuList, MenuOpenChangeData, MenuOpenEvent, MenuPopover, MenuTrigger, SplitButton } from "@fluentui/react-components"; +import * as ic from "@fluentui/react-icons"; +import CollectionContext, { CollectionContextType } from "../../contexts/CollectionContext"; +import { useCollections } from "../../contexts/CollectionsProvider"; +import { openCollection } from "../../utils/opener"; + +export default function OpenCollectionButton({ onOpenChange }: OpenCollectionButtonProps): React.ReactElement +{ + const [defaultAction] = useSettings("defaultRestoreAction"); + const [listLocation] = useSettings("listLocation"); + const { removeItem } = useCollections(); + const dialog = useDialog(); + const { collection } = useContext(CollectionContext); + + const OpenIcon = ic.bundleIcon(ic.Open20Filled, ic.Open20Regular); + const RestoreIcon = ic.bundleIcon(ic.ArrowExportRtl20Filled, ic.ArrowExportRtl20Regular); + const NewWindowIcon = ic.bundleIcon(ic.WindowNew20Filled, ic.WindowNew20Regular); + const InPrivateIcon = ic.bundleIcon(ic.TabInPrivate20Filled, ic.TabInPrivate20Regular); + + const handleIncognito = async () => + { + if (await browser.extension.isAllowedIncognitoAccess()) + { + if (import.meta.env.FIREFOX && listLocation === "popup") + sendMessage("openCollection", { collection, targetWindow: "incognito" }); + else + openCollection(collection, "incognito"); + } + else + dialog.pushPrompt({ + title: i18n.t("collections.incognito_check.title"), + content: ( + <> + { i18n.t(`collections.incognito_check.message.${browserLocaleKey}.p1`) } +
+
+ { i18n.t(`collections.incognito_check.message.${browserLocaleKey}.p2`) } + + ), + confirmText: i18n.t("collections.incognito_check.action"), + onConfirm: async () => import.meta.env.FIREFOX ? + await browser.runtime.openOptionsPage() : + await browser.tabs.create({ + url: `chrome://extensions/?id=${browser.runtime.id}`, + active: true + }) + }); + }; + + const handleOpen = (mode: "current" | "new") => + import.meta.env.FIREFOX && listLocation === "popup" && mode === "new" ? + () => sendMessage("openCollection", { collection, targetWindow: "new" }) : + () => openCollection(collection, mode); + + const handleRestore = async () => + { + await openCollection(collection); + removeItem(collection.timestamp); + }; + + return ( + + + { (triggerProps: MenuButtonProps) => defaultAction === "restore" ? + } menuButton={ triggerProps } + primaryActionButton={ { onClick: handleRestore } } + > + { i18n.t("collections.actions.restore") } + + : + } menuButton={ triggerProps } + primaryActionButton={ { onClick: handleOpen("current") } } + > + { i18n.t("collections.actions.open") } + + } + + + + + { defaultAction === "restore" ? + } onClick={ handleOpen("current") }> + { i18n.t("collections.actions.open") } + + : + } onClick={ handleRestore }> + { i18n.t("collections.actions.restore") } + + } + } onClick={ handleOpen("new") }> + { i18n.t("collections.actions.new_window") } + + } onClick={ handleIncognito }> + { i18n.t(`collections.actions.incognito.${browserLocaleKey}`) } + + + + + ); +} + +export type OpenCollectionButtonProps = + { + onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; + }; diff --git a/entrypoints/sidepanel/contexts/CollectionContext.tsx b/entrypoints/sidepanel/contexts/CollectionContext.tsx new file mode 100644 index 0000000..55f1dde --- /dev/null +++ b/entrypoints/sidepanel/contexts/CollectionContext.tsx @@ -0,0 +1,13 @@ +import { CollectionItem } from "@/models/CollectionModels"; +import { createContext } from "react"; + +const CollectionContext = createContext(null!); + +export default CollectionContext; + +export type CollectionContextType = + { + collection: CollectionItem; + tabCount: number; + hasPinnedGroup: boolean; + }; diff --git a/entrypoints/sidepanel/contexts/CollectionsProvider.tsx b/entrypoints/sidepanel/contexts/CollectionsProvider.tsx new file mode 100644 index 0000000..c5249bb --- /dev/null +++ b/entrypoints/sidepanel/contexts/CollectionsProvider.tsx @@ -0,0 +1,121 @@ +import { CloudStorageIssueType, getCollections, graphics as graphicsStorage, saveCollections } from "@/features/collectionStorage"; +import useSettings from "@/hooks/useSettings"; +import { CollectionItem, GraphicsStorage, GroupItem } from "@/models/CollectionModels"; +import getLogger from "@/utils/getLogger"; +import { onMessage, sendMessage } from "@/utils/messaging"; +import { createContext } from "react"; +import mergePinnedGroups from "../utils/mergePinnedGroups"; + +const logger = getLogger("CollectionsProvider"); + +const CollectionsContext = createContext(null!); + +export const useCollections = () => useContext(CollectionsContext); + +export default function CollectionsProvider({ children }: React.PropsWithChildren): React.ReactElement +{ + const [collections, setCollections] = useState(null!); + const [cloudIssue, setCloudIssue] = useState(null); + const [graphics, setGraphics] = useState({}); + const [tilesView] = useSettings("tilesView"); + + useEffect(() => + { + refreshCollections(); + onMessage("refreshCollections", refreshCollections); + }, []); + + const refreshCollections = async (): Promise => + { + const [result, issues] = await getCollections(); + setCloudIssue(issues); + setCollections(result); + setGraphics(await graphicsStorage.getValue()); + }; + + const updateStorage = async (collectionList: CollectionItem[]): Promise => + { + logger("save"); + collectionList.forEach(mergePinnedGroups); + setCollections([...collectionList]); + await saveCollections(collectionList, cloudIssue === null); + setGraphics(await graphicsStorage.getValue()); + sendMessage("refreshCollections", undefined); + }; + + const addCollection = (collection: CollectionItem): void => + { + updateStorage([collection, ...collections]); + }; + + const removeItem = (...indices: number[]): void => + { + const collectionIndex: number = collections.findIndex(i => i.timestamp === indices[0]); + + if (indices.length > 2) + (collections[collectionIndex].items[indices[1]] as GroupItem).items.splice(indices[2], 1); + else if (indices.length > 1) + collections[collectionIndex].items.splice(indices[1], 1); + else + collections.splice(collectionIndex, 1); + + updateStorage(collections); + }; + + const updateCollections = (collectionList: CollectionItem[]): void => + { + updateStorage(collectionList); + }; + + const updateCollection = (collection: CollectionItem, id: number): void => + { + const index: number = collections.findIndex(i => i.timestamp === id); + collections[index] = collection; + updateStorage(collections); + }; + + const updateGroup = (group: GroupItem, collectionId: number, groupIndex: number): void => + { + const collectionIndex: number = collections.findIndex(i => i.timestamp === collectionId); + collections[collectionIndex].items[groupIndex] = group; + updateStorage(collections); + }; + + const ungroup = (collectionId: number, groupIndex: number): void => + { + const collectionIndex: number = collections.findIndex(i => i.timestamp === collectionId); + const group = collections[collectionIndex].items[groupIndex] as GroupItem; + collections[collectionIndex].items.splice(groupIndex, 1, ...group.items); + updateStorage(collections); + }; + + return ( + + { children } + + ); +} + +export type CollectionsContextType = + { + collections: CollectionItem[] | null; + cloudIssue: CloudStorageIssueType | null; + graphics: GraphicsStorage; + tilesView: boolean; + + refreshCollections: () => Promise; + addCollection: (collection: CollectionItem) => void; + + updateCollections: (collections: CollectionItem[]) => void; + updateCollection: (collection: CollectionItem, id: number) => void; + updateGroup: (group: GroupItem, collectionId: number, groupIndex: number) => void; + ungroup: (collectionId: number, groupIndex: number) => void; + + removeItem: (...indices: number[]) => void; + }; diff --git a/entrypoints/sidepanel/contexts/GroupContext.tsx b/entrypoints/sidepanel/contexts/GroupContext.tsx new file mode 100644 index 0000000..d0d8c06 --- /dev/null +++ b/entrypoints/sidepanel/contexts/GroupContext.tsx @@ -0,0 +1,12 @@ +import { GroupItem } from "@/models/CollectionModels"; +import { createContext } from "react"; + +const GroupContext = createContext(null!); + +export default GroupContext; + +export type GroupContextType = + { + group: GroupItem; + indices: number[]; + }; diff --git a/entrypoints/sidepanel/hooks/useDndItem.ts b/entrypoints/sidepanel/hooks/useDndItem.ts new file mode 100644 index 0000000..a2bb2e5 --- /dev/null +++ b/entrypoints/sidepanel/hooks/useDndItem.ts @@ -0,0 +1,61 @@ +import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels"; +import { useSortable } from "@dnd-kit/sortable"; +import { Arguments } from "@dnd-kit/sortable/dist/hooks/useSortable"; + +export default function useDndItem(args: Arguments): DndItemHook +{ + const { + setActivatorNodeRef, setNodeRef, + transform, attributes, listeners, + active, over, + isDragging, + isSorting, + isOver + } = useSortable({ transition: null, ...args }); + + return { + setActivatorNodeRef, + setNodeRef, + nodeProps: + { + style: + { + transform: transform ? `translate(${transform.x}px, ${transform.y}px)` : undefined + }, + ...attributes + }, + activatorProps: + { + ...listeners, + style: + { + cursor: args.disabled ? undefined : "grab" + } + }, + activeItem: active ? { ...active.data.current, id: active.id } as DndItem : null, + overItem: over ? { ...over.data.current, id: over.id } as DndItem : null, + isBeingDragged: isDragging, + isCurrentlySorting: isSorting, + isActiveOverThis: isOver + }; +} + +export type DndItem = + { + id: string; + indices: number[]; + item: (TabItem | CollectionItem | GroupItem); + }; + +export type DndItemHook = + { + setNodeRef: (element: HTMLElement | null) => void; + setActivatorNodeRef: (element: HTMLElement | null) => void; + nodeProps: React.HTMLAttributes; + activatorProps: React.HTMLAttributes; + activeItem: DndItem | null; + overItem: DndItem | null; + isBeingDragged: boolean; + isCurrentlySorting: boolean; + isActiveOverThis: boolean; + }; diff --git a/entrypoints/sidepanel/index.html b/entrypoints/sidepanel/index.html new file mode 100644 index 0000000..2611a2e --- /dev/null +++ b/entrypoints/sidepanel/index.html @@ -0,0 +1,18 @@ + + + + + + + Tabs aside + + + + + + +
+ + + + diff --git a/entrypoints/sidepanel/layouts/collections/CollectionListView.styles.ts b/entrypoints/sidepanel/layouts/collections/CollectionListView.styles.ts new file mode 100644 index 0000000..4faa635 --- /dev/null +++ b/entrypoints/sidepanel/layouts/collections/CollectionListView.styles.ts @@ -0,0 +1,55 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useStyles_CollectionListView = makeStyles({ + root: + { + display: "flex", + flexFlow: "column", + gap: tokens.spacingVerticalM, + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalS}`, + overflowX: "hidden", + overflowY: "auto" + }, + collectionList: + { + display: "flex", + flexFlow: "column", + gap: tokens.spacingVerticalM + }, + searchBar: + { + boxShadow: tokens.shadow2 + }, + emptySearch: + { + display: "flex", + flexFlow: "column", + flexGrow: 1, + alignItems: "center", + justifyContent: "center", + gap: tokens.spacingVerticalS + }, + empty: + { + display: "flex", + flexFlow: "column", + alignItems: "center", + justifyContent: "center", + gap: tokens.spacingVerticalS, + padding: `${tokens.spacingVerticalM} ${tokens.spacingHorizontalM}`, + color: tokens.colorNeutralForeground2 + }, + msgBar: + { + flex: "none" + }, + listView: + { + display: "grid", + + "@media screen and (min-width: 360px)": + { + gridTemplateColumns: "repeat(auto-fit, minmax(360px, 1fr))" + } + } +}); diff --git a/entrypoints/sidepanel/layouts/collections/CollectionListView.tsx b/entrypoints/sidepanel/layouts/collections/CollectionListView.tsx new file mode 100644 index 0000000..7b2f9e1 --- /dev/null +++ b/entrypoints/sidepanel/layouts/collections/CollectionListView.tsx @@ -0,0 +1,152 @@ +import CollectionView from "@/entrypoints/sidepanel/components/CollectionView"; +import GroupView from "@/entrypoints/sidepanel/components/GroupView"; +import { DndItem } from "@/entrypoints/sidepanel/hooks/useDndItem"; +import CloudIssueMessages from "@/entrypoints/sidepanel/layouts/collections/messages/CloudIssueMessages"; +import CtaMessage from "@/entrypoints/sidepanel/layouts/collections/messages/CtaMessage"; +import filterCollections, { CollectionFilterType } from "@/entrypoints/sidepanel/utils/filterCollections"; +import sortCollections from "@/entrypoints/sidepanel/utils/sortCollections"; +import { track } from "@/features/analytics"; +import useSettings from "@/hooks/useSettings"; +import { CollectionItem } from "@/models/CollectionModels"; +import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, MouseSensor, TouchSensor, useSensor, useSensors } from "@dnd-kit/core"; +import { rectSortingStrategy, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"; +import { Body1, Button, Caption1, mergeClasses, Subtitle2 } from "@fluentui/react-components"; +import { ArrowUndo20Regular, SearchInfo24Regular, Sparkle48Regular } from "@fluentui/react-icons"; +import { ReactElement } from "react"; +import TabView from "../../components/TabView"; +import CollectionContext from "../../contexts/CollectionContext"; +import { useCollections } from "../../contexts/CollectionsProvider"; +import applyReorder from "../../utils/dnd/applyReorder"; +import { collisionDetector } from "../../utils/dnd/collisionDetector"; +import { useStyles_CollectionListView } from "./CollectionListView.styles"; +import SearchBar from "./SearchBar"; +import StorageCapacityIssueMessage from "./messages/StorageCapacityIssueMessage"; +import { snapHandleToCursor } from "../../utils/dnd/snapHandleToCursor"; + +export default function CollectionListView(): ReactElement +{ + const { tilesView, updateCollections, collections } = useCollections(); + + const [sortMode, setSortMode] = useSettings("sortMode"); + const [query, setQuery] = useState(""); + const [colors, setColors] = useState([]); + + const [active, setActive] = useState(null); + + const sensors = useSensors( + useSensor(MouseSensor, { activationConstraint: { delay: 10, tolerance: 20 } }), + useSensor(TouchSensor, { activationConstraint: { delay: 300, tolerance: 20 } }) + ); + + const resultList = useMemo( + () => sortCollections(filterCollections(collections, { query, colors }), sortMode), + [query, colors, sortMode, collections] + ); + + const cls = useStyles_CollectionListView(); + + const resetFilter = useCallback(() => + { + setQuery(""); + setColors([]); + }, []); + + const handleDragStart = (event: DragStartEvent): void => + { + setActive(event.active.data.current as DndItem); + }; + + const handleDragEnd = (args: DragEndEvent): void => + { + setActive(null); + const result: CollectionItem[] | null = applyReorder(resultList, args); + + if (result !== null) + { + updateCollections(result); + if (sortMode !== "custom") + setSortMode("custom"); + + track("used_drag_and_drop"); + } + }; + + if (sortMode === null || collections === null) + return <>; + + if (collections.length < 1) + return ( +
+ + { i18n.t("main.list.empty.title") } + { i18n.t("main.list.empty.message") } +
+ ); + + return ( +
+ + + + + + + { resultList.length < 1 ? +
+ + { i18n.t("main.list.empty_search.title") } + { i18n.t("main.list.empty_search.message") } + +
+ : +
+ + index.toString()) } + strategy={ tilesView ? verticalListSortingStrategy : rectSortingStrategy } + > + { resultList.map((collection, index) => + + ) } + + + + { active !== null ? + active.item.type === "collection" ? + + : + + { active.item.type === "group" ? + + : + + } + + : + <> + } + + +
+ } +
+ ); +} diff --git a/entrypoints/sidepanel/layouts/collections/FilterCollectionsButton.tsx b/entrypoints/sidepanel/layouts/collections/FilterCollectionsButton.tsx new file mode 100644 index 0000000..a36842b --- /dev/null +++ b/entrypoints/sidepanel/layouts/collections/FilterCollectionsButton.tsx @@ -0,0 +1,71 @@ +import { useGroupColors } from "@/hooks/useGroupColors"; +import * as fui from "@fluentui/react-components"; +import * as ic from "@fluentui/react-icons"; +import { CollectionFilterType } from "../../utils/filterCollections"; + +export default function FilterCollectionsButton({ value, onChange }: FilterCollectionsButtonProps): React.ReactElement +{ + const cls = useStyles(); + const colorCls = useGroupColors(); + + const ColorFilterIcon = ic.bundleIcon(ic.Color20Filled, ic.Color20Regular); + const ColorIcon = ic.bundleIcon(ic.Circle20Filled, ic.CircleHalfFill20Regular); + const NoColorIcon = ic.bundleIcon(ic.CircleOffFilled, ic.CircleOffRegular); + const AnyColorIcon = ic.bundleIcon(ic.PhotoFilter20Filled, ic.PhotoFilter20Regular); + + return ( + + onChange?.(e.checkedItems.includes("any") ? [] : e.checkedItems as CollectionFilterType["colors"]) + } + > + + + + } /> + + + + + + }> + { i18n.t("colors.any") } + + + }> + { i18n.t("colors.none") } + + + { Object.keys(colorCls).map(i => + + } + > + { i18n.t(`colors.${i as chrome.tabGroups.ColorEnum}`) } + + ) } + + + + ); +} + +export type FilterCollectionsButtonProps = + { + value?: CollectionFilterType["colors"]; + onChange?: (value: CollectionFilterType["colors"]) => void; + }; + +const useStyles = fui.makeStyles({ + colorIcon: + { + color: "var(--border)" + } +}); diff --git a/entrypoints/sidepanel/layouts/collections/SearchBar.tsx b/entrypoints/sidepanel/layouts/collections/SearchBar.tsx new file mode 100644 index 0000000..a525e19 --- /dev/null +++ b/entrypoints/sidepanel/layouts/collections/SearchBar.tsx @@ -0,0 +1,51 @@ +import { Button, Input, makeStyles, tokens, Tooltip } from "@fluentui/react-components"; +import { ArrowUndo20Filled, ArrowUndo20Regular, bundleIcon, Search20Regular } from "@fluentui/react-icons"; +import { CollectionFilterType } from "../../utils/filterCollections"; +import { CollectionSortMode } from "../../utils/sortCollections"; +import FilterCollectionsButton from "./FilterCollectionsButton"; +import SortCollectionsButton from "./SortCollectionsButton"; + +export default function SearchBar(props: SearchBarProps): React.ReactElement +{ + const cls = useStyles(); + + const ResetIcon = bundleIcon(ArrowUndo20Filled, ArrowUndo20Regular); + + return ( + } + placeholder={ i18n.t("main.list.searchbar.title") } + value={ props.query } onChange={ (_, e) => props.onQueryChange?.(e.value) } + contentAfter={ + <> + { (props.query || (props.filter && props.filter.length > 0)) && + + + + + } + + { cloudIssue === "merge_conflict" && + + + { i18n.t("merge_conflict_message.title") } + { i18n.t("merge_conflict_message.message") } + + + + + + + } + + ); +} diff --git a/entrypoints/sidepanel/layouts/collections/messages/CtaMessage.tsx b/entrypoints/sidepanel/layouts/collections/messages/CtaMessage.tsx new file mode 100644 index 0000000..73afa51 --- /dev/null +++ b/entrypoints/sidepanel/layouts/collections/messages/CtaMessage.tsx @@ -0,0 +1,66 @@ +import { BuyMeACoffee20Regular } from "@/assets/BuyMeACoffee20"; +import { buyMeACoffeeLink, storeLink } from "@/data/links"; +import { track } from "@/features/analytics"; +import { useBmcStyles } from "@/hooks/useBmcStyles"; +import extLink from "@/utils/extLink"; +import { Button, Link, MessageBar, MessageBarActions, MessageBarBody, MessageBarProps, MessageBarTitle } from "@fluentui/react-components"; +import { DismissRegular, HeartFilled } from "@fluentui/react-icons"; +import { ReactElement } from "react"; + +export default function CtaMessage(props: MessageBarProps): ReactElement +{ + const [counter, setCounter] = useState(0); + const bmcCls = useBmcStyles(); + + useEffect(() => + { + ctaCounter.getValue().then(c => + { + if (c >= 0) + { + setCounter(c); + ctaCounter.setValue(c + 1); + } + }); + }, []); + + const resetCounter = async (counter: number) => + { + await ctaCounter.setValue(counter); + setCounter(counter); + + if (counter === -1) + track("bmc_clicked"); + else + track("cta_dismissed"); + }; + + if (counter < 50) + return <>; + + return ( + } { ...props }> + + { i18n.t("cta_message.title") } + { i18n.t("cta_message.message") } track("feedback_clicked") }>{ i18n.t("cta_message.feedback") } + + } appearance="transparent" onClick={ () => resetCounter(0) } /> + } + > + + + + ); +} + +const ctaCounter = storage.defineItem("local:ctaCounter", { fallback: 0 }); diff --git a/entrypoints/sidepanel/layouts/collections/messages/StorageCapacityIssueMessage.tsx b/entrypoints/sidepanel/layouts/collections/messages/StorageCapacityIssueMessage.tsx new file mode 100644 index 0000000..537981e --- /dev/null +++ b/entrypoints/sidepanel/layouts/collections/messages/StorageCapacityIssueMessage.tsx @@ -0,0 +1,21 @@ +import useStorageInfo from "@/hooks/useStorageInfo"; +import { MessageBar, MessageBarBody, MessageBarProps, MessageBarTitle } from "@fluentui/react-components"; + +export default function StorageCapacityIssueMessage(props: MessageBarProps): JSX.Element +{ + const { usedStorageRatio } = useStorageInfo(); + + if (usedStorageRatio < 0.8) + return <>; + + return ( + + + + { i18n.t("storage_full_message.title", [(usedStorageRatio * 100).toFixed(1)]) } + + { i18n.t("storage_full_message.message") } + + + ); +} diff --git a/entrypoints/sidepanel/layouts/header/ActionButton.tsx b/entrypoints/sidepanel/layouts/header/ActionButton.tsx new file mode 100644 index 0000000..810b1a5 --- /dev/null +++ b/entrypoints/sidepanel/layouts/header/ActionButton.tsx @@ -0,0 +1,74 @@ +import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider"; +import useSettings, { SettingsValue } from "@/hooks/useSettings"; +import saveTabsToCollection from "@/utils/saveTabsToCollection"; +import watchTabSelection from "@/utils/watchTabSelection"; +import { Menu, MenuButtonProps, MenuItem, MenuList, MenuPopover, MenuTrigger, SplitButton } from "@fluentui/react-components"; +import * as ic from "@fluentui/react-icons"; +import { ReactElement } from "react"; + +export default function ActionButton(): ReactElement +{ + const { addCollection } = useCollections(); + const [defaultAction] = useSettings("defaultSaveAction"); + const [selection, setSelection] = useState<"all" | "selected">("all"); + + const handleAction = async (primary: boolean) => + { + const colection = await saveTabsToCollection(primary === (defaultAction === "set_aside")); + addCollection(colection); + }; + + useEffect(() => + { + return watchTabSelection(setSelection); + }, []); + + if (defaultAction === null) + return
; + + const primaryActionKey: ActionsKey = `${defaultAction}.${selection}`; + const PrimaryIcon = actionIcons[primaryActionKey]; + const secondaryActionKey: ActionsKey = `${defaultAction === "save" ? "set_aside" : "save"}.${selection}`; + const SecondaryIcon = actionIcons[secondaryActionKey]; + + return ( + + + { (triggerProps: MenuButtonProps) => ( + } + menuButton={ triggerProps } + primaryActionButton={ { onClick: () => handleAction(true) } } + > + { i18n.t(`actions.${primaryActionKey}`) } + + ) } + + + + + } onClick={ () => handleAction(false) }> + { i18n.t(`actions.${secondaryActionKey}`) } + + + + + ); +} + +const actionIcons: Record = +{ + "save.all": ic.bundleIcon(ic.SaveArrowRight20Filled, ic.SaveArrowRight20Regular), + "save.selected": ic.bundleIcon(ic.SaveCopy20Filled, ic.SaveCopy20Regular), + "set_aside.all": ic.bundleIcon(ic.ArrowRight20Filled, ic.ArrowRight20Regular), + "set_aside.selected": ic.bundleIcon(ic.CopyArrowRight20Filled, ic.CopyArrowRight20Regular) +}; + +export type ActionsKey = `${SettingsValue<"defaultSaveAction">}.${"all" | "selected"}`; + +export type ActionsValue = + { + label: string; + icon: ic.FluentIcon; + }; diff --git a/entrypoints/sidepanel/layouts/header/Header.tsx b/entrypoints/sidepanel/layouts/header/Header.tsx new file mode 100644 index 0000000..22150b9 --- /dev/null +++ b/entrypoints/sidepanel/layouts/header/Header.tsx @@ -0,0 +1,53 @@ +import { useDialog } from "@/contexts/DialogProvider"; +import { useCollections } from "@/entrypoints/sidepanel/contexts/CollectionsProvider"; +import { Button, makeStyles, tokens, Tooltip } from "@fluentui/react-components"; +import { CollectionsAddRegular } from "@fluentui/react-icons"; +import { ReactElement } from "react"; +import EditDialog from "../../components/EditDialog"; +import ActionButton from "./ActionButton"; +import MoreButton from "./MoreButton"; + +export default function Header(): ReactElement +{ + const { addCollection } = useCollections(); + const dialog = useDialog(); + const cls = useStyles(); + + const handleCreateCollection = () => + dialog.pushCustom( + + ); + + return ( +
+ + +
+ + +
+
+ ); +} + +const useStyles = makeStyles({ + header: + { + display: "flex", + justifyContent: "space-between", + padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalS}`, + gap: tokens.spacingHorizontalS + }, + headerSecondary: + { + display: "flex", + gap: tokens.spacingHorizontalXS + } +}); diff --git a/entrypoints/sidepanel/layouts/header/MoreButton.tsx b/entrypoints/sidepanel/layouts/header/MoreButton.tsx new file mode 100644 index 0000000..e6f0f0f --- /dev/null +++ b/entrypoints/sidepanel/layouts/header/MoreButton.tsx @@ -0,0 +1,86 @@ +import { BuyMeACoffee20Filled, BuyMeACoffee20Regular } from "@/assets/BuyMeACoffee20"; +import { buyMeACoffeeLink, githubLinks, storeLink } from "@/data/links"; +import { track } from "@/features/analytics"; +import useSettings from "@/hooks/useSettings"; +import extLink from "@/utils/extLink"; +import sendNotification from "@/utils/sendNotification"; +import * as fui from "@fluentui/react-components"; +import * as ic from "@fluentui/react-icons"; +import { ReactElement } from "react"; + +export default function MoreButton(): ReactElement +{ + const [tilesView, setTilesView] = useSettings("tilesView"); + + const SettingsIcon: ic.FluentIcon = ic.bundleIcon(ic.Settings20Filled, ic.Settings20Regular); + const ViewIcon: ic.FluentIcon = ic.bundleIcon(ic.GridKanban20Filled, ic.GridKanban20Regular); + const FeedbackIcon: ic.FluentIcon = ic.bundleIcon(ic.PersonFeedback20Filled, ic.PersonFeedback20Regular); + const LearnIcon: ic.FluentIcon = ic.bundleIcon(ic.QuestionCircle20Filled, ic.QuestionCircle20Regular); + const BmcIcon: ic.FluentIcon = ic.bundleIcon(BuyMeACoffee20Filled, BuyMeACoffee20Regular); + + return ( + setTilesView(e.checkedItems.length > 0) } + > + + + } /> + + + + + + + } onClick={ () => browser.runtime.openOptionsPage() }> + { i18n.t("options_page.title") } + + }> + { i18n.t("main.header.menu.tiles_view") } + + + + + } { ...extLink(buyMeACoffeeLink) } onClick={ () => track("feedback_clicked") }> + { i18n.t("common.cta.sponsor") } + + } { ...extLink(storeLink) } onClick={ () => track("bmc_clicked") }> + { i18n.t("common.cta.feedback") } + + } { ...extLink(githubLinks.release) } > + { i18n.t("main.header.menu.changelog") } + + + { import.meta.env.DEV && + + Dev tools + } + onClick={ () => document.location.reload() } + > + Reload page + + } + onClick={ () => browser.tabs.create({ url: browser.runtime.getURL("/sidepanel.html"), active: true }) } + > + Open in tab + + } + onClick={ async () => await sendNotification({ + icon: "/notification_icons/cloud_error.png", + message: "Notification message", + title: "Notification title" + }) } + > + Show test notification + + + } + + + + ); +} diff --git a/entrypoints/sidepanel/main.tsx b/entrypoints/sidepanel/main.tsx new file mode 100644 index 0000000..55c3d39 --- /dev/null +++ b/entrypoints/sidepanel/main.tsx @@ -0,0 +1,45 @@ +import App from "@/App.tsx"; +import "@/assets/global.css"; +import { useLocalMigration } from "@/features/migration"; +import useWelcomeDialog from "@/features/v3welcome/hooks/useWelcomeDialog"; +import { Divider, makeStyles } from "@fluentui/react-components"; +import ReactDOM from "react-dom/client"; +import CollectionsProvider from "./contexts/CollectionsProvider"; +import CollectionListView from "./layouts/collections/CollectionListView"; +import Header from "./layouts/header/Header"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + +); + +document.title = i18n.t("manifest.name"); +analytics.page("collection_list"); + +function MainPage(): React.ReactElement +{ + const cls = useStyles(); + + useLocalMigration(); + useWelcomeDialog(); + + return ( + +
+
+ + +
+
+ ); +} + +const useStyles = makeStyles({ + main: + { + display: "grid", + gridTemplateRows: "auto auto 1fr", + height: "100vh" + } +}); diff --git a/entrypoints/sidepanel/utils/dnd/applyReorder.ts b/entrypoints/sidepanel/utils/dnd/applyReorder.ts new file mode 100644 index 0000000..b5b29a2 --- /dev/null +++ b/entrypoints/sidepanel/utils/dnd/applyReorder.ts @@ -0,0 +1,61 @@ +import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels"; +import { DragEndEvent } from "@dnd-kit/core"; +import { arrayMove } from "@dnd-kit/sortable"; +import { DndItem } from "../../hooks/useDndItem"; + +export default function applyReorder(collections: CollectionItem[], { over, active }: DragEndEvent): null | CollectionItem[] +{ + if (!over || active.id === over.id) + return null; + + const activeItem: DndItem = active.data.current as DndItem; + const overItem: DndItem = over.data.current as DndItem; + + console.log("DragEnd", `active: ${active.id} ${activeItem.item.type}`, `over: ${over.id} ${overItem.item.type}`); + + let newList: CollectionItem[] = [ + ...collections.map(collection => ({ + ...collection, + items: collection.items.map(item => + item.type === "group" ? + { ...item, items: item.items.map(tab => ({ ...tab })) } : + { ...item } + ) + })) + ]; + + if (activeItem.item.type === "collection") + { + newList = arrayMove( + newList, + activeItem.indices[0], + overItem.indices[0] + ); + + return newList; + } + + const sourceItem: GroupItem | CollectionItem = activeItem.indices.length > 2 ? + (newList[activeItem.indices[0]].items[activeItem.indices[1]] as GroupItem) : + newList[activeItem.indices[0]]; + + if ((over.id as string).endsWith("_dropzone") || overItem.item.type === "collection") + { + const destItem: GroupItem | CollectionItem = overItem.indices.length > 1 ? + (newList[overItem.indices[0]].items[overItem.indices[1]] as GroupItem) : + newList[overItem.indices[0]]; + + destItem.items.push(activeItem.item as any); + sourceItem.items.splice(activeItem.indices[activeItem.indices.length - 1], 1); + } + else + { + sourceItem.items = arrayMove( + sourceItem.items, + activeItem.indices[activeItem.indices.length - 1], + overItem.indices[overItem.indices.length - 1] + ); + } + + return newList; +} diff --git a/entrypoints/sidepanel/utils/dnd/collisionDetector.ts b/entrypoints/sidepanel/utils/dnd/collisionDetector.ts new file mode 100644 index 0000000..7e4ec75 --- /dev/null +++ b/entrypoints/sidepanel/utils/dnd/collisionDetector.ts @@ -0,0 +1,153 @@ +import { ClientRect, Collision, CollisionDescriptor, CollisionDetection } from "@dnd-kit/core"; +import { DndItem } from "../../hooks/useDndItem"; +import { centerOfRectangle, distanceBetween, getIntersectionRatio, getMaxIntersectionRatio, getRectSideCoordinates, sortCollisionsAsc } from "./dndUtils"; + +export function collisionDetector(vertical?: boolean): CollisionDetection +{ + return (args): Collision[] => + { + const { collisionRect, droppableContainers, droppableRects, active, pointerCoordinates } = args; + const activeItem = active.data.current as DndItem; + + if (!pointerCoordinates) + return []; + + const collisions: CollisionDescriptor[] = []; + const centerRect = centerOfRectangle( + collisionRect, + collisionRect.left, + collisionRect.top + ); + + for (const droppableContainer of droppableContainers) + { + const { id, data } = droppableContainer; + const rect = droppableRects.get(id); + + const droppableItem: DndItem = data.current as DndItem; + + if (!rect) + continue; + + let value: number = 0; + + if (activeItem.item.type === "collection") + { + // If we drag a collection, we should ignore other items, like tabs or groups + if (droppableItem.item.type !== "collection") + continue; + + // Using distance between centers + value = distanceBetween(centerOfRectangle(rect), centerRect); + collisions.push({ id, data: { droppableContainer, value } }); + continue; + } + + const intersectionRatio: number = getIntersectionRatio(rect, collisionRect); + const intersectionCoefficient: number = intersectionRatio / getMaxIntersectionRatio(rect, collisionRect); + + // Dragging a tab or a group over a collection + if (droppableItem.item.type === "collection") + { + // Ignoring collection, if the tab or the group is inside that collection + if (activeItem.indices.length === 2 && activeItem.indices[0] === droppableItem.indices[0]) + continue; + + // Ignoring collection if we're dragging a tab or a group that doesn't belong to the collection, + // but intersection ratio is less than 0.7 + if (intersectionCoefficient < 0.7) + continue; + + // If we're dragging a tab, that's inside a group that belongs to the collection, + // we substract the group's intersection from the collection's one + if (activeItem.indices.length === 3 && activeItem.indices[0] === droppableItem.indices[0]) + { + const [collectionId, groupId] = activeItem.indices; + const groupRect: ClientRect | undefined = droppableRects.get(`${collectionId}/${groupId}`); + + if (!groupRect) + continue; + + value = 1 / (intersectionRatio - getIntersectionRatio(groupRect, collisionRect)); + } + // Otherwise, use intersection ratio + // At this point we're dragging either: + // - a group, that doesn't belong to the collection + // - a tab, that either belongs to the collection's group, or has intersection coefficient >= .7 + else + { + value = 2 / intersectionRatio; + } + } + // If we're dragging a tab or a group over another group's dropzone + else if (droppableItem.item.type === "group" && (id as string).endsWith("_dropzone")) + { + // Ignore, if we're dragging a group + if (activeItem.item.type === "group") + continue; + + // Ignore, if we're dragging a tab, that's inside the group + if ( + activeItem.indices.length === 3 && + activeItem.indices[0] === droppableItem.indices[0] && + activeItem.indices[1] === droppableItem.indices[1] + ) + continue; + + // Ignore, if coefficient is less than .5 + // (at this point we're dragging a tab, that's outside of the group's dropzone) + if (intersectionCoefficient < 0.5) + continue; + + // Use intersection between the tab and the group's dropzone + value = 1 / intersectionRatio; + } + // We're dragging a group or a tab over its sibling + else if (activeItem.indices.length === droppableItem.indices.length) + { + if (activeItem.indices[0] !== droppableItem.indices[0]) + continue; + + if (activeItem.indices.length === 3 && activeItem.indices[1] !== droppableItem.indices[1]) + continue; + + // Ignore pinned groups + if (droppableItem.item.type === "group" && droppableItem.item.pinned === true) + continue; + + const collectionRect: ClientRect | undefined = droppableRects.get(activeItem.indices[0].toString()); + + if (!collectionRect) + continue; + + const collectionIntersectionRatio: number = getIntersectionRatio(collectionRect, collisionRect); + const collectionIntersectionCoefficient: number = collectionIntersectionRatio / getMaxIntersectionRatio(collectionRect, collisionRect); + + // Ignore if we are outside of the home collection + if (collectionIntersectionCoefficient < 0.7) + continue; + + if (activeItem.item.type === "tab" && droppableItem.item.type === "tab") + { + value = distanceBetween(centerOfRectangle(rect), centerRect); + } + else + { + const activeIndex: number = activeItem.indices[activeItem.indices.length - 1]; + const droppableIndex: number = droppableItem.indices[droppableItem.indices.length - 1]; + const before: boolean = activeIndex < droppableIndex; + + value = distanceBetween( + getRectSideCoordinates(rect, before, vertical), + getRectSideCoordinates(collisionRect, before, vertical) + ); + } + } + + if ((value > 0 && value < Number.POSITIVE_INFINITY) || active.id === id) + collisions.push({ id, data: { droppableContainer, value } }); + }; + + return collisions.sort(sortCollisionsAsc); + }; +} diff --git a/entrypoints/sidepanel/utils/dnd/dndUtils.ts b/entrypoints/sidepanel/utils/dnd/dndUtils.ts new file mode 100644 index 0000000..389bb3b --- /dev/null +++ b/entrypoints/sidepanel/utils/dnd/dndUtils.ts @@ -0,0 +1,128 @@ +import { ClientRect, CollisionDescriptor } from "@dnd-kit/core"; +import { Coordinates } from "@dnd-kit/utilities"; + +export function getRectSideCoordinates(rect: ClientRect, before: boolean, vertical?: boolean) +{ + if (before) + return vertical ? bottomsideOfRect(rect) : rightsideOfRect(rect); + + return vertical ? topsideOfRect(rect) : leftsideOfRect(rect); +} + +export function getMaxIntersectionRatio(entry: ClientRect, target: ClientRect): number +{ + const entrySize = entry.width * entry.height; + const targetSize = target.width * target.height; + + return Math.min(targetSize / entrySize, entrySize / targetSize); +} + +function topsideOfRect(rect: ClientRect): Coordinates +{ + const { left, top } = rect; + + return { + x: left + rect.width * 0.5, + y: top + }; +} + +function bottomsideOfRect(rect: ClientRect): Coordinates +{ + const { left, bottom } = rect; + return { + x: left + rect.width * 0.5, + y: bottom + }; +} + +function rightsideOfRect(rect: ClientRect): Coordinates +{ + const { right, top } = rect; + return { + x: right, + y: top + rect.height * 0.5 + }; +} + +function leftsideOfRect(rect: ClientRect): Coordinates +{ + const { left, top } = rect; + return { + x: left, + y: top + rect.height * 0.5 + }; +} + +/* + * MIT License + * + * Copyright (c) 2021, Claudรฉric Demers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +export function distanceBetween(p1: Coordinates, p2: Coordinates) +{ + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); +} + +export function sortCollisionsAsc( + { data: { value: a } }: CollisionDescriptor, + { data: { value: b } }: CollisionDescriptor +) +{ + return a - b; +} + +export function getIntersectionRatio(entry: ClientRect, target: ClientRect): number +{ + const top = Math.max(target.top, entry.top); + const left = Math.max(target.left, entry.left); + const right = Math.min(target.left + target.width, entry.left + entry.width); + const bottom = Math.min(target.top + target.height, entry.top + entry.height); + const width = right - left; + const height = bottom - top; + + if (left < right && top < bottom) + { + const targetArea = target.width * target.height; + const entryArea = entry.width * entry.height; + const intersectionArea = width * height; + const intersectionRatio = + intersectionArea / (targetArea + entryArea - intersectionArea); + + return Number(intersectionRatio.toFixed(4)); + } + + // Rectangles do not overlap, or overlap has an area of zero (edge/corner overlap) + return 0; +} + +export function centerOfRectangle( + rect: ClientRect, + left = rect.left, + top = rect.top +): Coordinates +{ + return { + x: left + rect.width * 0.5, + y: top + rect.height * 0.5 + }; +} diff --git a/entrypoints/sidepanel/utils/dnd/snapHandleToCursor.ts b/entrypoints/sidepanel/utils/dnd/snapHandleToCursor.ts new file mode 100644 index 0000000..7b3127f --- /dev/null +++ b/entrypoints/sidepanel/utils/dnd/snapHandleToCursor.ts @@ -0,0 +1,34 @@ +import { Modifier } from "@dnd-kit/core"; +import { Coordinates, getEventCoordinates } from "@dnd-kit/utilities"; +import { DndItem } from "../../hooks/useDndItem"; + +export const snapHandleToCursor: Modifier = ({ + activatorEvent, + draggingNodeRect, + transform, + active +}) => +{ + if (draggingNodeRect && activatorEvent) + { + const activeItem: DndItem | undefined = active?.data.current as DndItem; + const activatorCoordinates: Coordinates | null = getEventCoordinates(activatorEvent); + + if (!activatorCoordinates) + return transform; + + const initX: number = activatorCoordinates.x - draggingNodeRect.left; + const initY: number = activatorCoordinates.y - draggingNodeRect.top; + + const offsetX: number = activeItem?.item.type === "group" ? 24 : draggingNodeRect.height / 2; + const offsetY: number = activeItem?.item.type === "group" ? 20 : draggingNodeRect.height / 2; + + return { + ...transform, + x: transform.x + initX - offsetX, + y: transform.y + initY - offsetY + }; + } + + return transform; +}; diff --git a/entrypoints/sidepanel/utils/exportCollectionToBookmarks.ts b/entrypoints/sidepanel/utils/exportCollectionToBookmarks.ts new file mode 100644 index 0000000..20c645e --- /dev/null +++ b/entrypoints/sidepanel/utils/exportCollectionToBookmarks.ts @@ -0,0 +1,48 @@ +import { CollectionItem, TabItem } from "@/models/CollectionModels"; +import sendNotification from "@/utils/sendNotification"; +import { Bookmarks } from "wxt/browser"; +import { getCollectionTitle } from "./getCollectionTitle"; + +export default async function exportCollectionToBookmarks(collection: CollectionItem) +{ + const rootFolder: Bookmarks.BookmarkTreeNode = await browser.bookmarks.create({ + title: getCollectionTitle(collection) + }); + + for (let i = 0; i < collection.items.length; i++) + { + const item = collection.items[i]; + + if (item.type === "tab") + { + await createTabBookmark(item, rootFolder.id); + } + else + { + const groupFolder = await browser.bookmarks.create({ + parentId: rootFolder.id, + title: item.pinned + ? `๐Ÿ“Œ ${i18n.t("groups.pinned")}` : + (item.title?.trim() || `${i18n.t("groups.title")} ${i}`) + }); + + for (const tab of item.items) + await createTabBookmark(tab, groupFolder.id); + } + } + + await sendNotification({ + title: i18n.t("notifications.bookmark_saved.title"), + message: i18n.t("notifications.bookmark_saved.message"), + icon: "/notification_icons/bookmark_add.png" + }); +} + +async function createTabBookmark(tab: TabItem, parentId: string): Promise +{ + await browser.bookmarks.create({ + parentId, + title: tab.title?.trim() || tab.url, + url: tab.url + }); +}; diff --git a/entrypoints/sidepanel/utils/filterCollections.ts b/entrypoints/sidepanel/utils/filterCollections.ts new file mode 100644 index 0000000..a1fbb48 --- /dev/null +++ b/entrypoints/sidepanel/utils/filterCollections.ts @@ -0,0 +1,65 @@ +import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionTitle"; +import { CollectionItem, TabItem } from "@/models/CollectionModels"; + +export default function filterCollections( + collections: CollectionItem[] | null, + filter: CollectionFilterType +): CollectionItem[] +{ + if (!collections || collections.length < 1) + return []; + + if (!filter.query && filter.colors.length < 1) + return collections; + + const query: string = filter.query.toLocaleLowerCase(); + + return collections.filter(collection => + { + let querySatisfied: boolean = query.length < 1 || + getCollectionTitle(collection).toLocaleLowerCase().includes(query); + let colorSatisfied: boolean = filter.colors.length < 1 || + filter.colors.includes(collection.color ?? "none"); + + if (querySatisfied && colorSatisfied) + return true; + + function probeTab(tab: TabItem, query: string): boolean + { + return tab.title?.toLocaleLowerCase().includes(query) || tab.url.toLocaleLowerCase().includes(query); + } + + for (const item of collection.items) + { + if (item.type === "tab" && !querySatisfied) + { + querySatisfied = probeTab(item, query); + } + else if (item.type === "group") + { + if (item.pinned !== true) + { + if (!querySatisfied) + querySatisfied = (item.title?.toLocaleLowerCase() ?? "").includes(query); + + if (!colorSatisfied) + colorSatisfied = filter.colors.includes(item.color); + } + + if (!querySatisfied) + querySatisfied = item.items.some(i => probeTab(i, query)); + } + + if (querySatisfied && colorSatisfied) + return true; + } + + return false; + }); +} + +export type CollectionFilterType = + { + query: string; + colors: (chrome.tabGroups.ColorEnum | "none")[]; + }; diff --git a/entrypoints/sidepanel/utils/getCollectionTitle.ts b/entrypoints/sidepanel/utils/getCollectionTitle.ts new file mode 100644 index 0000000..c6eb39e --- /dev/null +++ b/entrypoints/sidepanel/utils/getCollectionTitle.ts @@ -0,0 +1,10 @@ +import { CollectionItem } from "@/models/CollectionModels"; + +export function getCollectionTitle(collection?: CollectionItem, useTimestamp?: boolean): string +{ + if (collection?.title !== undefined && useTimestamp !== true) + return collection.title; + + return new Date(collection?.timestamp ?? Date.now()) + .toLocaleDateString(browser.i18n.getUILanguage(), { year: "numeric", month: "short", day: "numeric" }); +} diff --git a/entrypoints/sidepanel/utils/getSelectedTabs.ts b/entrypoints/sidepanel/utils/getSelectedTabs.ts new file mode 100644 index 0000000..a99363d --- /dev/null +++ b/entrypoints/sidepanel/utils/getSelectedTabs.ts @@ -0,0 +1,24 @@ +import { TabItem } from "@/models/CollectionModels"; +import sendNotification from "@/utils/sendNotification"; +import { Tabs } from "wxt/browser"; + +export default async function getSelectedTabs(): Promise +{ + let tabs: Tabs.Tab[] = await browser.tabs.query({ currentWindow: true, highlighted: true }); + const tabCount: number = tabs.length; + + tabs = tabs.filter(i => + i.url + && new URL(i.url).protocol !== "about:" + && new URL(i.url).hostname !== "newtab" + ); + + if (tabs.length < tabCount) + await sendNotification({ + title: i18n.t("notifications.partial_save.title"), + message: i18n.t("notifications.partial_save.message"), + icon: "/notification_icons/save_warning.png" + }); + + return tabs.map(i => ({ type: "tab", url: i.url!, title: i.title })); +} diff --git a/entrypoints/sidepanel/utils/mergePinnedGroups.ts b/entrypoints/sidepanel/utils/mergePinnedGroups.ts new file mode 100644 index 0000000..26d554f --- /dev/null +++ b/entrypoints/sidepanel/utils/mergePinnedGroups.ts @@ -0,0 +1,27 @@ +import { CollectionItem, TabItem } from "@/models/CollectionModels"; + +export default function mergePinnedGroups(collection: CollectionItem): void +{ + const pinnedItems: TabItem[] = []; + const otherItems: CollectionItem["items"] = []; + let pinExists: boolean = false; + + collection.items.forEach(item => + { + if (item.type === "group" && item.pinned === true) + { + pinExists = true; + pinnedItems.push(...item.items); + } + else + otherItems.push(item); + }); + + if (pinnedItems.length > 0 || pinExists) + collection.items = [ + { type: "group", pinned: true, items: pinnedItems }, + ...otherItems + ]; + else + collection.items = otherItems; +} diff --git a/entrypoints/sidepanel/utils/opener.ts b/entrypoints/sidepanel/utils/opener.ts new file mode 100644 index 0000000..2acba4f --- /dev/null +++ b/entrypoints/sidepanel/utils/opener.ts @@ -0,0 +1,117 @@ +import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionTitle"; +import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels"; +import { settings } from "@/utils/settings"; +import { Tabs, Windows } from "wxt/browser"; + +export async function openCollection(collection: CollectionItem, targetWindow?: "current" | "new" | "incognito"): Promise +{ + if (targetWindow === "incognito" && !(await browser.extension.isAllowedIncognitoAccess())) + throw new Error("The extension doesn't have incognito permission"); + + const discard: boolean = await settings.dismissOnLoad.getValue(); + + await manageWindow( + async windowId => + { + if (collection.items.some(i => i.type === "group")) + // Open tabs as regular, open groups as groups + await Promise.all(collection.items.map(async i => + { + if (i.type === "tab") + await createTab(i.url, windowId, discard); + else + await createGroup(i, windowId, discard); + })); + + else if (collection.color) + // Open collection as one big group + await createGroup({ + type: "group", + color: collection.color, + title: getCollectionTitle(collection), + items: collection.items as TabItem[] + }, windowId); + + else + // Open collection tabs as is + await Promise.all(collection.items.map(async i => + await createTab((i as TabItem).url, windowId, discard) + )); + }, + (!targetWindow || targetWindow === "current") ? + undefined : + { incognito: targetWindow === "incognito" } + ); +} + +export async function openGroup(group: GroupItem, newWindow: boolean = false): Promise +{ + await manageWindow( + windowId => createGroup(group, windowId), + newWindow ? {} : undefined + ); +} + +async function createGroup(group: GroupItem, windowId: number, discard?: boolean): Promise +{ + discard ??= await settings.dismissOnLoad.getValue(); + const tabs: Tabs.Tab[] = await Promise.all(group.items.map(async i => + await createTab(i.url, windowId, discard, group.pinned) + )); + + // "Pinned" group is technically not a group, so not much else to do here + if (group.pinned === true) + return; + + const groupId: number = await chrome.tabs.group({ + tabIds: tabs.filter(i => i.windowId === windowId).map(i => i.id!), + createProperties: { windowId } + }); + + await chrome.tabGroups.update(groupId, { + title: group.title, + color: group.color + }); +} + +async function manageWindow(handle: (windowId: number) => Promise, windowProps?: Windows.CreateCreateDataType): Promise +{ + const currentWindow: Windows.Window = windowProps ? + await browser.windows.create({ url: "about:blank", focused: false, ...windowProps }) : + await browser.windows.getCurrent(); + const windowId: number = currentWindow.id!; + + await handle(windowId); + + await browser.windows.update(windowId, { focused: true }); + + if (windowProps) + // Close "about:blank" tab + await browser.tabs.remove(currentWindow.tabs![0].id!); +} + +async function createTab(url: string, windowId: number, discard: boolean, pinned?: boolean): Promise +{ + const tab = await browser.tabs.create({ url, windowId: windowId, active: false, pinned }); + + if (discard) + discardOnLoad(tab.id!); + + return tab; +} + +function discardOnLoad(tabId: number): void +{ + const handleTabUpdated = (id: number, _: any, tab: Tabs.Tab) => + { + if (id !== tabId || !tab.url) + return; + + browser.tabs.onUpdated.removeListener(handleTabUpdated); + + if (!tab.active) + browser.tabs.discard(tabId); + }; + + browser.tabs.onUpdated.addListener(handleTabUpdated); +} diff --git a/entrypoints/sidepanel/utils/sortCollections.ts b/entrypoints/sidepanel/utils/sortCollections.ts new file mode 100644 index 0000000..5e1aa69 --- /dev/null +++ b/entrypoints/sidepanel/utils/sortCollections.ts @@ -0,0 +1,23 @@ +import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionTitle"; +import { CollectionItem } from "@/models/CollectionModels"; + +export default function sortCollections( + collections: CollectionItem[], + mode?: CollectionSortMode | null +): CollectionItem[] +{ + return sorters[mode ?? "custom"]([...collections]); +} + +export type CollectionSortMode = "ascending" | "descending" | "newest" | "oldest" | "custom"; + +const sorters: Record = +{ + ascending: i => i.sort((a, b) => getCollectionTitle(a).localeCompare(getCollectionTitle(b))), + descending: i => i.sort((a, b) => getCollectionTitle(b).localeCompare(getCollectionTitle(a))), + newest: i => i.sort((a, b) => b.timestamp - a.timestamp), + oldest: i => i.sort((a, b) => a.timestamp - b.timestamp), + custom: i => i +}; + +type CollectionSorter = (collections: CollectionItem[]) => CollectionItem[]; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5f74e6f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,103 @@ +import css from "@eslint/css"; +import js from "@eslint/js"; +import json from "@eslint/json"; +import stylistic from "@stylistic/eslint-plugin"; +import pluginReact from "eslint-plugin-react"; +import { defineConfig } from "eslint/config"; +import globals from "globals"; +import tseslint from "typescript-eslint"; + +export default defineConfig([ + { + ignores: [".wxt/", ".output/"] + }, + { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], plugins: { js }, extends: ["js/recommended"] }, + { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], languageOptions: { globals: globals.browser } }, + { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], extends: [tseslint.configs.recommended] }, + { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], extends: [pluginReact.configs.flat.recommended] }, + { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], extends: [stylistic.configs.recommended] }, + { files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] }, + { + files: ["**/*.{jsonc,json}"], + plugins: { json }, + language: "json/jsonc", + extends: ["json/recommended"] + }, + { + files: ["**/*.json"], + ignores: [".devcontainer/devcontainer.json"], + plugins: { json }, + language: "json/json", + extends: ["json/recommended"] + }, + { + files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], + settings: + { + react: + { + version: "detect" + } + } + }, + { + files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], + plugins: { + "@stylistic": stylistic + }, + rules: + { + "@stylistic/semi": ["error", "always"], + "@stylistic/block-spacing": ["warn", "always"], + "@stylistic/arrow-spacing": ["warn", { before: true, after: true }], + "@stylistic/indent": ["warn", "tab"], + "@stylistic/quotes": ["error", "double"], + "@stylistic/comma-spacing": ["warn"], + "@stylistic/comma-dangle": ["warn", "never"], + "@stylistic/no-tabs": ["warn", { allowIndentationTabs: true }], + "@stylistic/brace-style": ["warn", "allman", { allowSingleLine: true }], + "@stylistic/member-delimiter-style": ["error", { multiline: { delimiter: "semi", requireLast: true }, singleline: { delimiter: "semi", requireLast: true } }], + "@stylistic/jsx-curly-spacing": ["warn", { when: "always", children: true, attributes: true }], + "react/react-in-jsx-scope": ["off"], + "@stylistic/jsx-indent-props": ["warn", "tab"], + "@stylistic/jsx-max-props-per-line": ["off"], + "@stylistic/indent-binary-ops": ["warn", "tab"], + "@stylistic/no-multiple-empty-lines": ["warn"], + "@stylistic/operator-linebreak": ["off"], + "@stylistic/jsx-wrap-multilines": ["off"], + "@typescript-eslint/no-explicit-any": ["off"], + "@stylistic/jsx-curly-newline": ["off"], + "@stylistic/jsx-tag-spacing": + [ + "warn", + { closingSlash: "never", beforeSelfClosing: "always", afterOpening: "never" } + ], + "@stylistic/jsx-closing-bracket-location": + [ + "warn", + { nonEmpty: "tag-aligned", selfClosing: "after-props" } + ], + "@stylistic/jsx-first-prop-new-line": ["warn", "multiline"], + "@stylistic/jsx-one-expression-per-line": ["off"], + "@stylistic/jsx-closing-tag-location": ["warn"], + "@stylistic/arrow-parens": ["off"], + "@stylistic/quote-props": ["off"], + "@stylistic/multiline-ternary": ["warn"], + "@stylistic/no-trailing-spaces": ["warn"], + "@stylistic/no-mixed-spaces-and-tabs": ["warn"], + "@typescript-eslint/no-unused-vars": ["warn"], + "prefer-const": ["warn"], + "@stylistic/padded-blocks": ["warn"], + "no-empty": ["off"], + "@stylistic/eol-last": ["warn"] + } + }, + { + files: ["**/*.css"], + plugins: { css }, + rules: + { + "css/use-baseline": ["off"] + } + } +]); diff --git a/features/analytics/index.ts b/features/analytics/index.ts new file mode 100644 index 0000000..301debd --- /dev/null +++ b/features/analytics/index.ts @@ -0,0 +1,3 @@ +export { default as userPropertiesStorage } from "./utils/userPropertiesStorage"; +export { default as trackError } from "./utils/trackError"; +export { default as track } from "./utils/track"; diff --git a/features/analytics/utils/track.ts b/features/analytics/utils/track.ts new file mode 100644 index 0000000..2162843 --- /dev/null +++ b/features/analytics/utils/track.ts @@ -0,0 +1,11 @@ +export default function track(eventName: string, eventProperties?: Record): void +{ + try + { + analytics.track(eventName, eventProperties); + } + catch (ex) + { + console.error("Failed to send analytics event", ex); + } +} diff --git a/features/analytics/utils/trackError.ts b/features/analytics/utils/trackError.ts new file mode 100644 index 0000000..004378c --- /dev/null +++ b/features/analytics/utils/trackError.ts @@ -0,0 +1,15 @@ +export default function trackError(name: string, error: Error): void +{ + try + { + analytics.track(name, { + name: error.name, + message: error.message, + stack: error.stack ?? "no_stack" + }); + } + catch (ex) + { + console.error("Failed to send error report", ex); + } +} diff --git a/features/analytics/utils/userPropertiesStorage.ts b/features/analytics/utils/userPropertiesStorage.ts new file mode 100644 index 0000000..3b57010 --- /dev/null +++ b/features/analytics/utils/userPropertiesStorage.ts @@ -0,0 +1,35 @@ +import { cloudDisabled, collectionCount } from "@/features/collectionStorage"; +import { settings } from "@/utils/settings"; +import { WxtStorageItem } from "wxt/storage"; + +// @ts-expect-error we don't need to implement a full storage item +const userPropertiesStorage: WxtStorageItem, any> = +{ + getValue: async (): Promise => + { + console.log("userPropertiesStorage.getValue"); + const properties: UserProperties = + { + cloud_used: await cloudDisabled.getValue() ? "-1" : (await browser.storage.sync.getBytesInUse() / 102400).toString(), + collection_count: (await collectionCount.getValue()).toString() + }; + + for (const key of Object.keys(settings)) + { + const value = await settings[key as keyof typeof settings].getValue(); + properties[`option_${key}`] = value.valueOf().toString(); + } + + return properties; + }, + setValue: async () => { } +}; + +export default userPropertiesStorage; + +export type UserProperties = + { + collection_count: string; + cloud_used: string; + [key: `option_${string}`]: string; + }; diff --git a/features/collectionStorage/index.ts b/features/collectionStorage/index.ts new file mode 100644 index 0000000..2045680 --- /dev/null +++ b/features/collectionStorage/index.ts @@ -0,0 +1,12 @@ +import { collectionStorage } from "./utils/collectionStorage"; +export * from "./utils/getCollections"; + +export { default as getCollections } from "./utils/getCollections"; +export { default as resoveConflict } from "./utils/resolveConflict"; +export { default as saveCollections } from "./utils/saveCollections"; +export { default as setCloudStorage } from "./utils/setCloudStorage"; + +export const collectionCount = collectionStorage.count; +export const graphics = collectionStorage.graphics; + +export const cloudDisabled = collectionStorage.disableCloud; diff --git a/features/collectionStorage/utils/collectionStorage.ts b/features/collectionStorage/utils/collectionStorage.ts new file mode 100644 index 0000000..41c4c17 --- /dev/null +++ b/features/collectionStorage/utils/collectionStorage.ts @@ -0,0 +1,13 @@ +import { CollectionItem, GraphicsStorage } from "@/models/CollectionModels"; + +export const collectionStorage = +{ + chunkCount: storage.defineItem("sync:chunkCount", { fallback: 0 }), + syncLastUpdated: storage.defineItem("sync:lastUpdated", { fallback: 0 }), + localLastUpdated: storage.defineItem("local:lastUpdated", { fallback: 0 }), + localCollections: storage.defineItem("local:collections", { fallback: [] }), + count: storage.defineItem("local:count", { fallback: 0 }), + graphics: storage.defineItem("local:graphics", { fallback: {} }), + disableCloud: storage.defineItem("sync:disableCloud", { fallback: false }), + maxChunkCount: 12 +}; diff --git a/features/collectionStorage/utils/getChunkKeys.ts b/features/collectionStorage/utils/getChunkKeys.ts new file mode 100644 index 0000000..06222d2 --- /dev/null +++ b/features/collectionStorage/utils/getChunkKeys.ts @@ -0,0 +1,6 @@ +import { collectionStorage } from "./collectionStorage"; + +export default function getChunkKeys(start: number = 0, end: number = collectionStorage.maxChunkCount): string[] +{ + return Array.from({ length: end - start }, (_, i) => "c" + (i + start)); +} diff --git a/features/collectionStorage/utils/getCollections.ts b/features/collectionStorage/utils/getCollections.ts new file mode 100644 index 0000000..96b67e2 --- /dev/null +++ b/features/collectionStorage/utils/getCollections.ts @@ -0,0 +1,41 @@ +import { trackError } from "@/features/analytics"; +import { CollectionItem } from "@/models/CollectionModels"; +import getLogger from "@/utils/getLogger"; +import { collectionStorage } from "./collectionStorage"; +import getCollectionsFromCloud from "./getCollectionsFromCloud"; +import getCollectionsFromLocal from "./getCollectionsFromLocal"; +import saveCollectionsToLocal from "./saveCollectionsToLocal"; + +const logger = getLogger("getCollections"); + +export default async function getCollections(): Promise<[CollectionItem[], CloudStorageIssueType | null]> +{ + if (await collectionStorage.disableCloud.getValue() === true) + return [await getCollectionsFromLocal(), null]; + + const lastUpdatedLocal: number = await collectionStorage.localLastUpdated.getValue(); + const lastUpdatedSync: number = await collectionStorage.syncLastUpdated.getValue(); + + if (lastUpdatedLocal === lastUpdatedSync) + return [await getCollectionsFromLocal(), null]; + + if (lastUpdatedLocal > lastUpdatedSync) + return [await getCollectionsFromLocal(), "merge_conflict"]; + + try + { + const collections: CollectionItem[] = await getCollectionsFromCloud(); + await saveCollectionsToLocal(collections, lastUpdatedSync); + + return [collections, null]; + } + catch (ex) + { + logger("Failed to get cloud storage"); + console.error(ex); + trackError("cloud_get_error", ex as Error); + return [await getCollectionsFromLocal(), "parse_error"]; + } +} + +export type CloudStorageIssueType = "parse_error" | "merge_conflict"; diff --git a/features/collectionStorage/utils/getCollectionsFromCloud.ts b/features/collectionStorage/utils/getCollectionsFromCloud.ts new file mode 100644 index 0000000..4f32a74 --- /dev/null +++ b/features/collectionStorage/utils/getCollectionsFromCloud.ts @@ -0,0 +1,20 @@ +import { CollectionItem } from "@/models/CollectionModels"; +import { decompress } from "lzutf8"; +import { collectionStorage } from "./collectionStorage"; +import getChunkKeys from "./getChunkKeys"; +import parseCollections from "./parseCollections"; + +export default async function getCollectionsFromCloud(): Promise +{ + const chunkCount: number = await collectionStorage.chunkCount.getValue(); + + if (chunkCount < 1) + return []; + + const chunks: Record = + await browser.storage.sync.get(getChunkKeys(0, chunkCount)) as Record; + + const data: string = decompress(Object.values(chunks).join(), { inputEncoding: "Base64" }); + + return parseCollections(data); +} diff --git a/features/collectionStorage/utils/getCollectionsFromLocal.ts b/features/collectionStorage/utils/getCollectionsFromLocal.ts new file mode 100644 index 0000000..f3d4c52 --- /dev/null +++ b/features/collectionStorage/utils/getCollectionsFromLocal.ts @@ -0,0 +1,7 @@ +import { CollectionItem } from "@/models/CollectionModels"; +import { collectionStorage } from "./collectionStorage"; + +export default async function getCollectionsFromLocal(): Promise +{ + return await collectionStorage.localCollections.getValue(); +} diff --git a/features/collectionStorage/utils/parseCollections.ts b/features/collectionStorage/utils/parseCollections.ts new file mode 100644 index 0000000..fd74191 --- /dev/null +++ b/features/collectionStorage/utils/parseCollections.ts @@ -0,0 +1,80 @@ +import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels"; + +export default function parseCollections(data: string): CollectionItem[] +{ + if (!data) + return []; + + const collections: CollectionItem[] = []; + const lines: string[] = data.split("\n"); + + for (const line of lines) + { + if (line.startsWith("c")) + { + const collection: CollectionItem = parseCollection(line); + collections.push(collection); + } + else if (line.startsWith("\tg")) + { + const group: GroupItem = parseGroup(line); + collections[collections.length - 1].items.push(group); + } + else if (line.startsWith("\t\tt")) + { + const tab: TabItem = parseTab(line); + + const collectionIndex: number = collections.length - 1; + const groupIndex: number = collections[collectionIndex].items.length - 1; + + (collections[collectionIndex].items[groupIndex] as GroupItem).items.push(tab); + } + else if (line.startsWith("\tt")) + { + const tab: TabItem = parseTab(line); + collections[collections.length - 1].items.push(tab); + } + } + + return collections; +} + +function parseCollection(data: string): CollectionItem +{ + return { + type: "collection", + timestamp: parseInt(data.match(/(?<=^c)\d+/)!.toString()), + color: data.match(/(?<=^c\d+\/)[a-z]+/)?.toString() as chrome.tabGroups.ColorEnum, + title: data.match(/(?<=^c[\da-z/]*\|).*/)?.toString(), + items: [] + }; +} + +function parseGroup(data: string): GroupItem +{ + const isPinned: boolean = data.match(/^\tg\/p$/) !== null; + + if (isPinned) + return { + type: "group", + pinned: true, + items: [] + }; + + return { + type: "group", + pinned: false, + color: data.match(/(?<=^\tg\/)[a-z]+/)?.toString() as chrome.tabGroups.ColorEnum, + title: data.match(/(?<=^\tg\/[a-z]+\|).*$/)?.toString(), + items: [] + }; +} + +function parseTab(data: string): TabItem +{ + return { + type: "tab", + url: data.match(/(?<=^(\t){1,2}t\|).*(?=\|)/)!.toString(), + title: data.match(/(?<=^(\t){1,2}t\|.*\|).*$/)?.toString() + }; +} diff --git a/features/collectionStorage/utils/resolveConflict.ts b/features/collectionStorage/utils/resolveConflict.ts new file mode 100644 index 0000000..d0d920f --- /dev/null +++ b/features/collectionStorage/utils/resolveConflict.ts @@ -0,0 +1,43 @@ +import { trackError } from "@/features/analytics"; +import { CollectionItem } from "@/models/CollectionModels"; +import getLogger from "@/utils/getLogger"; +import { collectionStorage } from "./collectionStorage"; +import getCollectionsFromCloud from "./getCollectionsFromCloud"; +import getCollectionsFromLocal from "./getCollectionsFromLocal"; +import saveCollectionsToCloud from "./saveCollectionsToCloud"; +import saveCollectionsToLocal from "./saveCollectionsToLocal"; + +const logger = getLogger("resolveConflict"); + +export default function resolveConflict(acceptSource: "local" | "sync"): Promise +{ + if (acceptSource === "local") + return replaceCloudWithLocal(); + + return replaceLocalWithCloud(); +} + +async function replaceCloudWithLocal(): Promise +{ + const collections: CollectionItem[] = await getCollectionsFromLocal(); + const lastUpdated: number = await collectionStorage.localLastUpdated.getValue(); + + await saveCollectionsToCloud(collections, lastUpdated); +} + +async function replaceLocalWithCloud(): Promise +{ + try + { + const collections: CollectionItem[] = await getCollectionsFromCloud(); + const lastUpdated: number = await collectionStorage.syncLastUpdated.getValue(); + + await saveCollectionsToLocal(collections, lastUpdated); + } + catch (ex) + { + logger("Failed to get cloud storage"); + console.error(ex); + trackError("conflict_resolve_with_cloud_error", ex as Error); + } +} diff --git a/features/collectionStorage/utils/saveCollections.ts b/features/collectionStorage/utils/saveCollections.ts new file mode 100644 index 0000000..857ee7a --- /dev/null +++ b/features/collectionStorage/utils/saveCollections.ts @@ -0,0 +1,47 @@ +import { trackError } from "@/features/analytics"; +import { CollectionItem, GraphicsStorage } from "@/models/CollectionModels"; +import getLogger from "@/utils/getLogger"; +import sendNotification from "@/utils/sendNotification"; +import { collectionStorage } from "./collectionStorage"; +import saveCollectionsToCloud from "./saveCollectionsToCloud"; +import saveCollectionsToLocal from "./saveCollectionsToLocal"; +import updateGraphics from "./updateGraphics"; + +const logger = getLogger("saveCollections"); + +export default async function saveCollections( + collections: CollectionItem[], + updateCloud: boolean = true, + graphicsCache?: GraphicsStorage +): Promise +{ + const timestamp: number = Date.now(); + await saveCollectionsToLocal(collections, timestamp); + + if (updateCloud && await collectionStorage.disableCloud.getValue() !== true) + try + { + await saveCollectionsToCloud(collections, timestamp); + } + catch (ex) + { + logger("Failed to save cloud storage"); + console.error(ex); + trackError("cloud_save_error", ex as Error); + + if ((ex as Error).message.includes("MAX_WRITE_OPERATIONS_PER_MINUTE")) + await sendNotification({ + title: i18n.t("notifications.error_quota_exceeded.title"), + message: i18n.t("notifications.error_quota_exceeded.message"), + icon: "/notification_icons/cloud_error.png" + }); + else + await sendNotification({ + title: i18n.t("notifications.error_storage_full.title"), + message: i18n.t("notifications.error_storage_full.message"), + icon: "/notification_icons/cloud_error.png" + }); + } + + await updateGraphics(collections, graphicsCache); +}; diff --git a/features/collectionStorage/utils/saveCollectionsToCloud.ts b/features/collectionStorage/utils/saveCollectionsToCloud.ts new file mode 100644 index 0000000..efd6fac --- /dev/null +++ b/features/collectionStorage/utils/saveCollectionsToCloud.ts @@ -0,0 +1,55 @@ +import { CollectionItem } from "@/models/CollectionModels"; +import { compress } from "lzutf8"; +import { WxtStorageItem } from "wxt/storage"; +import { collectionStorage } from "./collectionStorage"; +import getChunkKeys from "./getChunkKeys"; +import serializeCollections from "./serializeCollections"; + +export default async function saveCollectionsToCloud(collections: CollectionItem[], timestamp: number): Promise +{ + if (!collections || collections.length < 1) + { + await collectionStorage.chunkCount.setValue(0); + await browser.storage.sync.remove(getChunkKeys()); + return; + } + + const data: string = compress(serializeCollections(collections), { outputEncoding: "Base64" }); + const chunks: string[] = splitIntoChunks(data); + + if (chunks.length > collectionStorage.maxChunkCount) + throw new Error("Data is too large to be stored in sync storage."); + + // Since there's a limit for cloud write operations, we need to write all chunks in one go. + const newRecords: Record = + { + [getStorageKey(collectionStorage.chunkCount)]: chunks.length, + [getStorageKey(collectionStorage.syncLastUpdated)]: timestamp + }; + + for (let i = 0; i < chunks.length; i++) + newRecords[`c${i}`] = chunks[i]; + + await browser.storage.sync.set(newRecords); + + if (chunks.length < collectionStorage.maxChunkCount) + await browser.storage.sync.remove(getChunkKeys(chunks.length)); +} + +function splitIntoChunks(data: string): string[] +{ + // QUOTA_BYTES_PER_ITEM includes length of key name, length of content and 2 more bytes (for unknown reason). + const chunkKey: string = getChunkKeys(collectionStorage.maxChunkCount - 1)[0]; + const chunkSize = (chrome.storage.sync.QUOTA_BYTES_PER_ITEM ?? 8192) - chunkKey.length - 2; + const chunks: string[] = []; + + for (let i = 0; i < data.length; i += chunkSize) + chunks.push(data.slice(i, i + chunkSize)); + + return chunks; +} + +function getStorageKey(storageItem: WxtStorageItem): string +{ + return storageItem.key.split(":")[1]; +} diff --git a/features/collectionStorage/utils/saveCollectionsToLocal.ts b/features/collectionStorage/utils/saveCollectionsToLocal.ts new file mode 100644 index 0000000..f6707bb --- /dev/null +++ b/features/collectionStorage/utils/saveCollectionsToLocal.ts @@ -0,0 +1,9 @@ +import { CollectionItem } from "@/models/CollectionModels"; +import { collectionStorage } from "./collectionStorage"; + +export default async function saveCollectionsToLocal(collections: CollectionItem[], timestamp: number): Promise +{ + await collectionStorage.localCollections.setValue(collections); + await collectionStorage.count.setValue(collections.length); + await collectionStorage.localLastUpdated.setValue(timestamp); +} diff --git a/features/collectionStorage/utils/serializeCollections.ts b/features/collectionStorage/utils/serializeCollections.ts new file mode 100644 index 0000000..76d7c8d --- /dev/null +++ b/features/collectionStorage/utils/serializeCollections.ts @@ -0,0 +1,74 @@ +import { CollectionItem, GroupItem, TabItem } from "@/models/CollectionModels"; + +export default function serializeCollections(collections: CollectionItem[]): string +{ + let data: string = ""; + + for (const collection of collections) + { + data += getCollectionString(collection); + + for (const item of collection.items) + { + if (item.type === "group") + { + data += getGroupString(item); + + for (const tab of item.items) + data += `\t${getTabString(tab)}`; + } + else if (item.type === "tab") + data += getTabString(item); + } + } + + return data; +} + +function getCollectionString(collection: CollectionItem): string +{ + let data: string = `c${collection.timestamp}`; + + if (collection.color) + data += `/${collection.color}`; + + if (collection.title) + data += `|${collection.title}`; + + data += "\n"; + + return data; +} + +function getGroupString(group: GroupItem): string +{ + let data: string = "\tg"; + + if (group.pinned === true) + data += "/p"; + else + { + data += `/${group.color}`; + + if (group.title) + data += `|${group.title}`; + } + + data += "\n"; + + return data; +} + +function getTabString(tab: TabItem): string +{ + let data: string = "\tt"; + + data += `|${tab.url}|`; + + if (tab.title) + data += tab.title; + + data += "\n"; + + return data; +} diff --git a/features/collectionStorage/utils/setCloudStorage.ts b/features/collectionStorage/utils/setCloudStorage.ts new file mode 100644 index 0000000..e902c93 --- /dev/null +++ b/features/collectionStorage/utils/setCloudStorage.ts @@ -0,0 +1,20 @@ +import { sendMessage } from "@/utils/messaging"; +import { collectionStorage } from "./collectionStorage"; +import saveCollectionsToCloud from "./saveCollectionsToCloud"; + +export default async function setCloudStorage(enable: boolean): Promise +{ + if (enable) + { + await collectionStorage.disableCloud.setValue(false); + const collections = await collectionStorage.localCollections.getValue(); + const lastUpdated = await collectionStorage.localLastUpdated.getValue(); + await saveCollectionsToCloud(collections, lastUpdated); + } + else + { + await collectionStorage.disableCloud.setValue(true); + await saveCollectionsToCloud([], 0); + await sendMessage("refreshCollections", undefined); + } +} diff --git a/features/collectionStorage/utils/updateGraphics.ts b/features/collectionStorage/utils/updateGraphics.ts new file mode 100644 index 0000000..a6669d0 --- /dev/null +++ b/features/collectionStorage/utils/updateGraphics.ts @@ -0,0 +1,54 @@ +import { CollectionItem, GraphicsItem, GraphicsStorage } from "@/models/CollectionModels"; +import { sendMessage } from "@/utils/messaging"; +import { collectionStorage } from "./collectionStorage"; + +export default async function updateGraphics( + collections: CollectionItem[], + graphicsCache?: GraphicsStorage +): Promise +{ + const localGraphics: GraphicsStorage = await collectionStorage.graphics.getValue(); + const tempGraphics: GraphicsStorage = graphicsCache || await sendMessage("getGraphicsCache", undefined); + + function getGraphics(url: string): GraphicsItem | null + { + const preview = tempGraphics[url]?.preview ?? localGraphics[url]?.preview; + const capture = tempGraphics[url]?.capture ?? localGraphics[url]?.capture; + const icon = tempGraphics[url]?.icon ?? localGraphics[url]?.icon; + + const graphics: GraphicsItem = {}; + + if (preview) + graphics.preview = preview; + if (icon) + graphics.icon = icon; + if (capture) + graphics.capture = capture; + + return preview || icon ? graphics : null; + } + + const newGraphics: GraphicsStorage = {}; + + for (const collection of collections) + for (const item of collection.items) + { + if (item.type === "group") + for (const tab of item.items) + { + const graphics = getGraphics(tab.url); + + if (graphics) + newGraphics[tab.url] = graphics; + } + else + { + const graphics = getGraphics(item.url); + + if (graphics) + newGraphics[item.url] = graphics; + } + } + + await collectionStorage.graphics.setValue(newGraphics); +} diff --git a/features/migration/hooks/useLocalMigration.ts b/features/migration/hooks/useLocalMigration.ts new file mode 100644 index 0000000..80b60bf --- /dev/null +++ b/features/migration/hooks/useLocalMigration.ts @@ -0,0 +1,10 @@ +import migrateLocalStorage from "../utils/migrateLocalStorage"; + +export default function useLocalMigration(): void +{ + useEffect(() => + { + if (globalThis.localStorage?.getItem("sets")) + migrateLocalStorage().then(() => document.location.reload()); + }, []); +} diff --git a/features/migration/index.ts b/features/migration/index.ts new file mode 100644 index 0000000..a3b01ea --- /dev/null +++ b/features/migration/index.ts @@ -0,0 +1,2 @@ +export { default as useLocalMigration } from "./hooks/useLocalMigration"; +export { default as migrateStorage } from "./utils/migrateStorage"; diff --git a/features/migration/models/LegacyModels.ts b/features/migration/models/LegacyModels.ts new file mode 100644 index 0000000..1c91228 --- /dev/null +++ b/features/migration/models/LegacyModels.ts @@ -0,0 +1,15 @@ +export type LegacyCollection = + { + timestamp: number; + tabsCount: number; + titles: string[]; + links: string[]; + icons?: string[]; + thumbnails?: string[]; + }; + +export type LegacyGraphics = + { + pageCapture?: string; + iconUrl?: string; + }; diff --git a/features/migration/utils/migrateCollections.ts b/features/migration/utils/migrateCollections.ts new file mode 100644 index 0000000..d1a74d9 --- /dev/null +++ b/features/migration/utils/migrateCollections.ts @@ -0,0 +1,38 @@ +import { CollectionItem, GraphicsStorage, TabItem } from "@/models/CollectionModels"; +import { LegacyCollection } from "../models/LegacyModels"; + +export default function migrateCollections(legacyCollections: LegacyCollection[]): [CollectionItem[], GraphicsStorage] +{ + const collections: CollectionItem[] = []; + const graphics: GraphicsStorage = {}; + + for (let i = 0; i < legacyCollections.length; i++) + { + const legacyCollection: LegacyCollection = legacyCollections[i]; + const items: TabItem[] = legacyCollection.links.map((url, index) => + { + const title: string | undefined = legacyCollection.titles[index]; + const icon: string | undefined = legacyCollection.icons?.[index]; + const capture: string | undefined = legacyCollection.thumbnails?.[index]; + + if (!graphics[url]) + graphics[url] = { icon, capture }; + else + graphics[url] = { icon: graphics[url].icon ?? icon, capture: graphics[url].preview ?? capture }; + + return { + type: "tab", + url, + title + }; + }); + + collections.push({ + type: "collection", + timestamp: legacyCollection.timestamp, + items + }); + } + + return [collections, graphics]; +} diff --git a/features/migration/utils/migrateLocalStorage.ts b/features/migration/utils/migrateLocalStorage.ts new file mode 100644 index 0000000..4c6a0e9 --- /dev/null +++ b/features/migration/utils/migrateLocalStorage.ts @@ -0,0 +1,18 @@ +import { getCollections } from "@/features/collectionStorage"; +import saveCollections from "@/features/collectionStorage/utils/saveCollections"; +import { LegacyCollection } from "../models/LegacyModels"; +import migrateCollections from "./migrateCollections"; + +export default async function migrateLocalStorage(): Promise +{ + // Retrieve v1 collections + const legacyCollections: LegacyCollection[] = JSON.parse(globalThis.localStorage?.getItem("sets") || "[]"); + + // Nuke localStorage + globalThis.localStorage?.clear(); + + // Migrate collections + const [resultCollections, resultGraphics] = migrateCollections(legacyCollections); + const [collections] = await getCollections(); + await saveCollections([...collections, ...resultCollections], true, resultGraphics); +} diff --git a/features/migration/utils/migrateStorage.ts b/features/migration/utils/migrateStorage.ts new file mode 100644 index 0000000..99eb257 --- /dev/null +++ b/features/migration/utils/migrateStorage.ts @@ -0,0 +1,60 @@ +import { saveCollections } from "@/features/collectionStorage"; +import { GraphicsStorage } from "@/models/CollectionModels"; +import { settings } from "@/utils/settings"; +import { decompress } from "lzutf8"; +import { LegacyCollection, LegacyGraphics } from "../models/LegacyModels"; +import migrateCollections from "./migrateCollections"; + +export default async function migrateStorage(): Promise +{ + // Retrieve settings + const loadOnRestore: boolean | null = await storage.getItem("sync:loadOnRestore"); + const setAsideOnClick: boolean | null = await storage.getItem("sync:setAsideOnClick"); + const showDeleteDialog: boolean | null = await storage.getItem("sync:showDeleteDialog"); + const listView: boolean | null = await storage.getItem("sync:listview"); + + // Retrieve v2 collections + const legacyCollections: LegacyCollection[] = []; + Object.entries(await browser.storage.sync.get(null)).forEach(([key, value]) => + { + if (key.startsWith("set_")) + legacyCollections.push({ + ...JSON.parse(decompress(value, { inputEncoding: "StorageBinaryString" })), + timestamp: parseInt(key.substring(4)) + }); + }); + + // Retrieve v2 graphics + const v2Graphics: Record = await storage.getItem("local:thumbnails") ?? {}; + + // Nuke everything + await browser.storage.local.clear(); + await browser.storage.sync.clear(); + + // Migrate collections & graphics + const [collections] = migrateCollections(legacyCollections); + const graphics: GraphicsStorage = {}; + + for (const [key, record] of Object.entries(v2Graphics)) + { + if (!graphics[key]) + graphics[key] = { icon: record.iconUrl, capture: record.pageCapture }; + else + { + graphics[key].icon ??= record.iconUrl; + graphics[key].capture ??= record.pageCapture; + } + } + + await saveCollections(collections, true, graphics); + + // Migrate settings + if (loadOnRestore !== null) + settings.dismissOnLoad.setValue(!loadOnRestore); + if (setAsideOnClick !== null) + settings.contextAction.setValue(setAsideOnClick ? "action" : "open"); + if (showDeleteDialog !== null) + settings.deletePrompt.setValue(showDeleteDialog); + if (listView !== null) + settings.tilesView.setValue(!listView); +} diff --git a/features/v3welcome/components/WelcomeDialog.tsx b/features/v3welcome/components/WelcomeDialog.tsx new file mode 100644 index 0000000..7e295ef --- /dev/null +++ b/features/v3welcome/components/WelcomeDialog.tsx @@ -0,0 +1,70 @@ +import { useTheme } from "@/contexts/ThemeProvider"; +import { v3blogPost } from "@/data/links"; +import { track } from "@/features/analytics"; +import extLink from "@/utils/extLink"; +import * as fui from "@fluentui/react-components"; + +export default function WelcomeDialog(): React.ReactElement +{ + const { isDark } = useTheme(); + const cls = useStyles(); + + return ( + + + + + + { i18n.t("features.v3welcome.title") } + + + { i18n.t("features.v3welcome.text1") } + + + { i18n.t("features.v3welcome.text2") } + +
    +
  • { i18n.t("features.v3welcome.list.item1") }
  • +
  • { i18n.t("features.v3welcome.list.item2") }
  • +
  • { i18n.t("features.v3welcome.list.item3") }
  • +
  • { i18n.t("features.v3welcome.list.item4") }
  • +
  • { i18n.t("features.v3welcome.list.item5") }
  • +
+ + { i18n.t("features.v3welcome.text3") } + + +
+ + + + track("visit_blog_button_click") } + > + { i18n.t("features.v3welcome.actions.visit_blog") } + + + + + { i18n.t("common.actions.close") } + + + +
+
+ ); +} + +const useStyles = fui.makeStyles({ + root: + { + display: "flex", + flexFlow: "column", + gap: fui.tokens.spacingVerticalS + }, + image: + { + display: "contents" + } +}); diff --git a/features/v3welcome/hooks/useWelcomeDialog.tsx b/features/v3welcome/hooks/useWelcomeDialog.tsx new file mode 100644 index 0000000..36f7fd8 --- /dev/null +++ b/features/v3welcome/hooks/useWelcomeDialog.tsx @@ -0,0 +1,17 @@ +import { useDialog } from "@/contexts/DialogProvider"; +import WelcomeDialog from "../components/WelcomeDialog"; +import { showWelcomeDialog } from "../utils/showWelcomeDialog"; + +export default function useWelcomeDialog(): void +{ + const dialog = useDialog(); + + useEffect(() => + { + showWelcomeDialog.getValue().then(showWelcome => + { + if (showWelcome || import.meta.env.DEV) + dialog.pushCustom(, undefined, () => showWelcomeDialog.removeValue()); + }); + }, []); +} diff --git a/features/v3welcome/index.ts b/features/v3welcome/index.ts new file mode 100644 index 0000000..7f2d83f --- /dev/null +++ b/features/v3welcome/index.ts @@ -0,0 +1,2 @@ +export { default as useWelcomeDialog } from "./hooks/useWelcomeDialog"; +export { showWelcomeDialog } from "./utils/showWelcomeDialog"; diff --git a/features/v3welcome/utils/showWelcomeDialog.ts b/features/v3welcome/utils/showWelcomeDialog.ts new file mode 100644 index 0000000..064da88 --- /dev/null +++ b/features/v3welcome/utils/showWelcomeDialog.ts @@ -0,0 +1,6 @@ +export const showWelcomeDialog = storage.defineItem( + "local:showWelcome", + { + fallback: false + } +); diff --git a/fonts/Microsoft_Fabric_assets_license_agreement_May_2019.pdf b/fonts/Microsoft_Fabric_assets_license_agreement_May_2019.pdf deleted file mode 100644 index 98dfb65..0000000 Binary files a/fonts/Microsoft_Fabric_assets_license_agreement_May_2019.pdf and /dev/null differ diff --git a/fonts/Segoe_EULA.txt b/fonts/Segoe_EULA.txt deleted file mode 100644 index 1df7881..0000000 --- a/fonts/Segoe_EULA.txt +++ /dev/null @@ -1 +0,0 @@ -๏ปฟYou may use the Segoe MDL2 Assets and Segoe UI fonts or glyphs included in this file (โ€œSoftwareโ€) solely to design, develop and test your programs for Microsoft Office. Microsoft Office includes but is not limited to any software product or service branded by trademark, trade dress, copyright or some other recognized means, as a product or service of Microsoft Office. This license does not grant you the right to distribute or sublicense all or part of the Software to any third party. By using the Software you agree to these terms. If you do not agree to these terms, do not use the Segoe MDL2 and Segoe UI Fonts or Glyphs. \ No newline at end of file diff --git a/fonts/segoemdl2.ttf b/fonts/segoemdl2.ttf deleted file mode 100644 index 81286ac..0000000 Binary files a/fonts/segoemdl2.ttf and /dev/null differ diff --git a/fonts/segoemdl2.woff b/fonts/segoemdl2.woff deleted file mode 100644 index cb78be9..0000000 Binary files a/fonts/segoemdl2.woff and /dev/null differ diff --git a/fonts/segoeui.ttf b/fonts/segoeui.ttf deleted file mode 100644 index b0e7889..0000000 Binary files a/fonts/segoeui.ttf and /dev/null differ diff --git a/fonts/segoeui.woff b/fonts/segoeui.woff deleted file mode 100644 index d0ed4b2..0000000 Binary files a/fonts/segoeui.woff and /dev/null differ diff --git a/hooks/useBmcStyles.ts b/hooks/useBmcStyles.ts new file mode 100644 index 0000000..2fc7a2b --- /dev/null +++ b/hooks/useBmcStyles.ts @@ -0,0 +1,13 @@ +import { makeStyles } from "@fluentui/react-components"; + +export const useBmcStyles = makeStyles({ + button: + { + backgroundColor: "#ff813f", + + "&:hover, &:hover:active": + { + backgroundColor: "#ff6b1c" + } + } +}); diff --git a/hooks/useDangerStyles.ts b/hooks/useDangerStyles.ts new file mode 100644 index 0000000..929e3b5 --- /dev/null +++ b/hooks/useDangerStyles.ts @@ -0,0 +1,42 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useDangerStyles = makeStyles({ + menuItem: + { + color: tokens.colorStatusDangerForeground1 + " !important", + + "& .fui-MenuItem__icon": + { + color: tokens.colorStatusDangerForeground1 + " !important" + } + }, + buttonPrimary: + { + backgroundColor: tokens.colorStatusDangerBackground3, + color: tokens.colorNeutralForegroundStaticInverted, + + "&:hover": + { + backgroundColor: tokens.colorStatusDangerBackground3Hover, + + "&:active": + { + backgroundColor: tokens.colorStatusDangerBackground3Pressed + } + } + }, + buttonSubtle: + { + color: tokens.colorStatusDangerForeground1, + + "&:hover": + { + color: tokens.colorStatusDangerForeground2, + + "&:active": + { + color: tokens.colorStatusDangerForeground3 + } + } + } +}); diff --git a/hooks/useGroupColors.ts b/hooks/useGroupColors.ts new file mode 100644 index 0000000..2ab500e --- /dev/null +++ b/hooks/useGroupColors.ts @@ -0,0 +1,49 @@ +import { makeStyles, tokens } from "@fluentui/react-components"; + +export const useGroupColors: () => Record = makeStyles({ + blue: + { + "--border": tokens.colorPaletteBlueBorderActive, + "--text": tokens.colorNeutralForegroundInverted + }, + cyan: + { + "--border": tokens.colorPaletteTealBorderActive, + "--text": tokens.colorNeutralForegroundInverted + }, + green: + { + "--border": tokens.colorPaletteGreenBorderActive, + "--text": tokens.colorNeutralForegroundInverted + }, + grey: + { + "--border": tokens.colorPalettePlatinumBorderActive, + "--text": tokens.colorNeutralForegroundInverted + }, + orange: + { + "--border": tokens.colorPalettePeachBorderActive, + "--text": tokens.colorNeutralForegroundInverted + }, + pink: + { + "--border": tokens.colorPalettePinkBorderActive, + "--text": tokens.colorNeutralForegroundInverted + }, + purple: + { + "--border": tokens.colorPalettePurpleBorderActive, + "--text": tokens.colorNeutralForegroundStaticInverted + }, + red: + { + "--border": tokens.colorPaletteRedBackground3, + "--text": tokens.colorNeutralForegroundStaticInverted + }, + yellow: + { + "--border": tokens.colorPaletteYellowBorderActive, + "--text": tokens.colorNeutralForeground1Static + } +}); diff --git a/hooks/useSettings.ts b/hooks/useSettings.ts new file mode 100644 index 0000000..3000497 --- /dev/null +++ b/hooks/useSettings.ts @@ -0,0 +1,24 @@ +import { settings } from "@/utils/settings"; + +export default function useSettings(key: K): SettingsHook +{ + const [value, setValue] = useState | null>(null); + + useEffect(() => + { + settings[key].getValue() + .then(value => setValue(value as SettingsValue)); + + const unwatch = settings[key].watch(value => setValue(value as SettingsValue)); + + return () => unwatch(); + }, [key]); + + return [value, settings[key].setValue] as SettingsHook; +} + +export type SettingsValue = + typeof settings[K] extends { fallback: infer T; } ? T : never; + +export type SettingsHook = + [SettingsValue | null, (newValue: SettingsValue) => Promise]; diff --git a/hooks/useStorageInfo.ts b/hooks/useStorageInfo.ts new file mode 100644 index 0000000..ed736a0 --- /dev/null +++ b/hooks/useStorageInfo.ts @@ -0,0 +1,27 @@ +export default function useStorageInfo(): StorageInfoHook +{ + const [bytesInUse, setBytesInUse] = useState(0); + + useEffect(() => + { + const updateValue = async () => + setBytesInUse(await browser.storage.sync.getBytesInUse()); + + updateValue(); + browser.storage.sync.onChanged.addListener(updateValue); + return () => browser.storage.sync.onChanged.removeListener(updateValue); + }, []); + + return { + bytesInUse, + storageQuota: chrome.storage.sync.QUOTA_BYTES ?? 102400, + usedStorageRatio: bytesInUse / (chrome.storage.sync.QUOTA_BYTES ?? 102400) + }; +} + +export type StorageInfoHook = + { + bytesInUse: number; + storageQuota: number; + usedStorageRatio: number; + }; diff --git a/icons/bmc.svg b/icons/bmc.svg deleted file mode 100644 index d7b9b32..0000000 --- a/icons/bmc.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - Group - Created with Sketch. - - - - - - \ No newline at end of file diff --git a/icons/check.svg b/icons/check.svg deleted file mode 100644 index ba82210..0000000 --- a/icons/check.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/icons/feedback.svg b/icons/feedback.svg deleted file mode 100644 index 69c40c4..0000000 --- a/icons/feedback.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/icons/github.svg b/icons/github.svg deleted file mode 100644 index 12b008f..0000000 --- a/icons/github.svg +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/icons/list.svg b/icons/list.svg deleted file mode 100644 index 11f8208..0000000 --- a/icons/list.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/images/edge_icon.png b/images/edge_icon.png deleted file mode 100644 index cda0f0d..0000000 Binary files a/images/edge_icon.png and /dev/null differ diff --git a/images/tab_icon.png b/images/tab_icon.png deleted file mode 100644 index d303887..0000000 Binary files a/images/tab_icon.png and /dev/null differ diff --git a/images/tab_icon_dark.png b/images/tab_icon_dark.png deleted file mode 100644 index ea35225..0000000 Binary files a/images/tab_icon_dark.png and /dev/null differ diff --git a/images/tab_thumbnail.png b/images/tab_thumbnail.png deleted file mode 100644 index ed1a455..0000000 Binary files a/images/tab_thumbnail.png and /dev/null differ diff --git a/images/tab_thumbnail_dark.png b/images/tab_thumbnail_dark.png deleted file mode 100644 index 34da513..0000000 Binary files a/images/tab_thumbnail_dark.png and /dev/null differ diff --git a/js/aside-script.js b/js/aside-script.js deleted file mode 100644 index 2609d0c..0000000 --- a/js/aside-script.js +++ /dev/null @@ -1,400 +0,0 @@ -if (window.location === window.parent.location && !window.location.protocol.includes("-extension:")) // For open/close call -{ - var iframe = document.querySelector("iframe.tabsAsideIframe"); - if (!iframe) - { - iframe = document.createElement('iframe'); - - iframe.setAttribute("class", "tabsAsideIframe"); - - iframe.style.position = "fixed"; - iframe.style.zIndex = "2147483647"; - - iframe.style.height = "100%"; - iframe.style.width = "100%"; - - iframe.style.top = "0px"; - iframe.style.right = "0px"; - iframe.style.left = "0px"; - iframe.style.bottom = "0px"; - - iframe.style.border = "none"; - iframe.style.background = "transparent"; - iframe.style.opacity = 0; - - var bodyStyle = document.body.getAttribute("style"); - document.body.setAttribute("style", "overflow: hidden !important"); - - iframe.onload = () => setTimeout(() => iframe.style.opacity = 1, 100); - - iframe.src = chrome.extension.getURL("TabsAside.html"); - document.body.appendChild(iframe); - } - else - { - iframe.contentWindow.postMessage({ target: "TabsAside", command: "TogglePane" }, "*"); - setTimeout(() => - { - iframe.remove(); - document.body.setAttribute("style", bodyStyle); - }, 250); - } -} -else // For init call - Initialize(); - -function Initialize() -{ - var pane = document.querySelector(".tabsAside.pane"); - if (!pane) - return; - - if (window.location !== window.parent.location) - { - pane.setAttribute("embedded", ""); - window.addEventListener('message', event => - { - if (event.data.target == "TabsAside") - { - pane.parentElement.style.opacity = 0; - pane.removeAttribute("opened"); - } - }); - } - - UpdateLocale(); - - if (window.matchMedia("(prefers-color-scheme: dark)").matches) - { - pane.parentElement.setAttribute("darkmode", ""); - document.querySelector("#icon").href = "icons/dark/empty/16.png"; - } - - document.querySelector(".tabsAside .saveTabs").onclick = SetTabsAside; - document.querySelector(".tabsAside header .btn.remove").addEventListener("click", () => - chrome.runtime.sendMessage({ command: "togglePane" }) - ); - document.querySelector(".tabsAside.closeArea").addEventListener("click", () => - chrome.runtime.sendMessage({ command: "togglePane" }) - ); - - document.querySelector("nav > p > small").textContent = chrome.runtime.getManifest()["version"]; - - // Tabs dismiss option - var loadOnRestoreCheckbox = document.querySelector("#loadOnRestore"); - chrome.storage.sync.get( - { "loadOnRestore": true }, - values => loadOnRestoreCheckbox.checked = values?.loadOnRestore ?? true - ); - chrome.storage.onChanged.addListener((changes, namespace) => - { - if (namespace == 'sync') - for (key in changes) - if (key === 'loadOnRestore') - loadOnRestoreCheckbox.checked = changes[key].newValue - }); - loadOnRestoreCheckbox.addEventListener("click", () => - chrome.storage.sync.set( - { - "loadOnRestore": loadOnRestoreCheckbox.checked - }) - ); - - // Extension browser icon action - var swapIconAction = document.querySelector("#swapIconAction"); - chrome.storage.sync.get( - { "setAsideOnClick": false }, - values => swapIconAction.checked = values?.setAsideOnClick ?? false - ); - chrome.storage.onChanged.addListener((changes, namespace) => - { - if (namespace == 'sync') - for (key in changes) - if (key === 'setAsideOnClick') - swapIconAction.checked = changes[key].newValue - }); - swapIconAction.addEventListener("click", () => - chrome.storage.sync.set( - { - "setAsideOnClick": swapIconAction.checked - }) - ); - - // Deletion confirmation dialog - var showDeleteDialog = document.querySelector("#showDeleteDialog"); - chrome.storage.sync.get( - { "showDeleteDialog": true }, - values => showDeleteDialog.checked = values.showDeleteDialog - ); - chrome.storage.onChanged.addListener((changes, namespace) => - { - if (namespace == 'sync') - for (key in changes) - if (key === 'showDeleteDialog') - showDeleteDialog.checked = changes[key].newValue - }); - showDeleteDialog.addEventListener("click", () => - chrome.storage.sync.set( - { - "showDeleteDialog": showDeleteDialog.checked - }) - ); - - // Collections view switch - chrome.storage.sync.get( - { "listview": false }, - values => - { - if (values?.listview) - pane.setAttribute("listview", ""); - } - ); - document.querySelectorAll(".listviewSwitch").forEach(i => - { - i.onclick = (args) => - { - if (args.currentTarget.classList[1] == "list") - { - pane.setAttribute("listview", ""); - chrome.storage.sync.set({ "listview": true }); - } - else - { - pane.removeAttribute("listview"); - chrome.storage.sync.set({ "listview": false }); - } - } - }); - chrome.storage.onChanged.addListener((changes, namespace) => - { - if (namespace == 'sync') - for (key in changes) - if (key === 'listview') - if (changes[key].newValue) - pane.setAttribute("listview", ""); - else - pane.removeAttribute("listview"); - }); - - document.querySelectorAll(".tabsAside.pane > header nav button").forEach(i => - i.onclick = () => - { - if (i.hasAttribute("feedback-button")) - { - if (chrome.runtime.getManifest()["update_url"] && chrome.runtime.getManifest()["update_url"].includes("edge.microsoft.com")) - window.open("https://microsoftedge.microsoft.com/addons/detail/tabs-aside/kmnblllmalkiapkfknnlpobmjjdnlhnd", "_blank") - else - window.open("https://chrome.google.com/webstore/detail/tabs-aside/mgmjbodjgijnebfgohlnjkegdpbdjgin", "_blank") - } - else - window.open(i.value, "_blank"); - }); - - // Showing changelog badge if updated - chrome.storage.local.get({ "showUpdateBadge": false }, values => - { - if (values.showUpdateBadge) - { - pane.setAttribute("updated", ""); - let settingsButton = document.querySelector("header .btn.more"); - settingsButton.addEventListener("focusout", () => - { - if (!pane.hasAttribute("updated")) - return; - pane.removeAttribute("updated"); - chrome.storage.local.set({ "showUpdateBadge": false }); - }); - } - }); - - chrome.runtime.sendMessage({ command: "loadData" }, ({ collections, thumbnails }) => - ReloadCollections(collections, thumbnails)); - - chrome.runtime.onMessage.addListener((message) => - { - switch (message.command) - { - case "reloadCollections": - ReloadCollections(message.collections, message.thumbnails); - break; - } - }); - - setTimeout(() => pane.setAttribute("opened", ""), 100); -} - -function UpdateLocale() -{ - document.querySelectorAll("*[loc]").forEach(i => i.textContent = chrome.i18n.getMessage(i.getAttribute("loc"))); - document.querySelectorAll("*[loc_alt]").forEach(i => i.title = chrome.i18n.getMessage(i.getAttribute("loc_alt"))); - - var swapActionsLabel = document.querySelector("span[loc=swapIconAction]"); - chrome.runtime.sendMessage({ command: "getShortcuts" }, (shortcuts) => - swapActionsLabel.textContent = swapActionsLabel.textContent.replace("%TOGGLE_SHORTCUT%", shortcuts.filter(i => i.name == "toggle-pane")[0].shortcut) - ); -} - - -function ReloadCollections(collections, thumbnails) -{ - document.querySelector(".tabsAside section h2").removeAttribute("hidden"); - document.querySelectorAll(".tabsAside section > div").forEach(i => i.remove()); - - for (var collection of Object.values(collections).reverse()) - AddCollection(collection, thumbnails); -} - -function AddCollection(collection, thumbnails) -{ - var list = document.querySelector(".tabsAside section"); - list.querySelector("h2").setAttribute("hidden", ""); - - var rawTabs = ""; - - for (var i = 0; i < collection.links.length; i++) - rawTabs += - "
" + - "
" + - "
" + - "" + collection.titles[i] + "" + - "" + - "
" + - "
"; - - list.innerHTML += - "
" + - "
" + - "" + - "Restore tabs" + - "
" + - "" + - "" + - "
" + - "" + - "" + collection.links.length + " " + chrome.i18n.getMessage("tabs") +"" + - "
" + - - "
" + rawTabs + "
" + - "
"; - - UpdateLocale(); - - list.querySelectorAll("input").forEach(i => - i.addEventListener("focusout",(event) => RenameCollection(i.parentElement.parentElement, event.target.value))); - - list.querySelectorAll(".restoreCollection").forEach(i => - i.onclick = () => RestoreTabs(i.parentElement.parentElement)); - - list.querySelectorAll(".restoreCollection.noDelete").forEach(i => - i.onclick = () => RestoreTabs(i.parentElement.parentElement.parentElement.parentElement, false)); - - list.querySelectorAll(".set > div").forEach(i => - i.onclick = (args) => - { - if (args.target.localName != "button") - chrome.runtime.sendMessage( - { - command: "openTab", - url: i.getAttribute("value") - } - ); - }); - - document.querySelectorAll(".header .btn.remove").forEach(i => - i.onclick = () => RemoveTabs(i.parentElement.parentElement)); - - document.querySelectorAll(".set .btn.remove").forEach(i => - i.onclick = () => - RemoveOneTab(i.parentElement.parentElement)); -} - -function SetTabsAside() -{ - chrome.runtime.sendMessage({ command: "saveTabs" }); -} - -function RenameCollection(collectionData, name) -{ - chrome.runtime.sendMessage( - { - command: "renameCollection", - newName: name, - collectionKey: collectionData.id - }); -} - -function RestoreTabs(collectionData, removeCollection = true) -{ - chrome.runtime.sendMessage( - { - command: "restoreTabs", - removeCollection: removeCollection, - collectionKey: collectionData.id - }, - () => - { - if (removeCollection) - RemoveCollectionElement(collectionData); - } - ); -} - -function RemoveTabs(collectionData) -{ - chrome.storage.sync.get({ "showDeleteDialog": true }, values => - { - if (values.showDeleteDialog && !confirm(chrome.i18n.getMessage("removeCollectionConfirm"))) - return; - - chrome.runtime.sendMessage( - { - command: "deleteTabs", - collectionKey: collectionData.id - }, - () => RemoveCollectionElement(collectionData) - ); - }); -} - -function RemoveOneTab(tabData) -{ - chrome.storage.sync.get({ "showDeleteDialog": true }, values => - { - if (values.showDeleteDialog && !confirm(chrome.i18n.getMessage("removeTabConfirm"))) - return; - - chrome.runtime.sendMessage( - { - command: "removeTab", - collectionKey: tabData.parentElement.parentElement.id, - tabIndex: Array.prototype.slice.call(tabData.parentElement.children).indexOf(tabData) - }, - () => - { - tabData.parentElement.previousElementSibling.querySelector("small").textContent = (tabData.parentElement.children.length - 1) + " " + chrome.i18n.getMessage("tabs"); - if (tabData.parentElement.children.length < 2) - { - RemoveElement(tabData.parentElement.parentElement); - if (document.querySelector("tabsAside.pane > section").children.length < 2) - setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250); - } - else - RemoveElement(tabData); - }); - }); -} - -function RemoveElement(el) -{ - el.style.opacity = 0; - setTimeout(() => el.remove(), 200); -} - -function RemoveCollectionElement(el) -{ - if (el.parentElement.children.length < 3) - setTimeout(() => document.querySelector(".tabsAside.pane > section > h2").removeAttribute("hidden"), 250); - RemoveElement(el); -} \ No newline at end of file diff --git a/js/background.js b/js/background.js deleted file mode 100644 index 120a39f..0000000 --- a/js/background.js +++ /dev/null @@ -1,493 +0,0 @@ -//This variable is populated when the browser action icon is clicked, or a command is called (with a shortcut for example). -//We can't populate it later, as selected tabs get deselected on a click inside a tab. -var tabsToSave = []; - -var syncEnabled = true; //This variable controls whether to use the chrome sync storage, along with its size limitations, or the much larger chrome local storage. The option is currently not exposed to the users. -var collectionStorage = syncEnabled ? chrome.storage.sync : chrome.storage.local; - -//Get the tabs to save, either all the window or the selected tabs only, and pass them through a callback. -function GetTabsToSave (callback) -{ - chrome.tabs.query({ currentWindow: true }, (windowTabs) => - { - var highlightedTabs = windowTabs.filter(item => item.highlighted); - //If there are more than one selected tab in the window, we set only those aside. - // Otherwise, all the window's tabs get saved. - return callback((highlightedTabs.length > 1 ? highlightedTabs : windowTabs)); - }); - -} - -function TogglePane (tab) -{ - if (tab.url.startsWith("http") - && !tab.url.includes("chrome.google.com") - && !tab.url.includes("addons.mozilla.org") - && !tab.url.includes("microsoftedge.microsoft.com")) - { - chrome.tabs.executeScript(tab.id, - { - file: "js/aside-script.js", - allFrames: true, - runAt: "document_idle" - }); - } - else if (tab.url == chrome.runtime.getURL("TabsAside.html")) - chrome.tabs.remove(tab.id); - else - { - chrome.tabs.create( - { - url: chrome.extension.getURL("TabsAside.html"), - active: true - }, - (activeTab) => - chrome.tabs.onActivated.addListener(function TabsAsideCloser(activeInfo) - { - chrome.tabs.query({ url: chrome.extension.getURL("TabsAside.html") }, (result) => - { - if (result.length) - setTimeout(() => - { - result.forEach(i => - { - if (activeInfo.tabId != i.id) - chrome.tabs.remove(i.id); - }); - }, 200); - else chrome.tabs.onActivated.removeListener(TabsAsideCloser); - }); - })); - } -} - -function ProcessCommand (command) -{ - GetTabsToSave((returnedTabs) => - { - tabsToSave = returnedTabs; - switch(command) - { - case "set-aside": - SaveCollection(); - break; - case "toggle-pane": - chrome.tabs.query( - { - active: true, - currentWindow: true - }, - (tabs) => TogglePane(tabs[0]) - ) - break; - } - }); -} - -chrome.browserAction.onClicked.addListener((tab) => -{ - GetTabsToSave((returnedTabs) => - { - tabsToSave = returnedTabs; - - chrome.storage.sync.get({ "setAsideOnClick": false }, values => - { - if (values?.setAsideOnClick) - SaveCollection(); - else - TogglePane(tab); - }); - }); -}); - -collections = { }; -thumbnails = { }; - -/** - * Updates the thumbnail storage, then the collection synced storage. Calls the onSuccess callback when successful, and onFailure when failed. - * @param {Object} collectionsToUpdate An object containing one or more collections to be updated. By default, updates the whole "collections" item. - * @param {function} onSuccess A function without arguments, that will be called after the collections and thumbnails values are obtained, if collections are successfully updated - * @param {function} onFailure A function that will be called with the error, after the collections and thumbnails values are obtained, if collections are not successfully updated - */ -function UpdateStorages (collectionsToUpdate = collections, onSuccess = () => null, onFailure = (error) => { throw error.message; }) -{ - //The collections storage is updated after the thumbnail storage, so that the thumbnails are ready when the collections are updated. - chrome.storage.local.set({ "thumbnails": thumbnails }, - () => collectionStorage.set(CompressCollectionsStorage(collectionsToUpdate), - () => - { - if (!chrome.runtime.lastError) - onSuccess(); - else - onFailure(chrome.runtime.lastError); - }) - ); - //When the collection storage is updated, a listener set up below reacts and updates the collections global variable, so we do not need to update that variable here -} - -/** - * Use a compression mechanism to compress collections in an object, one by one. - * @param {Object} collectionsToCompress object of collections to compress - * @returns {Object} Object of compressed stringified collections. - */ -function CompressCollectionsStorage (collectionsToCompress) -{ - var compressedStorage = { }; - for (var [key, value] of Object.entries(collectionsToCompress)) //For each collection in the uncompressed collectionsToCompress - { - var cloneWithoutTimestamp = Object.assign({ }, value, { timestamp: null }); - compressedStorage[key] = LZUTF8.compress(JSON.stringify(cloneWithoutTimestamp), { outputEncoding: "StorageBinaryString" }); - } - return compressedStorage; -} - -/** - * Load and decompresses the thumbnails and collections global variables from the storage, updates the theme and eventually sends the collections and thumbnails to a callback - * @param {function} callback A function that will be called after the collections and thumbnails values are obtained. - * These collections and thumbnails are sent as a data={"collections":collections,"thumbnails":thumbnails} argument to the callback. - */ -function LoadStorages (callback = () => null) -{ - chrome.storage.local.get("thumbnails", values => - { - thumbnails = values?.thumbnails ?? { }; - collectionStorage.get(null, values => - { - collections = DecompressCollectionsStorage(values); - UpdateBadgeCounter(); - callback({ "collections": collections, "thumbnails": thumbnails }); - }); - }); -} - -/** - * Use a decompression mechanism to decompress stringified collections in an object, one by one. - * Ignores non collections items (items with a key not starting with "set_" - * @param {Object} compressedCollections object of stringified collections to decompress - * @returns {Object} Object of decompressed and parsed collections. - */ -function DecompressCollectionsStorage (compressedCollections) -{ - var decompressedStorage = { }; - for (var [key, value] of Object.entries(compressedCollections)) - { - if (!key.startsWith("set_")) - continue; - - decompressedStorage[key] = JSON.parse(LZUTF8.decompress(value, { inputEncoding: "StorageBinaryString" })); - decompressedStorage[key].timestamp = parseInt(key.substr(4)); - } - return decompressedStorage; -} - -/** - * Merges a provided collections array with older pre v2 collections, - * saving the result into the new post v2 storage, or into bookmarks on failure. - * Allows to preserve backward compatibility with the localStorage method of storing collections in pre v2 versions. - * @param {Object} collections The current collections object - */ -function MergePreV2Collections () -{ - if (localStorage.getItem("sets")) - { - console.log("Found pre-v2 data"); - var old_collections = JSON.parse(localStorage.getItem("sets")); - //Migrate thumbnails and icons to follow the new format . - old_collections.forEach(collection => - { - for (var i = 0; i < collection.links.length; i++) - thumbnails[collection.links[i]] = - { - "pageCapture": collection.thumbnails[i], - "iconUrl": collection.icons[i] - }; - - delete collection.thumbnails; - delete collection.icons; - - UpdateStorages({ ["set_" + collection.timestamp]: collection }, - () => null, - () => - { - SaveCollectionAsBookmarks(collection); - alert(chrome.i18n.getMessage("olderDataMigrationFailed")); - }); - }); - localStorage.removeItem("sets"); - } -} - -function SaveCollectionAsBookmarks (collection) -{ - //The id 1 is the browser's bookmark bar - chrome.bookmarks.create({ - "parentId": "1", - "title": "TabsAside " + (collection.name ?? new Date(collection.timestamp).toISOString()) - }, - (collectionFolder) => - { - for (var i = 0; i < collection.links.length; i++) - chrome.bookmarks.create( - { - "parentId": collectionFolder.id, - "title": collection.titles[i], - "url": collection.links[i] - }); - }); -} - -LoadStorages(MergePreV2Collections); - -chrome.storage.onChanged.addListener((changes, namespace) => -{ - if (namespace == "sync") - for (key in changes) - if (key.startsWith("set_")) - { - if (changes[key].newValue) - { - collections[key] = DecompressCollectionsStorage({ [key]: changes[key].newValue })[key]; - - } - else - delete collections[key]; - - UpdateBadgeCounter(); - chrome.runtime.sendMessage( - { - command: "reloadCollections", - collections: collections, - thumbnails: thumbnails - }); - } -}); - -var shortcuts; -chrome.commands.getAll((commands) => shortcuts = commands); - -chrome.commands.onCommand.addListener(ProcessCommand); -chrome.contextMenus.onClicked.addListener((info) => ProcessCommand(info.menuItemId)); - -chrome.runtime.onInstalled.addListener((updateInfo) => -{ - if (updateInfo.reason == "update" && updateInfo.previousVersion != chrome.runtime.getManifest()["version"]) - chrome.storage.local.set({ "showUpdateBadge": true }); - - // Adding context menu options, must be done on extension install and update, and probably chrome update as well. - chrome.contextMenus.create( - { - id: "toggle-pane", - contexts: ["browser_action"], - title: chrome.i18n.getMessage("togglePaneContext") - } - ); - chrome.contextMenus.create( - { - id: "set-aside", - contexts: ["browser_action"], - title: chrome.i18n.getMessage("setAside") - } - ); -}); - -//We receive a message from the pane aside-script, which means the tabsToSave are already assigned on message reception. -chrome.runtime.onMessage.addListener((message, sender, sendResponse) => -{ - switch (message.command) - { - case "openTab": - chrome.tabs.create({ url: message.url }); - break; - case "loadData": - LoadStorages(sendResponse); //Sends the collections as a response - return true; //Required to indicate the answer will be sent asynchronously https://developer.chrome.com/extensions/messaging - case "saveTabs": - SaveCollection(); - break; - case "restoreTabs": - RestoreCollection(message.collectionKey, message.removeCollection); - sendResponse(); - break; - case "deleteTabs": - DeleteCollection(message.collectionKey); - sendResponse(); - break; - case "removeTab": - RemoveTab(message.collectionKey, message.tabIndex); - sendResponse(); - break; - case "renameCollection": - collections[message.collectionKey].name = message.newName; - UpdateStorages({ [message.collectionKey]: collections[message.collectionKey] }); - break; - case "togglePane": - chrome.tabs.query( - { - active: true, - currentWindow: true - }, - (tabs) => TogglePane(tabs[0]) - ); - break; - case "getShortcuts": - sendResponse(shortcuts); - break; - } -}); - -// This function updates the extension's toolbar icon -function UpdateBadgeCounter () -{ - var collectionsLength = Object.keys(collections).length; - // Updating badge counter - chrome.browserAction.setBadgeText({ text: collectionsLength < 1 ? "" : collectionsLength.toString() }); - -} - -// Set current tabs aside -function SaveCollection () -{ - var tabs = tabsToSave.filter(i => i.url != chrome.runtime.getURL("TabsAside.html") && !i.pinned && !i.url.includes("//newtab") && !i.url.includes("about:blank") && !i.url.includes("about:home")); - - if (tabs.length < 1) - { - alert(chrome.i18n.getMessage("noTabsToSave")); - return; - } - - var collection = - { - timestamp: Date.now(), - tabsCount: tabs.length, - titles: tabs.map(tab => (tab.title ?? "").substr(0, 100)), - links: tabs.map(tab => tab.url ?? "") - }; - - tabs.forEach(tab => //For each tab to save, Add relevant thumbnails in the thumbnails object - { - thumbnails[tab.url] = - { - "pageCapture": sessionCaptures[tab.url] ?? thumbnails[tab.url]?.pageCapture, - "iconUrl": tab.favIconUrl - }; - }); - - UpdateStorages({["set_" + collection.timestamp]: collection}, () => - chrome.tabs.create({}, (tab) => - { - var newTabId = tab.id; - chrome.tabs.remove(tabsToSave.filter(i => !i.pinned && i.id != newTabId).map(tab => tab.id)); - }), - () => alert(chrome.i18n.getMessage("errorSavingTabs")) - ); -} - -function DeleteCollection (collectionKey) -{ - var deletedUrls = collections[collectionKey].links; - delete collections[collectionKey]; - ForEachUnusedUrl(deletedUrls, (url) => delete thumbnails[url]); //We delete the thumbnails that are not used in any other collection. - UpdateStorages({});//Updates the thumbnails storage only, by providing an empty "collectionsToUpdate" object. - collectionStorage.remove(collectionKey);//Remove the collection from the collectionstorage -} - -function RestoreCollection (collectionKey, removeCollection) -{ - collections[collectionKey].links.forEach(i => - { - chrome.tabs.create( - { - url: i, - active: false - }, - (createdTab) => - { - chrome.storage.sync.get({"loadOnRestore": true}, values => - { - if (!(values?.loadOnRestore)) - chrome.tabs.onUpdated.addListener(function DiscardTab (updatedTabId, changeInfo, updatedTab) - { - if (updatedTabId === createdTab.id) - { - chrome.tabs.onUpdated.removeListener(DiscardTab); - if (!updatedTab.active) - { - chrome.tabs.discard(updatedTabId); - } - } - }); - }); - }); - }); - - //We added new tabs by restoring a collection, so we refresh the array of tabs ready to be saved. - GetTabsToSave((returnedTabs) => - tabsToSave = returnedTabs); - - if (!removeCollection) - return; - - DeleteCollection(collectionKey); -} - -function RemoveTab (collectionKey, tabIndex) -{ - var set = collections[collectionKey]; - if (--set.tabsCount < 1) - { - DeleteCollection(collectionKey); - return; - } - - var urlToRemove = set.links[tabIndex]; - set.titles.splice(tabIndex, 1); - set.links.splice(tabIndex, 1); - - ForEachUnusedUrl([urlToRemove], (url) => delete thumbnails[url]); - UpdateStorages({[collectionKey]: set}); -} - -/** - * Execute a callback for each url in urlsToFilter that is not in any collection urls - * @param {Array} urlsToFilter array of urls to check - * @param {function} callback callback to execute on an url - */ -function ForEachUnusedUrl (urlsToFilter, callback) -{ - for (var i = 0; i < urlsToFilter.length; i++) // If the url of the tab nยฐi is not present among all the collections, we call the callback on it - if (!Object.values(collections).some(collection => collection.links.some(link => link == urlsToFilter[i]))) - callback(urlsToFilter[i]); -} - -// page Captures are not always used in a collection, so we keep them in a specific variable until a collection is saved. -var sessionCaptures = { }; - -function AppendThumbnail (tab) -{ - if (!tab.active || !tab.url.startsWith("http")) - return; - - chrome.tabs.captureVisibleTab( - { - format: "jpeg", - quality: 1 - }, - (image) => - { - if (!image) - { - console.log("Failed to retrieve thumbnail"); - return; - } - - console.log("Thumbnail retrieved"); - sessionCaptures[tab.url] = image; - } - ); -} - -chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => -{ - if (changeInfo.status == "complete") - AppendThumbnail(tab); -}); diff --git a/js/lib/lzutf8.min.js b/js/lib/lzutf8.min.js deleted file mode 100644 index f464837..0000000 --- a/js/lib/lzutf8.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - LZ-UTF8 v0.5.6 - - Copyright (c) 2018, Rotem Dan - Released under the MIT license. - - Build date: 2020-08-10 - - Please report any issue at https://github.com/rotemdan/lzutf8.js/issues -*/ -var IE10SubarrayBugPatcher,LZUTF8;!function(n){n.runningInNodeJS=function(){return"object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node},n.runningInMainNodeJSModule=function(){return n.runningInNodeJS()&&require.main===module},n.commonJSAvailable=function(){return"object"==typeof module&&"object"==typeof module.exports},n.runningInWebWorker=function(){return"undefined"==typeof window&&"object"==typeof self&&"function"==typeof self.addEventListener&&"function"==typeof self.close},n.runningInNodeChildProcess=function(){return n.runningInNodeJS()&&"function"==typeof process.send},n.runningInNullOrigin=function(){return"object"==typeof window&&"object"==typeof window.location&&("http:"!==document.location.protocol&&"https:"!==document.location.protocol)},n.webWorkersAvailable=function(){return"function"==typeof Worker&&!n.runningInNullOrigin()&&(!n.runningInNodeJS()&&!(navigator&&navigator.userAgent&&0<=navigator.userAgent.indexOf("Android 4.3")))},n.log=function(e,t){void 0===t&&(t=!1),"object"==typeof console&&(console.log(e),t&&"object"==typeof document&&(document.body.innerHTML+=e+"
"))},n.createErrorMessage=function(e,t){if(void 0===t&&(t="Unhandled exception"),null==e)return t;if(t+=": ","object"==typeof e.content){if(n.runningInNodeJS())return t+e.content.stack;var r=JSON.stringify(e.content);return"{}"!==r?t+r:t+e.content}return"string"==typeof e.content?t+e.content:t+e},n.printExceptionAndStackTraceToConsole=function(e,t){void 0===t&&(t="Unhandled exception"),n.log(n.createErrorMessage(e,t))},n.getGlobalObject=function(){return"object"==typeof global?global:"object"==typeof window?window:"object"==typeof self?self:{}},n.toString=Object.prototype.toString,n.commonJSAvailable()&&(module.exports=n)}(LZUTF8||(LZUTF8={})),function(e){if("function"==typeof Uint8Array&&0!==new Uint8Array(1).subarray(1).byteLength){var t=function(e,t){var r=function(e,t,r){return e>>10)),this.appendCharCode(56320+(e-65536&1023))}},e.prototype.getOutputString=function(){return this.flushBufferToOutputString(),this.outputString},e.prototype.flushBufferToOutputString=function(){this.outputPosition===this.outputBufferCapacity?this.outputString+=String.fromCharCode.apply(null,this.outputBuffer):this.outputString+=String.fromCharCode.apply(null,this.outputBuffer.subarray(0,this.outputPosition)),this.outputPosition=0},e}();e.StringBuilder=t}(LZUTF8||(LZUTF8={})),function(o){var e=function(){function e(){this.restart()}return e.prototype.restart=function(){this.startTime=e.getTimestamp()},e.prototype.getElapsedTime=function(){return e.getTimestamp()-this.startTime},e.prototype.getElapsedTimeAndRestart=function(){var e=this.getElapsedTime();return this.restart(),e},e.prototype.logAndRestart=function(e,t){void 0===t&&(t=!0);var r=this.getElapsedTime(),n=e+": "+r.toFixed(3)+"ms";return o.log(n,t),this.restart(),r},e.getTimestamp=function(){return this.timestampFunc||this.createGlobalTimestampFunction(),this.timestampFunc()},e.getMicrosecondTimestamp=function(){return Math.floor(1e3*e.getTimestamp())},e.createGlobalTimestampFunction=function(){if("object"==typeof process&&"function"==typeof process.hrtime){var r=0;this.timestampFunc=function(){var e=process.hrtime(),t=1e3*e[0]+e[1]/1e6;return r+t},r=Date.now()-this.timestampFunc()}else if("object"==typeof chrome&&chrome.Interval){var e=Date.now(),t=new chrome.Interval;t.start(),this.timestampFunc=function(){return e+t.microseconds()/1e3}}else if("object"==typeof performance&&performance.now){var n=Date.now()-performance.now();this.timestampFunc=function(){return n+performance.now()}}else Date.now?this.timestampFunc=function(){return Date.now()}:this.timestampFunc=function(){return(new Date).getTime()}},e}();o.Timer=e}(LZUTF8||(LZUTF8={})),function(n){var e=function(){function e(e){void 0===e&&(e=!0),this.MinimumSequenceLength=4,this.MaximumSequenceLength=31,this.MaximumMatchDistance=32767,this.PrefixHashTableSize=65537,this.inputBufferStreamOffset=1,e&&"function"==typeof Uint32Array?this.prefixHashTable=new n.CompressorCustomHashTable(this.PrefixHashTableSize):this.prefixHashTable=new n.CompressorSimpleHashTable(this.PrefixHashTableSize)}return e.prototype.compressBlock=function(e){if(null==e)throw new TypeError("compressBlock: undefined or null input received");return"string"==typeof e&&(e=n.encodeUTF8(e)),e=n.BufferTools.convertToUint8ArrayIfNeeded(e),this.compressUtf8Block(e)},e.prototype.compressUtf8Block=function(e){if(!e||0==e.length)return new Uint8Array(0);var t=this.cropAndAddNewBytesToInputBuffer(e),r=this.inputBuffer,n=this.inputBuffer.length;this.outputBuffer=new Uint8Array(e.length);for(var o=this.outputBufferPosition=0,i=t;in-this.MinimumSequenceLength)s||this.outputRawByte(u);else{var a=this.getBucketIndexForPrefix(i);if(!s){var c=this.findLongestMatch(i,a);null!=c&&(this.outputPointerBytes(c.length,c.distance),o=i+c.length,s=!0)}s||this.outputRawByte(u);var f=this.inputBufferStreamOffset+i;this.prefixHashTable.addValueToBucket(a,f)}}return this.outputBuffer.subarray(0,this.outputBufferPosition)},e.prototype.findLongestMatch=function(e,t){var r=this.prefixHashTable.getArraySegmentForBucketIndex(t,this.reusableArraySegmentObject);if(null==r)return null;for(var n,o=this.inputBuffer,i=0,u=0;u>>1):i,a>this.MaximumMatchDistance||c>=this.MaximumSequenceLength||e+c>=o.length)break;if(o[s+c]===o[e+c])for(var f=0;;f++){if(e+f===o.length||o[s+f]!==o[e+f]){c>>8),this.outputRawByte(255&t))},e.prototype.outputRawByte=function(e){this.outputBuffer[this.outputBufferPosition++]=e},e.prototype.cropAndAddNewBytesToInputBuffer=function(e){if(void 0===this.inputBuffer)return this.inputBuffer=e,0;var t=Math.min(this.inputBuffer.length,this.MaximumMatchDistance),r=this.inputBuffer.length-t;return this.inputBuffer=n.CompressionCommon.getCroppedAndAppendedByteArray(this.inputBuffer,r,t,e),this.inputBufferStreamOffset+=r,t},e}();n.Compressor=e}(LZUTF8||(LZUTF8={})),function(s){var e=function(){function e(e){this.minimumBucketCapacity=4,this.maximumBucketCapacity=64,this.bucketLocators=new Uint32Array(2*e),this.storage=new Uint32Array(2*e),this.storageIndex=1}return e.prototype.addValueToBucket=function(e,t){e<<=1,this.storageIndex>=this.storage.length>>>1&&this.compact();var r,n=this.bucketLocators[e];if(0===n)n=this.storageIndex,r=1,this.storage[this.storageIndex]=t,this.storageIndex+=this.minimumBucketCapacity;else{(r=this.bucketLocators[e+1])===this.maximumBucketCapacity-1&&(r=this.truncateBucketToNewerElements(n,r,this.maximumBucketCapacity/2));var o=n+r;0===this.storage[o]?(this.storage[o]=t,o===this.storageIndex&&(this.storageIndex+=r)):(s.ArrayTools.copyElements(this.storage,n,this.storage,this.storageIndex,r),n=this.storageIndex,this.storageIndex+=r,this.storage[this.storageIndex++]=t,this.storageIndex+=r),r++}this.bucketLocators[e]=n,this.bucketLocators[e+1]=r},e.prototype.truncateBucketToNewerElements=function(e,t,r){var n=e+t-r;return s.ArrayTools.copyElements(this.storage,n,this.storage,e,r),s.ArrayTools.zeroElements(this.storage,e+r,t-r),r},e.prototype.compact=function(){var e=this.bucketLocators,t=this.storage;this.bucketLocators=new Uint32Array(this.bucketLocators.length),this.storageIndex=1;for(var r=0;r>>6==3){var i=o>>>5;if(r==n-1||r==n-2&&7==i){this.inputBufferRemainder=e.subarray(r);break}if(e[r+1]>>>7==1)this.outputByte(o);else{var u=31&o,s=void 0;6==i?(s=e[r+1],r+=1):(s=e[r+1]<<8|e[r+2],r+=2);for(var a=this.outputPosition-s,c=0;c>>3==30||e<3&&t>>>4==14||e<2&&t>>>5==6)return this.outputBufferRemainder=this.outputBuffer.subarray(this.outputPosition-e,this.outputPosition),void(this.outputPosition-=e)}},e}();f.Decompressor=e}(LZUTF8||(LZUTF8={})),function(s){var e,t,a,c;e=s.Encoding||(s.Encoding={}),t=e.Base64||(e.Base64={}),a=new Uint8Array([65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,43,47]),c=new Uint8Array([255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,62,255,255,255,63,52,53,54,55,56,57,58,59,60,61,255,255,255,0,255,255,255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,255,255,255,255,255,255,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,255,255,255,255]),t.encode=function(e){return e&&0!=e.length?s.runningInNodeJS()?s.BufferTools.uint8ArrayToBuffer(e).toString("base64"):t.encodeWithJS(e):""},t.decode=function(e){return e?s.runningInNodeJS()?s.BufferTools.bufferToUint8Array(new Buffer(e,"base64")):t.decodeWithJS(e):new Uint8Array(0)},t.encodeWithJS=function(e,t){if(void 0===t&&(t=!0),!e||0==e.length)return"";for(var r,n=a,o=new s.StringBuilder,i=0,u=e.length;i>>18&63]),o.appendCharCode(n[r>>>12&63]),o.appendCharCode(n[r>>>6&63]),o.appendCharCode(n[63&r]),r=0):i===u-2?(r=e[i]<<16|e[i+1]<<8,o.appendCharCode(n[r>>>18&63]),o.appendCharCode(n[r>>>12&63]),o.appendCharCode(n[r>>>6&63]),t&&o.appendCharCode(61)):i===u-1&&(r=e[i]<<16,o.appendCharCode(n[r>>>18&63]),o.appendCharCode(n[r>>>12&63]),t&&(o.appendCharCode(61),o.appendCharCode(61)));return o.getOutputString()},t.decodeWithJS=function(e,t){if(!e||0==e.length)return new Uint8Array(0);var r=e.length%4;if(1===r)throw new Error("Invalid Base64 string: length % 4 == 1");2===r?e+="==":3===r&&(e+="="),t||(t=new Uint8Array(e.length));for(var n=0,o=e.length,i=0;i>>16&255,t[n++]=u>>>8&255,t[n++]=255&u}return 61==e.charCodeAt(o-1)&&n--,61==e.charCodeAt(o-2)&&n--,t.subarray(0,n)}}(LZUTF8||(LZUTF8={})),function(s){var e,t;e=s.Encoding||(s.Encoding={}),(t=e.BinaryString||(e.BinaryString={})).encode=function(e){if(null==e)throw new TypeError("BinaryString.encode: undefined or null input received");if(0===e.length)return"";for(var t=e.length,r=new s.StringBuilder,n=0,o=1,i=0;i>>o),n=u&(1<>>15-i,r[n++]=t>>>8,r[n++]=255&t,o=s&(1<<15-i)-1),15==i?i=0:i+=1)}return r.subarray(0,n)}}(LZUTF8||(LZUTF8={})),function(e){var t,r;t=e.Encoding||(e.Encoding={}),(r=t.CodePoint||(t.CodePoint={})).encodeFromString=function(e,t){var r=e.charCodeAt(t);if(r<55296||56319>>10),56320+(e-65536&1023));throw new Error("getStringFromUnicodeCodePoint: A code point of "+e+" cannot be encoded in UTF-16")}}(LZUTF8||(LZUTF8={})),function(e){var t,r,n;t=e.Encoding||(e.Encoding={}),r=t.DecimalString||(t.DecimalString={}),n=["000","001","002","003","004","005","006","007","008","009","010","011","012","013","014","015","016","017","018","019","020","021","022","023","024","025","026","027","028","029","030","031","032","033","034","035","036","037","038","039","040","041","042","043","044","045","046","047","048","049","050","051","052","053","054","055","056","057","058","059","060","061","062","063","064","065","066","067","068","069","070","071","072","073","074","075","076","077","078","079","080","081","082","083","084","085","086","087","088","089","090","091","092","093","094","095","096","097","098","099","100","101","102","103","104","105","106","107","108","109","110","111","112","113","114","115","116","117","118","119","120","121","122","123","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","144","145","146","147","148","149","150","151","152","153","154","155","156","157","158","159","160","161","162","163","164","165","166","167","168","169","170","171","172","173","174","175","176","177","178","179","180","181","182","183","184","185","186","187","188","189","190","191","192","193","194","195","196","197","198","199","200","201","202","203","204","205","206","207","208","209","210","211","212","213","214","215","216","217","218","219","220","221","222","223","224","225","226","227","228","229","230","231","232","233","234","235","236","237","238","239","240","241","242","243","244","245","246","247","248","249","250","251","252","253","254","255"],r.encode=function(e){for(var t=[],r=0;r>>6,t[r++]=128|63&o;else if(o<=65535)t[r++]=224|o>>>12,t[r++]=128|o>>>6&63,t[r++]=128|63&o;else{if(!(o<=1114111))throw new Error("Invalid UTF-16 string: Encountered a character unsupported by UTF-8/16 (RFC 3629)");t[r++]=240|o>>>18,t[r++]=128|o>>>12&63,t[r++]=128|o>>>6&63,t[r++]=128|63&o,n++}}return t.subarray(0,r)},t.decodeWithJS=function(e,t,r){if(void 0===t&&(t=0),!e||0==e.length)return"";void 0===r&&(r=e.length);for(var n,o,i=new a.StringBuilder,u=t,s=r;u>>7==0)n=o,u+=1;else if(o>>>5==6){if(r<=u+1)throw new Error("Invalid UTF-8 stream: Truncated codepoint sequence encountered at position "+u);n=(31&o)<<6|63&e[u+1],u+=2}else if(o>>>4==14){if(r<=u+2)throw new Error("Invalid UTF-8 stream: Truncated codepoint sequence encountered at position "+u);n=(15&o)<<12|(63&e[u+1])<<6|63&e[u+2],u+=3}else{if(o>>>3!=30)throw new Error("Invalid UTF-8 stream: An invalid lead byte value encountered at position "+u);if(r<=u+3)throw new Error("Invalid UTF-8 stream: Truncated codepoint sequence encountered at position "+u);n=(7&o)<<18|(63&e[u+1])<<12|(63&e[u+2])<<6|63&e[u+3],u+=4}i.appendCodePoint(n)}return i.getOutputString()},t.createNativeTextEncoderAndDecoderIfAvailable=function(){return!!r||"function"==typeof TextEncoder&&(r=new TextEncoder("utf-8"),n=new TextDecoder("utf-8"),!0)}}(LZUTF8||(LZUTF8={})),function(o){o.compress=function(e,t){if(void 0===t&&(t={}),null==e)throw new TypeError("compress: undefined or null input received");var r=o.CompressionCommon.detectCompressionSourceEncoding(e);t=o.ObjectTools.override({inputEncoding:r,outputEncoding:"ByteArray"},t);var n=(new o.Compressor).compressBlock(e);return o.CompressionCommon.encodeCompressedBytes(n,t.outputEncoding)},o.decompress=function(e,t){if(void 0===t&&(t={}),null==e)throw new TypeError("decompress: undefined or null input received");t=o.ObjectTools.override({inputEncoding:"ByteArray",outputEncoding:"String"},t);var r=o.CompressionCommon.decodeCompressedBytes(e,t.inputEncoding),n=(new o.Decompressor).decompressBlock(r);return o.CompressionCommon.encodeDecompressedBytes(n,t.outputEncoding)},o.compressAsync=function(e,t,r){var n;null==r&&(r=function(){});try{n=o.CompressionCommon.detectCompressionSourceEncoding(e)}catch(e){return void r(void 0,e)}t=o.ObjectTools.override({inputEncoding:n,outputEncoding:"ByteArray",useWebWorker:!0,blockSize:65536},t),o.enqueueImmediate(function(){t.useWebWorker&&o.WebWorker.createGlobalWorkerIfNeeded()?o.WebWorker.compressAsync(e,t,r):o.AsyncCompressor.compressAsync(e,t,r)})},o.decompressAsync=function(e,t,r){if(null==r&&(r=function(){}),null!=e){t=o.ObjectTools.override({inputEncoding:"ByteArray",outputEncoding:"String",useWebWorker:!0,blockSize:65536},t);var n=o.BufferTools.convertToUint8ArrayIfNeeded(e);o.EventLoop.enqueueImmediate(function(){t.useWebWorker&&o.WebWorker.createGlobalWorkerIfNeeded()?o.WebWorker.decompressAsync(n,t,r):o.AsyncDecompressor.decompressAsync(e,t,r)})}else r(void 0,new TypeError("decompressAsync: undefined or null input received"))},o.createCompressionStream=function(){return o.AsyncCompressor.createCompressionStream()},o.createDecompressionStream=function(){return o.AsyncDecompressor.createDecompressionStream()},o.encodeUTF8=function(e){return o.Encoding.UTF8.encode(e)},o.decodeUTF8=function(e){return o.Encoding.UTF8.decode(e)},o.encodeBase64=function(e){return o.Encoding.Base64.encode(e)},o.decodeBase64=function(e){return o.Encoding.Base64.decode(e)},o.encodeBinaryString=function(e){return o.Encoding.BinaryString.encode(e)},o.decodeBinaryString=function(e){return o.Encoding.BinaryString.decode(e)},o.encodeStorageBinaryString=function(e){return o.Encoding.StorageBinaryString.encode(e)},o.decodeStorageBinaryString=function(e){return o.Encoding.StorageBinaryString.decode(e)}}(LZUTF8||(LZUTF8={})); \ No newline at end of file diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 0000000..62dca2a --- /dev/null +++ b/locales/en.yml @@ -0,0 +1,251 @@ +manifest: + name: "Tabs aside" + description: "Save and organize your tabs for later. Pick up where you left off" + author: "Eugene Fox" + +shortcuts: + toggle_sidebar: "Open collection list" + set_aside: "Set tabs aside" + save_tabs: "Save tabs without closing" + +common: + actions: + cancel: "Cancel" + save: "Save" + close: "Close" + delete: "Delete" + reset_filters: "Reset filters" + cta: + feedback: "Leave feedback" + sponsor: "Buy me a coffee" + tooltips: + more: "More" + delete_prompt: "Are you sure? This action cannot be undone." + +features: + v3welcome: + title: "Welcome to Tabs aside 3.0" + text1: "We are happy to announce our new major update for Tabs aside extension!" + text2: "This update brings a brand new UI, and a lot of new features, including:" + list: + item1: "Tab groups support" + item2: "Collection customization" + item3: "Drag and drop reordering and organizing" + item4: "Manual collection creation from scratch" + item5: "And more!" + text3: "Visit our dev blog to learn more about this update and all of its features!" + actions: + visit_blog: "Read dev blog" + +notifications: + tabs_saved: + title: "New collection created" + message: "Your tabs have been saved to a new collection" + error_quota_exceeded: + title: "Maximum cloud write operations exceeded" + message: "We saved your tabs in local storage. You will have to manually update your cloud storage" + error_storage_full: + title: "Your cloud storage is full" + message: "We saved your tabs in local storage. Please clear some space in your cloud storage" + bookmark_saved: + title: "Exported to bookmarks" + message: "Your collection has been exported to bookmarks" + partial_save: + title: "Some tabs couldn't be saved" + message: "Some of the tabs were system tabs we could not access. They were skipped" + +actions: + save: + all: "Save all tabs" + selected: "Save selected tabs" + set_aside: + all: "Set all tabs aside" + selected: "Set selected tabs aside" + show_collections: "Show collections" + +options_page: + title: "Settings" + general: + title: "General" + options: + always_show_toolbars: "Always show toolbars" + include_pinned: "Include pinned tabs when saving all tabs" + show_delete_prompt: "Ask for confirmation when deleting an item" + show_badge: "Show counter badge" + show_notification: "Show notification when saving tabs using context menu" + unload_tabs: "Do not load tabs after opening" + list_locations: + title: "Open collection list in:" + options: + sidebar: "Sidebar" + popup: "Popup" + tab: "Separate tab" + pinned: "Separate pinned tab" + icon_action: + title: "When clicking on the extension icon:" + options: + action: "Perform default save action" + context: "Show context menu" + open: "Open collection list" + change_shortcuts: "Change extension shortcuts" + actions: + title: "Default actions" + options: + save_actions: + title: "Default action when saving tabs" + options: + set_aside: "Save and close tabs" + save: "Save tabs without closing" + restore_actions: + title: "Default action when opening collections" + options: + open: "Just open the tabs" + restore: "Open tabs and remove the collection" + storage: + title: "Storage" + capacity: + title: "Cloud storage capacity" + description: "$1 of $2 KiB" + import: "Import data" + export: "Export data" + import_results: + success: "Data successfully imported" + error: "Provided file appears to be corrupted. Nothing was imported" + import_prompt: + title: "Import data" + warning_title: "This is an irreversible action" + warning_text: "This will overwrite all your data. Make sure you picked a correct file, otherwise data corruption or loss may occur. It is recommended to export data first." + proceed: "Pick a file" + enable: "Enable cloud storage" + disable: "Disable cloud storage" + disable_prompt: + text: "This action will disable collection synchronization between your devices. Extension's settings will still be synchronized." + action: "Disable and reload the extension" + about: + title: "About" + developed_by: "Developed by Eugene Fox" + licensed_under: "Licensed under" + mit_license: "MIT License" + translation_cta: + text: "Found a typo or want a translation for your language?" + button: "Get started here" + links: + website: "My website" + source: "Source code" + changelog: "Changelog" + +collections: + empty: "This collection is empty" + tabs_count: "$1 tabs" + actions: + open: "Open all" + restore: "Restore all" + new_window: "Open all in new window" + incognito: + edge: "Open all in new InPrivate window" + firefox: "Open all in new private window" + chrome: "Open all in incognito window" + incognito_check: + title: "Permissions required" + message: + edge: + p1: "The extension needs permission to open tabs in InPrivate window" + p2: "To do this, click \"Settings\" and then check \"Allow in InPrivate\" option" + firefox: + p1: "The extension needs permission to open tabs in private window" + p2: "To do this, click \"Settings\", go to \"Details\" and set \"Run in Private Windows\" to \"Allow\"" + chrome: + p1: "The extension needs permission to open tabs in incognito window" + p2: "To do this, click \"Settings\" and then check \"Allow in Incognito\" option" + action: "Settings" + menu: + delete: "Delete collection" + add_selected: "Add selected tabs" + add_all: "Add all tabs" + add_group: "Add empty group" + export_bookmarks: "Export to bookmarks" + edit: "Edit collection" + +groups: + title: "Group" + pinned: "Pinned" + open: "Open all" + empty: "This group is empty" + menu: + new_window: "Open in new window" + add_selected: "Add selected tabs" + add_all: "Add all tabs" + edit: "Edit group" + ungroup: "Ungroup" + delete: "Delete group" + +tabs: + delete: "Delete tab" + +colors: + none: "No color" + any: "Any color" + grey: "Grey" + blue: "Blue" + red: "Red" + yellow: "Yellow" + green: "Green" + pink: "Pink" + purple: "Purple" + cyan: "Cyan" + orange: "Orange" + +dialogs: + edit: + title: + edit_collection: "Edit collection" + edit_group: "Edit group" + new_group: "New group" + new_collection: "New collection" + collection_title: "Title" + color: "Color" + +main: + header: + create_collection: "Create new collection" + menu: + tiles_view: "Tiles view" + changelog: "What's new?" + list: + searchbar: + title: "Search" + filter: "Filter" + sort: + title: "Sort" + options: + newest: "Newest first" + oldest: "Oldest first" + ascending: "From A to Z" + descending: "From Z to A" + custom: "Custom" + empty: + title: "Nothing to show here yet" + message: "Set aside your current tabs, or create a new collection" + empty_search: + title: "Couldn't find anything" + message: "Try to change your search query" + +cta_message: + title: "Like this extension?" + message: "Consider supporting the author with a donation, or" + feedback: "leaving a feedback" + +storage_full_message: + title: "Your cloud storage is almost full ($1%)" + message: "You can free up some space by deleting unused collections." + +parse_error_message: + title: "We couldn't get collections from your cloud storage." + message: "Your cloud storage appears to be corrupted. You can fix it by replacing it with your local copy." + action: "Fix with local copy" + +merge_conflict_message: + title: "Your local and cloud storages have conflicting changes." + message: "To fix this, you can either upload your local copy to the cloud, or accept the cloud changes." + accept_local: "Replace with local" + accept_cloud: "Accept cloud changes" diff --git a/locales/es.yml b/locales/es.yml new file mode 100644 index 0000000..d094149 --- /dev/null +++ b/locales/es.yml @@ -0,0 +1,251 @@ +manifest: + name: "Pestaรฑas a un lado" + description: "Guarda y organiza tus pestaรฑas para mรกs tarde. Retoma donde lo dejaste" + author: "Eugene Fox" + +shortcuts: + toggle_sidebar: "Abrir lista de colecciones" + set_aside: "Apartar pestaรฑas" + save_tabs: "Guardar pestaรฑas sin cerrar" + +common: + actions: + cancel: "Cancelar" + save: "Guardar" + close: "Cerrar" + delete: "Eliminar" + reset_filters: "Restablecer filtros" + cta: + feedback: "Dejar comentarios" + sponsor: "Invรญtame un cafรฉ" + tooltips: + more: "Mรกs" + delete_prompt: "ยฟEstรกs seguro? Esta acciรณn no se puede deshacer." + +features: + v3welcome: + title: "Bienvenido a Pestaรฑas a un lado 3.0" + text1: "ยกEstamos felices de anunciar nuestra nueva actualizaciรณn principal para la extensiรณn Pestaรฑas a un lado!" + text2: "Esta actualizaciรณn trae una nueva interfaz de usuario y muchas caracterรญsticas nuevas, incluyendo:" + list: + item1: "Soporte para grupos de pestaรฑas" + item2: "Personalizaciรณn de colecciones" + item3: "Reordenamiento y organizaciรณn mediante arrastrar y soltar" + item4: "Creaciรณn manual de colecciones desde cero" + item5: "ยกY mรกs!" + text3: "ยกVisita nuestro blog de desarrollo para aprender mรกs sobre esta actualizaciรณn y todas sus caracterรญsticas!" + actions: + visit_blog: "Leer el blog de desarrollo" + +notifications: + tabs_saved: + title: "Nueva colecciรณn creada" + message: "Tus pestaรฑas se han guardado en una nueva colecciรณn" + error_quota_exceeded: + title: "Se excediรณ el mรกximo de operaciones de escritura en la nube" + message: "Guardamos tus pestaรฑas en el almacenamiento local. Tendrรกs que actualizar manualmente tu almacenamiento en la nube" + error_storage_full: + title: "Tu almacenamiento en la nube estรก lleno" + message: "Guardamos tus pestaรฑas en el almacenamiento local. Por favor, libera espacio en tu almacenamiento en la nube" + bookmark_saved: + title: "Exportado a marcadores" + message: "Tu colecciรณn ha sido exportada a marcadores" + partial_save: + title: "Algunas pestaรฑas no se pudieron guardar" + message: "Algunas de las pestaรฑas eran pestaรฑas del sistema a las que no pudimos acceder. Fueron omitidas" + +actions: + save: + all: "Guardar todas las pestaรฑas" + selected: "Guardar pestaรฑas seleccionadas" + set_aside: + all: "Apartar todas las pestaรฑas" + selected: "Apartar pestaรฑas seleccionadas" + show_collections: "Mostrar colecciones" + +options_page: + title: "Configuraciรณn" + general: + title: "General" + options: + always_show_toolbars: "Mostrar siempre las barras de herramientas" + include_pinned: "Incluir pestaรฑas fijadas al guardar todas las pestaรฑas" + show_delete_prompt: "Pedir confirmaciรณn al eliminar un elemento" + show_badge: "Mostrar insignia de contador" + show_notification: "Mostrar notificaciรณn al guardar pestaรฑas usando el menรบ contextual" + unload_tabs: "No cargar pestaรฑas despuรฉs de abrir" + list_locations: + title: "Abrir lista de colecciones en:" + options: + sidebar: "Barra lateral" + popup: "Ventana emergente" + tab: "Pestaรฑa separada" + pinned: "Pestaรฑa separada fijada" + icon_action: + title: "Al hacer clic en el icono de la extensiรณn:" + options: + action: "Realizar la acciรณn de guardado predeterminada" + context: "Mostrar menรบ contextual" + open: "Abrir lista de colecciones" + change_shortcuts: "Cambiar atajos de la extensiรณn" + actions: + title: "Acciones predeterminadas" + options: + save_actions: + title: "Acciรณn predeterminada al guardar pestaรฑas" + options: + set_aside: "Guardar y cerrar pestaรฑas" + save: "Guardar pestaรฑas sin cerrar" + restore_actions: + title: "Acciรณn predeterminada al abrir colecciones" + options: + open: "Solo abrir las pestaรฑas" + restore: "Abrir pestaรฑas y eliminar la colecciรณn" + storage: + title: "Almacenamiento" + capacity: + title: "Capacidad de almacenamiento en la nube" + description: "$1 de $2 KiB" + import: "Importar datos" + export: "Exportar datos" + import_results: + success: "Datos importados con รฉxito" + error: "El archivo proporcionado parece estar corrupto. No se importรณ nada" + import_prompt: + title: "Importar datos" + warning_title: "Esta es una acciรณn irreversible" + warning_text: "Esto sobrescribirรก todos tus datos. Asegรบrate de haber elegido el archivo correcto, de lo contrario, podrรญa ocurrir corrupciรณn o pรฉrdida de datos. Se recomienda exportar los datos primero." + proceed: "Seleccionar un archivo" + enable: "Habilitar almacenamiento en la nube" + disable: "Deshabilitar almacenamiento en la nube" + disable_prompt: + text: "Esta acciรณn deshabilitarรก la sincronizaciรณn de colecciones entre tus dispositivos. La configuraciรณn de la extensiรณn seguirรก sincronizรกndose." + action: "Deshabilitar y recargar la extensiรณn" + about: + title: "Acerca de" + developed_by: "Desarrollado por Eugene Fox" + licensed_under: "Licenciado bajo" + mit_license: "Licencia MIT" + translation_cta: + text: "ยฟEncontraste un error tipogrรกfico o quieres una traducciรณn para tu idioma?" + button: "Comienza aquรญ" + links: + website: "Mi sitio web" + source: "Cรณdigo fuente" + changelog: "Registro de cambios" + +collections: + empty: "Esta colecciรณn estรก vacรญa" + tabs_count: "$1 pestaรฑas" + actions: + open: "Abrir todo" + restore: "Restaurar todo" + new_window: "Abrir todo en una ventana nueva" + incognito: + edge: "Abrir todo en ventana InPrivate" + firefox: "Abrir todo en una nueva ventana privada" + chrome: "Abrir todo en una ventana de incรณgnito" + incognito_check: + title: "Permisos requeridos" + message: + edge: + p1: "La extensiรณn necesita permiso para abrir pestaรฑas en ventana InPrivate" + p2: "Para hacerlo, haz clic en \"Configuraciรณn\" y luego selecciona la opciรณn \"Permitir en InPrivate\"" + firefox: + p1: "La extensiรณn necesita permiso para abrir pestaรฑas en una ventana privada" + p2: "Para hacerlo, haz clic en \"Configuraciรณn\", ve a \"Detalles\" y configura \"Ejecutar en ventana privada\" en \"Permitir\"" + chrome: + p1: "La extensiรณn necesita permiso para abrir pestaรฑas en una ventana de incรณgnito" + p2: "Para hacerlo, haz clic en \"Configuraciรณn\" y luego selecciona la opciรณn \"Permitir en incรณgnito\"" + action: "Configuraciรณn" + menu: + delete: "Eliminar colecciรณn" + add_selected: "Agregar pestaรฑas seleccionadas" + add_all: "Agregar todas las pestaรฑas" + add_group: "Agregar grupo vacรญo" + export_bookmarks: "Exportar a marcadores" + edit: "Editar colecciรณn" + +groups: + title: "Grupo" + pinned: "Fijado" + open: "Abrir todo" + empty: "Este grupo estรก vacรญo" + menu: + new_window: "Abrir en una nueva ventana" + add_selected: "Agregar pestaรฑas seleccionadas" + add_all: "Agregar todas las pestaรฑas" + edit: "Editar grupo" + ungroup: "Desagrupar" + delete: "Eliminar grupo" + +tabs: + delete: "Eliminar pestaรฑa" + +colors: + none: "Sin color" + any: "Cualquier color" + grey: "Gris" + blue: "Azul" + red: "Rojo" + yellow: "Amarillo" + green: "Verde" + pink: "Rosa" + purple: "Morado" + cyan: "Cian" + orange: "Naranja" + +dialogs: + edit: + title: + edit_collection: "Editar colecciรณn" + edit_group: "Editar grupo" + new_group: "Nuevo grupo" + new_collection: "Nueva colecciรณn" + collection_title: "Tรญtulo" + color: "Color" + +main: + header: + create_collection: "Crear nueva colecciรณn" + menu: + tiles_view: "Vista de mosaicos" + changelog: "ยฟQuรฉ hay de nuevo?" + list: + searchbar: + title: "Buscar" + filter: "Filtrar" + sort: + title: "Ordenar" + options: + newest: "Mรกs recientes primero" + oldest: "Mรกs antiguos primero" + ascending: "De la A a la Z" + descending: "De la Z a la A" + custom: "Personalizado" + empty: + title: "Nada que mostrar aquรญ todavรญa" + message: "Aparta tus pestaรฑas actuales o crea una nueva colecciรณn" + empty_search: + title: "No se encontrรณ nada" + message: "Intenta cambiar tu consulta de bรบsqueda" + +cta_message: + title: "ยฟTe gusta esta extensiรณn?" + message: "Considera apoyar al autor con una donaciรณn o" + feedback: "dejando un comentario" + +storage_full_message: + title: "Tu almacenamiento en la nube estรก casi lleno ($1%)" + message: "Puedes liberar espacio eliminando colecciones no utilizadas." + +parse_error_message: + title: "No pudimos obtener colecciones de tu almacenamiento en la nube." + message: "Tu almacenamiento en la nube parece estar corrupto. Puedes solucionarlo reemplazรกndolo con tu copia local." + action: "Solucionar con copia local" + +merge_conflict_message: + title: "Tus almacenamientos local y en la nube tienen cambios en conflicto." + message: "Para solucionarlo, puedes cargar tu copia local en la nube o aceptar los cambios de la nube." + accept_local: "Reemplazar con local" + accept_cloud: "Aceptar cambios de la nube" diff --git a/locales/it.yml b/locales/it.yml new file mode 100644 index 0000000..c3b871b --- /dev/null +++ b/locales/it.yml @@ -0,0 +1,251 @@ +manifest: + name: "Schede a parte" + description: "Salva e organizza le tue schede per dopo. Riprendi da dove avevi lasciato" + author: "Eugene Fox" + +shortcuts: + toggle_sidebar: "Apri elenco delle collezioni" + set_aside: "Metti da parte le schede" + save_tabs: "Salva le schede senza chiuderle" + +common: + actions: + cancel: "Annulla" + save: "Salva" + close: "Chiudi" + delete: "Elimina" + reset_filters: "Reimposta filtri" + cta: + feedback: "Lascia un feedback" + sponsor: "Offrimi un caffรจ" + tooltips: + more: "Altro" + delete_prompt: "Sei sicuro? Questa azione non puรฒ essere annullata." + +features: + v3welcome: + title: "Benvenuto in Schede a parte 3.0" + text1: "Siamo felici di annunciare il nostro nuovo aggiornamento principale per l'estensione Schede a parte!" + text2: "Questo aggiornamento porta una nuova interfaccia utente e molte nuove funzionalitร , tra cui:" + list: + item1: "Supporto per gruppi di schede" + item2: "Personalizzazione delle collezioni" + item3: "Riordino e organizzazione tramite drag and drop" + item4: "Creazione manuale di collezioni da zero" + item5: "E altro ancora!" + text3: "Visita il nostro blog per saperne di piรน su questo aggiornamento e tutte le sue funzionalitร !" + actions: + visit_blog: "Leggi il blog degli sviluppatori" + +notifications: + tabs_saved: + title: "Nuova collezione creata" + message: "Le tue schede sono state salvate in una nuova collezione" + error_quota_exceeded: + title: "Superato il limite massimo di operazioni di scrittura sul cloud" + message: "Abbiamo salvato le tue schede nella memoria locale. Dovrai aggiornare manualmente il tuo spazio cloud" + error_storage_full: + title: "Il tuo spazio cloud รจ pieno" + message: "Abbiamo salvato le tue schede nella memoria locale. Libera spazio nel tuo cloud storage" + bookmark_saved: + title: "Esportato nei segnalibri" + message: "La tua collezione รจ stata esportata nei segnalibri" + partial_save: + title: "Alcune schede non sono state salvate" + message: "Alcune schede erano schede di sistema a cui non potevamo accedere. Sono state saltate" + +actions: + save: + all: "Salva tutte le schede" + selected: "Salva le schede selezionate" + set_aside: + all: "Metti da parte tutte le schede" + selected: "Metti da parte le schede selezionate" + show_collections: "Mostra collezioni" + +options_page: + title: "Impostazioni" + general: + title: "Generale" + options: + always_show_toolbars: "Mostra sempre le barre degli strumenti" + include_pinned: "Includi schede bloccate quando salvi tutte le schede" + show_delete_prompt: "Chiedi conferma quando elimini un elemento" + show_badge: "Mostra il badge del contatore" + show_notification: "Mostra notifica quando salvi le schede usando il menu contestuale" + unload_tabs: "Non caricare le schede dopo l'apertura" + list_locations: + title: "Apri elenco delle collezioni in:" + options: + sidebar: "Barra laterale" + popup: "Popup" + tab: "Scheda separata" + pinned: "Scheda separata bloccata" + icon_action: + title: "Quando clicchi sull'icona dell'estensione:" + options: + action: "Esegui l'azione di salvataggio predefinita" + context: "Mostra menu contestuale" + open: "Apri elenco delle collezioni" + change_shortcuts: "Modifica le scorciatoie dell'estensione" + actions: + title: "Azioni predefinite" + options: + save_actions: + title: "Azione predefinita quando salvi le schede" + options: + set_aside: "Salva e chiudi le schede" + save: "Salva le schede senza chiuderle" + restore_actions: + title: "Azione predefinita quando apri le collezioni" + options: + open: "Apri solo le schede" + restore: "Apri le schede e rimuovi la collezione" + storage: + title: "Archiviazione" + capacity: + title: "Capacitร  di archiviazione cloud" + description: "$1 di $2 KiB" + import: "Importa dati" + export: "Esporta dati" + import_results: + success: "Dati importati con successo" + error: "Il file fornito sembra essere corrotto. Non รจ stato importato nulla" + import_prompt: + title: "Importa dati" + warning_title: "Questa รจ un'azione irreversibile" + warning_text: "Questo sovrascriverร  tutti i tuoi dati. Assicurati di aver scelto il file corretto, altrimenti potrebbero verificarsi corruzioni o perdite di dati. Si consiglia di esportare prima i dati." + proceed: "Scegli un file" + enable: "Abilita archiviazione cloud" + disable: "Disabilita archiviazione cloud" + disable_prompt: + text: "Questa azione disabiliterร  la sincronizzazione delle collezioni tra i tuoi dispositivi. Le impostazioni dell'estensione saranno comunque sincronizzate." + action: "Disabilita e ricarica l'estensione" + about: + title: "Informazioni" + developed_by: "Sviluppato da Eugene Fox" + licensed_under: "Concesso in licenza sotto" + mit_license: "Licenza MIT" + translation_cta: + text: "Hai trovato un errore di battitura o vuoi una traduzione per la tua lingua?" + button: "Inizia qui" + links: + website: "Il mio sito web" + source: "Codice sorgente" + changelog: "Registro delle modifiche" + +collections: + empty: "Questa collezione รจ vuota" + tabs_count: "$1 schede" + actions: + open: "Apri tutto" + restore: "Ripristina tutto" + new_window: "Apri tutto in una nova finestra" + incognito: + edge: "Apri tutto in una nuova finestra InPrivate" + firefox: "Apri tutto in nuova finestra anonima" + chrome: "Apri tutto in finestra di navigazione in incognito" + incognito_check: + title: "Permessi richiesti" + message: + edge: + p1: "L'estensione necessita del permesso per aprire schede in finestra InPrivate" + p2: "Per farlo, clicca su \"Impostazioni\" e poi seleziona l'opzione \"Consenti in InPrivate\"" + firefox: + p1: "L'estensione necessita del permesso per aprire schede in finestra anonima" + p2: "Per farlo, clicca su \"Impostazioni\", vai su \"Dettagli\" e imposta \"Funzionamento in finestre anonime\" su \"Consenti\"" + chrome: + p1: "L'estensione necessita del permesso per aprire schede in finestra di navigazione in incognito" + p2: "Per farlo, clicca su \"Impostazioni\" e poi seleziona l'opzione \"Consenti modalitร  di navigazione in incognito\"" + action: "Impostazioni" + menu: + delete: "Elimina collezione" + add_selected: "Aggiungi schede selezionate" + add_all: "Aggiungi tutte le schede" + add_group: "Aggiungi gruppo vuoto" + export_bookmarks: "Esporta nei segnalibri" + edit: "Modifica collezione" + +groups: + title: "Gruppo" + pinned: "Bloccato" + open: "Apri tutto" + empty: "Questo gruppo รจ vuoto" + menu: + new_window: "Apri in una nuova finestra" + add_selected: "Aggiungi schede selezionate" + add_all: "Aggiungi tutte le schede" + edit: "Modifica gruppo" + ungroup: "Rimuovi dal gruppo" + delete: "Elimina gruppo" + +tabs: + delete: "Elimina scheda" + +colors: + none: "Nessun colore" + any: "Qualsiasi colore" + grey: "Grigio" + blue: "Blu" + red: "Rosso" + yellow: "Giallo" + green: "Verde" + pink: "Rosa" + purple: "Viola" + cyan: "Ciano" + orange: "Arancione" + +dialogs: + edit: + title: + edit_collection: "Modifica collezione" + edit_group: "Modifica gruppo" + new_group: "Nuovo gruppo" + new_collection: "Nuova collezione" + collection_title: "Titolo" + color: "Colore" + +main: + header: + create_collection: "Crea nuova collezione" + menu: + tiles_view: "Vista a riquadri" + changelog: "Cosa c'รจ di nuovo?" + list: + searchbar: + title: "Cerca" + filter: "Filtra" + sort: + title: "Ordina" + options: + newest: "Piรน recenti prima" + oldest: "Piรน vecchi prima" + ascending: "Dalla A alla Z" + descending: "Dalla Z alla A" + custom: "Personalizzato" + empty: + title: "Niente da mostrare qui per ora" + message: "Metti da parte le tue schede attuali o crea una nuova collezione" + empty_search: + title: "Non รจ stato trovato nulla" + message: "Prova a cambiare la tua query di ricerca" + +cta_message: + title: "Ti piace questa estensione?" + message: "Considera di supportare l'autore con una donazione o" + feedback: "lasciando un feedback" + +storage_full_message: + title: "Il tuo spazio cloud รจ quasi pieno ($1%)" + message: "Puoi liberare spazio eliminando collezioni inutilizzate." + +parse_error_message: + title: "Non siamo riusciti a ottenere le collezioni dal tuo spazio cloud." + message: "Il tuo spazio cloud sembra essere corrotto. Puoi risolvere sostituendolo con la tua copia locale." + action: "Risolvere con copia locale" + +merge_conflict_message: + title: "Le tue memorie locali e cloud hanno modifiche in conflitto." + message: "Per risolvere, puoi caricare la tua copia locale nel cloud o accettare le modifiche del cloud." + accept_local: "Sostituisci con locale" + accept_cloud: "Accetta modifiche del cloud" diff --git a/locales/pl.yml b/locales/pl.yml new file mode 100644 index 0000000..274070e --- /dev/null +++ b/locales/pl.yml @@ -0,0 +1,251 @@ +manifest: + name: "Odล‚oลผone karty" + description: "Odkล‚adaj i organizuj swoje karty. Kontynuuj tam, gdzie przerwaล‚eล›" + author: "Eugeniusz Lis" + +shortcuts: + toggle_sidebar: "Otwรณrz listฤ™ kolekcji" + set_aside: "Odล‚รณลผ karty" + save_tabs: "Zapisz karty" + +common: + actions: + cancel: "Anuluj" + save: "Zapisz" + close: "Zamknij" + delete: "Usuล„" + reset_filters: "Resetuj filtry" + cta: + feedback: "Zostaw opiniฤ™" + sponsor: "Wesprzyj" + tooltips: + more: "Wiฤ™cej" + delete_prompt: "Czy jesteล› pewien? Tej akcji nie moลผna cofnฤ…ฤ‡." + +features: + v3welcome: + title: "Witamy w Odล‚oลผonych kartach 3.0" + text1: "Z radoล›ciฤ… przedstawiamy nowฤ… duลผฤ… aktualizacjฤ™ rozszerzenia!" + text2: "Ta aktualizacja zawiera zupeล‚nie nowy interfejs i wiele nowych funkcji, takich jak:" + list: + item1: "Obsล‚uga grupowania kart" + item2: "Personalizacja kolekcji" + item3: "Przeciฤ…ganie kolekcji i elementรณw" + item4: "Tworzenie kolekcji od zera" + item5: "I wiele wiฤ™cej!" + text3: "Odwiedลบ blog dewelopera (tylko w jฤ™zyku angielskim), aby dowiedzieฤ‡ siฤ™ wiฤ™cej o tej aktualizacji i wszystkich jej funkcjach!" + actions: + visit_blog: "Czytaj blog" + +notifications: + tabs_saved: + title: "Utworzono nowฤ… kolekcjฤ™" + message: "Twoje karty zostaล‚y zapisane w nowej kolekcji" + error_quota_exceeded: + title: "Przekroczono limit operacji zapisu w chmurze" + message: "Twoje karty zostaล‚y zapisane w lokalnym magazynie. Musisz rฤ™cznie zaktualizowaฤ‡ magazyn w chmurze" + error_storage_full: + title: "Magazyn w chmurze jest peล‚ny" + message: "Twoje karty zostaล‚y zapisane w lokalnym magazynie. Proszฤ™ zwolniฤ‡ miejsce w magazynie w chmurze" + bookmark_saved: + title: "Wyeksportowano do zakล‚adek" + message: "Twoja kolekcja zostaล‚a wyeksportowana do zakล‚adek" + partial_save: + title: "Niektรณre karty nie zostaล‚y zapisane" + message: "Niektรณre z kart sฤ… systemowe i nie mogฤ… byฤ‡ zapisane" + +actions: + save: + all: "Zapisz wszystkie karty" + selected: "Zapisz wybrane karty" + set_aside: + all: "Odล‚รณลผ wszystkie karty" + selected: "Odล‚รณลผ wybrane karty" + show_collections: "Pokaลผ listฤ™ kolekcji" + +options_page: + title: "Ustawienia" + general: + title: "Ogรณlne" + options: + always_show_toolbars: "Zawsze pokazuj paski narzฤ™dzi" + include_pinned: "Zapisuj przypiฤ™te karty przy zapisywaniu wszystkich kart" + show_delete_prompt: "Pytaj o potwierdzenie przy usuwaniu elementรณw" + show_badge: "Pokaลผ licznik" + show_notification: "Pokaลผ powiadomienie przy zapisywaniu przez menu kontekstowe" + unload_tabs: "Nie ล‚aduj kart po otwarciu" + list_locations: + title: "Otwieraj listฤ™ kolekcji w:" + options: + sidebar: "Panel boczny" + popup: "Okno popup" + tab: "Osobna karta" + pinned: "Przypiฤ™ta karta" + icon_action: + title: "Po klikniฤ™ciu ikony rozszerzenia:" + options: + action: "Zapisz karty (domyล›lna akcja)" + context: "Pokaลผ menu kontekstowe" + open: "Otwรณrz listฤ™ kolekcji" + change_shortcuts: "Zmieล„ skrรณty klawiszowe" + actions: + title: "Akcje" + options: + save_actions: + title: "Domyล›lna akcja przy zapisywaniu kart" + options: + set_aside: "Zapisz i zamknij karty" + save: "Zapisz karty bez ich zamykania" + restore_actions: + title: "Domyล›lna akcja przy otwieraniu kolekcji" + options: + open: "Po prostu otwรณrz karty" + restore: "Otwรณrz karty i usuล„ kolekcjฤ™" + storage: + title: "Magazyn" + capacity: + title: "Magazyn w chmurze" + description: "$1 z $2 KiB" + import: "Importuj dane" + export: "Eksportuj dane" + import_results: + success: "Dane zostaล‚y pomyล›lnie zaimportowane" + error: "Wyglฤ…da na to, ลผe wybrany plik jest uszkodzony. Nic nie zostaล‚o zaimportowane" + import_prompt: + title: "Import danych" + warning_title: "To jest nieodwracalna akcja!" + warning_text: "Zastฤ…pi wszystkie twoje dane. Upewnij siฤ™, ลผe wybraล‚eล› wล‚aล›ciwy plik, w przeciwnym razie moลผe to prowadziฤ‡ do uszkodzenia lub utraty danych. Zaleca siฤ™ najpierw wyeksportowaฤ‡ dane." + proceed: "Wybierz plik" + disable: "Wyล‚ฤ…cz magazyn w chmurze" + enable: "Wล‚ฤ…cz magazyn w chmurze" + disable_prompt: + text: "Ta akcja wyล‚ฤ…czy synchronizacjฤ™ kolekcji miฤ™dzy twoimi urzฤ…dzeniami. Ustawienia nadal bฤ™dฤ… przechowywane w chmurze." + action: "Wyล‚ฤ…cz i przeล‚aduj rozszerzenie" + about: + title: "O rozszerzeniu" + developed_by: "Wywoล‚ywacz: Eugeniusz Lis" + licensed_under: "" + mit_license: "Licencja MIT" + translation_cta: + text: "Znalazล‚eล› bล‚ฤ…d lub chcesz tล‚umaczenie na swรณj jฤ™zyk?" + button: "Zacznij tutaj" + links: + website: "Moja strona internetowa" + source: "Kod ลบrรณdล‚owy" + changelog: "Lista zmian" + +collections: + empty: "Ta kolekcja jest pusta" + tabs_count: "Karty: $1" + actions: + open: "Otwรณrz wszystkie" + restore: "Przywrรณฤ‡ wszystkie" + new_window: "Otwรณrz w nowym oknie" + incognito: + edge: "Otwรณrz w oknie InPrivate" + firefox: "Otwรณrz w nowym oknie w trybie prywatnym" + chrome: "Otwรณrz w oknie incognito" + incognito_check: + title: "Wymagane uprawnienie" + message: + edge: + p1: "Rozszerzenie wymaga dodatkowego uprawnienia, aby otworzyฤ‡ karty w oknie InPrivate" + p2: "Aby to zrobiฤ‡, kliknij \"Ustawienia\" i zaznacz opcjฤ™ \"Zezwalaj w trybie InPrivate\"" + firefox: + p1: "Rozszerzenie wymaga dodatkowego uprawnienia, aby otworzyฤ‡ karty w trybie prywatnym" + p2: "Aby to zrobiฤ‡, kliknij \"Ustawienia\", przejdลบ do \"Szczegรณล‚y\" i zezwรณl na \"Dziaล‚anie w oknach prywatnych\"" + chrome: + p1: "Rozszerzenie wymaga dodatkowego uprawnienia, aby otworzyฤ‡ karty w oknie incognito" + p2: "Aby to zrobiฤ‡, kliknij \"Ustawienia\" i zaznacz opcjฤ™ \"Zezwalaj w trybie incognito\"" + action: "Ustawienia" + menu: + delete: "Usuล„ kolekcjฤ™" + add_selected: "Dodaj wybrane karty" + add_all: "Dodaj wszystkie karty" + add_group: "Dodaj pustฤ… grupฤ™" + export_bookmarks: "Eksportuj do zakล‚adek" + edit: "Edytuj kolekcjฤ™" + +groups: + title: "Grupa" + pinned: "Przypiฤ™te" + open: "Otwรณrz" + empty: "Ta grupa jest pusta" + menu: + new_window: "Otwรณrz w nowym oknie" + add_selected: "Dodaj wybrane karty" + add_all: "Dodaj wszystkie karty" + edit: "Edytuj grupฤ™" + ungroup: "Rozgrupuj" + delete: "Usuล„ grupฤ™" + +tabs: + delete: "Usuล„ zakล‚adkฤ™" + +colors: + none: "Bez koloru" + any: "Dowolny kolor" + grey: "Szary" + blue: "Niebieski" + red: "Czerwony" + yellow: "ลปรณล‚ty" + green: "Zielony" + pink: "Rรณลผowy" + purple: "Purpurowy" + cyan: "Cyjan" + orange: "Pomaraล„czowy" + +dialogs: + edit: + title: + edit_collection: "Edytuj kolekcjฤ™" + edit_group: "Edytuj grupฤ™" + new_group: "Nowa grupa" + new_collection: "Nowa kolekcja" + collection_title: "Nazwij" + color: "Kolor" + +main: + header: + create_collection: "Utwรณrz nowฤ… kolekcjฤ™" + menu: + tiles_view: "Kafelki" + changelog: "Co nowego?" + list: + searchbar: + title: "Szukaj" + filter: "Filtr" + sort: + title: "Sortowanie" + options: + newest: "Najpierw nowe" + oldest: "Najpierw stare" + ascending: "Od A do Z" + descending: "Od Z do A" + custom: "Niestandardowe" + empty: + title: "Na razie nic tu nie ma" + message: "Odล‚รณลผ bieลผฤ…ce zakล‚adki lub utwรณrz nowฤ… kolekcjฤ™" + empty_search: + title: "Nic nie znaleziono" + message: "Sprรณbuj zmieniฤ‡ zapytanie wyszukiwania" + +cta_message: + title: "Podoba Ci siฤ™ rozszerzenie?" + message: "Wesprzyj autora darowiznฤ… lub" + feedback: "zostaw opiniฤ™" + +storage_full_message: + title: "Magazyn w chmurze prawie peล‚ny ($1%)" + message: "Moลผesz zwolniฤ‡ miejsce, usuwajฤ…c nieuลผywane kolekcje." + +parse_error_message: + title: "Nie udaล‚o siฤ™ pobraฤ‡ kolekcji z magazynu w chmurze." + message: "Wyglฤ…da na to, ลผe magazyn w chmurze jest uszkodzony. Aby to naprawiฤ‡, moลผesz zastฤ…piฤ‡ go lokalnฤ… kopiฤ…." + action: "Uลผyj lokalnej kopii" + +merge_conflict_message: + title: "W lokalnym i chmurowym magazynie sฤ… konfliktujฤ…ce zmiany." + message: "Aby to naprawiฤ‡, moลผesz zapisaฤ‡ lokalnฤ… kopiฤ™ w chmurze lub zaakceptowaฤ‡ zmiany z chmury." + accept_local: "Zastฤ…p lokalnฤ…" + accept_cloud: "Zaakceptuj zmiany z chmury" diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml new file mode 100644 index 0000000..94eba10 --- /dev/null +++ b/locales/pt_BR.yml @@ -0,0 +1,251 @@ +manifest: + name: "Tabs aside" + description: "Salve e organize suas abas para depois. Continue de onde parou" + author: "Eugene Fox" + +shortcuts: + toggle_sidebar: "Abrir lista de coleรงรตes" + set_aside: "Colocar abas de lado" + save_tabs: "Salvar abas sem fechar" + +common: + actions: + cancel: "Cancelar" + save: "Salvar" + close: "Fechar" + delete: "Excluir" + reset_filters: "Limpar filtros" + cta: + feedback: "Deixar feedback" + sponsor: "Me pague um cafรฉ" + tooltips: + more: "Mais" + delete_prompt: "Tem certeza? Esta aรงรฃo nรฃo pode ser desfeita." + +features: + v3welcome: + title: "Bem-vindo ao Tabs aside 3.0" + text1: "Estamos felizes em anunciar nossa nova grande atualizaรงรฃo para a extensรฃo Tabs aside!" + text2: "Esta atualizaรงรฃo traz uma nova interface e muitos novos recursos, incluindo:" + list: + item1: "Suporte a grupos de abas" + item2: "Personalizaรงรฃo de coleรงรตes" + item3: "Reordenaรงรฃo e organizaรงรฃo por arrastar e soltar" + item4: "Criaรงรฃo manual de coleรงรตes do zero" + item5: "E mais!" + text3: "Visite nosso blog de desenvolvimento para saber mais sobre esta atualizaรงรฃo e todos os seus recursos!" + actions: + visit_blog: "Ler blog de desenvolvimento" + +notifications: + tabs_saved: + title: "Nova coleรงรฃo criada" + message: "Suas abas foram salvas em uma nova coleรงรฃo" + error_quota_exceeded: + title: "Limite mรกximo de operaรงรตes na nuvem excedido" + message: "Salvamos suas abas no armazenamento local. Vocรช precisarรก atualizar o armazenamento na nuvem manualmente" + error_storage_full: + title: "Seu armazenamento na nuvem estรก cheio" + message: "Salvamos suas abas no armazenamento local. Por favor, libere espaรงo na nuvem" + bookmark_saved: + title: "Exportado para favoritos" + message: "Sua coleรงรฃo foi exportada para os favoritos" + partial_save: + title: "Algumas abas nรฃo puderam ser salvas" + message: "Algumas abas eram abas do sistema que nรฃo pudemos acessar. Elas foram ignoradas" + +actions: + save: + all: "Salvar todas as abas" + selected: "Salvar abas selecionadas" + set_aside: + all: "Colocar todas as abas de lado" + selected: "Colocar abas selecionadas de lado" + show_collections: "Mostrar coleรงรตes" + +options_page: + title: "Configuraรงรตes" + general: + title: "Geral" + options: + always_show_toolbars: "Sempre mostrar barras de ferramentas" + include_pinned: "Incluir abas fixadas ao salvar todas as abas" + show_delete_prompt: "Pedir confirmaรงรฃo ao excluir um item" + show_badge: "Mostrar contador no รญcone" + show_notification: "Mostrar notificaรงรฃo ao salvar abas pelo menu de contexto" + unload_tabs: "Nรฃo carregar abas apรณs abrir" + list_locations: + title: "Abrir lista de coleรงรตes em:" + options: + sidebar: "Barra lateral" + popup: "Popup" + tab: "Aba separada" + pinned: "Aba fixada separada" + icon_action: + title: "Ao clicar no รญcone da extensรฃo:" + options: + action: "Executar aรงรฃo padrรฃo de salvar" + context: "Mostrar menu de contexto" + open: "Abrir lista de coleรงรตes" + change_shortcuts: "Alterar atalhos da extensรฃo" + actions: + title: "Aรงรตes padrรฃo" + options: + save_actions: + title: "Aรงรฃo padrรฃo ao salvar abas" + options: + set_aside: "Salvar e fechar abas" + save: "Salvar abas sem fechar" + restore_actions: + title: "Aรงรฃo padrรฃo ao abrir coleรงรตes" + options: + open: "Apenas abrir abas" + restore: "Abrir abas e remover a coleรงรฃo" + storage: + title: "Armazenamento" + capacity: + title: "Capacidade de armazenamento na nuvem" + description: "$1 de $2 KiB" + import: "Importar dados" + export: "Exportar dados" + import_results: + success: "Dados importados com sucesso" + error: "O arquivo fornecido parece estar corrompido. Nada foi importado" + import_prompt: + title: "Importar dados" + warning_title: "Esta รฉ uma aรงรฃo irreversรญvel" + warning_text: "Isso irรก sobrescrever todos os seus dados. Certifique-se de ter escolhido o arquivo correto, caso contrรกrio pode ocorrer corrupรงรฃo ou perda de dados. Recomenda-se exportar os dados antes." + proceed: "Escolher um arquivo" + enable: "Ativar armazenamento na nuvem" + disable: "Desativar armazenamento na nuvem" + disable_prompt: + text: "Esta aรงรฃo desativarรก a sincronizaรงรฃo de coleรงรตes entre seus dispositivos. As configuraรงรตes da extensรฃo ainda serรฃo sincronizadas." + action: "Desativar e recarregar a extensรฃo" + about: + title: "Sobre" + developed_by: "Desenvolvido por Eugene Fox" + licensed_under: "Licenciado sob" + mit_license: "Licenรงa MIT" + translation_cta: + text: "Encontrou um erro ou quer uma traduรงรฃo para seu idioma?" + button: "Comece aqui" + links: + website: "Meu site" + source: "Cรณdigo-fonte" + changelog: "Registro de alteraรงรตes" + +collections: + empty: "Esta coleรงรฃo estรก vazia" + tabs_count: "$1 abas" + actions: + open: "Abrir todas" + restore: "Restaurar todas" + new_window: "Abrir todas em nova janela" + incognito: + edge: "Abrir todas em nova janela InPrivate" + firefox: "Abrir todas em nova janela privativa" + chrome: "Abrir todas em janela anรดnima" + incognito_check: + title: "Permissรตes necessรกrias" + message: + edge: + p1: "A extensรฃo precisa de permissรฃo para abrir abas em janela InPrivate" + p2: "Para isso, clique em \"Configuraรงรตes\" e marque a opรงรฃo \"Permitir em InPrivate\"" + firefox: + p1: "A extensรฃo precisa de permissรฃo para abrir abas em janela privativa" + p2: "Para isso, clique em \"Configuraรงรตes\", vรก em \"Detalhes\" e defina \"Executar em janelas privadas\" como \"Permitir\"" + chrome: + p1: "A extensรฃo precisa de permissรฃo para abrir abas em janela anรดnima" + p2: "Para isso, clique em \"Configuraรงรตes\" e marque a opรงรฃo \"Permitir em modo anรดnimo\"" + action: "Configuraรงรตes" + menu: + delete: "Excluir coleรงรฃo" + add_selected: "Adicionar abas selecionadas" + add_all: "Adicionar todas as abas" + add_group: "Adicionar grupo vazio" + export_bookmarks: "Exportar para favoritos" + edit: "Editar coleรงรฃo" + +groups: + title: "Grupo" + pinned: "Fixado" + open: "Abrir todas" + empty: "Este grupo estรก vazio" + menu: + new_window: "Abrir em nova janela" + add_selected: "Adicionar abas selecionadas" + add_all: "Adicionar todas as abas" + edit: "Editar grupo" + ungroup: "Desagrupar" + delete: "Excluir grupo" + +tabs: + delete: "Excluir aba" + +colors: + none: "Sem cor" + any: "Qualquer cor" + grey: "Cinza" + blue: "Azul" + red: "Vermelho" + yellow: "Amarelo" + green: "Verde" + pink: "Rosa" + purple: "Roxo" + cyan: "Ciano" + orange: "Laranja" + +dialogs: + edit: + title: + edit_collection: "Editar coleรงรฃo" + edit_group: "Editar grupo" + new_group: "Novo grupo" + new_collection: "Nova coleรงรฃo" + collection_title: "Tรญtulo" + color: "Cor" + +main: + header: + create_collection: "Criar nova coleรงรฃo" + menu: + tiles_view: "Visualizaรงรฃo em blocos" + changelog: "O que hรก de novo?" + list: + searchbar: + title: "Pesquisar" + filter: "Filtrar" + sort: + title: "Ordenar" + options: + newest: "Mais recentes primeiro" + oldest: "Mais antigas primeiro" + ascending: "De A a Z" + descending: "De Z a A" + custom: "Personalizado" + empty: + title: "Nada para mostrar aqui ainda" + message: "Coloque suas abas atuais de lado ou crie uma nova coleรงรฃo" + empty_search: + title: "Nada encontrado" + message: "Tente alterar sua busca" + +cta_message: + title: "Gostou desta extensรฃo?" + message: "Considere apoiar o autor com uma doaรงรฃo ou" + feedback: "deixando um feedback" + +storage_full_message: + title: "Seu armazenamento na nuvem estรก quase cheio ($1%)" + message: "Vocรช pode liberar espaรงo excluindo coleรงรตes nรฃo utilizadas." + +parse_error_message: + title: "Nรฃo foi possรญvel obter coleรงรตes do seu armazenamento na nuvem." + message: "Seu armazenamento na nuvem parece estar corrompido. Vocรช pode corrigir isso substituindo pela sua cรณpia local." + action: "Corrigir com cรณpia local" + +merge_conflict_message: + title: "Seu armazenamento local e na nuvem possuem alteraรงรตes conflitantes." + message: "Para corrigir, vocรช pode enviar sua cรณpia local para a nuvem ou aceitar as alteraรงรตes da nuvem." + accept_local: "Substituir pela local" + accept_cloud: "Aceitar alteraรงรตes da nuvem" diff --git a/locales/ru.yml b/locales/ru.yml new file mode 100644 index 0000000..5f39b1b --- /dev/null +++ b/locales/ru.yml @@ -0,0 +1,251 @@ +manifest: + name: "ะžั‚ะปะพะถะตะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ" + description: "ะžั‚ะบะปะฐะดั‹ะฒะฐะนั‚ะต ะธ ะพั€ะณะฐะฝะธะทัƒะนั‚ะต ัะฒะพะธ ะฒะบะปะฐะดะบะธ. ะŸั€ะพะดะพะปะถะฐะนั‚ะต ั ั‚ะพะณะพ ะผะตัั‚ะฐ, ะณะดะต ะพัั‚ะฐะฝะพะฒะธะปะธััŒ" + author: "ะ•ะฒะณะตะฝะธะน ะ›ะธั" + +shortcuts: + toggle_sidebar: "ะžั‚ะบั€ั‹ั‚ัŒ ัะฟะธัะพะบ ะบะพะปะปะตะบั†ะธะน" + set_aside: "ะžั‚ะปะพะถะธั‚ัŒ ะฒะบะปะฐะดะบะธ" + save_tabs: "ะกะพั…ั€ะฐะฝะธั‚ัŒ ะฒะบะปะฐะดะบะธ" + +common: + actions: + cancel: "ะžั‚ะผะตะฝะฐ" + save: "ะกะพั…ั€ะฐะฝะธั‚ัŒ" + close: "ะ—ะฐะบั€ั‹ั‚ัŒ" + delete: "ะฃะดะฐะปะธั‚ัŒ" + reset_filters: "ะกะฑั€ะพัะธั‚ัŒ ั„ะธะปัŒั‚ั€ั‹" + cta: + feedback: "ะžัั‚ะฐะฒะธั‚ัŒ ะพั‚ะทั‹ะฒ" + sponsor: "ะŸะพะดะดะตั€ะถะฐั‚ัŒ" + tooltips: + more: "ะ•ั‰ั‘" + delete_prompt: "ะ’ั‹ ัƒะฒะตั€ะตะฝั‹? ะญั‚ะพ ะดะตะนัั‚ะฒะธะต ะฝะตะปัŒะทั ะพั‚ะผะตะฝะธั‚ัŒ." + +features: + v3welcome: + title: "ะ”ะพะฑั€ะพ ะฟะพะถะฐะปะพะฒะฐั‚ัŒ ะฒ ะžั‚ะปะพะถะตะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ 3.0" + text1: "ะœั‹ ั€ะฐะดั‹ ะฟั€ะตะดัั‚ะฐะฒะธั‚ัŒ ะฝะพะฒะพะต ะฑะพะปัŒัˆะพะต ะพะฑะฝะพะฒะปะตะฝะธะต ั€ะฐััˆะธั€ะตะฝะธั!" + text2: "ะญั‚ะพ ะพะฑะฝะพะฒะปะตะฝะธะต ะฒะบะปัŽั‡ะฐะตั‚ ัะพะฒะตั€ัˆะตะฝะฝะพ ะฝะพะฒั‹ะน ะธะฝั‚ะตั€ั„ะตะนั ะธ ะผะฝะพะถะตัั‚ะฒะพ ะฝะพะฒั‹ั… ั„ัƒะฝะบั†ะธะน, ั‚ะฐะบะธั… ะบะฐะบ:" + list: + item1: "ะŸะพะดะดะตั€ะถะบะฐ ะณั€ัƒะฟะฟะธั€ะพะฒะบะธ ะฒะบะปะฐะดะพะบ" + item2: "ะŸะตั€ัะพะฝะฐะปะธะทะฐั†ะธั ะบะพะปะปะตะบั†ะธะน" + item3: "ะŸะตั€ะตั‚ะฐัะบะธะฒะฐะฝะธะต ะบะพะปะปะตะบั†ะธะน ะธ ัะปะตะผะตะฝั‚ะพะฒ" + item4: "ะกะพะทะดะฐะฝะธะต ะบะพะปะปะตะบั†ะธะน ั ะฝัƒะปั" + item5: "ะ˜ ะผะฝะพะณะพะต ะดั€ัƒะณะพะต!" + text3: "ะŸะพัะตั‚ะธั‚ะต ะฑะปะพะณ ั€ะฐะทั€ะฐะฑะพั‚ั‡ะธะบะฐ (ั‚ะพะปัŒะบะพ ะฝะฐ ะฐะฝะณะปะธะนัะบะพะผ), ั‡ั‚ะพะฑั‹ ัƒะทะฝะฐั‚ัŒ ะฑะพะปัŒัˆะต ะพะฑ ัั‚ะพะผ ะพะฑะฝะพะฒะปะตะฝะธะธ ะธ ะฒัะตั… ะตะณะพ ั„ัƒะฝะบั†ะธัั…!" + actions: + visit_blog: "ะงะธั‚ะฐั‚ัŒ ะฑะปะพะณ" + +notifications: + tabs_saved: + title: "ะกะพะทะดะฐะฝะฐ ะฝะพะฒะฐั ะบะพะปะปะตะบั†ะธั" + message: "ะ’ะฐัˆะธ ะฒะบะปะฐะดะบะธ ัะพั…ั€ะฐะฝะตะฝั‹ ะฒ ะฝะพะฒัƒัŽ ะบะพะปะปะตะบั†ะธัŽ" + error_quota_exceeded: + title: "ะŸั€ะตะฒั‹ัˆะตะฝ ะปะธะผะธั‚ ะพะฟะตั€ะฐั†ะธะน ะทะฐะฟะธัะธ ะฒ ะพะฑะปะฐะบะพ" + message: "ะœั‹ ัะพั…ั€ะฐะฝะธะปะธ ะฒะฐัˆะธ ะฒะบะปะฐะดะบะธ ะฒ ะปะพะบะฐะปัŒะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต. ะ’ะฐะผ ะฝัƒะถะฝะพ ะฑัƒะดะตั‚ ะฒั€ัƒั‡ะฝัƒัŽ ะพะฑะฝะพะฒะธั‚ัŒ ะพะฑะปะฐั‡ะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต" + error_storage_full: + title: "ะžะฑะปะฐั‡ะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต ะทะฐะฟะพะปะฝะตะฝะพ" + message: "ะœั‹ ัะพั…ั€ะฐะฝะธะปะธ ะฒะฐัˆะธ ะฒะบะปะฐะดะบะธ ะฒ ะปะพะบะฐะปัŒะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต. ะŸะพะถะฐะปัƒะนัั‚ะฐ, ะพัะฒะพะฑะพะดะธั‚ะต ะผะตัั‚ะพ ะฒ ะพะฑะปะฐั‡ะฝะพะผ ั…ั€ะฐะฝะธะปะธั‰ะต" + bookmark_saved: + title: "ะญะบัะฟะพั€ั‚ะธั€ะพะฒะฐะฝะพ ะฒ ะทะฐะบะปะฐะดะบะธ" + message: "ะ’ะฐัˆะฐ ะบะพะปะปะตะบั†ะธั ัะบัะฟะพั€ั‚ะธั€ะพะฒะฐะฝะฐ ะฒ ะทะฐะบะปะฐะดะบะธ" + partial_save: + title: "ะะตะบะพั‚ะพั€ั‹ะต ะฒะบะปะฐะดะบะธ ะฝะต ะฑั‹ะปะธ ัะพั…ั€ะฐะฝะตะฝั‹" + message: "ะะตะบะพั‚ะพั€ั‹ะต ะธะท ะฒะบะปะฐะดะพะบ ัะฒะปััŽั‚ัั ัะธัั‚ะตะผะฝั‹ะผะธ ะธ ะฝะต ะผะพะณัƒั‚ ะฑั‹ั‚ัŒ ัะพั…ั€ะฐะฝะตะฝั‹" + +actions: + save: + all: "ะกะพั…ั€ะฐะฝะธั‚ัŒ ะฒัะต ะฒะบะปะฐะดะบะธ" + selected: "ะกะพั…ั€ะฐะฝะธั‚ัŒ ะฒั‹ะฑั€ะฐะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ" + set_aside: + all: "ะžั‚ะปะพะถะธั‚ัŒ ะฒัะต ะฒะบะปะฐะดะบะธ" + selected: "ะžั‚ะปะพะถะธั‚ัŒ ะฒั‹ะฑั€ะฐะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ" + show_collections: "ะŸะพะบะฐะทะฐั‚ัŒ ัะฟะธัะพะบ ะบะพะปะปะตะบั†ะธะน" + +options_page: + title: "ะะฐัั‚ั€ะพะนะบะธ" + general: + title: "ะžะฑั‰ะธะต" + options: + always_show_toolbars: "ะ’ัะตะณะดะฐ ะฟะพะบะฐะทั‹ะฒะฐั‚ัŒ ะฟะฐะฝะตะปะธ ะดะตะนัั‚ะฒะธะน" + include_pinned: "ะกะพั…ั€ะฐะฝัั‚ัŒ ะทะฐะบั€ะตะฟะปะตะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ ะฟั€ะธ ัะพั…ั€ะฐะฝะตะฝะธะธ ะฒัะตั… ะฒะบะปะฐะดะพะบ" + show_delete_prompt: "ะกะฟั€ะฐัˆะธะฒะฐั‚ัŒ ะฟะพะดั‚ะฒะตั€ะถะดะตะฝะธะต ะฟั€ะธ ัƒะดะฐะปะตะฝะธะธ ัะปะตะผะตะฝั‚ะพะฒ" + show_badge: "ะŸะพะบะฐะทั‹ะฒะฐั‚ัŒ ัั‡ะตั‚ั‡ะธะบ" + show_notification: "ะŸะพะบะฐะทั‹ะฒะฐั‚ัŒ ัƒะฒะตะดะพะผะปะตะฝะธะต ะฟั€ะธ ัะพั…ั€ะฐะฝะตะฝะธะธ ั‡ะตั€ะตะท ะบะพะฝั‚ะตะบัั‚ะฝะพะต ะผะตะฝัŽ" + unload_tabs: "ะะต ะทะฐะณั€ัƒะถะฐั‚ัŒ ะฒะบะปะฐะดะบะธ ะฟะพัะปะต ะพั‚ะบั€ั‹ั‚ะธั" + list_locations: + title: "ะžั‚ะบั€ั‹ะฒะฐั‚ัŒ ัะฟะธัะพะบ ะบะพะปะปะตะบั†ะธะน ะฒ:" + options: + sidebar: "ะ‘ะพะบะพะฒะพะน ะฟะฐะฝะตะปะธ" + popup: "ะ’ัะฟะปั‹ะฒะฐัŽั‰ะตะผ ะพะบะฝะต" + tab: "ะžั‚ะดะตะปัŒะฝะพะน ะฒะบะปะฐะดะบะต" + pinned: "ะžั‚ะดะตะปัŒะฝะพะน ะทะฐะบั€ะตะฟะปะตะฝะฝะพะน ะฒะบะปะฐะดะบะต" + icon_action: + title: "ะŸั€ะธ ะฝะฐะถะฐั‚ะธะธ ะฝะฐ ะธะบะพะฝะบัƒ ั€ะฐััˆะธั€ะตะฝะธั:" + options: + action: "ะกะพั…ั€ะฐะฝะธั‚ัŒ ะฒะบะปะฐะดะบะธ (ะดะตะนัั‚ะฒะธะต ะฟะพ ัƒะผะพะปั‡ะฐะฝะธัŽ)" + context: "ะŸะพะบะฐะทะฐั‚ัŒ ะบะพะฝั‚ะตะบัั‚ะฝะพะต ะผะตะฝัŽ" + open: "ะžั‚ะบั€ั‹ั‚ัŒ ัะฟะธัะพะบ ะบะพะปะปะตะบั†ะธะน" + change_shortcuts: "ะ˜ะทะผะตะฝะธั‚ัŒ ะณะพั€ัั‡ะธะต ะบะปะฐะฒะธัˆะธ" + actions: + title: "ะ”ะตะนัั‚ะฒะธั" + options: + save_actions: + title: "ะ”ะตะนัั‚ะฒะธะต ะฟะพ ัƒะผะพะปั‡ะฐะฝะธัŽ ะฟั€ะธ ัะพั…ั€ะฐะฝะตะฝะธะธ ะฒะบะปะฐะดะพะบ" + options: + set_aside: "ะกะพั…ั€ะฐะฝะธั‚ัŒ ะธ ะทะฐะบั€ั‹ั‚ัŒ ะฒะบะปะฐะดะบะธ" + save: "ะกะพั…ั€ะฐะฝะธั‚ัŒ ะฒะบะปะฐะดะบะธ, ะฝะต ะทะฐะบั€ั‹ะฒะฐั ะธั…" + restore_actions: + title: "ะ”ะตะนัั‚ะฒะธะต ะฟะพ ัƒะผะพะปั‡ะฐะฝะธัŽ ะฟั€ะธ ะพั‚ะบั€ั‹ั‚ะธะธ ะบะพะปะปะตะบั†ะธะน" + options: + open: "ะŸั€ะพัั‚ะพ ะพั‚ะบั€ั‹ั‚ัŒ ะฒะบะปะฐะดะบะธ" + restore: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒะบะปะฐะดะบะธ ะธ ัƒะดะฐะปะธั‚ัŒ ะบะพะปะปะตะบั†ะธัŽ" + storage: + title: "ะฅั€ะฐะฝะธะปะธั‰ะต" + capacity: + title: "ะžะฑัŠั‘ะผ ะพะฑะปะฐั‡ะฝะพะณะพ ั…ั€ะฐะฝะธะปะธั‰ะฐ" + description: "$1 ะธะท $2 ะšะธะ‘" + import: "ะ˜ะผะฟะพั€ั‚ ะดะฐะฝะฝั‹ั…" + export: "ะญะบัะฟะพั€ั‚ ะดะฐะฝะฝั‹ั…" + import_results: + success: "ะ”ะฐะฝะฝั‹ะต ัƒัะฟะตัˆะฝะพ ะธะผะฟะพั€ั‚ะธั€ะพะฒะฐะฝั‹" + error: "ะŸะพั…ะพะถะต, ะฒั‹ะฑั€ะฐะฝะฝั‹ะน ั„ะฐะนะป ะฟะพะฒั€ะตะถะดะตะฝ. ะะธั‡ะตะณะพ ะฝะต ะฑั‹ะปะพ ะธะผะฟะพั€ั‚ะธั€ะพะฒะฐะฝะพ" + import_prompt: + title: "ะ˜ะผะฟะพั€ั‚ ะดะฐะฝะฝั‹ั…" + warning_title: "ะญั‚ะพ ะฝะตะพะฑั€ะฐั‚ะธะผะพะต ะดะตะนัั‚ะฒะธะต!" + warning_text: "ะžะฝะพ ะฟะตั€ะตะทะฐะฟะธัˆะตั‚ ะฒัะต ะฒะฐัˆะธ ะดะฐะฝะฝั‹ะต. ะฃะฑะตะดะธั‚ะตััŒ, ั‡ั‚ะพ ะฒั‹ ะฒั‹ะฑั€ะฐะปะธ ะฟั€ะฐะฒะธะปัŒะฝั‹ะน ั„ะฐะนะป, ะธะฝะฐั‡ะต ัั‚ะพ ะผะพะถะตั‚ ะฟั€ะธะฒะตัั‚ะธ ะบ ะฟะพะฒั€ะตะถะดะตะฝะธัŽ ะธะปะธ ะฟะพั‚ะตั€ะต ะดะฐะฝะฝั‹ั…. ะ ะตะบะพะผะตะฝะดัƒะตั‚ัั ัะฝะฐั‡ะฐะปะฐ ัะบัะฟะพั€ั‚ะธั€ะพะฒะฐั‚ัŒ ะดะฐะฝะฝั‹ะต." + proceed: "ะ’ั‹ะฑั€ะฐั‚ัŒ ั„ะฐะนะป" + enable: "ะ’ะบะปัŽั‡ะธั‚ัŒ ะพะฑะปะฐั‡ะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต" + disable: "ะžั‚ะบะปัŽั‡ะธั‚ัŒ ะพะฑะปะฐั‡ะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต" + disable_prompt: + text: "ะญั‚ะพ ะดะตะนัั‚ะฒะธะต ะพั‚ะบะปัŽั‡ะธั‚ ัะธะฝั…ั€ะพะฝะธะทะฐั†ะธัŽ ะบะพะปะปะตะบั†ะธะน ะผะตะถะดัƒ ะฒะฐัˆะธะผะธ ัƒัั‚ั€ะพะนัั‚ะฒะฐะผะธ. ะะฐัั‚ั€ะพะนะบะธ ั€ะฐััˆะธั€ะตะฝะธั ะฟั€ะพะดะพะปะถะฐั‚ ั…ั€ะฐะฝะธั‚ัŒัั ะฒ ะพะฑะปะฐะบะต." + action: "ะžั‚ะบะปัŽั‡ะธั‚ัŒ ะธ ะฟะตั€ะตะทะฐะณั€ัƒะทะธั‚ัŒ ั€ะฐััˆะธั€ะตะฝะธะต" + about: + title: "ะž ั€ะฐััˆะธั€ะตะฝะธะธ" + developed_by: "ะ ะฐะทั€ะฐะฑะพั‚ั‡ะธะบ: ะ•ะฒะณะตะฝะธะน ะ›ะธั" + licensed_under: "" + mit_license: "ะ›ะธั†ะตะฝะทะธั MIT" + translation_cta: + text: "ะะฐัˆะปะธ ะพะฟะตั‡ะฐั‚ะบัƒ ะธะปะธ ั…ะพั‚ะธั‚ะต ะฟะตั€ะตะฒะพะด ะฝะฐ ะฒะฐัˆ ัะทั‹ะบ?" + button: "ะะฐั‡ะฝะธั‚ะต ะทะดะตััŒ" + links: + website: "ะœะพะน ะฒะตะฑ-ัะฐะนั‚" + source: "ะ˜ัั…ะพะดะฝั‹ะน ะบะพะด" + changelog: "ะกะฟะธัะพะบ ะธะทะผะตะฝะตะฝะธะน" + +collections: + empty: "ะญั‚ะฐ ะบะพะปะปะตะบั†ะธั ะฟัƒัั‚ะฐ" + tabs_count: "ะ’ะบะปะฐะดะพะบ: $1" + actions: + open: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒัะต" + restore: "ะ’ะพััั‚ะฐะฝะพะฒะธั‚ัŒ ะฒัะต" + new_window: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒ ะฝะพะฒะพะผ ะพะบะฝะต" + incognito: + edge: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒ ะพะบะฝะต InPrivate" + firefox: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒ ะฝะพะฒะพะผ ะฟั€ะธะฒะฐั‚ะฝะพะผ ะพะบะฝะต" + chrome: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒ ั€ะตะถะธะผะต ะธะฝะบะพะณะฝะธั‚ะพ" + incognito_check: + title: "ะขั€ะตะฑัƒะตั‚ัั ั€ะฐะทั€ะตัˆะตะฝะธะต" + message: + edge: + p1: "ะ ะฐััˆะธั€ะตะฝะธัŽ ะฝะตะพะฑั…ะพะดะธะผะพ ะดะพะฟะพะปะฝะธั‚ะตะปัŒะฝะพะต ั€ะฐะทั€ะตัˆะตะฝะธะต, ั‡ั‚ะพะฑั‹ ะพั‚ะบั€ั‹ั‚ัŒ ะฒะบะปะฐะดะบะธ ะฒ ั€ะตะถะธะผะต InPrivate" + p2: "ะ”ะปั ัั‚ะพะณะพ ะฝะฐะถะผะธั‚ะต \"ะะฐัั‚ั€ะพะนะบะธ\" ะธ ะทะฐั‚ะตะผ ะพั‚ะผะตั‚ัŒั‚ะต ะพะฟั†ะธัŽ \"ะ ะฐะทั€ะตัˆะธั‚ัŒ ะฒ ั€ะตะถะธะผะต InPrivate\"" + firefox: + p1: "ะ ะฐััˆะธั€ะตะฝะธัŽ ะฝะตะพะฑั…ะพะดะธะผะพ ะดะพะฟะพะปะฝะธั‚ะตะปัŒะฝะพะต ั€ะฐะทั€ะตัˆะตะฝะธะต, ั‡ั‚ะพะฑั‹ ะพั‚ะบั€ั‹ั‚ัŒ ะฒะบะปะฐะดะบะธ ะฒ ะฟั€ะธะฒะฐั‚ะฝะพะผ ะพะบะฝะต" + p2: "ะ”ะปั ัั‚ะพะณะพ ะฝะฐะถะผะธั‚ะต \"ะะฐัั‚ั€ะพะนะบะธ\", ะฟะตั€ะตะนะดะธั‚ะต ะฒ \"ะŸะพะดั€ะพะฑะฝะพัั‚ะธ\" ะธ ั€ะฐะทั€ะตัˆะธั‚ะต \"ะ—ะฐะฟัƒัะบ ะฒ ะฟั€ะธะฒะฐั‚ะฝั‹ั… ะพะบะฝะฐั…\"" + chrome: + p1: "ะ ะฐััˆะธั€ะตะฝะธัŽ ะฝะตะพะฑั…ะพะดะธะผะพ ะดะพะฟะพะปะฝะธั‚ะตะปัŒะฝะพะต ั€ะฐะทั€ะตัˆะตะฝะธะต, ั‡ั‚ะพะฑั‹ ะพั‚ะบั€ั‹ั‚ัŒ ะฒะบะปะฐะดะบะธ ะฒ ั€ะตะถะธะผะต ะธะฝะบะพะณะฝะธั‚ะพ" + p2: "ะ”ะปั ัั‚ะพะณะพ ะฝะฐะถะผะธั‚ะต \"ะะฐัั‚ั€ะพะนะบะธ\" ะธ ะพั‚ะผะตั‚ัŒั‚ะต ะพะฟั†ะธัŽ \"ะ ะฐะทั€ะตัˆะธั‚ัŒ ะธัะฟะพะปัŒะทะพะฒะฐะฝะธะต ะฒ ั€ะตะถะธะผะต ะธะฝะบะพะณะฝะธั‚ะพ\"" + action: "ะะฐัั‚ั€ะพะนะบะธ" + menu: + delete: "ะฃะดะฐะปะธั‚ัŒ ะบะพะปะปะตะบั†ะธัŽ" + add_selected: "ะ”ะพะฑะฐะฒะธั‚ัŒ ะฒั‹ะฑั€ะฐะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ" + add_all: "ะ”ะพะฑะฐะฒะธั‚ัŒ ะฒัะต ะฒะบะปะฐะดะบะธ" + add_group: "ะ”ะพะฑะฐะฒะธั‚ัŒ ะฟัƒัั‚ัƒัŽ ะณั€ัƒะฟะฟัƒ" + export_bookmarks: "ะญะบัะฟะพั€ั‚ะธั€ะพะฒะฐั‚ัŒ ะฒ ะทะฐะบะปะฐะดะบะธ" + edit: "ะ ะตะดะฐะบั‚ะธั€ะพะฒะฐั‚ัŒ ะบะพะปะปะตะบั†ะธัŽ" + +groups: + title: "ะ“ั€ัƒะฟะฟะฐ" + pinned: "ะ—ะฐะบั€ะตะฟะปะตะฝะฝั‹ะต" + open: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒัะต" + empty: "ะญั‚ะฐ ะณั€ัƒะฟะฟะฐ ะฟัƒัั‚ะฐ" + menu: + new_window: "ะžั‚ะบั€ั‹ั‚ัŒ ะฒ ะฝะพะฒะพะผ ะพะบะฝะต" + add_selected: "ะ”ะพะฑะฐะฒะธั‚ัŒ ะฒั‹ะฑั€ะฐะฝะฝั‹ะต ะฒะบะปะฐะดะบะธ" + add_all: "ะ”ะพะฑะฐะฒะธั‚ัŒ ะฒัะต ะฒะบะปะฐะดะบะธ" + edit: "ะ ะตะดะฐะบั‚ะธั€ะพะฒะฐั‚ัŒ ะณั€ัƒะฟะฟัƒ" + ungroup: "ะ ะฐะทะณั€ัƒะฟะฟะธั€ะพะฒะฐั‚ัŒ" + delete: "ะฃะดะฐะปะธั‚ัŒ ะณั€ัƒะฟะฟัƒ" + +tabs: + delete: "ะฃะดะฐะปะธั‚ัŒ ะฒะบะปะฐะดะบัƒ" + +colors: + none: "ะ‘ะตะท ั†ะฒะตั‚ะฐ" + any: "ะ›ัŽะฑะพะน ั†ะฒะตั‚" + grey: "ะกะตั€ั‹ะน" + blue: "ะกะธะฝะธะน" + red: "ะšั€ะฐัะฝั‹ะน" + yellow: "ะ–ั‘ะปั‚ั‹ะน" + green: "ะ—ะตะปั‘ะฝั‹ะน" + pink: "ะ ะพะทะพะฒั‹ะน" + purple: "ะคะธะพะปะตั‚ะพะฒั‹ะน" + cyan: "ะ“ะพะปัƒะฑะพะน" + orange: "ะžั€ะฐะฝะถะตะฒั‹ะน" + +dialogs: + edit: + title: + edit_collection: "ะ ะตะดะฐะบั‚ะธั€ะพะฒะฐั‚ัŒ ะบะพะปะปะตะบั†ะธัŽ" + edit_group: "ะ ะตะดะฐะบั‚ะธั€ะพะฒะฐั‚ัŒ ะณั€ัƒะฟะฟัƒ" + new_group: "ะะพะฒะฐั ะณั€ัƒะฟะฟะฐ" + new_collection: "ะะพะฒะฐั ะบะพะปะปะตะบั†ะธั" + collection_title: "ะะฐะทะฒะฐะฝะธะต" + color: "ะฆะฒะตั‚" + +main: + header: + create_collection: "ะกะพะทะดะฐั‚ัŒ ะฝะพะฒัƒัŽ ะบะพะปะปะตะบั†ะธัŽ" + menu: + tiles_view: "ะŸะปะธั‚ะบะธ" + changelog: "ะงั‚ะพ ะฝะพะฒะพะณะพ?" + list: + searchbar: + title: "ะŸะพะธัะบ" + filter: "ะคะธะปัŒั‚ั€" + sort: + title: "ะกะพั€ั‚ะธั€ะพะฒะบะฐ" + options: + newest: "ะกะฝะฐั‡ะฐะปะฐ ะฝะพะฒั‹ะต" + oldest: "ะกะฝะฐั‡ะฐะปะฐ ัั‚ะฐั€ั‹ะต" + ascending: "ะžั‚ ะ ะดะพ ะฏ" + descending: "ะžั‚ ะฏ ะดะพ ะ" + custom: "ะŸั€ะพะธะทะฒะพะปัŒะฝะฐั" + empty: + title: "ะŸะพะบะฐ ั‡ั‚ะพ ะทะดะตััŒ ะฝะธั‡ะตะณะพ ะฝะตั‚" + message: "ะžั‚ะปะพะถะธั‚ะต ั‚ะตะบัƒั‰ะธะต ะฒะบะปะฐะดะบะธ ะธะปะธ ัะพะทะดะฐะนั‚ะต ะฝะพะฒัƒัŽ ะบะพะปะปะตะบั†ะธัŽ" + empty_search: + title: "ะะธั‡ะตะณะพ ะฝะต ะฝะฐะนะดะตะฝะพ" + message: "ะŸะพะฟั€ะพะฑัƒะนั‚ะต ะธะทะผะตะฝะธั‚ัŒ ะฟะพะธัะบะพะฒั‹ะน ะทะฐะฟั€ะพั" + +cta_message: + title: "ะั€ะฐะฒะธั‚ัั ั€ะฐััˆะธั€ะตะฝะธะต?" + message: "ะŸะพะดะดะตั€ะถะธั‚ะต ะฐะฒั‚ะพั€ะฐ ะฟะพะถะตั€ั‚ะฒะพะฒะฐะฝะธะตะผ ะธะปะธ" + feedback: "ะพัั‚ะฐะฒัŒั‚ะต ะพั‚ะทั‹ะฒ" + +storage_full_message: + title: "ะžะฑะปะฐั‡ะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต ะฟะพั‡ั‚ะธ ะทะฐะฟะพะปะฝะตะฝะพ ($1%)" + message: "ะ’ั‹ ะผะพะถะตั‚ะต ะพัะฒะพะฑะพะดะธั‚ัŒ ะผะตัั‚ะพ, ัƒะดะฐะปะธะฒ ะฝะตะธัะฟะพะปัŒะทัƒะตะผั‹ะต ะบะพะปะปะตะบั†ะธะธ." + +parse_error_message: + title: "ะœั‹ ะฝะต ัะผะพะณะปะธ ะฟะพะปัƒั‡ะธั‚ัŒ ะบะพะปะปะตะบั†ะธะธ ะธะท ะฒะฐัˆะตะณะพ ะพะฑะปะฐั‡ะฝะพะณะพ ั…ั€ะฐะฝะธะปะธั‰ะฐ." + message: "ะŸะพั…ะพะถะต, ะพะฑะปะฐั‡ะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต ะฟะพะฒั€ะตะถะดะตะฝะพ. ะงั‚ะพะฑั‹ ัั‚ะพ ะธัะฟั€ะฐะฒะธั‚ัŒ, ะฒั‹ ะผะพะถะตั‚ะต ะทะฐะผะตะฝะธั‚ัŒ ะตะณะพ ะปะพะบะฐะปัŒะฝะพะน ะบะพะฟะธะตะน." + action: "ะ˜ัะฟะพะปัŒะทะพะฒะฐั‚ัŒ ะปะพะบะฐะปัŒะฝัƒัŽ ะบะพะฟะธัŽ" + +merge_conflict_message: + title: "ะ’ ะปะพะบะฐะปัŒะฝะพะผ ะธ ะพะฑะปะฐั‡ะฝะพะผ ั…ั€ะฐะฝะธะปะธั‰ะฐั… ะตัั‚ัŒ ะบะพะฝั„ะปะธะบั‚ัƒัŽั‰ะธะต ะธะทะผะตะฝะตะฝะธั." + message: "ะงั‚ะพะฑั‹ ัั‚ะพ ะธัะฟั€ะฐะฒะธั‚ัŒ, ะฒั‹ ะผะพะถะตั‚ะต ัะพั…ั€ะฐะฝะธั‚ัŒ ะปะพะบะฐะปัŒะฝัƒัŽ ะบะพะฟะธัŽ ะฒ ะพะฑะปะฐะบะพ ะปะธะฑะพ ะฟั€ะธะฝัั‚ัŒ ะธะทะผะตะฝะตะฝะธั ะธะท ะพะฑะปะฐะบะฐ." + accept_local: "ะ—ะฐะผะตะฝะธั‚ัŒ ะปะพะบะฐะปัŒะฝะพะน" + accept_cloud: "ะŸั€ะธะฝัั‚ัŒ ะพะฑะปะฐั‡ะฝั‹ะต ะธะทะผะตะฝะตะฝะธั" diff --git a/locales/uk.yml b/locales/uk.yml new file mode 100644 index 0000000..f83a2bc --- /dev/null +++ b/locales/uk.yml @@ -0,0 +1,251 @@ +manifest: + name: "ะ’ั–ะดะบะปะฐะดะตะฝั– ะฒะบะปะฐะดะบะธ" + description: "ะ’ั–ะดะบะปะฐะดะฐะนั‚ะต ั‚ะฐ ะพั€ะณะฐะฝั–ะทะพะฒัƒะนั‚ะต ัะฒะพั— ะฒะบะปะฐะดะบะธ. ะŸั€ะพะดะพะฒะถั‚ะต ะท ั‚ะพะณะพ ะผั–ัั†ั, ะดะต ะทัƒะฟะธะฝะธะปะธัั" + author: "ะ„ะฒะณะตะฝ ะ›ะธั" + +shortcuts: + toggle_sidebar: "ะ’ั–ะดะบั€ะธั‚ะธ ัะฟะธัะพะบ ะบะพะปะตะบั†ั–ะน" + set_aside: "ะ’ั–ะดะบะปะฐัั‚ะธ ะฒะบะปะฐะดะบะธ" + save_tabs: "ะ—ะฑะตั€ะตะณั‚ะธ ะฒะบะปะฐะดะบะธ" + +common: + actions: + cancel: "ะกะบะฐััƒะฒะฐั‚ะธ" + save: "ะ—ะฑะตั€ะตะณั‚ะธ" + close: "ะ—ะฐะบั€ะธั‚ะธ" + delete: "ะ’ะธะดะฐะปะธั‚ะธ" + reset_filters: "ะกะบะธะฝัƒั‚ะธ ั„ั–ะปัŒั‚ั€ะธ" + cta: + feedback: "ะ—ะฐะปะธัˆะธั‚ะธ ะฒั–ะดะณัƒะบ" + sponsor: "ะŸั–ะดั‚ั€ะธะผะฐั‚ะธ" + tooltips: + more: "ะฉะต" + delete_prompt: "ะ’ะธ ะฒะฟะตะฒะฝะตะฝั–? ะฆัŽ ะดั–ัŽ ะฝะต ะผะพะถะฝะฐ ัะบะฐััƒะฒะฐั‚ะธ." + +features: + v3welcome: + title: "ะ›ะฐัะบะฐะฒะพ ะฟั€ะพัะธะผะพ ะดะพ ะ’ั–ะดะบะปะฐะดะตะฝะธั… ะฒะบะปะฐะดะพะบ 3.0" + text1: "ะœะธ ั€ะฐะดั– ะฟั€ะตะดัั‚ะฐะฒะธั‚ะธ ะฝะพะฒะต ะฒะตะปะธะบะต ะพะฝะพะฒะปะตะฝะฝั ั€ะพะทัˆะธั€ะตะฝะฝั!" + text2: "ะฆะต ะพะฝะพะฒะปะตะฝะฝั ะฒะบะปัŽั‡ะฐั” ะฐะฑัะพะปัŽั‚ะฝะพ ะฝะพะฒะธะน ั–ะฝั‚ะตั€ั„ะตะนั ั– ะฑะตะทะปั–ั‡ ะฝะพะฒะธั… ั„ัƒะฝะบั†ั–ะน, ั‚ะฐะบะธั… ัะบ:" + list: + item1: "ะŸั–ะดั‚ั€ะธะผะบะฐ ะณั€ัƒะฟัƒะฒะฐะฝะฝั ะฒะบะปะฐะดะพะบ" + item2: "ะŸะตั€ัะพะฝะฐะปั–ะทะฐั†ั–ั ะบะพะปะตะบั†ั–ะน" + item3: "ะŸะตั€ะตั‚ัะณัƒะฒะฐะฝะฝั ะบะพะปะตะบั†ั–ะน ั‚ะฐ ะตะปะตะผะตะฝั‚ั–ะฒ" + item4: "ะกั‚ะฒะพั€ะตะฝะฝั ะบะพะปะตะบั†ั–ะน ะท ะฝัƒะปั" + item5: "ะ† ั‰ะต ะฑะฐะณะฐั‚ะพ ั–ะฝัˆะพะณะพ!" + text3: "ะ’ั–ะดะฒั–ะดะฐะนั‚ะต ะฑะปะพะณ ั€ะพะทั€ะพะฑะฝะธะบะฐ (ั‚ั–ะปัŒะบะธ ะฐะฝะณะปั–ะนััŒะบะพัŽ), ั‰ะพะฑ ะดั–ะทะฝะฐั‚ะธัั ะฑั–ะปัŒัˆะต ะฟั€ะพ ั†ะต ะพะฝะพะฒะปะตะฝะฝั ั‚ะฐ ะฒัั– ะนะพะณะพ ั„ัƒะฝะบั†ั–ั—!" + actions: + visit_blog: "ะงะธั‚ะฐั‚ะธ ะฑะปะพะณ" + +notifications: + tabs_saved: + title: "ะกั‚ะฒะพั€ะตะฝะพ ะฝะพะฒัƒ ะบะพะปะตะบั†ั–ัŽ" + message: "ะ’ะฐัˆั– ะฒะบะปะฐะดะบะธ ะทะฑะตั€ะตะถะตะฝะพ ะฒ ะฝะพะฒัƒ ะบะพะปะตะบั†ั–ัŽ" + error_quota_exceeded: + title: "ะŸะตั€ะตะฒะธั‰ะตะฝะพ ะปั–ะผั–ั‚ ะพะฟะตั€ะฐั†ั–ะน ะทะฐะฟะธััƒ ะฒ ั…ะผะฐั€ั–" + message: "ะœะธ ะทะฑะตั€ะตะณะปะธ ะฒะฐัˆั– ะฒะบะปะฐะดะบะธ ะฒ ะปะพะบะฐะปัŒะฝะพะผัƒ ัั…ะพะฒะธั‰ั–. ะ’ะฐะผ ะฟะพั‚ั€ั–ะฑะฝะพ ะฑัƒะดะต ะฒั€ัƒั‡ะฝัƒ ะพะฝะพะฒะธั‚ะธ ั…ะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต" + error_storage_full: + title: "ะฅะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต ะทะฐะฟะพะฒะฝะตะฝะต" + message: "ะœะธ ะทะฑะตั€ะตะณะปะธ ะฒะฐัˆั– ะฒะบะปะฐะดะบะธ ะฒ ะปะพะบะฐะปัŒะฝะพะผัƒ ัั…ะพะฒะธั‰ั–. ะ‘ัƒะดัŒ ะปะฐัะบะฐ, ะทะฒั–ะปัŒะฝั–ั‚ัŒ ะผั–ัั†ะต ะฒ ั…ะผะฐั€ะฝะพะผัƒ ัั…ะพะฒะธั‰ั–" + bookmark_saved: + title: "ะ•ะบัะฟะพั€ั‚ะพะฒะฐะฝะพ ะฒ ะทะฐะบะปะฐะดะบะธ" + message: "ะ’ะฐัˆะฐ ะบะพะปะตะบั†ั–ั ะตะบัะฟะพั€ั‚ะพะฒะฐะฝะฐ ะฒ ะทะฐะบะปะฐะดะบะธ" + partial_save: + title: "ะ”ะตัะบั– ะฒะบะปะฐะดะบะธ ะฝะต ะฑัƒะปะธ ะทะฑะตั€ะตะถะตะฝั–" + message: "ะ”ะตัะบั– ะท ะฒะบะปะฐะดะพะบ ั” ัะธัั‚ะตะผะฝะธะผะธ ั– ะฝะต ะผะพะถัƒั‚ัŒ ะฑัƒั‚ะธ ะทะฑะตั€ะตะถะตะฝั–" + +actions: + save: + all: "ะ—ะฑะตั€ะตะณั‚ะธ ะฒัั– ะฒะบะปะฐะดะบะธ" + selected: "ะ—ะฑะตั€ะตะณั‚ะธ ะฒะธะฑั€ะฐะฝั– ะฒะบะปะฐะดะบะธ" + set_aside: + all: "ะ’ั–ะดะบะปะฐัั‚ะธ ะฒัั– ะฒะบะปะฐะดะบะธ" + selected: "ะ’ั–ะดะบะปะฐัั‚ะธ ะฒะธะฑั€ะฐะฝั– ะฒะบะปะฐะดะบะธ" + show_collections: "ะŸะพะบะฐะทะฐั‚ะธ ัะฟะธัะพะบ ะบะพะปะตะบั†ั–ะน" + +options_page: + title: "ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั" + general: + title: "ะ—ะฐะณะฐะปัŒะฝั–" + options: + always_show_toolbars: "ะ—ะฐะฒะถะดะธ ะฟะพะบะฐะทัƒะฒะฐั‚ะธ ะฟะฐะฝะตะปั– ะดั–ะน" + include_pinned: "ะ—ะฑะตั€ั–ะณะฐั‚ะธ ะทะฐะบั€ั–ะฟะปะตะฝั– ะฒะบะปะฐะดะบะธ ะฟั€ะธ ะทะฑะตั€ะตะถะตะฝะฝั– ะฒัั–ั… ะฒะบะปะฐะดะพะบ" + show_delete_prompt: "ะ—ะฐะฟะธั‚ัƒะฒะฐั‚ะธ ะฟั–ะดั‚ะฒะตั€ะดะถะตะฝะฝั ะฟั€ะธ ะฒะธะดะฐะปะตะฝะฝั– ะตะปะตะผะตะฝั‚ั–ะฒ" + show_badge: "ะŸะพะบะฐะทัƒะฒะฐั‚ะธ ะปั–ั‡ะธะปัŒะฝะธะบ" + show_notification: "ะŸะพะบะฐะทัƒะฒะฐั‚ะธ ัะฟะพะฒั–ั‰ะตะฝะฝั ะฟั€ะธ ะทะฑะตั€ะตะถะตะฝะฝั– ั‡ะตั€ะตะท ะบะพะฝั‚ะตะบัั‚ะฝะต ะผะตะฝัŽ" + unload_tabs: "ะะต ะทะฐะฒะฐะฝั‚ะฐะถัƒะฒะฐั‚ะธ ะฒะบะปะฐะดะบะธ ะฟั–ัะปั ะฒั–ะดะบั€ะธั‚ั‚ั" + list_locations: + title: "ะ’ั–ะดะบั€ะธะฒะฐั‚ะธ ัะฟะธัะพะบ ะบะพะปะตะบั†ั–ะน ัƒ:" + options: + sidebar: "ะ‘ั–ั‡ะฝะพั— ะฟะฐะฝะตะปั–" + popup: "ะ’ัะฟะปะธะฒะฐัŽั‡ะพะผัƒ ะฒั–ะบะฝั–" + tab: "ะžะบั€ะตะผั–ะน ะฒะบะปะฐะดั†ั–" + pinned: "ะ—ะฐะบั€ั–ะฟะปะตะฝั–ะน ะฒะบะปะฐะดั†ั–" + icon_action: + title: "ะŸั€ะธ ะฝะฐั‚ะธัะบะฐะฝะฝั– ะฝะฐ ั–ะบะพะฝะบัƒ ั€ะพะทัˆะธั€ะตะฝะฝั:" + options: + action: "ะ—ะฑะตั€ะตะณั‚ะธ ะฒะบะปะฐะดะบะธ (ะดั–ั ะทะฐ ะทะฐะผะพะฒั‡ัƒะฒะฐะฝะฝัะผ)" + context: "ะŸะพะบะฐะทะฐั‚ะธ ะบะพะฝั‚ะตะบัั‚ะฝะต ะผะตะฝัŽ" + open: "ะ’ั–ะดะบั€ะธั‚ะธ ัะฟะธัะพะบ ะบะพะปะตะบั†ั–ะน" + change_shortcuts: "ะ—ะผั–ะฝะธั‚ะธ ะณะฐั€ัั‡ั– ะบะปะฐะฒั–ัˆั–" + actions: + title: "ะ”ั–ั—" + options: + save_actions: + title: "ะ”ั–ั ะทะฐ ะทะฐะผะพะฒั‡ัƒะฒะฐะฝะฝัะผ ะฟั€ะธ ะทะฑะตั€ะตะถะตะฝะฝั– ะฒะบะปะฐะดะพะบ" + options: + set_aside: "ะ—ะฑะตั€ะตะณั‚ะธ ั‚ะฐ ะทะฐะบั€ะธั‚ะธ ะฒะบะปะฐะดะบะธ" + save: "ะ—ะฑะตั€ะตะณั‚ะธ ะฒะบะปะฐะดะบะธ, ะฝะต ะทะฐะบั€ะธะฒะฐัŽั‡ะธ ั—ั…" + restore_actions: + title: "ะ”ั–ั ะทะฐ ะทะฐะผะพะฒั‡ัƒะฒะฐะฝะฝัะผ ะฟั€ะธ ะฒั–ะดะบั€ะธั‚ั‚ั– ะบะพะปะตะบั†ั–ะน" + options: + open: "ะŸั€ะพัั‚ะพ ะฒั–ะดะบั€ะธั‚ะธ ะฒะบะปะฐะดะบะธ" + restore: "ะ’ั–ะดะบั€ะธั‚ะธ ะฒะบะปะฐะดะบะธ ั‚ะฐ ะฒะธะดะฐะปะธั‚ะธ ะบะพะปะตะบั†ั–ัŽ" + storage: + title: "ะกั…ะพะฒะธั‰ะต" + capacity: + title: "ะฅะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต" + description: "$1 ะท $2 ะšั–ะ‘" + import: "ะ†ะผะฟะพั€ั‚ัƒะฒะฐั‚ะธ ะดะฐะฝั–" + export: "ะ•ะบัะฟะพั€ั‚ัƒะฒะฐั‚ะธ ะดะฐะฝั–" + import_results: + success: "ะ”ะฐะฝั– ัƒัะฟั–ัˆะฝะพ ั–ะผะฟะพั€ั‚ะพะฒะฐะฝั–" + error: "ะกั…ะพะถะต, ะฒะธะฑั€ะฐะฝะธะน ั„ะฐะนะป ะฟะพัˆะบะพะดะถะตะฝะธะน. ะั–ั‡ะพะณะพ ะฝะต ะฑัƒะปะพ ั–ะผะฟะพั€ั‚ะพะฒะฐะฝะพ" + import_prompt: + title: "ะ†ะผะฟะพั€ั‚ ะดะฐะฝะธั…" + warning_title: "ะฆะต ะฝะตะทะฒะพั€ะพั‚ะฝะฐ ะดั–ั!" + warning_text: "ะ’ะพะฝะฐ ะฟะตั€ะตะทะฐะฟะธัˆะต ะฒัั– ะฒะฐัˆั– ะดะฐะฝั–. ะŸะตั€ะตะบะพะฝะฐะนั‚ะตัั, ั‰ะพ ะฒะธ ะฒะธะฑั€ะฐะปะธ ะฟั€ะฐะฒะธะปัŒะฝะธะน ั„ะฐะนะป, ั–ะฝะฐะบัˆะต ั†ะต ะผะพะถะต ะฟั€ะธะทะฒะตัั‚ะธ ะดะพ ะฟะพัˆะบะพะดะถะตะฝะฝั ะฐะฑะพ ะฒั‚ั€ะฐั‚ะธ ะดะฐะฝะธั…. ะ ะตะบะพะผะตะฝะดัƒั”ั‚ัŒัั ัะฟะพั‡ะฐั‚ะบัƒ ะตะบัะฟะพั€ั‚ัƒะฒะฐั‚ะธ ะดะฐะฝั–." + proceed: "ะ’ะธะฑั€ะฐั‚ะธ ั„ะฐะนะป" + disable: "ะ’ะธะผะบะฝัƒั‚ะธ ั…ะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต" + enable: "ะฃะฒั–ะผะบะฝัƒั‚ะธ ั…ะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต" + disable_prompt: + text: "ะฆั ะดั–ั ะฒะธะผะบะฝะต ัะธะฝั…ั€ะพะฝั–ะทะฐั†ั–ัŽ ะบะพะปะตะบั†ั–ะน ะผั–ะถ ะฒะฐัˆะธะผะธ ะฟั€ะธัั‚ั€ะพัะผะธ. ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฟั€ะพะดะพะฒะถะฐั‚ัŒ ะทะฑะตั€ั–ะณะฐั‚ะธัั ัƒ ั…ะผะฐั€ั–." + action: "ะ’ะธะผะบะฝัƒั‚ะธ ั‚ะฐ ะฟะตั€ะตะทะฐะฒะฐะฝั‚ะฐะถะธั‚ะธ ั€ะพะทัˆะธั€ะตะฝะฝั" + about: + title: "ะž ั€ะพะทัˆะธั€ะตะฝะฝั–" + developed_by: "ะ ะพะทั€ะพะฑะฝะธะบ: ะ„ะฒะณะตะฝ ะ›ะธั" + licensed_under: "" + mit_license: "ะ›ั–ั†ะตะฝะทั–ั MIT" + translation_cta: + text: "ะ—ะฝะฐะนัˆะปะธ ะฟะพะผะธะปะบัƒ ะฐะฑะพ ั…ะพั‡ะตั‚ะต ะฟะตั€ะตะบะปะฐะด ะฝะฐ ะฒะฐัˆัƒ ะผะพะฒัƒ?" + button: "ะŸะพั‡ะฝั–ั‚ัŒ ั‚ัƒั‚" + links: + website: "ะœั–ะน ะฒะตะฑ-ัะฐะนั‚" + source: "ะ’ะธั…ั–ะดะฝะธะน ะบะพะด" + changelog: "ะกะฟะธัะพะบ ะทะผั–ะฝ" + +collections: + empty: "ะฆั ะบะพะปะตะบั†ั–ั ะฟัƒัั‚ะฐ" + tabs_count: "ะ’ะบะปะฐะดะพะบ: $1" + actions: + open: "ะ’ั–ะดะบั€ะธั‚ะธ ะฒัั–" + restore: "ะ’ั–ะดะฝะพะฒะธั‚ะธ ะฒัั–" + new_window: "ะ’ั–ะดะบั€ะธั‚ะธ ัƒ ะฝะพะฒะพะผัƒ ะฒั–ะบะฝั–" + incognito: + edge: "ะ’ั–ะดะบั€ะธั‚ะธ ัƒ ะฒั–ะบะฝั– InPrivate" + firefox: "ะ’ั–ะดะบั€ะธั‚ะธ ะฒ ะฟั€ะธะฒะฐั‚ะฝะพะผัƒ ะฒั–ะบะฝั–" + chrome: "ะ’ั–ะดะบั€ะธั‚ะธ ะฒ ะฐะฝะพะฝั–ะผะฝะพะผัƒ ะฒั–ะบะฝั–" + incognito_check: + title: "ะŸะพั‚ั€ั–ะฑะตะฝ ะดะพะทะฒั–ะป" + message: + edge: + p1: "ะ ะพะทัˆะธั€ะตะฝะฝัŽ ะฝะตะพะฑั…ั–ะดะฝะพ ะดะพะดะฐั‚ะบะพะฒะต ะดะพะทะฒะพะปะตะฝะฝั, ั‰ะพะฑ ะฒั–ะดะบั€ะธั‚ะธ ะฒะบะปะฐะดะบะธ ะฒ ั€ะตะถะธะผั– InPrivate" + p2: "ะ”ะปั ั†ัŒะพะณะพ ะฝะฐั‚ะธัะฝั–ั‚ัŒ \"ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั\" ั– ะฟะพั‚ั–ะผ ะฒั–ะดะผั–ั‚ัŒั‚ะต ะพะฟั†ั–ัŽ \"ะ”ะพะทะฒะพะปะธั‚ะธ ั€ะตะถะธะผ ะฟั€ะธะฒะฐั‚ะฝะพัั‚ั– InPrivate\"" + firefox: + p1: "ะ ะพะทัˆะธั€ะตะฝะฝัŽ ะฝะตะพะฑั…ั–ะดะฝะพ ะดะพะดะฐั‚ะบะพะฒะต ะดะพะทะฒะพะปะตะฝะฝั, ั‰ะพะฑ ะฒั–ะดะบั€ะธั‚ะธ ะฒะบะปะฐะดะบะธ ะฒ ะฟั€ะธะฒะฐั‚ะฝะพะผัƒ ะฒั–ะบะฝั–" + p2: "ะ”ะปั ั†ัŒะพะณะพ ะฝะฐั‚ะธัะฝั–ั‚ัŒ \"ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั\", ะฟะตั€ะตะนะดั–ั‚ัŒ ัƒ \"ะŸะพะดั€ะพะฑะธั†ั–\" ั– ะดะพะทะฒะพะปัŒั‚ะต \"ะ’ะธะบะพะฝัƒะฒะฐั‚ะธ ะฒ ะฟั€ะธะฒะฐั‚ะฝะธั… ะฒั–ะบะฝะฐั…\"" + chrome: + p1: "ะ ะพะทัˆะธั€ะตะฝะฝัŽ ะฝะตะพะฑั…ั–ะดะฝะพ ะดะพะดะฐั‚ะบะพะฒะต ะดะพะทะฒะพะปะตะฝะฝั, ั‰ะพะฑ ะฒั–ะดะบั€ะธั‚ะธ ะฒะบะปะฐะดะบะธ ะฒ ะฐะฝะพะฝั–ะผะฝะพะผัƒ ั€ะตะถะธะผั–" + p2: "ะ”ะปั ั†ัŒะพะณะพ ะฝะฐั‚ะธัะฝั–ั‚ัŒ \"ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั\" ั– ะฒั–ะดะผั–ั‚ัŒั‚ะต ะพะฟั†ั–ัŽ \"ะ”ะพะทะฒะพะปะธั‚ะธ ะฒ ะฐะฝะพะฝั–ะผะฝะพะผัƒ ั€ะตะถะธะผั–\"" + action: "ะะฐัั‚ั€ะพะนะบะธ" + menu: + delete: "ะ’ะธะดะฐะปะธั‚ะธ ะบะพะปะตะบั†ั–ัŽ" + add_selected: "ะ”ะพะดะฐั‚ะธ ะฒะธะฑั€ะฐะฝั– ะฒะบะปะฐะดะบะธ" + add_all: "ะ”ะพะดะฐั‚ะธ ะฒัั– ะฒะบะปะฐะดะบะธ" + add_group: "ะ”ะพะดะฐั‚ะธ ะฟะพั€ะพะถะฝัŽ ะณั€ัƒะฟัƒ" + export_bookmarks: "ะ•ะบัะฟะพั€ั‚ัƒะฒะฐั‚ะธ ะฒ ะทะฐะบะปะฐะดะบะธ" + edit: "ะ ะตะดะฐะณัƒะฒะฐั‚ะธ ะบะพะปะตะบั†ั–ัŽ" + +groups: + title: "ะ“ั€ัƒะฟะฐ" + pinned: "ะ—ะฐะบั€ั–ะฟะปะตะฝั–" + open: "ะ’ั–ะดะบั€ะธั‚ะธ ะฒัั–" + empty: "ะฆั ะณั€ัƒะฟะฐ ะฟัƒัั‚ะฐ" + menu: + new_window: "ะ’ั–ะดะบั€ะธั‚ะธ ัƒ ะฝะพะฒะพะผัƒ ะฒั–ะบะฝั–" + add_selected: "ะ”ะพะดะฐั‚ะธ ะฒะธะฑั€ะฐะฝั– ะฒะบะปะฐะดะบะธ" + add_all: "ะ”ะพะดะฐั‚ะธ ะฒัั– ะฒะบะปะฐะดะบะธ" + edit: "ะ ะตะดะฐะณัƒะฒะฐั‚ะธ ะณั€ัƒะฟัƒ" + ungroup: "ะ ะพะทะณั€ัƒะฟัƒะฒะฐั‚ะธ" + delete: "ะ’ะธะดะฐะปะธั‚ะธ ะณั€ัƒะฟัƒ" + +tabs: + delete: "ะ’ะธะดะฐะปะธั‚ะธ ะฒะบะปะฐะดะบัƒ" + +colors: + none: "ะ‘ะตะท ะบะพะปัŒะพั€ัƒ" + any: "ะ‘ัƒะดัŒ-ัะบะธะน ะบะพะปั–ั€" + grey: "ะกั–ั€ะธะน" + blue: "ะกะธะฝั–ะน" + red: "ะงะตั€ะฒะพะฝะธะน" + yellow: "ะ–ะพะฒั‚ะธะน" + green: "ะ—ะตะปะตะฝะธะน" + pink: "ะ ะพะถะตะฒะธะน" + purple: "ะŸัƒั€ะฟัƒั€ะพะฒะธะน" + cyan: "ะ‘ั–ั€ัŽะทะพะฒะธะน" + orange: "ะžั€ะฐะฝะถะตะฒะธะน" + +dialogs: + edit: + title: + edit_collection: "ะ ะตะดะฐะณัƒะฒะฐั‚ะธ ะบะพะปะตะบั†ั–ัŽ" + edit_group: "ะ ะตะดะฐะณัƒะฒะฐั‚ะธ ะณั€ัƒะฟัƒ" + new_group: "ะะพะฒะฐ ะณั€ัƒะฟะฐ" + new_collection: "ะะพะฒะฐ ะบะพะปะตะบั†ั–ั" + collection_title: "ะะฐะทะฒะฐ" + color: "ะšะพะปั–ั€" + +main: + header: + create_collection: "ะกั‚ะฒะพั€ะธั‚ะธ ะฝะพะฒัƒ ะบะพะปะตะบั†ั–ัŽ" + menu: + tiles_view: "ะŸะปะธั‚ะบะธ" + changelog: "ะฉะพ ะฝะพะฒะพะณะพ?" + list: + searchbar: + title: "ะŸะพัˆัƒะบ" + filter: "ะคั–ะปัŒั‚ั€" + sort: + title: "ะกะพั€ั‚ัƒะฒะฐะฝะฝั" + options: + newest: "ะกะฟะพั‡ะฐั‚ะบัƒ ะฝะพะฒั–" + oldest: "ะกะฟะพั‡ะฐั‚ะบัƒ ัั‚ะฐั€ั–" + ascending: "ะ’ั–ะด ะ ะดะพ ะฏ" + descending: "ะ’ั–ะด ะฏ ะดะพ ะ" + custom: "ะŸั€ะพะธะทะฒะพะปัŒะฝะฐั" + empty: + title: "ะŸะพะบะธ ั‰ะพ ั‚ัƒั‚ ะฝั–ั‡ะพะณะพ ะฝะตะผะฐั”" + message: "ะ’ั–ะดะบะปะฐะดั–ั‚ัŒ ะฟะพั‚ะพั‡ะฝั– ะฒะบะปะฐะดะบะธ ะฐะฑะพ ัั‚ะฒะพั€ั–ั‚ัŒ ะฝะพะฒัƒ ะบะพะปะตะบั†ั–ัŽ" + empty_search: + title: "ะั–ั‡ะพะณะพ ะฝะต ะทะฝะฐะนะดะตะฝะพ" + message: "ะกะฟั€ะพะฑัƒะนั‚ะต ะทะผั–ะฝะธั‚ะธ ะฟะพัˆัƒะบะพะฒะธะน ะทะฐะฟะธั‚" + +cta_message: + title: "ะŸะพะดะพะฑะฐั”ั‚ัŒัั ั€ะพะทัˆะธั€ะตะฝะฝั?" + message: "ะŸั–ะดั‚ั€ะธะผะฐะนั‚ะต ะฐะฒั‚ะพั€ะฐ ะฟะพะถะตั€ั‚ะฒัƒะฒะฐะฝะฝัะผ ะฐะฑะพ" + feedback: "ะทะฐะปะธัˆั‚ะต ะฒั–ะดะณัƒะบ" + +storage_full_message: + title: "ะฅะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต ะผะฐะนะถะต ะทะฐะฟะพะฒะฝะตะฝะต ($1%)" + message: "ะ’ะธ ะผะพะถะตั‚ะต ะทะฒั–ะปัŒะฝะธั‚ะธ ะผั–ัั†ะต, ะฒะธะดะฐะปะธะฒัˆะธ ะฝะตะฒะธะบะพั€ะธัั‚ะพะฒัƒะฒะฐะฝั– ะบะพะปะตะบั†ั–ั—." + +parse_error_message: + title: "ะœะธ ะฝะต ะทะผะพะณะปะธ ะพั‚ั€ะธะผะฐั‚ะธ ะบะพะปะตะบั†ั–ั— ะท ะฒะฐัˆะพะณะพ ั…ะผะฐั€ะฝะพะณะพ ัั…ะพะฒะธั‰ะฐ." + message: "ะกั…ะพะถะต, ั…ะผะฐั€ะฝะต ัั…ะพะฒะธั‰ะต ะฟะพัˆะบะพะดะถะตะฝะต. ะฉะพะฑ ั†ะต ะฒะธะฟั€ะฐะฒะธั‚ะธ, ะฒะธ ะผะพะถะตั‚ะต ะทะฐะผั–ะฝะธั‚ะธ ะนะพะณะพ ะปะพะบะฐะปัŒะฝะพัŽ ะบะพะฟั–ั”ัŽ." + action: "ะ’ะธะบะพั€ะธัั‚ะฐั‚ะธ ะปะพะบะฐะปัŒะฝัƒ ะบะพะฟั–ัŽ" + +merge_conflict_message: + title: "ะ’ ะปะพะบะฐะปัŒะฝะพะผัƒ ั– ะพะฑะปะฐั‡ะฝะพะผัƒ ั…ั€ะฐะฝะธะปะธั‰ะฐั… ั” ะบะพะฝั„ะปั–ะบั‚ัƒัŽั‡ั– ะทะผั–ะฝะธ." + message: "ะฉะพะฑ ั†ะต ะฒะธะฟั€ะฐะฒะธั‚ะธ, ะฒะธ ะผะพะถะตั‚ะต ะทะฑะตั€ะตะณั‚ะธ ะปะพะบะฐะปัŒะฝัƒ ะบะพะฟั–ัŽ ะฒ ั…ะผะฐั€ั– ะฐะฑะพ ะฟั€ะธะนะฝัั‚ะธ ะทะผั–ะฝะธ ะท ั…ะผะฐั€ะธ." + accept_local: "ะ—ะฐะผะตะฝะธั‚ัŒ ะปะพะบะฐะปัŒะฝะพัŽ" + accept_cloud: "ะŸั€ะธะนะฝัั‚ะธ ะพะฑะปะฐั‡ะฝั– ะทะผั–ะฝะธ" diff --git a/locales/zh_CN.yml b/locales/zh_CN.yml new file mode 100644 index 0000000..e08ce0f --- /dev/null +++ b/locales/zh_CN.yml @@ -0,0 +1,251 @@ +manifest: + name: "ๆ็ฝฎ็š„ๆ ‡็ญพ้กต" + description: "ไฟๅญ˜ๅนถ็ป„็ป‡ๆ‚จ็š„ๆ ‡็ญพไปฅๅค‡ๅŽ็”จใ€‚ไปŽๆ‚จ็ฆปๅผ€็š„ๅœฐๆ–น็ปง็ปญ" + author: "ๅฐค้‡‘ยท็ฆๅ…‹ๆ–ฏ" + +shortcuts: + toggle_sidebar: "ๆ‰“ๅผ€ๆ”ถ่—ๅˆ—่กจ" + set_aside: "ๅฐ†ๆ ‡็ญพๆ”พๅˆฐไธ€่พน" + save_tabs: "ไฟๅญ˜ๆ ‡็ญพ่€Œไธๅ…ณ้—ญ" + +common: + actions: + cancel: "ๅ–ๆถˆ" + save: "ไฟๅญ˜" + close: "ๅ…ณ้—ญ" + delete: "ๅˆ ้™ค" + reset_filters: "้‡็ฝฎ็ญ›้€‰ๅ™จ" + cta: + feedback: "็•™ไธ‹ๅ้ฆˆ" + sponsor: "่ฏทๆˆ‘ๅ–ๅ’–ๅ•ก" + tooltips: + more: "ๆ›ดๅคš" + delete_prompt: "ๆ‚จ็กฎๅฎšๅ—๏ผŸๆญคๆ“ไฝœๆ— ๆณ•ๆ’ค้”€ใ€‚" + +features: + v3welcome: + title: "ๆฌข่ฟŽไฝฟ็”จๆ็ฝฎ็š„ๆ ‡็ญพ้กต 3.0" + text1: "ๆˆ‘ไปฌๅพˆ้ซ˜ๅ…ดๅฎฃๅธƒๆ็ฝฎ็š„ๆ ‡็ญพ้กตๆ‰ฉๅฑ•็š„ๆ–ฐ้‡ๅคงๆ›ดๆ–ฐ๏ผ" + text2: "ๆญคๆ›ดๆ–ฐๅธฆๆฅไบ†ๅ…จๆ–ฐ็š„็”จๆˆท็•Œ้ข๏ผŒไปฅๅŠ่ฎธๅคšๆ–ฐๅŠŸ่ƒฝ๏ผŒๅŒ…ๆ‹ฌ๏ผš" + list: + item1: "ๆ”ฏๆŒๆ ‡็ญพ็ป„" + item2: "ๆ”ถ่—่‡ชๅฎšไน‰" + item3: "ๆ‹–ๆ”พ้‡ๆ–ฐๆŽ’ๅบๅ’Œ็ป„็ป‡" + item4: "ไปŽๅคดๅผ€ๅง‹ๆ‰‹ๅŠจๅˆ›ๅปบๆ”ถ่—" + item5: "ไปฅๅŠๆ›ดๅคš๏ผ" + text3: "่ฎฟ้—ฎๆˆ‘ไปฌ็š„ๅผ€ๅ‘ๅšๅฎขไปฅไบ†่งฃๆœ‰ๅ…ณๆญคๆ›ดๆ–ฐๅŠๅ…ถๆ‰€ๆœ‰ๅŠŸ่ƒฝ็š„ๆ›ดๅคšไฟกๆฏ๏ผ" + actions: + visit_blog: "้˜…่ฏปๅผ€ๅ‘ๅšๅฎข" + +notifications: + tabs_saved: + title: "ๆ–ฐๆ”ถ่—ๅทฒๅˆ›ๅปบ" + message: "ๆ‚จ็š„ๆ ‡็ญพๅทฒไฟๅญ˜ๅˆฐๆ–ฐๆ”ถ่—ไธญ" + error_quota_exceeded: + title: "่ถ…ๅ‡บๆœ€ๅคงไบ‘ๅ†™ๅ…ฅๆ“ไฝœ" + message: "ๆˆ‘ไปฌๅทฒๅฐ†ๆ‚จ็š„ๆ ‡็ญพไฟๅญ˜ๅˆฐๆœฌๅœฐๅญ˜ๅ‚จใ€‚ๆ‚จ้œ€่ฆๆ‰‹ๅŠจๆ›ดๆ–ฐไบ‘ๅญ˜ๅ‚จ" + error_storage_full: + title: "ๆ‚จ็š„ไบ‘ๅญ˜ๅ‚จๅทฒๆปก" + message: "ๆˆ‘ไปฌๅทฒๅฐ†ๆ‚จ็š„ๆ ‡็ญพไฟๅญ˜ๅˆฐๆœฌๅœฐๅญ˜ๅ‚จใ€‚่ฏทๆธ…็†ไธ€ไบ›ไบ‘ๅญ˜ๅ‚จ็ฉบ้—ด" + bookmark_saved: + title: "ๅทฒๅฏผๅ‡บๅˆฐไนฆ็ญพ" + message: "ๆ‚จ็š„ๆ”ถ่—ๅทฒๅฏผๅ‡บๅˆฐไนฆ็ญพ" + partial_save: + title: "ๆŸไบ›ๆ ‡็ญพๆ— ๆณ•ไฟๅญ˜" + message: "ๆŸไบ›ๆ ‡็ญพๆ˜ฏๆˆ‘ไปฌๆ— ๆณ•่ฎฟ้—ฎ็š„็ณป็ปŸๆ ‡็ญพใ€‚ๅฎƒไปฌๅทฒ่ขซ่ทณ่ฟ‡" + +actions: + save: + all: "ไฟๅญ˜ๆ‰€ๆœ‰ๆ ‡็ญพ" + selected: "ไฟๅญ˜้€‰ๅฎš็š„ๆ ‡็ญพ" + set_aside: + all: "ๅฐ†ๆ‰€ๆœ‰ๆ ‡็ญพๆ”พๅˆฐไธ€่พน" + selected: "ๅฐ†้€‰ๅฎš็š„ๆ ‡็ญพๆ”พๅˆฐไธ€่พน" + show_collections: "ๆ˜พ็คบๆ”ถ่—" + +options_page: + title: "่ฎพ็ฝฎ" + general: + title: "ๅธธ่ง„" + options: + always_show_toolbars: "ๅง‹็ปˆๆ˜พ็คบๅทฅๅ…ทๆ " + include_pinned: "ไฟๅญ˜ๆ‰€ๆœ‰ๆ ‡็ญพๆ—ถๅŒ…ๆ‹ฌๅ›บๅฎšๆ ‡็ญพ" + show_delete_prompt: "ๅˆ ้™ค้กน็›ฎๆ—ถ่ฆๆฑ‚็กฎ่ฎค" + show_badge: "ๆ˜พ็คบ่ฎกๆ•ฐๅพฝ็ซ " + show_notification: "ไฝฟ็”จไธŠไธ‹ๆ–‡่œๅ•ไฟๅญ˜ๆ ‡็ญพๆ—ถๆ˜พ็คบ้€š็Ÿฅ" + unload_tabs: "ๆ‰“ๅผ€ๅŽไธๅŠ ่ฝฝๆ ‡็ญพ" + list_locations: + title: "ๅœจไปฅไธ‹ไฝ็ฝฎๆ‰“ๅผ€ๆ”ถ่—ๅˆ—่กจ๏ผš" + options: + sidebar: "ไพง่พนๆ " + popup: "ๅผนๅ‡บ็ช—ๅฃ" + tab: "ๅ•็‹ฌ็š„ๆ ‡็ญพ้กต" + pinned: "ๅ•็‹ฌ็š„ๅ›บๅฎšๆ ‡็ญพ้กต" + icon_action: + title: "ๅ•ๅ‡ปๆ‰ฉๅฑ•ๅ›พๆ ‡ๆ—ถ๏ผš" + options: + action: "ๆ‰ง่กŒ้ป˜่ฎคไฟๅญ˜ๆ“ไฝœ" + context: "ๆ˜พ็คบไธŠไธ‹ๆ–‡่œๅ•" + open: "ๆ‰“ๅผ€ๆ”ถ่—ๅˆ—่กจ" + change_shortcuts: "ๆ›ดๆ”นๆ‰ฉๅฑ•ๅฟซๆทๆ–นๅผ" + actions: + title: "้ป˜่ฎคๆ“ไฝœ" + options: + save_actions: + title: "ไฟๅญ˜ๆ ‡็ญพๆ—ถ็š„้ป˜่ฎคๆ“ไฝœ" + options: + set_aside: "ไฟๅญ˜ๅนถๅ…ณ้—ญๆ ‡็ญพ" + save: "ไฟๅญ˜ๆ ‡็ญพ่€Œไธๅ…ณ้—ญ" + restore_actions: + title: "ๆ‰“ๅผ€ๆ”ถ่—ๆ—ถ็š„้ป˜่ฎคๆ“ไฝœ" + options: + open: "ไป…ๆ‰“ๅผ€ๆ ‡็ญพ" + restore: "ๆ‰“ๅผ€ๆ ‡็ญพๅนถๅˆ ้™คๆ”ถ่—" + storage: + title: "ๅญ˜ๅ‚จ" + capacity: + title: "ไบ‘ๅญ˜ๅ‚จๅฎน้‡" + description: "$1 / $2 KiB" + import: "ๅฏผๅ…ฅๆ•ฐๆฎ" + export: "ๅฏผๅ‡บๆ•ฐๆฎ" + import_results: + success: "ๆ•ฐๆฎๅทฒๆˆๅŠŸๅฏผๅ…ฅ" + error: "ๆไพ›็š„ๆ–‡ไปถไผผไนŽๅทฒๆŸๅใ€‚ๆœชๅฏผๅ…ฅไปปไฝ•ๅ†…ๅฎน" + import_prompt: + title: "ๅฏผๅ…ฅๆ•ฐๆฎ" + warning_title: "่ฟ™ๆ˜ฏไธๅฏ้€†็š„ๆ“ไฝœ" + warning_text: "่ฟ™ๅฐ†่ฆ†็›–ๆ‚จ็š„ๆ‰€ๆœ‰ๆ•ฐๆฎใ€‚่ฏท็กฎไฟ้€‰ๆ‹ฉไบ†ๆญฃ็กฎ็š„ๆ–‡ไปถ๏ผŒๅฆๅˆ™ๅฏ่ƒฝไผšๅฏผ่‡ดๆ•ฐๆฎๆŸๅๆˆ–ไธขๅคฑใ€‚ๅปบ่ฎฎๅ…ˆๅฏผๅ‡บๆ•ฐๆฎใ€‚" + proceed: "้€‰ๆ‹ฉๆ–‡ไปถ" + enable: "ๅฏ็”จไบ‘ๅญ˜ๅ‚จ" + disable: "็ฆ็”จไบ‘ๅญ˜ๅ‚จ" + disable_prompt: + text: "ๆญคๆ“ไฝœๅฐ†็ฆ็”จ่ฎพๅค‡ไน‹้—ด็š„ๆ”ถ่—ๅŒๆญฅใ€‚ๆ‰ฉๅฑ•่ฎพ็ฝฎไปๅฐ†ๅŒๆญฅใ€‚" + action: "็ฆ็”จๅนถ้‡ๆ–ฐๅŠ ่ฝฝๆ‰ฉๅฑ•" + about: + title: "ๅ…ณไบŽ" + developed_by: "็”ฑๅฐค้‡‘ยท็ฆๅ…‹ๆ–ฏๅผ€ๅ‘" + licensed_under: "่ฎธๅฏๅ่ฎฎ" + mit_license: "MIT ่ฎธๅฏๅ่ฎฎ" + translation_cta: + text: "ๅ‘็Žฐ้”™ๅˆซๅญ—ๆˆ–ๆƒณไธบๆ‚จ็š„่ฏญ่จ€ๆไพ›็ฟป่ฏ‘๏ผŸ" + button: "ไปŽ่ฟ™้‡Œๅผ€ๅง‹" + links: + website: "ๆˆ‘็š„็ฝ‘็ซ™" + source: "ๆบไปฃ็ " + changelog: "ๆ›ดๆ–ฐๆ—ฅๅฟ—" + +collections: + empty: "ๆญคๆ”ถ่—ไธบ็ฉบ" + tabs_count: "$1 ไธชๆ ‡็ญพ" + actions: + open: "ๆ‰“ๅผ€ๆ‰€ๆœ‰" + restore: "ๆขๅคๆ‰€ๆœ‰" + new_window: "ๅœจๆ–ฐ็ช—ๅฃไธญๆ‰“ๅผ€ๆ‰€ๆœ‰" + incognito: + edge: "ๅœจๆ–ฐ InPrivate ็ช—ๅฃไธญๆ‰“ๅผ€ๆ‰€ๆœ‰" + firefox: "ๅœจๆ–ฐ้š็ง็ช—ๅฃไธญๆ‰“ๅผ€ๆ‰€ๆœ‰" + chrome: "ๅœจ้š่บซ็ช—ๅฃไธญๆ‰“ๅผ€ๆ‰€ๆœ‰" + incognito_check: + title: "้œ€่ฆๆƒ้™" + message: + edge: + p1: "ๆ‰ฉๅฑ•้œ€่ฆๆƒ้™ๆ‰่ƒฝๅœจ InPrivate ็ช—ๅฃไธญๆ‰“ๅผ€ๆ ‡็ญพ" + p2: "ไธบๆญค๏ผŒ่ฏทๅ•ๅ‡ปโ€œ่ฎพ็ฝฎโ€๏ผŒ็„ถๅŽๅ‹พ้€‰โ€œๅ…่ฎธๅœจ InPrivate ไธญโ€้€‰้กน" + firefox: + p1: "ๆ‰ฉๅฑ•้œ€่ฆๆƒ้™ๆ‰่ƒฝๅœจ้š็ง็ช—ๅฃไธญๆ‰“ๅผ€ๆ ‡็ญพ" + p2: "ไธบๆญค๏ผŒ่ฏทๅ•ๅ‡ปโ€œ่ฎพ็ฝฎโ€๏ผŒ่ฝฌๅˆฐโ€œ่ฏฆ็ป†ไฟกๆฏโ€ๅนถๅฐ†โ€œๅœจ้š็ง็ช—ๅฃไธญ่ฟ่กŒโ€่ฎพ็ฝฎไธบโ€œๅ…่ฎธโ€" + chrome: + p1: "ๆ‰ฉๅฑ•้œ€่ฆๆƒ้™ๆ‰่ƒฝๅœจ้š่บซ็ช—ๅฃไธญๆ‰“ๅผ€ๆ ‡็ญพ" + p2: "ไธบๆญค๏ผŒ่ฏทๅ•ๅ‡ปโ€œ่ฎพ็ฝฎโ€๏ผŒ็„ถๅŽๅ‹พ้€‰โ€œๅ…่ฎธๅœจ้š่บซไธญโ€้€‰้กน" + action: "่ฎพ็ฝฎ" + menu: + delete: "ๅˆ ้™คๆ”ถ่—" + add_selected: "ๆทปๅŠ ้€‰ๅฎš็š„ๆ ‡็ญพ" + add_all: "ๆทปๅŠ ๆ‰€ๆœ‰ๆ ‡็ญพ" + add_group: "ๆทปๅŠ ็ฉบ็ป„" + export_bookmarks: "ๅฏผๅ‡บๅˆฐไนฆ็ญพ" + edit: "็ผ–่พ‘ๆ”ถ่—" + +groups: + title: "็ป„" + pinned: "ๅทฒๅ›บๅฎš" + open: "ๆ‰“ๅผ€ๆ‰€ๆœ‰" + empty: "ๆญค็ป„ไธบ็ฉบ" + menu: + new_window: "ๅœจๆ–ฐ็ช—ๅฃไธญๆ‰“ๅผ€" + add_selected: "ๆทปๅŠ ้€‰ๅฎš็š„ๆ ‡็ญพ" + add_all: "ๆทปๅŠ ๆ‰€ๆœ‰ๆ ‡็ญพ" + edit: "็ผ–่พ‘็ป„" + ungroup: "ๅ–ๆถˆๅˆ†็ป„" + delete: "ๅˆ ้™ค็ป„" + +tabs: + delete: "ๅˆ ้™คๆ ‡็ญพ" + +colors: + none: "ๆ— ้ขœ่‰ฒ" + any: "ไปปไฝ•้ขœ่‰ฒ" + grey: "็ฐ่‰ฒ" + blue: "่“่‰ฒ" + red: "็บข่‰ฒ" + yellow: "้ป„่‰ฒ" + green: "็ปฟ่‰ฒ" + pink: "็ฒ‰่‰ฒ" + purple: "็ดซ่‰ฒ" + cyan: "้’่‰ฒ" + orange: "ๆฉ™่‰ฒ" + +dialogs: + edit: + title: + edit_collection: "็ผ–่พ‘ๆ”ถ่—" + edit_group: "็ผ–่พ‘็ป„" + new_group: "ๆ–ฐ็ป„" + new_collection: "ๆ–ฐๆ”ถ่—" + collection_title: "ๆ ‡้ข˜" + color: "้ขœ่‰ฒ" + +main: + header: + create_collection: "ๅˆ›ๅปบๆ–ฐๆ”ถ่—" + menu: + tiles_view: "ๅนณ้“บ่ง†ๅ›พ" + changelog: "ๆ›ดๆ–ฐๅ†…ๅฎน๏ผŸ" + list: + searchbar: + title: "ๆœ็ดข" + filter: "็ญ›้€‰" + sort: + title: "ๆŽ’ๅบ" + options: + newest: "ๆœ€ๆ–ฐไผ˜ๅ…ˆ" + oldest: "ๆœ€ๆ—งไผ˜ๅ…ˆ" + ascending: "ไปŽ A ๅˆฐ Z" + descending: "ไปŽ Z ๅˆฐ A" + custom: "่‡ชๅฎšไน‰" + empty: + title: "่ฟ™้‡Œ่ฟ˜ๆฒกๆœ‰ๅ†…ๅฎน" + message: "ๅฐ†ๅฝ“ๅ‰ๆ ‡็ญพๆ”พๅˆฐไธ€่พน๏ผŒๆˆ–ๅˆ›ๅปบๆ–ฐๆ”ถ่—" + empty_search: + title: "ๆœชๆ‰พๅˆฐไปปไฝ•ๅ†…ๅฎน" + message: "ๅฐ่ฏ•ๆ›ดๆ”นๆœ็ดขๆŸฅ่ฏข" + +cta_message: + title: "ๅ–œๆฌข่ฟ™ไธชๆ‰ฉๅฑ•ๅ—๏ผŸ" + message: "่€ƒ่™‘ๆ”ฏๆŒไฝœ่€…ๆ่ต ๏ผŒๆˆ–" + feedback: "็•™ไธ‹ๅ้ฆˆ" + +storage_full_message: + title: "ๆ‚จ็š„ไบ‘ๅญ˜ๅ‚จๅ‡ ไนŽๅทฒๆปก๏ผˆ$1%๏ผ‰" + message: "ๆ‚จๅฏไปฅ้€š่ฟ‡ๅˆ ้™คๆœชไฝฟ็”จ็š„ๆ”ถ่—ๆฅ้‡Šๆ”พไธ€ไบ›็ฉบ้—ดใ€‚" + +parse_error_message: + title: "ๆˆ‘ไปฌๆ— ๆณ•ไปŽๆ‚จ็š„ไบ‘ๅญ˜ๅ‚จไธญ่Žทๅ–ๆ”ถ่—ใ€‚" + message: "ๆ‚จ็š„ไบ‘ๅญ˜ๅ‚จไผผไนŽๅทฒๆŸๅใ€‚ๆ‚จๅฏไปฅ้€š่ฟ‡็”จๆœฌๅœฐๅ‰ฏๆœฌๆ›ฟๆขๅฎƒๆฅไฟฎๅคๅฎƒใ€‚" + action: "็”จๆœฌๅœฐๅ‰ฏๆœฌไฟฎๅค" + +merge_conflict_message: + title: "ๆ‚จ็š„ๆœฌๅœฐๅ’Œไบ‘ๅญ˜ๅ‚จๆœ‰ๅ†ฒ็ช็š„ๆ›ดๆ”นใ€‚" + message: "่ฆ่งฃๅ†ณๆญค้—ฎ้ข˜๏ผŒๆ‚จๅฏไปฅๅฐ†ๆœฌๅœฐๅ‰ฏๆœฌไธŠไผ ๅˆฐไบ‘็ซฏ๏ผŒๆˆ–ๆŽฅๅ—ไบ‘็ซฏๆ›ดๆ”นใ€‚" + accept_local: "็”จๆœฌๅœฐๆ›ฟๆข" + accept_cloud: "ๆŽฅๅ—ไบ‘็ซฏๆ›ดๆ”น" diff --git a/manifest.json b/manifest.json deleted file mode 100644 index fd894ab..0000000 --- a/manifest.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "__MSG_name__", - "version": "2.0.6", - "manifest_version": 2, - "description": "__MSG_description__", - "author": "__MSG_author__", - "default_locale": "en", - - "permissions": - [ - "tabs", - "bookmarks", - "unlimitedStorage", - "storage", - "", - "contextMenus" - ], - - "icons": - { - "128": "icons/icon-128.png", - "48": "icons/icon-48.png", - "32": "icons/icon-32.png", - "16": "icons/icon-16.png" - }, - "browser_action": - { - "default_icon": "icons/icon-32.png" - }, - "web_accessible_resources": [ "*" ], - - "background": - { - "scripts": [ "js/lib/lzutf8.min.js","js/background.js" ], - "persistent": false - }, - - "commands": - { - "set-aside": - { - "description": "__MSG_setAside__", - "suggested_key": - { - "default": "Shift+Alt+Left", - "mac": "MacCtrl+T" - } - }, - "toggle-pane": - { - "description": "__MSG_togglePaneContext__", - "suggested_key": - { - "default": "Alt+P", - "mac": "Command+Shift+P" - } - } - }, - "browser_specific_settings": - { - "gecko": - { - "id": "tabsaside@xfox111.net", - "strict_min_version": "58.0" - } - } -} diff --git a/models/CollectionModels.ts b/models/CollectionModels.ts new file mode 100644 index 0000000..0e0a1e5 --- /dev/null +++ b/models/CollectionModels.ts @@ -0,0 +1,42 @@ +export type TabItem = + { + type: "tab"; + url: string; + title?: string; + }; + +export type PinnedGroupItem = + { + type: "group"; + pinned: true; + items: TabItem[]; + }; + +export type DefaultGroupItem = + { + type: "group"; + pinned?: false; + title?: string; + color: chrome.tabGroups.ColorEnum; + items: TabItem[]; + }; + +export type GroupItem = PinnedGroupItem | DefaultGroupItem; + +export type CollectionItem = + { + type: "collection"; + timestamp: number; + title?: string; + color?: chrome.tabGroups.ColorEnum; + items: (TabItem | GroupItem)[]; + }; + +export type GraphicsStorage = Record; + +export type GraphicsItem = + { + preview?: string; + capture?: string; + icon?: string; + }; diff --git a/package.json b/package.json new file mode 100644 index 0000000..af82799 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "tabs-aside", + "private": true, + "version": "3.0.0", + "type": "module", + "scripts": { + "dev": "wxt", + "build": "yarn lint && wxt build --mv3", + "zip": "yarn lint && wxt zip --mv3", + "lint": "tsc --noEmit && eslint . -c eslint.config.js", + "prepare": "wxt prepare", + "postinstall": "yarn prepare" + }, + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@fluentui/react-components": "^9.68.1", + "@fluentui/react-icons": "^2.0.307", + "@webext-core/messaging": "^2.3.0", + "@wxt-dev/analytics": "^0.4.1", + "@wxt-dev/i18n": "^0.2.4", + "lzutf8": "^0.6.3", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/css": "^0.10.0", + "@eslint/js": "^9.32.0", + "@eslint/json": "^0.13.1", + "@stylistic/eslint-plugin": "^5.2.2", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", + "@types/scheduler": "0.23.0", + "@wxt-dev/module-react": "^1.1.3", + "eslint": "^9.32.0", + "eslint-plugin-react": "^7.37.5", + "globals": "^16.3.0", + "scheduler": "0.23.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0", + "vite": "^7.0.6", + "wxt": "~0.19.29" + }, + "packageManager": "yarn@4.9.2" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..a2fc7f2 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icon/1024.png b/public/icon/1024.png new file mode 100644 index 0000000..fea6e6b Binary files /dev/null and b/public/icon/1024.png differ diff --git a/icons/icon-128.png b/public/icon/128.png similarity index 100% rename from icons/icon-128.png rename to public/icon/128.png diff --git a/icons/icon-16.png b/public/icon/16.png similarity index 100% rename from icons/icon-16.png rename to public/icon/16.png diff --git a/icons/icon-32.png b/public/icon/32.png similarity index 100% rename from icons/icon-32.png rename to public/icon/32.png diff --git a/icons/icon-48.png b/public/icon/48.png similarity index 100% rename from icons/icon-48.png rename to public/icon/48.png diff --git a/public/icon/96.png b/public/icon/96.png new file mode 100644 index 0000000..016877a Binary files /dev/null and b/public/icon/96.png differ diff --git a/public/notification_icons/bookmark_add.png b/public/notification_icons/bookmark_add.png new file mode 100644 index 0000000..395d060 Binary files /dev/null and b/public/notification_icons/bookmark_add.png differ diff --git a/public/notification_icons/cloud_checkmark.png b/public/notification_icons/cloud_checkmark.png new file mode 100644 index 0000000..9d7e446 Binary files /dev/null and b/public/notification_icons/cloud_checkmark.png differ diff --git a/public/notification_icons/cloud_error.png b/public/notification_icons/cloud_error.png new file mode 100644 index 0000000..f33c67b Binary files /dev/null and b/public/notification_icons/cloud_error.png differ diff --git a/public/notification_icons/save_warning.png b/public/notification_icons/save_warning.png new file mode 100644 index 0000000..405f543 Binary files /dev/null and b/public/notification_icons/save_warning.png differ diff --git a/public/promo/dark.webp b/public/promo/dark.webp new file mode 100644 index 0000000..0f1284e Binary files /dev/null and b/public/promo/dark.webp differ diff --git a/public/promo/light.webp b/public/promo/light.webp new file mode 100644 index 0000000..b47e365 Binary files /dev/null and b/public/promo/light.webp differ diff --git a/store_descriptions/en.txt b/store_descriptions/en.txt new file mode 100644 index 0000000..c2fb440 --- /dev/null +++ b/store_descriptions/en.txt @@ -0,0 +1,49 @@ + + +Stemming its roots from the original Microsoft Edge browser feature, this extension has grown much bigger than just a temporary storage for tabs. + +It allows you to save and manage your tabs in a convenient way, providing a range of features that make it easy to organize and access your saved tabs. + +## Features +- Save tabs: Save all your open tabs in a single click, and restore them later +- Organize tabs: Create collections and subgroups to organize your saved tabs +- Search tabs: Quickly find the tabs you need using the search feature +- Sync across devices: Access your saved tabs from any device with your account +- Go dark: Dark mode support for a more comfortable browsing experience +- Personalize: Change the appearance and behavior of the extension to suit your needs + +Check out our blog post regarding all the new features and improvements in Tabs aside 3.0 at: +https://at.xfox111.net/tabs-aside-3-0 + +## Hey, it's an open-source software! +If you know how to improve this extension you can check its GitHub Repository at: +https://github.com/xfox111/TabsAsideExtension + +Check out release changelog on: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + + +Stemming its roots from the original Microsoft Edge browser feature, this extension has grown much bigger than just a temporary storage for tabs. + +It allows you to save and manage your tabs in a convenient way, providing a range of features that make it easy to organize and access your saved tabs. + +Features +
    +
  • Save tabs: Save all your open tabs in a single click, and restore them later
  • +
  • Organize tabs: Create collections and subgroups to organize your saved tabs
  • +
  • Search tabs: Quickly find the tabs you need using the search feature
  • +
  • Sync across devices: Access your saved tabs from any device with your account
  • +
  • Go dark: Dark mode support for a more comfortable browsing experience
  • +
  • Personalize: Change the appearance and behavior of the extension to suit your needs
  • +
+ +Check out our blog post regarding all the new features and improvements in Tabs aside 3.0 + +Hey, it's an open-source software! +If you know how to improve this extension you can check its GitHub Repository + +Check out release changelog diff --git a/store_descriptions/es.txt b/store_descriptions/es.txt new file mode 100644 index 0000000..6a821a5 --- /dev/null +++ b/store_descriptions/es.txt @@ -0,0 +1,48 @@ + + +Basada en la funcionalidad original del navegador Microsoft Edge, esta extensiรณn ha crecido mucho mรกs allรก de ser solo un almacenamiento temporal para pestaรฑas. + +Te permite guardar y gestionar tus pestaรฑas de manera conveniente, proporcionando una gama de caracterรญsticas que facilitan organizar y acceder a tus pestaรฑas guardadas. + +## Caracterรญsticas +- Guardar pestaรฑas: Guarda todas tus pestaรฑas abiertas con un solo clic y restรกuralas mรกs tarde +- Organizar pestaรฑas: Crea colecciones y subgrupos para organizar tus pestaรฑas guardadas +- Buscar pestaรฑas: Encuentra rรกpidamente las pestaรฑas que necesitas usando la funciรณn de bรบsqueda +- Sincronizar entre dispositivos: Accede a tus pestaรฑas guardadas desde cualquier dispositivo con tu cuenta +- Modo oscuro: Soporte para modo oscuro para una experiencia de navegaciรณn mรกs cรณmoda +- Personalizar: Cambia la apariencia y el comportamiento de la extensiรณn para adaptarla a tus necesidades + +Consulta nuestra publicaciรณn en el blog sobre todas las nuevas caracterรญsticas y mejoras en Pestaรฑas a un lado 3.0 en: +https://at.xfox111.net/tabs-aside-3-0 + +## ยกOye, es un software de cรณdigo abierto! +Si sabes cรณmo mejorar esta extensiรณn, puedes revisar su repositorio de GitHub en: +https://github.com/xfox111/TabsAsideExtension + +Consulta el registro de cambios en: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + +Basada en la funcionalidad original del navegador Microsoft Edge, esta extensiรณn ha crecido mucho mรกs allรก de ser solo un almacenamiento temporal para pestaรฑas. + +Te permite guardar y gestionar tus pestaรฑas de manera conveniente, proporcionando una gama de caracterรญsticas que facilitan organizar y acceder a tus pestaรฑas guardadas. + +Caracterรญsticas +
    +
  • Guardar pestaรฑas: Guarda todas tus pestaรฑas abiertas con un solo clic y restรกuralas mรกs tarde
  • +
  • Organizar pestaรฑas: Crea colecciones y subgrupos para organizar tus pestaรฑas guardadas
  • +
  • Buscar pestaรฑas: Encuentra rรกpidamente las pestaรฑas que necesitas usando la funciรณn de bรบsqueda
  • +
  • Sincronizar entre dispositivos: Accede a tus pestaรฑas guardadas desde cualquier dispositivo con tu cuenta
  • +
  • Modo oscuro: Soporte para modo oscuro para una experiencia de navegaciรณn mรกs cรณmoda
  • +
  • Personalizar: Cambia la apariencia y el comportamiento de la extensiรณn para adaptarla a tus necesidades
  • +
+ +Consulta nuestra publicaciรณn en el blog sobre todas las nuevas caracterรญsticas y mejoras en Pestaรฑas a un lado 3.0 + +ยกOye, es un software de cรณdigo abierto! +Si sabes cรณmo mejorar esta extensiรณn, puedes revisar su repositorio de GitHub + +Consulta el registro de cambios diff --git a/store_descriptions/it.txt b/store_descriptions/it.txt new file mode 100644 index 0000000..5b164a3 --- /dev/null +++ b/store_descriptions/it.txt @@ -0,0 +1,48 @@ + + +Radicata nella funzionalitร  originale del browser Microsoft Edge, questa estensione รจ cresciuta molto piรน di un semplice spazio di archiviazione temporaneo per le schede. + +Ti consente di salvare e gestire le tue schede in modo conveniente, fornendo una gamma di funzionalitร  che rendono facile organizzare e accedere alle tue schede salvate. + +## Funzionalitร  +- Salva schede: Salva tutte le tue schede aperte con un solo clic e ripristinale in seguito +- Organizza schede: Crea collezioni e sottogruppi per organizzare le tue schede salvate +- Cerca schede: Trova rapidamente le schede di cui hai bisogno utilizzando la funzione di ricerca +- Sincronizza tra dispositivi: Accedi alle tue schede salvate da qualsiasi dispositivo con il tuo account +- Modalitร  scura: Supporto per la modalitร  scura per un'esperienza di navigazione piรน confortevole +- Personalizza: Cambia l'aspetto e il comportamento dell'estensione per soddisfare le tue esigenze + +Dai un'occhiata al nostro post sul blog riguardante tutte le nuove funzionalitร  e miglioramenti in Schede a parte 3.0 su: +https://at.xfox111.net/tabs-aside-3-0 + +## Ehi, รจ un software open-source! +Se sai come migliorare questa estensione, puoi controllare il suo repository GitHub su: +https://github.com/xfox111/TabsAsideExtension + +Consulta il registro delle modifiche alla versione su: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + +Radicata nella funzionalitร  originale del browser Microsoft Edge, questa estensione รจ cresciuta molto piรน di un semplice spazio di archiviazione temporaneo per le schede. + +Ti consente di salvare e gestire le tue schede in modo conveniente, fornendo una gamma di funzionalitร  che rendono facile organizzare e accedere alle tue schede salvate. + +Funzionalitร  +
    +
  • Salva schede: Salva tutte le tue schede aperte con un solo clic e ripristinale in seguito
  • +
  • Organizza schede: Crea collezioni e sottogruppi per organizzare le tue schede salvate
  • +
  • Cerca schede: Trova rapidamente le schede di cui hai bisogno utilizzando la funzione di ricerca
  • +
  • Sincronizza tra dispositivi: Accedi alle tue schede salvate da qualsiasi dispositivo con il tuo account
  • +
  • Modalitร  scura: Supporto per la modalitร  scura per un'esperienza di navigazione piรน confortevole
  • +
  • Personalizza: Cambia l'aspetto e il comportamento dell'estensione per soddisfare le tue esigenze
  • +
+ +Dai un'occhiata al nostro post sul blog riguardante tutte le nuove funzionalitร  e miglioramenti in Schede a parte 3.0 + +Ehi, รจ un software open-source! +Se sai come migliorare questa estensione, puoi controllare il suo repository GitHub + +Consulta il registro delle modifiche diff --git a/store_descriptions/pl.txt b/store_descriptions/pl.txt new file mode 100644 index 0000000..3a04d7a --- /dev/null +++ b/store_descriptions/pl.txt @@ -0,0 +1,48 @@ + + +Zainspirowane funkcjฤ… z pierwszych wersji Microsoft Edge, to rozszerzenie staล‚o siฤ™ czymล› wiฤ™cej niลผ tylko tymczasowym magazynem kart. + +Pozwala wygodnie zapisywaฤ‡ i zarzฤ…dzaฤ‡ kartami, oferujฤ…c wiele funkcji, ktรณre uล‚atwiajฤ… organizacjฤ™ i dostฤ™p do zapisanych kart. + +## Funkcje +- Zapisywanie kart: Zapisz wszystkie otwarte karty jednym klikniฤ™ciem i przywrรณฤ‡ je pรณลบniej +- Organizacja kart: Twรณrz kolekcje i podgrupy, aby organizowaฤ‡ zapisane karty +- Wyszukiwanie kart: Szybko znajdลบ potrzebne karty za pomocฤ… funkcji wyszukiwania +- Synchronizacja miฤ™dzy urzฤ…dzeniami: Dostฤ™p do zapisanych kart z dowolnego urzฤ…dzenia za pomocฤ… swojego konta +- Tryb ciemny: Obsล‚uga trybu ciemnego dla bardziej komfortowego uลผytkowania +- Personalizacja: Dostosuj wyglฤ…d i dziaล‚anie rozszerzenia do swoich potrzeb + +Odwiedลบ naszego bloga, aby dowiedzieฤ‡ siฤ™ wiฤ™cej o wszystkich nowych funkcjach i ulepszeniach w Odล‚oลผonych kartach 3.0 pod adresem: +https://at.xfox111.net/tabs-aside-3-0 + +## Przy okazji, to rozszerzenie open-source! +Jeล›li wiesz, jak ulepszyฤ‡ to rozszerzenie, moลผesz odwiedziฤ‡ jego repozytorium na GitHubie: +https://github.com/xfox111/TabsAsideExtension + +Lista zmian w najnowszej wersji: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + +Zainspirowane funkcjฤ… z pierwszych wersji Microsoft Edge, to rozszerzenie staล‚o siฤ™ czymล› wiฤ™cej niลผ tylko tymczasowym magazynem kart. + +Pozwala wygodnie zapisywaฤ‡ i zarzฤ…dzaฤ‡ kartami, oferujฤ…c wiele funkcji, ktรณre uล‚atwiajฤ… organizacjฤ™ i dostฤ™p do zapisanych kart. + +Funkcje +
    +
  • Zapisywanie kart: Zapisz wszystkie otwarte karty jednym klikniฤ™ciem i przywrรณฤ‡ je pรณลบniej
  • +
  • Organizacja kart: Twรณrz kolekcje i podgrupy, aby organizowaฤ‡ zapisane karty
  • +
  • Wyszukiwanie kart: Szybko znajdลบ potrzebne karty za pomocฤ… funkcji wyszukiwania
  • +
  • Synchronizacja miฤ™dzy urzฤ…dzeniami: Dostฤ™p do zapisanych kart z dowolnego urzฤ…dzenia za pomocฤ… swojego konta
  • +
  • Tryb ciemny: Obsล‚uga trybu ciemnego dla bardziej komfortowego uลผytkowania
  • +
  • Personalizacja: Dostosuj wyglฤ…d i dziaล‚anie rozszerzenia do swoich potrzeb
  • +
+ +Odwiedลบ naszego bloga, aby dowiedzieฤ‡ siฤ™ wiฤ™cej o wszystkich nowych funkcjach i ulepszeniach w Odล‚oลผonych kartach 3.0 + +Przy okazji, to rozszerzenie open-source! +Jeล›li wiesz, jak ulepszyฤ‡ to rozszerzenie, moลผesz odwiedziฤ‡ jego repozytorium na GitHubie + +Lista zmian w najnowszej wersji diff --git a/store_descriptions/pt_BR.txt b/store_descriptions/pt_BR.txt new file mode 100644 index 0000000..aa00c5b --- /dev/null +++ b/store_descriptions/pt_BR.txt @@ -0,0 +1,49 @@ + + +Originando-se do recurso original do navegador Microsoft Edge, esta extensรฃo cresceu muito alรฉm de apenas um armazenamento temporรกrio para abas. + +Ela permite que vocรช salve e gerencie suas abas de forma conveniente, oferecendo uma variedade de recursos que facilitam a organizaรงรฃo e o acesso ร s abas salvas. + +## Recursos +- Salvar abas: Salve todas as suas abas abertas com um รบnico clique e restaure-as depois +- Organizar abas: Crie coleรงรตes e subgrupos para organizar suas abas salvas +- Pesquisar abas: Encontre rapidamente as abas que vocรช precisa usando o recurso de pesquisa +- Sincronizar entre dispositivos: Acesse suas abas salvas de qualquer dispositivo com sua conta +- Modo escuro: Suporte ao modo escuro para uma experiรชncia de navegaรงรฃo mais confortรกvel +- Personalizar: Altere a aparรชncia e o comportamento da extensรฃo conforme suas necessidades + +Confira nossa postagem no blog sobre todos os novos recursos e melhorias do Tabs Aside 3.0 em: +https://at.xfox111.net/tabs-aside-3-0 + +## Ei, รฉ um software de cรณdigo aberto! +Se vocรช sabe como melhorar esta extensรฃo, confira seu repositรณrio no GitHub em: +https://github.com/xfox111/TabsAsideExtension + +Veja o changelog das versรตes em: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + + +Originando-se do recurso original do navegador Microsoft Edge, esta extensรฃo cresceu muito alรฉm de apenas um armazenamento temporรกrio para abas. + +Ela permite que vocรช salve e gerencie suas abas de forma conveniente, oferecendo uma variedade de recursos que facilitam a organizaรงรฃo e o acesso ร s abas salvas. + +Recursos +
    +
  • Salvar abas: Salve todas as suas abas abertas com um รบnico clique e restaure-as depois
  • +
  • Organizar abas: Crie coleรงรตes e subgrupos para organizar suas abas salvas
  • +
  • Pesquisar abas: Encontre rapidamente as abas que vocรช precisa usando o recurso de pesquisa
  • +
  • Sincronizar entre dispositivos: Acesse suas abas salvas de qualquer dispositivo com sua conta
  • +
  • Modo escuro: Suporte ao modo escuro para uma experiรชncia de navegaรงรฃo mais confortรกvel
  • +
  • Personalizar: Altere a aparรชncia e o comportamento da extensรฃo conforme suas necessidades
  • +
+ +Confira nossa postagem no blog sobre todos os novos recursos e melhorias do Tabs Aside 3.0 + +Ei, รฉ um software de cรณdigo aberto! +Se vocรช sabe como melhorar esta extensรฃo, confira seu repositรณrio no GitHub + +Veja o changelog das versรตes diff --git a/store_descriptions/ru.txt b/store_descriptions/ru.txt new file mode 100644 index 0000000..716c64f --- /dev/null +++ b/store_descriptions/ru.txt @@ -0,0 +1,48 @@ + + +ะ’ะดะพั…ะฝะพะฒะปะตะฝะฝะพะต ั„ัƒะฝะบั†ะธะตะน ะฟั€ะตะฒั‹ั… ะฒะตั€ัะธะน ะฑั€ะฐัƒะทะตั€ะฐ Microsoft Edge, ัั‚ะพ ั€ะฐััˆะธั€ะตะฝะธะต ัั‚ะฐะปะพ ะณะพั€ะฐะทะดะพ ะฑะพะปัŒัˆะต, ั‡ะตะผ ะฟั€ะพัั‚ะพ ะฒั€ะตะผะตะฝะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต ะดะปั ะฒะบะปะฐะดะพะบ. + +ะžะฝะพ ะฟะพะทะฒะพะปัะตั‚ ัะพั…ั€ะฐะฝัั‚ัŒ ะธ ัƒะฟั€ะฐะฒะปัั‚ัŒ ะฒะฐัˆะธะผะธ ะฒะบะปะฐะดะบะฐะผะธ ัƒะดะพะฑะฝั‹ะผ ะพะฑั€ะฐะทะพะผ, ะฟั€ะตะดะพัั‚ะฐะฒะปัั ะผะฝะพะถะตัั‚ะฒะพ ั„ัƒะฝะบั†ะธะน, ะบะพั‚ะพั€ั‹ะต ัƒะฟั€ะพั‰ะฐัŽั‚ ะพั€ะณะฐะฝะธะทะฐั†ะธัŽ ะธ ะดะพัั‚ัƒะฟ ะบ ัะพั…ั€ะฐะฝะตะฝะฝั‹ะผ ะฒะบะปะฐะดะบะฐะผ. + +## ะ’ะพะทะผะพะถะฝะพัั‚ะธ +- ะกะพั…ั€ะฐะฝะตะฝะธะต ะฒะบะปะฐะดะพะบ: ะกะพั…ั€ะฐะฝะธั‚ะต ะฒัะต ะพั‚ะบั€ั‹ั‚ั‹ะต ะฒะบะปะฐะดะบะธ ะพะดะฝะธะผ ะบะปะธะบะพะผ ะธ ะฒะพััั‚ะฐะฝะพะฒะธั‚ะต ะธั… ะฟะพะทะถะต +- ะžั€ะณะฐะฝะธะทะฐั†ะธั ะฒะบะปะฐะดะพะบ: ะกะพะทะดะฐะฒะฐะนั‚ะต ะบะพะปะปะตะบั†ะธะธ ะธ ะฟะพะดะณั€ัƒะฟะฟั‹ ะดะปั ะพั€ะณะฐะฝะธะทะฐั†ะธะธ ัะพั…ั€ะฐะฝะตะฝะฝั‹ั… ะฒะบะปะฐะดะพะบ +- ะŸะพะธัะบ ะฒะบะปะฐะดะพะบ: ะ‘ั‹ัั‚ั€ะพ ะฝะฐั…ะพะดะธั‚ะต ะฝัƒะถะฝั‹ะต ะฒะบะปะฐะดะบะธ ั ะฟะพะผะพั‰ัŒัŽ ั„ัƒะฝะบั†ะธะธ ะฟะพะธัะบะฐ +- ะกะธะฝั…ั€ะพะฝะธะทะฐั†ะธั ะผะตะถะดัƒ ัƒัั‚ั€ะพะนัั‚ะฒะฐะผะธ: ะ”ะพัั‚ัƒะฟ ะบ ัะพั…ั€ะฐะฝะตะฝะฝั‹ะผ ะฒะบะปะฐะดะบะฐะผ ั ะปัŽะฑะพะณะพ ัƒัั‚ั€ะพะนัั‚ะฒะฐ ั‡ะตั€ะตะท ะฒะฐัˆ ะฐะบะบะฐัƒะฝั‚ +- ะขะตะผะฝั‹ะน ั€ะตะถะธะผ: ะŸะพะดะดะตั€ะถะบะฐ ั‚ะตะผะฝะพะณะพ ั€ะตะถะธะผะฐ ะดะปั ะฑะพะปะตะต ะบะพะผั„ะพั€ั‚ะฝะพะณะพ ะธัะฟะพะปัŒะทะพะฒะฐะฝะธั +- ะŸะตั€ัะพะฝะฐะปะธะทะฐั†ะธั: ะ˜ะทะผะตะฝัะนั‚ะต ะฒะฝะตัˆะฝะธะน ะฒะธะด ะธ ะฟะพะฒะตะดะตะฝะธะต ั€ะฐััˆะธั€ะตะฝะธั ะฟะพะด ัะฒะพะธ ะฝัƒะถะดั‹ + +ะŸะพัะตั‚ะธั‚ะต ะฝะฐัˆ ะฑะปะพะณ, ั‡ั‚ะพะฑั‹ ัƒะทะฝะฐั‚ัŒ ะฑะพะปัŒัˆะต ะพ ะฒัะตั… ะฝะพะฒั‹ั… ั„ัƒะฝะบั†ะธัั… ะธ ัƒะปัƒั‡ัˆะตะฝะธัั… ะฒ ะžั‚ะปะพะถะตะฝะฝั‹ั… ะฒะบะปะฐะดะบะฐั… 3.0 ะฟะพ ััั‹ะปะบะต: +https://at.xfox111.net/tabs-aside-3-0 + +## ะšัั‚ะฐั‚ะธ ัั‚ะพ ะพะฟะตะฝัะพั€ั ั€ะฐััˆะธั€ะตะฝะธะต! +ะ•ัะปะธ ะฒั‹ ะทะฝะฐะตั‚ะต, ะบะฐะบ ะผะพะถะฝะพ ะตะณะพ ัƒะปัƒั‡ัˆะธั‚ัŒ, ะผะพะถะตั‚ะต ะฟะตั€ะตะนั‚ะธ ะฝะฐ ัั‚ั€ะฐะฝะธั†ัƒ GitHub ั€ะตะฟะพะทะธั‚ะพั€ะธั ะฟั€ะพะตะบั‚ะฐ: +https://github.com/xfox111/TabsAsideExtension + +ะกะฟะธัะพะบ ะธะทะผะตะฝะตะฝะธะน ะฟะพัะปะตะดะฝะตะน ะฒะตั€ัะธะธ: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + +ะ’ะดะพั…ะฝะพะฒะปะตะฝะฝะพะต ั„ัƒะฝะบั†ะธะตะน ะฟั€ะตะฒั‹ั… ะฒะตั€ัะธะน ะฑั€ะฐัƒะทะตั€ะฐ Microsoft Edge, ัั‚ะพ ั€ะฐััˆะธั€ะตะฝะธะต ัั‚ะฐะปะพ ะณะพั€ะฐะทะดะพ ะฑะพะปัŒัˆะต, ั‡ะตะผ ะฟั€ะพัั‚ะพ ะฒั€ะตะผะตะฝะฝะพะต ั…ั€ะฐะฝะธะปะธั‰ะต ะดะปั ะฒะบะปะฐะดะพะบ. + +ะžะฝะพ ะฟะพะทะฒะพะปัะตั‚ ัะพั…ั€ะฐะฝัั‚ัŒ ะธ ัƒะฟั€ะฐะฒะปัั‚ัŒ ะฒะฐัˆะธะผะธ ะฒะบะปะฐะดะบะฐะผะธ ัƒะดะพะฑะฝั‹ะผ ะพะฑั€ะฐะทะพะผ, ะฟั€ะตะดะพัั‚ะฐะฒะปัั ะผะฝะพะถะตัั‚ะฒะพ ั„ัƒะฝะบั†ะธะน, ะบะพั‚ะพั€ั‹ะต ัƒะฟั€ะพั‰ะฐัŽั‚ ะพั€ะณะฐะฝะธะทะฐั†ะธัŽ ะธ ะดะพัั‚ัƒะฟ ะบ ัะพั…ั€ะฐะฝะตะฝะฝั‹ะผ ะฒะบะปะฐะดะบะฐะผ. + +ะ’ะพะทะผะพะถะฝะพัั‚ะธ +
    +
  • ะกะพั…ั€ะฐะฝะตะฝะธะต ะฒะบะปะฐะดะพะบ: ะกะพั…ั€ะฐะฝะธั‚ะต ะฒัะต ะพั‚ะบั€ั‹ั‚ั‹ะต ะฒะบะปะฐะดะบะธ ะพะดะฝะธะผ ะบะปะธะบะพะผ ะธ ะฒะพััั‚ะฐะฝะพะฒะธั‚ะต ะธั… ะฟะพะทะถะต
  • +
  • ะžั€ะณะฐะฝะธะทะฐั†ะธั ะฒะบะปะฐะดะพะบ: ะกะพะทะดะฐะฒะฐะนั‚ะต ะบะพะปะปะตะบั†ะธะธ ะธ ะฟะพะดะณั€ัƒะฟะฟั‹ ะดะปั ะพั€ะณะฐะฝะธะทะฐั†ะธะธ ัะพั…ั€ะฐะฝะตะฝะฝั‹ั… ะฒะบะปะฐะดะพะบ
  • +
  • ะŸะพะธัะบ ะฒะบะปะฐะดะพะบ: ะ‘ั‹ัั‚ั€ะพ ะฝะฐั…ะพะดะธั‚ะต ะฝัƒะถะฝั‹ะต ะฒะบะปะฐะดะบะธ ั ะฟะพะผะพั‰ัŒัŽ ั„ัƒะฝะบั†ะธะธ ะฟะพะธัะบะฐ
  • +
  • ะกะธะฝั…ั€ะพะฝะธะทะฐั†ะธั ะผะตะถะดัƒ ัƒัั‚ั€ะพะนัั‚ะฒะฐะผะธ: ะ”ะพัั‚ัƒะฟ ะบ ัะพั…ั€ะฐะฝะตะฝะฝั‹ะผ ะฒะบะปะฐะดะบะฐะผ ั ะปัŽะฑะพะณะพ ัƒัั‚ั€ะพะนัั‚ะฒะฐ ั‡ะตั€ะตะท ะฒะฐัˆ ะฐะบะบะฐัƒะฝั‚
  • +
  • ะขะตะผะฝั‹ะน ั€ะตะถะธะผ: ะŸะพะดะดะตั€ะถะบะฐ ั‚ะตะผะฝะพะณะพ ั€ะตะถะธะผะฐ ะดะปั ะฑะพะปะตะต ะบะพะผั„ะพั€ั‚ะฝะพะณะพ ะธัะฟะพะปัŒะทะพะฒะฐะฝะธั
  • +
  • ะŸะตั€ัะพะฝะฐะปะธะทะฐั†ะธั: ะ˜ะทะผะตะฝัะนั‚ะต ะฒะฝะตัˆะฝะธะน ะฒะธะด ะธ ะฟะพะฒะตะดะตะฝะธะต ั€ะฐััˆะธั€ะตะฝะธั ะฟะพะด ัะฒะพะธ ะฝัƒะถะดั‹
  • +
+ +ะŸะพัะตั‚ะธั‚ะต ะฝะฐัˆ ะฑะปะพะณ, ั‡ั‚ะพะฑั‹ ัƒะทะฝะฐั‚ัŒ ะฑะพะปัŒัˆะต ะพ ะฒัะตั… ะฝะพะฒั‹ั… ั„ัƒะฝะบั†ะธัั… ะธ ัƒะปัƒั‡ัˆะตะฝะธัั… ะฒ ะžั‚ะปะพะถะตะฝะฝั‹ั… ะฒะบะปะฐะดะบะฐั… 3.0 ะฟะพ ััั‹ะปะบะต + +ะšัั‚ะฐั‚ะธ ัั‚ะพ ะพะฟะตะฝัะพั€ั ั€ะฐััˆะธั€ะตะฝะธะต! +ะ•ัะปะธ ะฒั‹ ะทะฝะฐะตั‚ะต, ะบะฐะบ ะผะพะถะฝะพ ะตะณะพ ัƒะปัƒั‡ัˆะธั‚ัŒ, ะผะพะถะตั‚ะต ะฟะตั€ะตะนั‚ะธ ะฝะฐ ัั‚ั€ะฐะฝะธั†ัƒ GitHub ั€ะตะฟะพะทะธั‚ะพั€ะธั ะฟั€ะพะตะบั‚ะฐ + +ะกะฟะธัะพะบ ะธะทะผะตะฝะตะฝะธะน ะฟะพัะปะตะดะฝะตะน ะฒะตั€ัะธะธ diff --git a/store_descriptions/uk.txt b/store_descriptions/uk.txt new file mode 100644 index 0000000..598586d --- /dev/null +++ b/store_descriptions/uk.txt @@ -0,0 +1,48 @@ + + +ะะฐะดะธั…ะฝัƒั‚ะต ั„ัƒะฝะบั†ั–ั”ัŽ ะฟะตั€ัˆะธั… ะฒะตั€ัั–ะน Microsoft Edge, ั†ะต ั€ะพะทัˆะธั€ะตะฝะฝั ัั‚ะฐะปะพ ะฝะฐะฑะฐะณะฐั‚ะพ ะฑั–ะปัŒัˆะต, ะฝั–ะถ ะฟั€ะพัั‚ะพ ั‚ะธะผั‡ะฐัะพะฒะต ัั…ะพะฒะธั‰ะต ะดะปั ะฒะบะปะฐะดะพะบ. + +ะ’ะพะฝะพ ะดะพะทะฒะพะปัั” ะทะฑะตั€ั–ะณะฐั‚ะธ ั‚ะฐ ะบะตั€ัƒะฒะฐั‚ะธ ะฒะฐัˆะธะผะธ ะฒะบะปะฐะดะบะฐะผะธ ะทั€ัƒั‡ะฝะพ, ะฝะฐะดะฐัŽั‡ะธ ะฑะตะทะปั–ั‡ ั„ัƒะฝะบั†ั–ะน, ัะบั– ัะฟั€ะพั‰ัƒัŽั‚ัŒ ะพั€ะณะฐะฝั–ะทะฐั†ั–ัŽ ั‚ะฐ ะดะพัั‚ัƒะฟ ะดะพ ะทะฑะตั€ะตะถะตะฝะธั… ะฒะบะปะฐะดะพะบ. + +## ะœะพะถะปะธะฒะพัั‚ั– +- ะ—ะฑะตั€ะตะถะตะฝะฝั ะฒะบะปะฐะดะพะบ: ะ—ะฑะตั€ะตะถั–ั‚ัŒ ัƒัั– ะฒั–ะดะบั€ะธั‚ั– ะฒะบะปะฐะดะบะธ ะพะดะฝะธะผ ะบะปั–ะบะพะผ ั– ะฒั–ะดะฝะพะฒั–ั‚ัŒ ั—ั… ะฟั–ะทะฝั–ัˆะต +- ะžั€ะณะฐะฝั–ะทะฐั†ั–ั ะฒะบะปะฐะดะพะบ: ะกั‚ะฒะพั€ัŽะนั‚ะต ะบะพะปะตะบั†ั–ั— ั‚ะฐ ะฟั–ะดะณั€ัƒะฟะธ ะดะปั ะพั€ะณะฐะฝั–ะทะฐั†ั–ั— ะทะฑะตั€ะตะถะตะฝะธั… ะฒะบะปะฐะดะพะบ +- ะŸะพัˆัƒะบ ะฒะบะปะฐะดะพะบ: ะจะฒะธะดะบะพ ะทะฝะฐั…ะพะดัŒั‚ะต ะฟะพั‚ั€ั–ะฑะฝั– ะฒะบะปะฐะดะบะธ ะทะฐ ะดะพะฟะพะผะพะณะพัŽ ั„ัƒะฝะบั†ั–ั— ะฟะพัˆัƒะบัƒ +- ะกะธะฝั…ั€ะพะฝั–ะทะฐั†ั–ั ะผั–ะถ ะฟั€ะธัั‚ั€ะพัะผะธ: ะ”ะพัั‚ัƒะฟ ะดะพ ะทะฑะตั€ะตะถะตะฝะธั… ะฒะบะปะฐะดะพะบ ะท ะฑัƒะดัŒ-ัะบะพะณะพ ะฟั€ะธัั‚ั€ะพัŽ ั‡ะตั€ะตะท ะฒะฐัˆ ะพะฑะปั–ะบะพะฒะธะน ะทะฐะฟะธั +- ะขะตะผะฝะธะน ั€ะตะถะธะผ: ะŸั–ะดั‚ั€ะธะผะบะฐ ั‚ะตะผะฝะพะณะพ ั€ะตะถะธะผัƒ ะดะปั ะฑั–ะปัŒัˆ ะบะพะผั„ะพั€ั‚ะฝะพะณะพ ะฒะธะบะพั€ะธัั‚ะฐะฝะฝั +- ะŸะตั€ัะพะฝะฐะปั–ะทะฐั†ั–ั: ะ—ะผั–ะฝัŽะนั‚ะต ะทะพะฒะฝั–ัˆะฝั–ะน ะฒะธะณะปัะด ั– ะฟะพะฒะตะดั–ะฝะบัƒ ั€ะพะทัˆะธั€ะตะฝะฝั ะฟั–ะด ัะฒะพั— ะฟะพั‚ั€ะตะฑะธ + +ะ’ั–ะดะฒั–ะดะฐะนั‚ะต ะฝะฐัˆ ะฑะปะพะณ, ั‰ะพะฑ ะดั–ะทะฝะฐั‚ะธัั ะฑั–ะปัŒัˆะต ะฟั€ะพ ะฒัั– ะฝะพะฒั– ั„ัƒะฝะบั†ั–ั— ั‚ะฐ ะฟะพะบั€ะฐั‰ะตะฝะฝั ัƒ ะ’ั–ะดะบะปะฐะดะตะฝะธั… ะฒะบะปะฐะดะบะฐั… 3.0 ะทะฐ ะฟะพัะธะปะฐะฝะฝัะผ: +https://at.xfox111.net/tabs-aside-3-0 + +## ะ”ะพ ั€ะตั‡ั–, ั†ะต ะพะฟะตะฝัะพั€ั ั€ะพะทัˆะธั€ะตะฝะฝั! +ะฏะบั‰ะพ ะฒะธ ะทะฝะฐั”ั‚ะต, ัะบ ะฟะพะบั€ะฐั‰ะธั‚ะธ ั†ะต ั€ะพะทัˆะธั€ะตะฝะฝั, ะฒะธ ะผะพะถะตั‚ะต ะฒั–ะดะฒั–ะดะฐั‚ะธ ะนะพะณะพ ั€ะตะฟะพะทะธั‚ะพั€ั–ะน ะฝะฐ GitHub: +https://github.com/xfox111/TabsAsideExtension + +ะกะฟะธัะพะบ ะทะผั–ะฝ ะพัั‚ะฐะฝะฝัŒะพั— ะฒะตั€ัั–ั—: +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + +ะะฐะดะธั…ะฝัƒั‚ะต ั„ัƒะฝะบั†ั–ั”ัŽ ะฟะตั€ัˆะธั… ะฒะตั€ัั–ะน Microsoft Edge, ั†ะต ั€ะพะทัˆะธั€ะตะฝะฝั ัั‚ะฐะปะพ ะฝะฐะฑะฐะณะฐั‚ะพ ะฑั–ะปัŒัˆะต, ะฝั–ะถ ะฟั€ะพัั‚ะพ ั‚ะธะผั‡ะฐัะพะฒะต ัั…ะพะฒะธั‰ะต ะดะปั ะฒะบะปะฐะดะพะบ. + +ะ’ะพะฝะพ ะดะพะทะฒะพะปัั” ะทะฑะตั€ั–ะณะฐั‚ะธ ั‚ะฐ ะบะตั€ัƒะฒะฐั‚ะธ ะฒะฐัˆะธะผะธ ะฒะบะปะฐะดะบะฐะผะธ ะทั€ัƒั‡ะฝะพ, ะฝะฐะดะฐัŽั‡ะธ ะฑะตะทะปั–ั‡ ั„ัƒะฝะบั†ั–ะน, ัะบั– ัะฟั€ะพั‰ัƒัŽั‚ัŒ ะพั€ะณะฐะฝั–ะทะฐั†ั–ัŽ ั‚ะฐ ะดะพัั‚ัƒะฟ ะดะพ ะทะฑะตั€ะตะถะตะฝะธั… ะฒะบะปะฐะดะพะบ. + +ะœะพะถะปะธะฒะพัั‚ั– +
    +
  • ะ—ะฑะตั€ะตะถะตะฝะฝั ะฒะบะปะฐะดะพะบ: ะ—ะฑะตั€ะตะถั–ั‚ัŒ ัƒัั– ะฒั–ะดะบั€ะธั‚ั– ะฒะบะปะฐะดะบะธ ะพะดะฝะธะผ ะบะปั–ะบะพะผ ั– ะฒั–ะดะฝะพะฒั–ั‚ัŒ ั—ั… ะฟั–ะทะฝั–ัˆะต
  • +
  • ะžั€ะณะฐะฝั–ะทะฐั†ั–ั ะฒะบะปะฐะดะพะบ: ะกั‚ะฒะพั€ัŽะนั‚ะต ะบะพะปะตะบั†ั–ั— ั‚ะฐ ะฟั–ะดะณั€ัƒะฟะธ ะดะปั ะพั€ะณะฐะฝั–ะทะฐั†ั–ั— ะทะฑะตั€ะตะถะตะฝะธั… ะฒะบะปะฐะดะพะบ
  • +
  • ะŸะพัˆัƒะบ ะฒะบะปะฐะดะพะบ: ะจะฒะธะดะบะพ ะทะฝะฐั…ะพะดัŒั‚ะต ะฟะพั‚ั€ั–ะฑะฝั– ะฒะบะปะฐะดะบะธ ะทะฐ ะดะพะฟะพะผะพะณะพัŽ ั„ัƒะฝะบั†ั–ั— ะฟะพัˆัƒะบัƒ
  • +
  • ะกะธะฝั…ั€ะพะฝั–ะทะฐั†ั–ั ะผั–ะถ ะฟั€ะธัั‚ั€ะพัะผะธ: ะ”ะพัั‚ัƒะฟ ะดะพ ะทะฑะตั€ะตะถะตะฝะธั… ะฒะบะปะฐะดะพะบ ะท ะฑัƒะดัŒ-ัะบะพะณะพ ะฟั€ะธัั‚ั€ะพัŽ ั‡ะตั€ะตะท ะฒะฐัˆ ะพะฑะปั–ะบะพะฒะธะน ะทะฐะฟะธั
  • +
  • ะขะตะผะฝะธะน ั€ะตะถะธะผ: ะŸั–ะดั‚ั€ะธะผะบะฐ ั‚ะตะผะฝะพะณะพ ั€ะตะถะธะผัƒ ะดะปั ะฑั–ะปัŒัˆ ะบะพะผั„ะพั€ั‚ะฝะพะณะพ ะฒะธะบะพั€ะธัั‚ะฐะฝะฝั
  • +
  • ะŸะตั€ัะพะฝะฐะปั–ะทะฐั†ั–ั: ะ—ะผั–ะฝัŽะนั‚ะต ะทะพะฒะฝั–ัˆะฝั–ะน ะฒะธะณะปัะด ั– ะฟะพะฒะตะดั–ะฝะบัƒ ั€ะพะทัˆะธั€ะตะฝะฝั ะฟั–ะด ัะฒะพั— ะฟะพั‚ั€ะตะฑะธ
  • +
+ +ะ’ั–ะดะฒั–ะดะฐะนั‚ะต ะฝะฐัˆ ะฑะปะพะณ, ั‰ะพะฑ ะดั–ะทะฝะฐั‚ะธัั ะฑั–ะปัŒัˆะต ะฟั€ะพ ะฒัั– ะฝะพะฒั– ั„ัƒะฝะบั†ั–ั— ั‚ะฐ ะฟะพะบั€ะฐั‰ะตะฝะฝั ัƒ ะ’ั–ะดะบะปะฐะดะตะฝะธั… ะฒะบะปะฐะดะบะฐั… 3.0 + +ะ”ะพ ั€ะตั‡ั–, ั†ะต ะพะฟะตะฝัะพั€ั ั€ะพะทัˆะธั€ะตะฝะฝั! +ะฏะบั‰ะพ ะฒะธ ะทะฝะฐั”ั‚ะต, ัะบ ะฟะพะบั€ะฐั‰ะธั‚ะธ ั†ะต ั€ะพะทัˆะธั€ะตะฝะฝั, ะฒะธ ะผะพะถะตั‚ะต ะฒั–ะดะฒั–ะดะฐั‚ะธ ะนะพะณะพ ั€ะตะฟะพะทะธั‚ะพั€ั–ะน ะฝะฐ GitHub + +ะกะฟะธัะพะบ ะทะผั–ะฝ ะพัั‚ะฐะฝะฝัŒะพั— ะฒะตั€ัั–ั— diff --git a/store_descriptions/zh_CN.txt b/store_descriptions/zh_CN.txt new file mode 100644 index 0000000..fde37a4 --- /dev/null +++ b/store_descriptions/zh_CN.txt @@ -0,0 +1,48 @@ + + +ๆบ่‡ชๅŽŸๅง‹ Microsoft Edge ๆต่งˆๅ™จๅŠŸ่ƒฝ๏ผŒๆญคๆ‰ฉๅฑ•ๅทฒๅ‘ๅฑ•ไธบ่ฟœไธๆญขๆ˜ฏๆ ‡็ญพ็š„ไธดๆ—ถๅญ˜ๅ‚จใ€‚ + +ๅฎƒๅ…่ฎธๆ‚จไปฅๆ–นไพฟ็š„ๆ–นๅผไฟๅญ˜ๅ’Œ็ฎก็†ๆ ‡็ญพ๏ผŒๆไพ›ไธ€็ณปๅˆ—ๅŠŸ่ƒฝ๏ผŒไฝฟๆ‚จๅฏไปฅ่ฝปๆพ็ป„็ป‡ๅ’Œ่ฎฟ้—ฎๅทฒไฟๅญ˜็š„ๆ ‡็ญพใ€‚ + +## ๅŠŸ่ƒฝ +- ไฟๅญ˜ๆ ‡็ญพ๏ผšไธ€้”ฎไฟๅญ˜ๆ‰€ๆœ‰ๆ‰“ๅผ€็š„ๆ ‡็ญพ๏ผŒๅนถ็จๅŽๆขๅค +- ็ป„็ป‡ๆ ‡็ญพ๏ผšๅˆ›ๅปบๆ”ถ่—ๅ’Œๅญ็ป„ไปฅ็ป„็ป‡ๅทฒไฟๅญ˜็š„ๆ ‡็ญพ +- ๆœ็ดขๆ ‡็ญพ๏ผšไฝฟ็”จๆœ็ดขๅŠŸ่ƒฝๅฟซ้€Ÿๆ‰พๅˆฐๆ‰€้œ€็š„ๆ ‡็ญพ +- ่ทจ่ฎพๅค‡ๅŒๆญฅ๏ผšไฝฟ็”จๆ‚จ็š„ๅธๆˆทไปŽไปปไฝ•่ฎพๅค‡่ฎฟ้—ฎๅทฒไฟๅญ˜็š„ๆ ‡็ญพ +- ๆทฑ่‰ฒๆจกๅผ๏ผšๆ”ฏๆŒๆทฑ่‰ฒๆจกๅผ๏ผŒๆไพ›ๆ›ด่ˆ’้€‚็š„ๆต่งˆไฝ“้ชŒ +- ไธชๆ€งๅŒ–๏ผšๆ›ดๆ”นๆ‰ฉๅฑ•็š„ๅค–่ง‚ๅ’Œ่กŒไธบไปฅๆปก่ถณๆ‚จ็š„้œ€ๆฑ‚ + +ๆŸฅ็œ‹ๆˆ‘ไปฌๅ…ณไบŽๆ็ฝฎ็š„ๆ ‡็ญพ้กต 3.0 ็š„ๆ‰€ๆœ‰ๆ–ฐๅŠŸ่ƒฝๅ’Œๆ”น่ฟ›็š„ๅšๅฎขๆ–‡็ซ ๏ผš +https://at.xfox111.net/tabs-aside-3-0 + +## ๅ˜ฟ๏ผŒ่ฟ™ๆ˜ฏไธ€ไธชๅผ€ๆบ่ฝฏไปถ๏ผ +ๅฆ‚ๆžœๆ‚จ็Ÿฅ้“ๅฆ‚ไฝ•ๆ”น่ฟ›ๆญคๆ‰ฉๅฑ•๏ผŒๅฏไปฅๆŸฅ็œ‹ๅ…ถ GitHub ไป“ๅบ“๏ผš +https://github.com/xfox111/TabsAsideExtension + +ๆŸฅ็œ‹ๅ‘ๅธƒๆ›ดๆ–ฐๆ—ฅๅฟ—๏ผš +https://github.com/xfox111/TabsAsideExtension/releases/latest + + + + + +ๆบ่‡ชๅŽŸๅง‹ Microsoft Edge ๆต่งˆๅ™จๅŠŸ่ƒฝ๏ผŒๆญคๆ‰ฉๅฑ•ๅทฒๅ‘ๅฑ•ไธบ่ฟœไธๆญขๆ˜ฏๆ ‡็ญพ็š„ไธดๆ—ถๅญ˜ๅ‚จใ€‚ + +ๅฎƒๅ…่ฎธๆ‚จไปฅๆ–นไพฟ็š„ๆ–นๅผไฟๅญ˜ๅ’Œ็ฎก็†ๆ ‡็ญพ๏ผŒๆไพ›ไธ€็ณปๅˆ—ๅŠŸ่ƒฝ๏ผŒไฝฟๆ‚จๅฏไปฅ่ฝปๆพ็ป„็ป‡ๅ’Œ่ฎฟ้—ฎๅทฒไฟๅญ˜็š„ๆ ‡็ญพใ€‚ + +ๅŠŸ่ƒฝ +
    +
  • ไฟๅญ˜ๆ ‡็ญพ๏ผšไธ€้”ฎไฟๅญ˜ๆ‰€ๆœ‰ๆ‰“ๅผ€็š„ๆ ‡็ญพ๏ผŒๅนถ็จๅŽๆขๅค
  • +
  • ็ป„็ป‡ๆ ‡็ญพ๏ผšๅˆ›ๅปบๆ”ถ่—ๅ’Œๅญ็ป„ไปฅ็ป„็ป‡ๅทฒไฟๅญ˜็š„ๆ ‡็ญพ
  • +
  • ๆœ็ดขๆ ‡็ญพ๏ผšไฝฟ็”จๆœ็ดขๅŠŸ่ƒฝๅฟซ้€Ÿๆ‰พๅˆฐๆ‰€้œ€็š„ๆ ‡็ญพ
  • +
  • ่ทจ่ฎพๅค‡ๅŒๆญฅ๏ผšไฝฟ็”จๆ‚จ็š„ๅธๆˆทไปŽไปปไฝ•่ฎพๅค‡่ฎฟ้—ฎๅทฒไฟๅญ˜็š„ๆ ‡็ญพ
  • +
  • ๆทฑ่‰ฒๆจกๅผ๏ผšๆ”ฏๆŒๆทฑ่‰ฒๆจกๅผ๏ผŒๆไพ›ๆ›ด่ˆ’้€‚็š„ๆต่งˆไฝ“้ชŒ
  • +
  • ไธชๆ€งๅŒ–๏ผšๆ›ดๆ”นๆ‰ฉๅฑ•็š„ๅค–่ง‚ๅ’Œ่กŒไธบไปฅๆปก่ถณๆ‚จ็š„้œ€ๆฑ‚
  • +
+ +ๆŸฅ็œ‹ๆˆ‘ไปฌๅ…ณไบŽ ๆ็ฝฎ็š„ๆ ‡็ญพ้กต 3.0 ็š„ๆ‰€ๆœ‰ๆ–ฐๅŠŸ่ƒฝๅ’Œๆ”น่ฟ›็š„ๅšๅฎขๆ–‡็ซ  + +ๅ˜ฟ๏ผŒ่ฟ™ๆ˜ฏไธ€ไธชๅผ€ๆบ่ฝฏไปถ๏ผ +ๅฆ‚ๆžœๆ‚จ็Ÿฅ้“ๅฆ‚ไฝ•ๆ”น่ฟ›ๆญคๆ‰ฉๅฑ•๏ผŒๅฏไปฅๆŸฅ็œ‹ๅ…ถ GitHub ไป“ๅบ“ + +ๆŸฅ็œ‹ๅ‘ๅธƒๆ›ดๆ–ฐๆ—ฅๅฟ— diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f39bbb5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./.wxt/tsconfig.json", + "compilerOptions": { + "allowImportingTsExtensions": true, + "jsx": "react-jsx", + "strict": true, + "strictNullChecks": true + } +} diff --git a/utils/browserLocaleKey.ts b/utils/browserLocaleKey.ts new file mode 100644 index 0000000..0b2979a --- /dev/null +++ b/utils/browserLocaleKey.ts @@ -0,0 +1,8 @@ +const browserLocaleKey: "firefox" | "edge" | "chrome" = + import.meta.env.FIREFOX ? + "firefox" : + import.meta.env.EDGE ? + "edge" : + "chrome"; + +export default browserLocaleKey; diff --git a/utils/extLink.ts b/utils/extLink.ts new file mode 100644 index 0000000..e3ccc98 --- /dev/null +++ b/utils/extLink.ts @@ -0,0 +1,7 @@ +const extLink = (href: string) => ({ + href, + target: "_blank", + rel: "noopener" +}); + +export default extLink; diff --git a/utils/getLogger.ts b/utils/getLogger.ts new file mode 100644 index 0000000..0aacb49 --- /dev/null +++ b/utils/getLogger.ts @@ -0,0 +1,13 @@ +/** + * Creates a logger function for a specific component. + * The logger prepends a standardized prefix to all log messages, + * indicating the component name for easier debugging. + * + * @param component - The name of the component to include in the log prefix. + * @returns A logging function that accepts any number of arguments and logs them + * to the console with the component-specific prefix. + */ +export default function getLogger(component: string): (...data: any[]) => void +{ + return (...data: any[]): void => console.log(`[TabsAside.${component}]`, ...data); +} diff --git a/utils/messaging.ts b/utils/messaging.ts new file mode 100644 index 0000000..83f0943 --- /dev/null +++ b/utils/messaging.ts @@ -0,0 +1,42 @@ +import { trackError } from "@/features/analytics"; +import { CollectionItem, GraphicsStorage, GroupItem } from "@/models/CollectionModels"; +import { defineExtensionMessaging, ExtensionMessagingConfig, ExtensionMessenger, ExtensionSendMessageArgs, GetDataType, GetReturnType } from "@webext-core/messaging"; + +type ProtocolMap = + { + addThumbnail(data: { url: string; thumbnail: string; }): void; + getGraphicsCache(): GraphicsStorage; + refreshCollections(): void; + + openCollection(data: { collection: CollectionItem; targetWindow: "new" | "incognito"; }): void; + openGroup(data: { group: GroupItem; newWindow: boolean; }): void; + }; + +function defineMessaging(config?: ExtensionMessagingConfig): ExtensionMessenger +{ + const { onMessage, sendMessage, removeAllListeners }: ExtensionMessenger = defineExtensionMessaging(config); + + return { + onMessage, + removeAllListeners, + async sendMessage( + type: TType, + data: GetDataType, + ...args: ExtensionSendMessageArgs + ): Promise> + { + try + { + return await sendMessage(type, data, ...args); + } + catch (ex) + { + console.error(ex); + trackError("messaging_error", ex as Error); + return undefined!; + } + } + }; +} + +export const { onMessage, sendMessage } = defineMessaging({ logger: console }); diff --git a/utils/saveTabsToCollection.ts b/utils/saveTabsToCollection.ts new file mode 100644 index 0000000..f6d9f1e --- /dev/null +++ b/utils/saveTabsToCollection.ts @@ -0,0 +1,136 @@ +import { track } from "@/features/analytics"; +import { CollectionItem, GroupItem } from "@/models/CollectionModels"; +import { Tabs } from "wxt/browser"; +import sendNotification from "./sendNotification"; +import { settings } from "./settings"; + +export default async function saveTabsToCollection(closeTabs: boolean): Promise +{ + let tabs: Tabs.Tab[] = await browser.tabs.query({ + currentWindow: true, + highlighted: true + }); + + if (tabs.length < 2) + { + const ignorePinned: boolean = await settings.ignorePinned.getValue(); + tabs = await browser.tabs.query({ + currentWindow: true, + pinned: ignorePinned ? false : undefined + }); + } + + const [collection, tabsToClose] = await createCollectionFromTabs(tabs); + + if (closeTabs) + { + await browser.tabs.create({ + active: true, + windowId: tabs[0].windowId + }); + await browser.tabs.remove(tabsToClose.map(i => i.id!)); + } + + track(closeTabs ? "set_aside" : "save"); + + return collection; +} + +async function createCollectionFromTabs(tabs: Tabs.Tab[]): Promise<[CollectionItem, Tabs.Tab[]]> +{ + if (tabs.length < 1) + return [{ type: "collection", timestamp: Date.now(), items: [] }, []]; + + const tabCount: number = tabs.length; + + tabs = tabs.filter(i => + i.url + && new URL(i.url).protocol !== "about:" + && new URL(i.url).hostname !== "newtab" + ); + + if (tabs.length < tabCount) + await sendNotification({ + title: i18n.t("notifications.partial_save.title"), + message: i18n.t("notifications.partial_save.message"), + icon: "/notification_icons/save_warning.png" + }); + + tabs = tabs.filter(i => !i.url!.startsWith(browser.runtime.getURL("/"))); + + const collection: CollectionItem = { + type: "collection", + timestamp: Date.now(), + items: [] + }; + + let tabIndex: number = 0; + + if (tabs[tabIndex].pinned) + { + collection.items.push({ type: "group", pinned: true, items: [] }); + + for (; tabIndex < tabs.length; tabIndex++) + { + if (!tabs[tabIndex].pinned) + break; + + (collection.items[0] as GroupItem).items.push({ + type: "tab", + url: tabs[tabIndex].url!, + title: tabs[tabIndex].title + }); + } + } + + // Special case, if all tabs are in the same group, create a collection with the group title + if (tabs[0].groupId && tabs[0].groupId !== -1 && + tabs.every(i => i.groupId === tabs[0].groupId) + ) + { + const group = await chrome.tabGroups.get(tabs[0].groupId); + collection.title = group.title; + collection.color = group.color; + + tabs.forEach(i => + collection.items.push({ type: "tab", url: i.url!, title: i.title }) + ); + + return [collection, tabs]; + } + + let activeGroup: number | null = null; + + for (; tabIndex < tabs.length; tabIndex++) + { + const tab = tabs[tabIndex]; + + if (!tab.groupId || tab.groupId === -1) + { + collection.items.push({ type: "tab", url: tab.url!, title: tab.title }); + activeGroup = null; + continue; + } + + if (!activeGroup || activeGroup !== tab.groupId) + { + activeGroup = tab.groupId; + const group = await chrome.tabGroups.get(activeGroup); + + collection.items.push({ + type: "group", + color: group.color, + title: group.title, + items: [] + }); + } + + (collection.items[collection.items.length - 1] as GroupItem).items.push({ + type: "tab", + url: tab.url!, + title: tab.title + }); + } + + return [collection, tabs]; +} diff --git a/utils/sendNotification.ts b/utils/sendNotification.ts new file mode 100644 index 0000000..f63f809 --- /dev/null +++ b/utils/sendNotification.ts @@ -0,0 +1,28 @@ +import { trackError } from "@/features/analytics"; +import { PublicPath } from "wxt/browser"; + +export default async function sendNotification(props: NotificationProps): Promise +{ + try + { + await browser.notifications.create({ + type: "basic", + title: props.title, + message: props.message, + iconUrl: browser.runtime.getURL(props.icon) + }); + } + catch (ex) + { + console.error("Error while showing notification (probably because of user restrictions)"); + console.error(ex); + trackError("notification_error", ex as Error); + } +} + +export type NotificationProps = + { + title: string; + message: string; + icon: PublicPath; + }; diff --git a/utils/settings.tsx b/utils/settings.tsx new file mode 100644 index 0000000..eb4753e --- /dev/null +++ b/utils/settings.tsx @@ -0,0 +1,99 @@ +import { CollectionSortMode } from "@/entrypoints/sidepanel/utils/sortCollections"; + +export const settings = { + defaultRestoreAction: storage.defineItem<"open" | "restore">( + "sync:defaultRestoreAction", + { + fallback: "open", + version: 1 + } + ), + + defaultSaveAction: storage.defineItem<"save" | "set_aside">( + "sync:defaultSaveAction", + { + fallback: "set_aside", + version: 1 + } + ), + + dismissOnLoad: storage.defineItem( + "sync:dismissOnLoad", + { + fallback: false, + version: 1 + } + ), + + deletePrompt: storage.defineItem( + "sync:deletePrompt", + { + fallback: true, + version: 1 + } + ), + + tilesView: storage.defineItem( + "sync:tilesView", + { + fallback: true, + version: 1 + } + ), + + sortMode: storage.defineItem( + "sync:sortMode", + { + fallback: "custom", + version: 1 + } + ), + + ignorePinned: storage.defineItem( + "sync:ignorePinned", + { + fallback: true, + version: 1 + } + ), + + alwaysShowToolbars: storage.defineItem( + "sync:alwaysShowToolbars", + { + fallback: false, + version: 1 + } + ), + + showBadge: storage.defineItem( + "sync:showBadge", + { + fallback: true, + version: 1 + } + ), + + contextAction: storage.defineItem<"action" | "context" | "open">( + "sync:contextAction", + { + fallback: "open", + version: 1 + } + ), + + listLocation: storage.defineItem<"sidebar" | "popup" | "tab" | "pinned">( + "sync:listLocation", + { + fallback: "sidebar", + version: 1 + } + ), + + notifyOnSave: storage.defineItem( + "sync:notifyOnSave", + { + fallback: true, + version: 1 + } + ) +}; diff --git a/utils/watchTabSelection.ts b/utils/watchTabSelection.ts new file mode 100644 index 0000000..20e95a4 --- /dev/null +++ b/utils/watchTabSelection.ts @@ -0,0 +1,20 @@ +import { Unwatch } from "wxt/storage"; + +export default function watchTabSelection(onChange: TabSelectChangeHandler): Unwatch +{ + const handleTabSelection = async () => + { + const highlightedTabs = await browser.tabs.query({ + currentWindow: true, + highlighted: true + }); + onChange(highlightedTabs.length > 1 ? "selected" : "all"); + }; + + handleTabSelection(); + browser.tabs.onHighlighted.addListener(handleTabSelection); + + return () => browser.tabs.onHighlighted.removeListener(handleTabSelection); +} + +export type TabSelectChangeHandler = (selection: "all" | "selected") => void; diff --git a/wxt.config.ts b/wxt.config.ts new file mode 100644 index 0000000..b84182d --- /dev/null +++ b/wxt.config.ts @@ -0,0 +1,93 @@ +import { ConfigEnv, defineConfig, UserManifest } from "wxt"; + +// See https://wxt.dev/api/config.html +export default defineConfig({ + modules: ["@wxt-dev/module-react", "@wxt-dev/i18n/module", "@wxt-dev/analytics/module"], + vite: () => ({ + build: + { + chunkSizeWarningLimit: 1000 + } + }), + imports: { + dirsScanOptions: + { + // Disable auto-imports for project's files + fileFilter: () => false + } + }, + + manifest: ({ browser }: ConfigEnv) => + { + const manifest: UserManifest = { + name: "__MSG_manifest_name__", + description: "__MSG_manifest_description__", + homepage_url: "https://github.com/xfox111/TabsAsideExtension/", + + action: + { + default_title: "__MSG_manifest_name__" + }, + + default_locale: "en", + permissions: + [ + "storage", + "unlimitedStorage", + "tabs", + "notifications", + "contextMenus", + "bookmarks", + "tabGroups" + ], + + commands: + { + show_collections: + { + suggested_key: + { + default: "Alt+P", + mac: "MacCtrl+P" + }, + description: "__MSG_shortcuts_toggle_sidebar__" + }, + set_aside: { + suggested_key: + { + default: "Alt+T", + mac: "MacCtrl+T" + }, + description: "__MSG_shortcuts_set_aside__" + }, + save: + { + suggested_key: + { + default: "Alt+U", + mac: "MacCtrl+U" + }, + description: "__MSG_shortcuts_save_tabs__" + } + }, + + host_permissions: [""] + }; + + if (browser === "firefox") + { + manifest.browser_specific_settings = { + gecko: + { + id: "tabsaside@xfox111.net", + strict_min_version: "139.0" + } + }; + + // @ts-expect-error author key in Firefox has a different format + manifest.author = "__MSG_manifest_author__"; + } + + return manifest; + } +}); diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..503dd11 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,10028 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@1natsu/wait-element@npm:^4.1.2": + version: 4.1.2 + resolution: "@1natsu/wait-element@npm:4.1.2" + dependencies: + defu: "npm:^6.1.4" + many-keys-map: "npm:^2.0.1" + checksum: 10c0/41ac5b0243306b725023a47d25df11032ece58b778dff05508b38629d447af090df095ad5be6c45f025ec6feaef05f802984698eb9ed312a647fe86b99d34bc3 + languageName: node + linkType: hard + +"@aklinker1/rollup-plugin-visualizer@npm:5.12.0": + version: 5.12.0 + resolution: "@aklinker1/rollup-plugin-visualizer@npm:5.12.0" + dependencies: + open: "npm:^8.4.0" + picomatch: "npm:^2.3.1" + source-map: "npm:^0.7.4" + yargs: "npm:^17.5.1" + peerDependencies: + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rollup: + optional: true + bin: + rollup-plugin-visualizer: dist/bin/cli.js + checksum: 10c0/9081f9cb6d6b2efb94ed5dfc888bcaafc3582e05c6e7263f99870163ad73e3ac0b937ff8e95224301a740364d5aef6021d9da3710851bad39ab587c7108a65c3 + languageName: node + linkType: hard + +"@ampproject/remapping@npm:^2.2.0": + version: 2.3.0 + resolution: "@ampproject/remapping@npm:2.3.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/code-frame@npm:7.27.1" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.27.1" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/5dd9a18baa5fce4741ba729acc3a3272c49c25cb8736c4b18e113099520e7ef7b545a4096a26d600e4416157e63e87d66db46aa3fbf0a5f2286da2705c12da00 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.27.2": + version: 7.28.0 + resolution: "@babel/compat-data@npm:7.28.0" + checksum: 10c0/c4e527302bcd61052423f757355a71c3bc62362bac13f7f130de16e439716f66091ff5bdecda418e8fa0271d4c725f860f0ee23ab7bf6e769f7a8bb16dfcb531 + languageName: node + linkType: hard + +"@babel/core@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/core@npm:7.28.0" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.27.1" + "@babel/generator": "npm:^7.28.0" + "@babel/helper-compilation-targets": "npm:^7.27.2" + "@babel/helper-module-transforms": "npm:^7.27.3" + "@babel/helpers": "npm:^7.27.6" + "@babel/parser": "npm:^7.28.0" + "@babel/template": "npm:^7.27.2" + "@babel/traverse": "npm:^7.28.0" + "@babel/types": "npm:^7.28.0" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/423302e7c721e73b1c096217880272e02020dfb697a55ccca60ad01bba90037015f84d0c20c6ce297cf33a19bb704bc5c2b3d3095f5284dfa592bd1de0b9e8c3 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/generator@npm:7.28.0" + dependencies: + "@babel/parser": "npm:^7.28.0" + "@babel/types": "npm:^7.28.0" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/1b3d122268ea3df50fde707ad864d9a55c72621357d5cebb972db3dd76859c45810c56e16ad23123f18f80cc2692f5a015d2858361300f0f224a05dc43d36a92 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.27.2": + version: 7.27.2 + resolution: "@babel/helper-compilation-targets@npm:7.27.2" + dependencies: + "@babel/compat-data": "npm:^7.27.2" + "@babel/helper-validator-option": "npm:^7.27.1" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/f338fa00dcfea931804a7c55d1a1c81b6f0a09787e528ec580d5c21b3ecb3913f6cb0f361368973ce953b824d910d3ac3e8a8ee15192710d3563826447193ad1 + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/helper-globals@npm:7.28.0" + checksum: 10c0/5a0cd0c0e8c764b5f27f2095e4243e8af6fa145daea2b41b53c0c1414fe6ff139e3640f4e2207ae2b3d2153a1abd346f901c26c290ee7cb3881dd922d4ee9232 + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-module-imports@npm:7.27.1" + dependencies: + "@babel/traverse": "npm:^7.27.1" + "@babel/types": "npm:^7.27.1" + checksum: 10c0/e00aace096e4e29290ff8648455c2bc4ed982f0d61dbf2db1b5e750b9b98f318bf5788d75a4f974c151bd318fd549e81dbcab595f46b14b81c12eda3023f51e8 + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.27.3": + version: 7.27.3 + resolution: "@babel/helper-module-transforms@npm:7.27.3" + dependencies: + "@babel/helper-module-imports": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.27.1" + "@babel/traverse": "npm:^7.27.3" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/fccb4f512a13b4c069af51e1b56b20f54024bcf1591e31e978a30f3502567f34f90a80da6a19a6148c249216292a8074a0121f9e52602510ef0f32dbce95ca01 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-plugin-utils@npm:7.27.1" + checksum: 10c0/94cf22c81a0c11a09b197b41ab488d416ff62254ce13c57e62912c85700dc2e99e555225787a4099ff6bae7a1812d622c80fbaeda824b79baa10a6c5ac4cf69b + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-string-parser@npm:7.27.1" + checksum: 10c0/8bda3448e07b5583727c103560bcf9c4c24b3c1051a4c516d4050ef69df37bb9a4734a585fe12725b8c2763de0a265aa1e909b485a4e3270b7cfd3e4dbe4b602 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-validator-identifier@npm:7.27.1" + checksum: 10c0/c558f11c4871d526498e49d07a84752d1800bf72ac0d3dad100309a2eaba24efbf56ea59af5137ff15e3a00280ebe588560534b0e894a4750f8b1411d8f78b84 + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-validator-option@npm:7.27.1" + checksum: 10c0/6fec5f006eba40001a20f26b1ef5dbbda377b7b68c8ad518c05baa9af3f396e780bdfded24c4eef95d14bb7b8fd56192a6ed38d5d439b97d10efc5f1a191d148 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.27.6": + version: 7.27.6 + resolution: "@babel/helpers@npm:7.27.6" + dependencies: + "@babel/template": "npm:^7.27.2" + "@babel/types": "npm:^7.27.6" + checksum: 10c0/448bac96ef8b0f21f2294a826df9de6bf4026fd023f8a6bb6c782fe3e61946801ca24381490b8e58d861fee75cd695a1882921afbf1f53b0275ee68c938bd6d3 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/parser@npm:7.28.0" + dependencies: + "@babel/types": "npm:^7.28.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/c2ef81d598990fa949d1d388429df327420357cb5200271d0d0a2784f1e6d54afc8301eb8bdf96d8f6c77781e402da93c7dc07980fcc136ac5b9d5f1fce701b5 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-self@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-react-jsx-self@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.27.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/00a4f917b70a608f9aca2fb39aabe04a60aa33165a7e0105fd44b3a8531630eb85bf5572e9f242f51e6ad2fa38c2e7e780902176c863556c58b5ba6f6e164031 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-source@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-react-jsx-source@npm:7.27.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.27.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/5e67b56c39c4d03e59e03ba80692b24c5a921472079b63af711b1d250fc37c1733a17069b63537f750f3e937ec44a42b1ee6a46cd23b1a0df5163b17f741f7f2 + languageName: node + linkType: hard + +"@babel/runtime@npm:7.27.0": + version: 7.27.0 + resolution: "@babel/runtime@npm:7.27.0" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/35091ea9de48bd7fd26fb177693d64f4d195eb58ab2b142b893b7f3fa0f1d7c677604d36499ae0621a3703f35ba0c6a8f6c572cc8f7dc0317213841e493cf663 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": + version: 7.27.6 + resolution: "@babel/runtime@npm:7.27.6" + checksum: 10c0/89726be83f356f511dcdb74d3ea4d873a5f0cf0017d4530cb53aa27380c01ca102d573eff8b8b77815e624b1f8c24e7f0311834ad4fb632c90a770fda00bd4c8 + languageName: node + linkType: hard + +"@babel/template@npm:^7.27.2": + version: 7.27.2 + resolution: "@babel/template@npm:7.27.2" + dependencies: + "@babel/code-frame": "npm:^7.27.1" + "@babel/parser": "npm:^7.27.2" + "@babel/types": "npm:^7.27.1" + checksum: 10c0/ed9e9022651e463cc5f2cc21942f0e74544f1754d231add6348ff1b472985a3b3502041c0be62dc99ed2d12cfae0c51394bf827452b98a2f8769c03b87aadc81 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/traverse@npm:7.28.0" + dependencies: + "@babel/code-frame": "npm:^7.27.1" + "@babel/generator": "npm:^7.28.0" + "@babel/helper-globals": "npm:^7.28.0" + "@babel/parser": "npm:^7.28.0" + "@babel/template": "npm:^7.27.2" + "@babel/types": "npm:^7.28.0" + debug: "npm:^4.3.1" + checksum: 10c0/32794402457827ac558173bcebdcc0e3a18fa339b7c41ca35621f9f645f044534d91bb923ff385f5f960f2e495f56ce18d6c7b0d064d2f0ccb55b285fa6bc7b9 + languageName: node + linkType: hard + +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.6, @babel/types@npm:^7.28.0": + version: 7.28.1 + resolution: "@babel/types@npm:7.28.1" + dependencies: + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.27.1" + checksum: 10c0/5e99b346c11ee42ffb0cadc28159fe0b184d865a2cc1593df79b199772a534f6453969b4942aa5e4a55a3081863096e1cc3fc1c724d826926dc787cf229b845d + languageName: node + linkType: hard + +"@ctrl/tinycolor@npm:^3.3.4": + version: 3.6.1 + resolution: "@ctrl/tinycolor@npm:3.6.1" + checksum: 10c0/444d81612cd8c5c802a3d1253df83d5f77d3db87f351861655683a4743990e6b38976bf2e4129591c5a258607b63574b3c7bed702cf6a0eb7912222edf4570e9 + languageName: node + linkType: hard + +"@devicefarmer/adbkit-logcat@npm:^2.1.2": + version: 2.1.3 + resolution: "@devicefarmer/adbkit-logcat@npm:2.1.3" + checksum: 10c0/d8d6108a0c47f994fd3073f19c8de9e38c6c70b420c55be3fc1a924b873f35cb24120f11e0173ab94c2f14e190f575ff62dc7de801b3272d56f6e46c4be8cde1 + languageName: node + linkType: hard + +"@devicefarmer/adbkit-monkey@npm:~1.2.1": + version: 1.2.1 + resolution: "@devicefarmer/adbkit-monkey@npm:1.2.1" + checksum: 10c0/3c397e7b5242034e29455b94792b6b3ce7d0adbd3e9da59b85c24aa6a5e99ae45f36078f56a8dc5b8df2e1c8f57726f88e5017081c6a4301e1945cf88d8864a2 + languageName: node + linkType: hard + +"@devicefarmer/adbkit@npm:3.3.8": + version: 3.3.8 + resolution: "@devicefarmer/adbkit@npm:3.3.8" + dependencies: + "@devicefarmer/adbkit-logcat": "npm:^2.1.2" + "@devicefarmer/adbkit-monkey": "npm:~1.2.1" + bluebird: "npm:~3.7" + commander: "npm:^9.1.0" + debug: "npm:~4.3.1" + node-forge: "npm:^1.3.1" + split: "npm:~1.0.1" + bin: + adbkit: bin/adbkit + checksum: 10c0/1d15f598ef7e2eae76580d463b61005a966faa5a5bf14e555657166ca638c381d88e5ead074ad8bbb3dc71c0f4ddd85d88cf13f6a1d014b66330292626ad7066 + languageName: node + linkType: hard + +"@dnd-kit/accessibility@npm:^3.1.1": + version: 3.1.1 + resolution: "@dnd-kit/accessibility@npm:3.1.1" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/be0bf41716dc58f9386bc36906ec1ce72b7b42b6d1d0e631d347afe9bd8714a829bd6f58a346dd089b1519e93918ae2f94497411a61a4f5e4d9247c6cfd1fef8 + languageName: node + linkType: hard + +"@dnd-kit/core@npm:^6.3.1": + version: 6.3.1 + resolution: "@dnd-kit/core@npm:6.3.1" + dependencies: + "@dnd-kit/accessibility": "npm:^3.1.1" + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/196db95d81096d9dc248983533eab91ba83591770fa5c894b1ac776f42af0d99522b3fd5bb3923411470e4733fcfa103e6ee17adc17b9b7eb54c7fbec5ff7c52 + languageName: node + linkType: hard + +"@dnd-kit/modifiers@npm:^9.0.0": + version: 9.0.0 + resolution: "@dnd-kit/modifiers@npm:9.0.0" + dependencies: + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@dnd-kit/core": ^6.3.0 + react: ">=16.8.0" + checksum: 10c0/ca8cc9da8296df10774d779c1611074dc327ccc3c49041c102111c98c7f2b2b73b6af5209c0eef6b2fe978ac63dc2a985efa87c85a8d786577304bd2e64cee1d + languageName: node + linkType: hard + +"@dnd-kit/sortable@npm:^10.0.0": + version: 10.0.0 + resolution: "@dnd-kit/sortable@npm:10.0.0" + dependencies: + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@dnd-kit/core": ^6.3.0 + react: ">=16.8.0" + checksum: 10c0/37ee48bc6789fb512dc0e4c374a96d19abe5b2b76dc34856a5883aaa96c3297891b94cc77bbc409e074dcce70967ebcb9feb40cd9abadb8716fc280b4c7f99af + languageName: node + linkType: hard + +"@dnd-kit/utilities@npm:^3.2.2": + version: 3.2.2 + resolution: "@dnd-kit/utilities@npm:3.2.2" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/9aa90526f3e3fd567b5acc1b625a63177b9e8d00e7e50b2bd0e08fa2bf4dba7e19529777e001fdb8f89a7ce69f30b190c8364d390212634e0afdfa8c395e85a0 + languageName: node + linkType: hard + +"@emotion/hash@npm:^0.9.0": + version: 0.9.2 + resolution: "@emotion/hash@npm:0.9.2" + checksum: 10c0/0dc254561a3cc0a06a10bbce7f6a997883fd240c8c1928b93713f803a2e9153a257a488537012efe89dbe1246f2abfe2add62cdb3471a13d67137fcb808e81c2 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/aix-ppc64@npm:0.25.8" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/android-arm64@npm:0.25.8" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/android-arm@npm:0.25.8" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/android-x64@npm:0.25.8" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/darwin-arm64@npm:0.25.8" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/darwin-x64@npm:0.25.8" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/freebsd-arm64@npm:0.25.8" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/freebsd-x64@npm:0.25.8" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-arm64@npm:0.25.8" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-arm@npm:0.25.8" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-ia32@npm:0.25.8" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-loong64@npm:0.25.8" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-mips64el@npm:0.25.8" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-ppc64@npm:0.25.8" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-riscv64@npm:0.25.8" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-s390x@npm:0.25.8" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/linux-x64@npm:0.25.8" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/netbsd-arm64@npm:0.25.8" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/netbsd-x64@npm:0.25.8" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/openbsd-arm64@npm:0.25.8" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/openbsd-x64@npm:0.25.8" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/openharmony-arm64@npm:0.25.8" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/sunos-x64@npm:0.25.8" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/win32-arm64@npm:0.25.8" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/win32-ia32@npm:0.25.8" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.25.8": + version: 0.25.8 + resolution: "@esbuild/win32-x64@npm:0.25.8" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.7.0": + version: 4.7.0 + resolution: "@eslint-community/eslint-utils@npm:4.7.0" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/c0f4f2bd73b7b7a9de74b716a664873d08ab71ab439e51befe77d61915af41a81ecec93b408778b3a7856185244c34c2c8ee28912072ec14def84ba2dec70adf + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": + version: 4.12.1 + resolution: "@eslint-community/regexpp@npm:4.12.1" + checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.21.0": + version: 0.21.0 + resolution: "@eslint/config-array@npm:0.21.0" + dependencies: + "@eslint/object-schema": "npm:^2.1.6" + debug: "npm:^4.3.1" + minimatch: "npm:^3.1.2" + checksum: 10c0/0ea801139166c4aa56465b309af512ef9b2d3c68f9198751bbc3e21894fe70f25fbf26e1b0e9fffff41857bc21bfddeee58649ae6d79aadcd747db0c5dca771f + languageName: node + linkType: hard + +"@eslint/config-helpers@npm:^0.3.0": + version: 0.3.0 + resolution: "@eslint/config-helpers@npm:0.3.0" + checksum: 10c0/013ae7b189eeae8b30cc2ee87bc5c9c091a9cd615579003290eb28bebad5d78806a478e74ba10b3fe08ed66975b52af7d2cd4b4b43990376412b14e5664878c8 + languageName: node + linkType: hard + +"@eslint/core@npm:^0.14.0": + version: 0.14.0 + resolution: "@eslint/core@npm:0.14.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/259f279445834ba2d2cbcc18e9d43202a4011fde22f29d5fb802181d66e0f6f0bd1f6b4b4b46663451f545d35134498231bd5e656e18d9034a457824b92b7741 + languageName: node + linkType: hard + +"@eslint/core@npm:^0.15.0, @eslint/core@npm:^0.15.1": + version: 0.15.1 + resolution: "@eslint/core@npm:0.15.1" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/abaf641940776638b8c15a38d99ce0dac551a8939310ec81b9acd15836a574cf362588eaab03ab11919bc2a0f9648b19ea8dee33bf12675eb5b6fd38bda6f25e + languageName: node + linkType: hard + +"@eslint/css-tree@npm:^3.6.1": + version: 3.6.1 + resolution: "@eslint/css-tree@npm:3.6.1" + dependencies: + mdn-data: "npm:2.21.0" + source-map-js: "npm:^1.0.1" + checksum: 10c0/bf482dd90061c8f1cd425f0f3da1322254525d96ab1fb5b542934703e349b29e72701bf394e3bb286d6eb2faa8b7172b29a86d8e1809be1038ce0c7367c4bac1 + languageName: node + linkType: hard + +"@eslint/css@npm:^0.10.0": + version: 0.10.0 + resolution: "@eslint/css@npm:0.10.0" + dependencies: + "@eslint/core": "npm:^0.14.0" + "@eslint/css-tree": "npm:^3.6.1" + "@eslint/plugin-kit": "npm:^0.3.1" + checksum: 10c0/43ffbabea89a6ff1df61c668d966a37d83b65e2466c87a87dae310bfae2119cbebf9de9a91569c2e1eb11fbaebbb8e707971ea6fffe0f323621109f61f636d22 + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.3.1": + version: 3.3.1 + resolution: "@eslint/eslintrc@npm:3.3.1" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.0" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/b0e63f3bc5cce4555f791a4e487bf999173fcf27c65e1ab6e7d63634d8a43b33c3693e79f192cbff486d7df1be8ebb2bd2edc6e70ddd486cbfa84a359a3e3b41 + languageName: node + linkType: hard + +"@eslint/js@npm:9.32.0, @eslint/js@npm:^9.32.0": + version: 9.32.0 + resolution: "@eslint/js@npm:9.32.0" + checksum: 10c0/f71e8f9146638d11fb15238279feff98801120a4d4130f1c587c4f09b024ff5ec01af1ba88e97ba6b7013488868898a668f77091300cc3d4394c7a8ed32d2667 + languageName: node + linkType: hard + +"@eslint/json@npm:^0.13.1": + version: 0.13.1 + resolution: "@eslint/json@npm:0.13.1" + dependencies: + "@eslint/core": "npm:^0.15.1" + "@eslint/plugin-kit": "npm:^0.3.4" + "@humanwhocodes/momoa": "npm:^3.3.8" + natural-compare: "npm:^1.4.0" + checksum: 10c0/8dae443777aa2d6cc8d24e3185fb718c527cecd33af65a776ebc1e2a6f7856cac6238daff03d57bbcd31c8c0ede4fa3e9f8dd69531b5eedd61d53517fe65502d + languageName: node + linkType: hard + +"@eslint/object-schema@npm:^2.1.6": + version: 2.1.6 + resolution: "@eslint/object-schema@npm:2.1.6" + checksum: 10c0/b8cdb7edea5bc5f6a96173f8d768d3554a628327af536da2fc6967a93b040f2557114d98dbcdbf389d5a7b290985ad6a9ce5babc547f36fc1fde42e674d11a56 + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.3.1": + version: 0.3.3 + resolution: "@eslint/plugin-kit@npm:0.3.3" + dependencies: + "@eslint/core": "npm:^0.15.1" + levn: "npm:^0.4.1" + checksum: 10c0/c61888eb8757abc0d25a53c1832f85521c2f347126c475eb32d3596be3505e8619e0ceddee7346d195089a2eb1633b61e6127a5772b8965a85eb9f55b8b1cebe + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.3.4": + version: 0.3.4 + resolution: "@eslint/plugin-kit@npm:0.3.4" + dependencies: + "@eslint/core": "npm:^0.15.1" + levn: "npm:^0.4.1" + checksum: 10c0/64331ca100f62a0115d10419a28059d0f377e390192163b867b9019517433d5073d10b4ec21f754fa01faf832aceb34178745924baab2957486f8bf95fd628d2 + languageName: node + linkType: hard + +"@floating-ui/core@npm:^1.7.2": + version: 1.7.2 + resolution: "@floating-ui/core@npm:1.7.2" + dependencies: + "@floating-ui/utils": "npm:^0.2.10" + checksum: 10c0/ea5909ae1bfad6d8dd60ab893c7751fd974d96b25481d13805935a089b39881b4d69425a0a84cc74c82269d8b64ca0117c472fc83e425143bee1bb21b247de9c + languageName: node + linkType: hard + +"@floating-ui/devtools@npm:0.2.1": + version: 0.2.1 + resolution: "@floating-ui/devtools@npm:0.2.1" + peerDependencies: + "@floating-ui/dom": ">=1.5.4" + checksum: 10c0/52efce754d3b705fc757a6c3a577f97c8d659aa2a3d44b71bfec1e18c5989792db14cc34a50d8c275575b0c44843b1583dde2a8fea5a9f713f2b7dc6630af8c9 + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.6.12": + version: 1.7.2 + resolution: "@floating-ui/dom@npm:1.7.2" + dependencies: + "@floating-ui/core": "npm:^1.7.2" + "@floating-ui/utils": "npm:^0.2.10" + checksum: 10c0/1b2ad76dc7fe245a1bb406cd5b64a1316f2ec642aebaa4d1928b56ced6fe71046f089e3fef9340bab234645b6333546211e363a630a9e7cfca6bf5031c39e0cb + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.2.10": + version: 0.2.10 + resolution: "@floating-ui/utils@npm:0.2.10" + checksum: 10c0/e9bc2a1730ede1ee25843937e911ab6e846a733a4488623cd353f94721b05ec2c9ec6437613a2ac9379a94c2fd40c797a2ba6fa1df2716f5ce4aa6ddb1cf9ea4 + languageName: node + linkType: hard + +"@fluentui/keyboard-keys@npm:^9.0.8": + version: 9.0.8 + resolution: "@fluentui/keyboard-keys@npm:9.0.8" + dependencies: + "@swc/helpers": "npm:^0.5.1" + checksum: 10c0/e6609f626da7e609b0b0a5bd20d197ceb80833f797e009f10869b36ce83bcbb7734be8af3e2302818cbb7236989b1e945c2156412eabb13278c6bb1bbec60c8c + languageName: node + linkType: hard + +"@fluentui/priority-overflow@npm:^9.1.15": + version: 9.1.15 + resolution: "@fluentui/priority-overflow@npm:9.1.15" + dependencies: + "@swc/helpers": "npm:^0.5.1" + checksum: 10c0/805450fd3eba130f372aa9b1e4b7c3c984c47f69b83fe05bf08b703639a981e1d19b48f714cbc80d70f470082c1fa237493a1caa30c61241838bae79b9138b74 + languageName: node + linkType: hard + +"@fluentui/react-accordion@npm:^9.8.2": + version: 9.8.2 + resolution: "@fluentui/react-accordion@npm:9.8.2" + dependencies: + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-motion-components-preview": "npm:^0.8.1" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/175661fafe0f22d6fd8768770ff7baf9bc1c3e981ff2888e66435937e70967217f470a8ed957947dc3dfcf5659451f0ebe303cc819fa05aeb704ea9789d0d930 + languageName: node + linkType: hard + +"@fluentui/react-alert@npm:9.0.0-beta.124": + version: 9.0.0-beta.124 + resolution: "@fluentui/react-alert@npm:9.0.0-beta.124" + dependencies: + "@fluentui/react-avatar": "npm:^9.6.29" + "@fluentui/react-button": "npm:^9.3.83" + "@fluentui/react-icons": "npm:^2.0.239" + "@fluentui/react-jsx-runtime": "npm:^9.0.39" + "@fluentui/react-tabster": "npm:^9.21.5" + "@fluentui/react-theme": "npm:^9.1.19" + "@fluentui/react-utilities": "npm:^9.18.10" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6f72b9c3d04ef72379cafd367045c791111b5d9c006b0d8aff9726894021e1a0ffc6e3ca8a557856acd9b82a981ca8017a18bd50e0885fbe2bcb9030d673afc4 + languageName: node + linkType: hard + +"@fluentui/react-aria@npm:^9.15.4": + version: 9.15.4 + resolution: "@fluentui/react-aria@npm:9.15.4" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-tabster": "npm:^9.26.0" + "@fluentui/react-utilities": "npm:^9.22.0" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/beb5bc9f28c6417b97e2e1f723965751f4859f674990762bfcac4057813e1619e44d2cfb2ea3d8d2f7a9301e9fa8f8fcca45a1cb44d66e715955cc8889d7a78a + languageName: node + linkType: hard + +"@fluentui/react-aria@npm:^9.16.1": + version: 9.16.1 + resolution: "@fluentui/react-aria@npm:9.16.1" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-utilities": "npm:^9.23.1" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/cb9df2f9ece1f44febf00fc6158ba683064375eb989ac0117e5808ccc4b56b64dbd60dba425b5bb23b7d5824b142fbac4e8ff2cc6853cf64fb654600a6b9ca6d + languageName: node + linkType: hard + +"@fluentui/react-avatar@npm:^9.6.29": + version: 9.9.0 + resolution: "@fluentui/react-avatar@npm:9.9.0" + dependencies: + "@fluentui/react-badge": "npm:^9.4.0" + "@fluentui/react-context-selector": "npm:^9.2.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-popover": "npm:^9.12.0" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-tabster": "npm:^9.26.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-tooltip": "npm:^9.8.0" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/ef7cc0a5835e22bd8e81604ccd35ff7ed325562f47f4e1a8f32723a4791b3eac12ad70be14179adc3d908268375453f5ab0259600bc99143015022512e027ae3 + languageName: node + linkType: hard + +"@fluentui/react-avatar@npm:^9.9.2": + version: 9.9.2 + resolution: "@fluentui/react-avatar@npm:9.9.2" + dependencies: + "@fluentui/react-badge": "npm:^9.4.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-popover": "npm:^9.12.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-tooltip": "npm:^9.8.2" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/20f35e429c822488d407fa81ba1130973ad247ebf20cb246b0c728b61d2d826fc6d5a6260fed2575fb48781711c1a8266d9e4c150e295f588c3c694209b01fc5 + languageName: node + linkType: hard + +"@fluentui/react-badge@npm:^9.4.0": + version: 9.4.0 + resolution: "@fluentui/react-badge@npm:9.4.0" + dependencies: + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/93f0be63c736b73ab7089c14e2a566f9b94e2a86a43522f64a1aaad91fb71811f292f67057f8c0d1a7f1003ccb2d2b28226cc69f8facbbe68df5406c870eae35 + languageName: node + linkType: hard + +"@fluentui/react-badge@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-badge@npm:9.4.2" + dependencies: + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/2bb0b0d4d8399451168544e6a3fc1cd26ddd180d030606b12376aea71bbe817dd63190868f055a8dd9b43ac4ba546e92d9c21955523096b24c60fb63e30298f6 + languageName: node + linkType: hard + +"@fluentui/react-breadcrumb@npm:^9.3.2": + version: 9.3.2 + resolution: "@fluentui/react-breadcrumb@npm:9.3.2" + dependencies: + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-link": "npm:^9.6.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/acd40526db4cbbdd887c7d310e04f8862a7c943e6cd3611fee3ec03393fda306de54a0850c7e20444ab1edeb68f14a741cde88091c8c934a950cb89687c2464a + languageName: node + linkType: hard + +"@fluentui/react-button@npm:^9.3.83": + version: 9.6.0 + resolution: "@fluentui/react-button@npm:9.6.0" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.15.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-tabster": "npm:^9.26.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/31fd4af8c4ebbf8b798232dfc7888c00daf87f1cad8fc183d685e9d1a01b4fab6528e6291dbb401d5e0510ea0235ac6695ec0459a5b9b01e93863a3e9f30d2b2 + languageName: node + linkType: hard + +"@fluentui/react-button@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-button@npm:9.6.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/fcdf68150bff0060519dafd8fed9e05197bd80e0ae18d73b2790193bd153b9c574021839ce8547a0be4b4822ff5137ffb7e293ed8e3cbbd1ef0a33b299ebb877 + languageName: node + linkType: hard + +"@fluentui/react-card@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-card@npm:9.4.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-text": "npm:^9.6.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/a918eae0aef6299a541e645d23bdde01a3fd46999fcb4d79add0c797eed6b0b10b5adaedf875318d22e2b523fb18565812a850e360e7e8d12dfd506bbc67637e + languageName: node + linkType: hard + +"@fluentui/react-carousel@npm:^9.8.2": + version: 9.8.2 + resolution: "@fluentui/react-carousel@npm:9.8.2" + dependencies: + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-tooltip": "npm:^9.8.2" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + embla-carousel: "npm:^8.5.1" + embla-carousel-autoplay: "npm:^8.5.1" + embla-carousel-fade: "npm:^8.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/fdcf1d0b56262acff49199202f0e1cdc15758e3339da88955b5dc0567ef1c5c3d004ad1977d8166ac6c8e059c57a5ac8cc68c2cbd1a0f9a32503f6300f1d36f5 + languageName: node + linkType: hard + +"@fluentui/react-checkbox@npm:^9.5.2": + version: 9.5.2 + resolution: "@fluentui/react-checkbox@npm:9.5.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/c8734ed8ca9e6162ebe6da2b9bc196b04bc73d6e1bc86eefc328fbf69c56cb556421706352c6dd0e7dddfafc6ce9b728cac6ccd97dc9bcdc709732fcc53632fd + languageName: node + linkType: hard + +"@fluentui/react-color-picker@npm:^9.2.2": + version: 9.2.2 + resolution: "@fluentui/react-color-picker@npm:9.2.2" + dependencies: + "@ctrl/tinycolor": "npm:^3.3.4" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/2a5c685aae1a9942cfbe2e03fa615a8232582f49cfb1d0ab721a6130ec42a5824696726c52ff6cbd76d823d99589466142c906be9dc9a7303d4c980d638ff602 + languageName: node + linkType: hard + +"@fluentui/react-combobox@npm:^9.16.2": + version: 9.16.2 + resolution: "@fluentui/react-combobox@npm:9.16.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-positioning": "npm:^9.20.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/1f83823aac443a2ef3eb47f1942194ac9687b5c6da441c7c02a990ada233424d880dabf0c927dbf74b1823ba1496c28d0d2f3afde78cfad01c78d77b06e1adb2 + languageName: node + linkType: hard + +"@fluentui/react-components@npm:^9.68.1": + version: 9.68.1 + resolution: "@fluentui/react-components@npm:9.68.1" + dependencies: + "@fluentui/react-accordion": "npm:^9.8.2" + "@fluentui/react-alert": "npm:9.0.0-beta.124" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-avatar": "npm:^9.9.2" + "@fluentui/react-badge": "npm:^9.4.2" + "@fluentui/react-breadcrumb": "npm:^9.3.2" + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-card": "npm:^9.4.2" + "@fluentui/react-carousel": "npm:^9.8.2" + "@fluentui/react-checkbox": "npm:^9.5.2" + "@fluentui/react-color-picker": "npm:^9.2.2" + "@fluentui/react-combobox": "npm:^9.16.2" + "@fluentui/react-dialog": "npm:^9.14.2" + "@fluentui/react-divider": "npm:^9.4.2" + "@fluentui/react-drawer": "npm:^9.9.2" + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-image": "npm:^9.3.2" + "@fluentui/react-infobutton": "npm:9.0.0-beta.102" + "@fluentui/react-infolabel": "npm:^9.4.2" + "@fluentui/react-input": "npm:^9.7.2" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-link": "npm:^9.6.2" + "@fluentui/react-list": "npm:^9.4.1" + "@fluentui/react-menu": "npm:^9.19.2" + "@fluentui/react-message-bar": "npm:^9.6.2" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-nav": "npm:^9.3.2" + "@fluentui/react-overflow": "npm:^9.5.2" + "@fluentui/react-persona": "npm:^9.5.2" + "@fluentui/react-popover": "npm:^9.12.2" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-positioning": "npm:^9.20.2" + "@fluentui/react-progress": "npm:^9.4.2" + "@fluentui/react-provider": "npm:^9.22.2" + "@fluentui/react-radio": "npm:^9.5.2" + "@fluentui/react-rating": "npm:^9.3.2" + "@fluentui/react-search": "npm:^9.3.2" + "@fluentui/react-select": "npm:^9.4.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-skeleton": "npm:^9.4.2" + "@fluentui/react-slider": "npm:^9.5.2" + "@fluentui/react-spinbutton": "npm:^9.5.2" + "@fluentui/react-spinner": "npm:^9.7.2" + "@fluentui/react-swatch-picker": "npm:^9.4.2" + "@fluentui/react-switch": "npm:^9.4.2" + "@fluentui/react-table": "npm:^9.18.2" + "@fluentui/react-tabs": "npm:^9.9.2" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-tag-picker": "npm:^9.7.2" + "@fluentui/react-tags": "npm:^9.7.2" + "@fluentui/react-teaching-popover": "npm:^9.6.2" + "@fluentui/react-text": "npm:^9.6.2" + "@fluentui/react-textarea": "npm:^9.6.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-toast": "npm:^9.6.2" + "@fluentui/react-toolbar": "npm:^9.6.2" + "@fluentui/react-tooltip": "npm:^9.8.2" + "@fluentui/react-tree": "npm:^9.12.2" + "@fluentui/react-utilities": "npm:^9.23.1" + "@fluentui/react-virtualizer": "npm:9.0.0-alpha.102" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/83b5539721fb9972ea05b81bf7a5d77641f3826266365ebaa30f3a13a113bb9f8dde5fa3749794da7574ebc3ec56f45d0f4435d5a060486ed9b5223f1f5325d3 + languageName: node + linkType: hard + +"@fluentui/react-context-selector@npm:^9.2.2": + version: 9.2.2 + resolution: "@fluentui/react-context-selector@npm:9.2.2" + dependencies: + "@fluentui/react-utilities": "npm:^9.22.0" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + scheduler: ">=0.19.0 <=0.23.0" + checksum: 10c0/b8b9ed1298636e98cbea26e0be434b4754ce516d570537570487327123475f0ef58176ebc2c6ee4797f0271413c23cdc89bd90ea464e197cddeaea7a1571d150 + languageName: node + linkType: hard + +"@fluentui/react-context-selector@npm:^9.2.4": + version: 9.2.4 + resolution: "@fluentui/react-context-selector@npm:9.2.4" + dependencies: + "@fluentui/react-utilities": "npm:^9.23.1" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + scheduler: ">=0.19.0 <=0.23.0" + checksum: 10c0/0436d7915bd1747a8c56b54f8542c196b9a6efe920b4f3f7d73f4cdeee03718591ba06feebef13f55960f70a441e266665a1440c78a3ac3f35009a6a7aa001f7 + languageName: node + linkType: hard + +"@fluentui/react-dialog@npm:^9.14.2": + version: 9.14.2 + resolution: "@fluentui/react-dialog@npm:9.14.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-motion-components-preview": "npm:^0.8.1" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/5a1f09094106fcf84d694a362bdc9bcb7ace81851f38110ef6ed4344999e74f5fad88c20632ac298d16f2e83dcaabad775fc23d2d69846d8d216e59a30579f4f + languageName: node + linkType: hard + +"@fluentui/react-divider@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-divider@npm:9.4.2" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/c24afc5d640069d77752d71fa70795554e1a411e22074145267508f64452e39c1bc7bad94401750d091344458d4efee193a19a175e64cce5db76d4f0391a36f3 + languageName: node + linkType: hard + +"@fluentui/react-drawer@npm:^9.9.2": + version: 9.9.2 + resolution: "@fluentui/react-drawer@npm:9.9.2" + dependencies: + "@fluentui/react-dialog": "npm:^9.14.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/aaf8b3be15c300117a757e1c5f8962eb913c454fce4a0d467d7e765e8a7b2129239c93c05e9a18140d565c4b89bbeb30165d6c1ac82fa3b020ce234119b46cf3 + languageName: node + linkType: hard + +"@fluentui/react-field@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-field@npm:9.4.2" + dependencies: + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6b2e93f49d6869f2932a6368a2489eb4fb73cfcaf51aaaa4ef5f9d81d654ef227c1abee6493f577607a517152745d570883ec320842e98dc3e7a242d4b0802df + languageName: node + linkType: hard + +"@fluentui/react-icons@npm:^2.0.237, @fluentui/react-icons@npm:^2.0.239, @fluentui/react-icons@npm:^2.0.245": + version: 2.0.306 + resolution: "@fluentui/react-icons@npm:2.0.306" + dependencies: + "@griffel/react": "npm:^1.0.0" + tslib: "npm:^2.1.0" + peerDependencies: + react: ">=16.8.0 <19.0.0" + checksum: 10c0/ec8e3c3fceea2f7ecd134f4cf6baeab2174b6ad28353b8041822df2425efdfe1fb3731b857129b2cdfa54211441eda90d2ea60042f08537d5cbc07b827c6405f + languageName: node + linkType: hard + +"@fluentui/react-icons@npm:^2.0.307": + version: 2.0.307 + resolution: "@fluentui/react-icons@npm:2.0.307" + dependencies: + "@griffel/react": "npm:^1.0.0" + tslib: "npm:^2.1.0" + peerDependencies: + react: ">=16.8.0 <19.0.0" + checksum: 10c0/a55951bc17e9a65704c8d25f71b5b9efbdbbf79fce21ba19ca4e35556de7acf5a63feb38adc0967e0f912a398ac6d8e0edfcadea0892fb5ec25f96ed92ac51cb + languageName: node + linkType: hard + +"@fluentui/react-image@npm:^9.3.2": + version: 9.3.2 + resolution: "@fluentui/react-image@npm:9.3.2" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/e25f32802a22fce09b722551b3cc35ce7ade302c117b8f446cde5b7f46ebdc7a1ddbf01ca1ea16c8f78fced49e13b580f309cecf39c6aa4c8b1938dc1bcdb32a + languageName: node + linkType: hard + +"@fluentui/react-infobutton@npm:9.0.0-beta.102": + version: 9.0.0-beta.102 + resolution: "@fluentui/react-infobutton@npm:9.0.0-beta.102" + dependencies: + "@fluentui/react-icons": "npm:^2.0.237" + "@fluentui/react-jsx-runtime": "npm:^9.0.36" + "@fluentui/react-label": "npm:^9.1.68" + "@fluentui/react-popover": "npm:^9.9.6" + "@fluentui/react-tabster": "npm:^9.21.0" + "@fluentui/react-theme": "npm:^9.1.19" + "@fluentui/react-utilities": "npm:^9.18.7" + "@griffel/react": "npm:^1.5.14" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/02d1ff8a5b21f3829970533973c9ce1ffd0d68f13a91a40df12572b50e7dff46958ca43b3d197c339bf13297e2e318bb38c44d132a5e9f0b8f88bb32776bcaec + languageName: node + linkType: hard + +"@fluentui/react-infolabel@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-infolabel@npm:9.4.2" + dependencies: + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-popover": "npm:^9.12.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/2d71d60e1ab6f0b3cf708e5d817199443005953593f071cc5dbd3ab6d0f9171552004742ecfb98e6d0a59d290432fef03b0654f9f7a5d384c6e0256a8b3f2aef + languageName: node + linkType: hard + +"@fluentui/react-input@npm:^9.7.2": + version: 9.7.2 + resolution: "@fluentui/react-input@npm:9.7.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6411f89384a3cc3af88551bfedf425182c0404e8e7bda637615a40679c4b3d0331a73e9dce8b43d1d248f6ebe43faff91c4ec871e0d0a9b38d0f21fcede674e9 + languageName: node + linkType: hard + +"@fluentui/react-jsx-runtime@npm:^9.0.36, @fluentui/react-jsx-runtime@npm:^9.0.39, @fluentui/react-jsx-runtime@npm:^9.1.2": + version: 9.1.2 + resolution: "@fluentui/react-jsx-runtime@npm:9.1.2" + dependencies: + "@fluentui/react-utilities": "npm:^9.22.0" + "@swc/helpers": "npm:^0.5.1" + react-is: "npm:^17.0.2" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + checksum: 10c0/05c79fbc079b1cc153322d35b73935be38bd3b19ad174fb1538e691359a0790286f3f9aac055f525d8b8b80c1ddd75474732781c4b45d8a69697043301f18a8a + languageName: node + linkType: hard + +"@fluentui/react-jsx-runtime@npm:^9.1.4": + version: 9.1.4 + resolution: "@fluentui/react-jsx-runtime@npm:9.1.4" + dependencies: + "@fluentui/react-utilities": "npm:^9.23.1" + "@swc/helpers": "npm:^0.5.1" + react-is: "npm:^17.0.2" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + checksum: 10c0/c0df24b02831ce77a623bc7494a7ad45b42cfeb942df23076b4aca33c5115862815e31fa435f5ad192f7f4950e89be667b94273fff1c2183e6c50d9ddf95a38d + languageName: node + linkType: hard + +"@fluentui/react-label@npm:^9.1.68": + version: 9.3.0 + resolution: "@fluentui/react-label@npm:9.3.0" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/62c115e0b7e597779cd33c20bb590b566d79f16eb1384adf8c35973482682b8c07f30d48446b590cbdd7c4f0fb7b470398223dbbd36797fcecbf039d7363315f + languageName: node + linkType: hard + +"@fluentui/react-label@npm:^9.3.2": + version: 9.3.2 + resolution: "@fluentui/react-label@npm:9.3.2" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/cfec5950b5fd228209d4d0c7f40ab64b0de3bde6e34005f68e592b44ab9961a089e8b6767191b46daf92c2bfff902ceb4167b75efe35e3495e844e1842400849 + languageName: node + linkType: hard + +"@fluentui/react-link@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-link@npm:9.6.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/b99046d6f3af17012e788e555e2ca66c41f0f6bb6e7209550281f861bf672f3afe774a9add8c8ef8a89d4ef713cb93e15d34352d2e13c1d4b9b38cadbbc4d5c5 + languageName: node + linkType: hard + +"@fluentui/react-list@npm:^9.4.1": + version: 9.4.1 + resolution: "@fluentui/react-list@npm:9.4.1" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-checkbox": "npm:^9.5.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/5cae10063ff78800be563482768c08c6c4fcb2adecb64f6ce1a0bc8399099462e62881828da119d3003349c92bc00e2bc059960180df863e72224000084c37b1 + languageName: node + linkType: hard + +"@fluentui/react-menu@npm:^9.19.2": + version: 9.19.2 + resolution: "@fluentui/react-menu@npm:9.19.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-positioning": "npm:^9.20.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/f387eb055e1b9f56dc59f5666cc683030532495a5666ed57b97a902684b59d6b48fbec963e2e4bcf7baba70ad6530de7b2690a92d47a2f7f4d878cacd7ac59c2 + languageName: node + linkType: hard + +"@fluentui/react-message-bar@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-message-bar@npm:9.6.2" + dependencies: + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-link": "npm:^9.6.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + react-transition-group: "npm:^4.4.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/f65ff5be340834dd8bb21a872fa7b6855c44b6bce0d1c77b7f689cf666bcc9c37e9a1d6a568174a39bb1d553fe415c8014c275d017ba73c84c10874a0bab9abf + languageName: node + linkType: hard + +"@fluentui/react-motion-components-preview@npm:^0.8.1": + version: 0.8.1 + resolution: "@fluentui/react-motion-components-preview@npm:0.8.1" + dependencies: + "@fluentui/react-motion": "npm:*" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/50eacadaa4e1bc0c34eff39a472da61dd6d656414214dc545555df4d69b44ac36510a7e580aab251c98373a014d90249085dc3eb94584437a3674a78fb6acdcf + languageName: node + linkType: hard + +"@fluentui/react-motion@npm:*": + version: 9.9.0 + resolution: "@fluentui/react-motion@npm:9.9.0" + dependencies: + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-utilities": "npm:^9.22.0" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/af52684d418b1a177a9652ffe04622420705703fd02f4c3696756ad014c7ce755e64ebfc98ce6a2699d1c3e7a51fdbe91037731618e9a186d0418d84dc9a9529 + languageName: node + linkType: hard + +"@fluentui/react-motion@npm:^9.10.1": + version: 9.10.1 + resolution: "@fluentui/react-motion@npm:9.10.1" + dependencies: + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-utilities": "npm:^9.23.1" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/e9ece131a93460b612ae75f21890c4684567386bd4993faec41e69d3fb145da6ca3d17bc2ff2f827abc349c1cc6eb7bb73962cf0c43b52f9d327b85e163d629f + languageName: node + linkType: hard + +"@fluentui/react-nav@npm:^9.3.2": + version: 9.3.2 + resolution: "@fluentui/react-nav@npm:9.3.2" + dependencies: + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-divider": "npm:^9.4.2" + "@fluentui/react-drawer": "npm:^9.9.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-tooltip": "npm:^9.8.2" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/85adce4ae09d132160d3f73ffb75cf5dba52e69c8a2af842a249d13a777fc7d8419629d97199d01e8fa437883c8841b7477c67e396046e9e5ca9a7a29f43a027 + languageName: node + linkType: hard + +"@fluentui/react-overflow@npm:^9.5.2": + version: 9.5.2 + resolution: "@fluentui/react-overflow@npm:9.5.2" + dependencies: + "@fluentui/priority-overflow": "npm:^9.1.15" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/bd480d712319dc14ceb1127616c6589ea0ffe6064547043651e92cdc64f9f7da6e70f3da7c860f2a9ad990cf2cc577bcd5d45ea3f716d03cf93dc0f79560fb53 + languageName: node + linkType: hard + +"@fluentui/react-persona@npm:^9.5.2": + version: 9.5.2 + resolution: "@fluentui/react-persona@npm:9.5.2" + dependencies: + "@fluentui/react-avatar": "npm:^9.9.2" + "@fluentui/react-badge": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/78d1d0da6b91539ca4bc868fdfe315647f06c6de5e613f4da9e7c31925c7c2ac620f73862e01852adaa1462215d5f3c44b13347f7c0559739379573c3a02b583 + languageName: node + linkType: hard + +"@fluentui/react-popover@npm:^9.12.0, @fluentui/react-popover@npm:^9.9.6": + version: 9.12.0 + resolution: "@fluentui/react-popover@npm:9.12.0" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.15.4" + "@fluentui/react-context-selector": "npm:^9.2.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-portal": "npm:^9.7.0" + "@fluentui/react-positioning": "npm:^9.20.0" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-tabster": "npm:^9.26.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/4d757a4b0c1cc7f0fb969ac14f5bccdc29fde7ab657563a0bea4c8250aa6f42b1542677473c08e1c5e58179d4792a7c40429ff1e1714ff6eea184e2961c5ac19 + languageName: node + linkType: hard + +"@fluentui/react-popover@npm:^9.12.2": + version: 9.12.2 + resolution: "@fluentui/react-popover@npm:9.12.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-positioning": "npm:^9.20.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/4798d1c8d29abba2f3db36262a08b32cd7ad3eb9acc52401a0802603044c48b2df0ada0f0358dac36e11f2806f1f8597f54dfed9c2275ea071ec3f94ccc66bc9 + languageName: node + linkType: hard + +"@fluentui/react-portal@npm:^9.7.0": + version: 9.7.0 + resolution: "@fluentui/react-portal@npm:9.7.0" + dependencies: + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-tabster": "npm:^9.26.0" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + use-disposable: "npm:^1.0.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6e8e3782c39ff6dc263e52f1debf14dfbb24af4075dd83acc7782093470010098953277b71f41f0a2b82d916848621b8741d39d033d337e12c196b4c9c2419c7 + languageName: node + linkType: hard + +"@fluentui/react-portal@npm:^9.7.2": + version: 9.7.2 + resolution: "@fluentui/react-portal@npm:9.7.2" + dependencies: + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + use-disposable: "npm:^1.0.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/d6f5f8d2cf5522eb6ee8aa326484463bdebfe65cd3009de206d083aa3f47b2cadfee78997c08b4a64c15b8c36b1cf3ec3e4e1d50d0f7e8388a1d2f4aaad69bac + languageName: node + linkType: hard + +"@fluentui/react-positioning@npm:^9.20.0": + version: 9.20.0 + resolution: "@fluentui/react-positioning@npm:9.20.0" + dependencies: + "@floating-ui/devtools": "npm:0.2.1" + "@floating-ui/dom": "npm:^1.6.12" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/578a83f13e0e34e2c8e3bed9bfa22a60354b9bb2872692ac54a8a2ef335d43bc69be8c7b0bbc1ebdbbf1b7601bf5398d50c5500685f1bccbccea3fa394f7fcc1 + languageName: node + linkType: hard + +"@fluentui/react-positioning@npm:^9.20.2": + version: 9.20.2 + resolution: "@fluentui/react-positioning@npm:9.20.2" + dependencies: + "@floating-ui/devtools": "npm:0.2.1" + "@floating-ui/dom": "npm:^1.6.12" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/11159df8a7306fb64bf14bcffc199f33329f2ec73b01e3d856978a6a41c3e37916b867dd6fd1676b5361b08fe430f7dd1a61e81e0f8c912169b2982f1850c7ab + languageName: node + linkType: hard + +"@fluentui/react-progress@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-progress@npm:9.4.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/0cd1868a57106f78b15ed21399860d1ca82916970c5f3e0eebea84d2e8d0de13af5035cfd2961be7826a3ac956a1f97705976ac831a82b23ac9908ace1cbdc18 + languageName: node + linkType: hard + +"@fluentui/react-provider@npm:^9.22.2": + version: 9.22.2 + resolution: "@fluentui/react-provider@npm:9.22.2" + dependencies: + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/core": "npm:^1.16.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/83215b305970011d4d378c0d6e2dc000df657ee192214f80cea24d1779be5b457ea0cc81d8184dfffeefcc02b33469a7f3e250a55fd1b2db1f6fa965f3db1e09 + languageName: node + linkType: hard + +"@fluentui/react-radio@npm:^9.5.2": + version: 9.5.2 + resolution: "@fluentui/react-radio@npm:9.5.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/b627f0d2184642ed3ac74d62a4c00a8dd1cfa2f798a60ad395d529e8dd6136fc041b40d01beafd5a52c88a05bc3d3927bec5f1af81a40124e820b1401b78d8a6 + languageName: node + linkType: hard + +"@fluentui/react-rating@npm:^9.3.2": + version: 9.3.2 + resolution: "@fluentui/react-rating@npm:9.3.2" + dependencies: + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/b3a49f5d679e5b0b267fa00ad124f1353244153363ded0072975eee42285b7795a436d4ad12943f16bd72a7682b77f7bfb04e016718d53e333c2c08b78517def + languageName: node + linkType: hard + +"@fluentui/react-search@npm:^9.3.2": + version: 9.3.2 + resolution: "@fluentui/react-search@npm:9.3.2" + dependencies: + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-input": "npm:^9.7.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/5dd89e4db77dfa29a245140627a597c3dfd273d103bf448a32b41da2409f428b54e91fa64e9d9e71698b58894b351e1a2f8de2765e4f890892506225f0143b40 + languageName: node + linkType: hard + +"@fluentui/react-select@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-select@npm:9.4.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/1f20881ff2e1efd7e6ef173bd3a5283005f0211f916a96f85c5692f2a5c9dd32bc2cbd50b06cc9fb8e3e318fad4ff40a87f5bb163454ecf705fcf5c838870bf4 + languageName: node + linkType: hard + +"@fluentui/react-shared-contexts@npm:^9.24.0": + version: 9.24.0 + resolution: "@fluentui/react-shared-contexts@npm:9.24.0" + dependencies: + "@fluentui/react-theme": "npm:^9.1.24" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + checksum: 10c0/79d65385f2dba21aa9161cc5a36d3fc11cd2e862a81dca0012ab8b8c4cd8763e637ef52095e28dbcfe52434cad17a978b43c463e94a9e1c0d5d2bd535b5084ab + languageName: node + linkType: hard + +"@fluentui/react-shared-contexts@npm:^9.24.1": + version: 9.24.1 + resolution: "@fluentui/react-shared-contexts@npm:9.24.1" + dependencies: + "@fluentui/react-theme": "npm:^9.2.0" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + checksum: 10c0/a96ed53ea94f19ac4c1794e3c7581dec9f078bf3c48860d2232a4be72e2a25577f94ae133bfdec3dcb3dbae987474192283f1e8a51284b4d79feb331bd3d783e + languageName: node + linkType: hard + +"@fluentui/react-skeleton@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-skeleton@npm:9.4.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/711fd4645b496d364851188ef57a9c0ff203b5aeecbca9e460786d09a5153abdd06f2d25fff781b8313cecf4784a8a732a90501e7c4b34b6f34247739db68312 + languageName: node + linkType: hard + +"@fluentui/react-slider@npm:^9.5.2": + version: 9.5.2 + resolution: "@fluentui/react-slider@npm:9.5.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/873af9bdcf40ace04df94293ef72c39cdb608f0ba99321010118a24de661e168ae807e435e329faa8177f0f512a5d7dd294842fe3f21298dc485e4e4feb2f27f + languageName: node + linkType: hard + +"@fluentui/react-spinbutton@npm:^9.5.2": + version: 9.5.2 + resolution: "@fluentui/react-spinbutton@npm:9.5.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/efc245cc7e598313b3b0949278b2226118b5883db43283e0eeaeaf7658ac86f627e0ff38c6c78372716b384c762df423de7cde30efd4653b8222f6145f54d9e3 + languageName: node + linkType: hard + +"@fluentui/react-spinner@npm:^9.7.2": + version: 9.7.2 + resolution: "@fluentui/react-spinner@npm:9.7.2" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6cfd31877af2cc132d714cb816efa0545c0ba5024c6ff1801065dcff705f8427593a683196f6e1f246e63b26319fe6af0d880a0f05e3d5c8acd257457883a55b + languageName: node + linkType: hard + +"@fluentui/react-swatch-picker@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-swatch-picker@npm:9.4.2" + dependencies: + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/54f61238992979a95c1904bbcfecb5d86991f15ab3aced105a2b5939b4164b9d9b073dd8f345bcf3ab897a7ec43a1e2b07105e5a6e0699ecdc776ad68a493c18 + languageName: node + linkType: hard + +"@fluentui/react-switch@npm:^9.4.2": + version: 9.4.2 + resolution: "@fluentui/react-switch@npm:9.4.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-label": "npm:^9.3.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/49803e41dd799c4411f6ad2b3abf08e88f01b28a8336ac98639e6aa1d5771ca068efc1181c64c1d5119a95402fb8c8ea28d0751b3c164b364fd29b887587506f + languageName: node + linkType: hard + +"@fluentui/react-table@npm:^9.18.2": + version: 9.18.2 + resolution: "@fluentui/react-table@npm:9.18.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-avatar": "npm:^9.9.2" + "@fluentui/react-checkbox": "npm:^9.5.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-radio": "npm:^9.5.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6914b9f17cacfa31b3f57e5cb99dce1cd6d9be6b3b6eaba04294c3bdc384be43eed10fbeb348f5e5651e9f55bf3613417c55fcaa6a879702aa0b2c79b16bcef5 + languageName: node + linkType: hard + +"@fluentui/react-tabs@npm:^9.9.2": + version: 9.9.2 + resolution: "@fluentui/react-tabs@npm:9.9.2" + dependencies: + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/617d4c054777d71e9fef66675daa7e1c8b03f98b0f86fe4ac759782b83103e859c21c3614ab455c939634522fbd620af5e5eacd7f4808a3f853fc80ee8aa8eb7 + languageName: node + linkType: hard + +"@fluentui/react-tabster@npm:^9.21.0, @fluentui/react-tabster@npm:^9.21.5, @fluentui/react-tabster@npm:^9.26.0": + version: 9.26.0 + resolution: "@fluentui/react-tabster@npm:9.26.0" + dependencies: + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + keyborg: "npm:^2.6.0" + tabster: "npm:^8.5.5" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/d6eb0840a82ffefc28b35534231c33c532f1ca8efbebefdd9ed244e89975603a849b8a2cbfd97205e88ec866b58103fdbfcf15477fbb7db92009208c9d5d369e + languageName: node + linkType: hard + +"@fluentui/react-tabster@npm:^9.26.2": + version: 9.26.2 + resolution: "@fluentui/react-tabster@npm:9.26.2" + dependencies: + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + keyborg: "npm:^2.6.0" + tabster: "npm:^8.5.5" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/39047d74995bf574d1faa882d693a505beb6fb3f8e1ba587f3f2f59078238e2426d431368972147efd50bcdd3d7ba9b6309261e9d48355277460ca728a735f14 + languageName: node + linkType: hard + +"@fluentui/react-tag-picker@npm:^9.7.2": + version: 9.7.2 + resolution: "@fluentui/react-tag-picker@npm:9.7.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-combobox": "npm:^9.16.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-positioning": "npm:^9.20.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-tags": "npm:^9.7.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/df8e4a743377d615fb24f6875568364016b8c2f179408df116839a779947a13e578b8ed9921b3a24b44dbff594538f7711f962431c01120fb6dcd18ece0ab041 + languageName: node + linkType: hard + +"@fluentui/react-tags@npm:^9.7.2": + version: 9.7.2 + resolution: "@fluentui/react-tags@npm:9.7.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-avatar": "npm:^9.9.2" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/6d045189c8eaeb399db5e3cb11edf1124129b92f3066cf186416d85ee85532cd3c0ab1708a1d4c83d2c026e09a12900d90862059e1cd6f00a625e4fdf8c5a64f + languageName: node + linkType: hard + +"@fluentui/react-teaching-popover@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-teaching-popover@npm:9.6.2" + dependencies: + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-popover": "npm:^9.12.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/80d7b47729eba31a1e0d4a39c1796994a909e069f42dd0468b9644e7db64a5f1fba70487f94c8fddae47f98d27703a7c91a60dbea1fa3e3a2185c8349095c998 + languageName: node + linkType: hard + +"@fluentui/react-text@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-text@npm:9.6.2" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/f2da9f04fd4c91e59ed0105ed230a446f197df608002aeadb309df6b5c7ee834445786a24b3d6cbe8aeecb33875dae20d64f54b91a06d11824b91f2f28782d9d + languageName: node + linkType: hard + +"@fluentui/react-textarea@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-textarea@npm:9.6.2" + dependencies: + "@fluentui/react-field": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/a95c86368d9ce4e40dd5e600349582127f358ea83f30f1c32214dac7aeada405c865e5d6deb86c6002c1f31280c792c10661bea493990a0c768cadd159a4a5ef + languageName: node + linkType: hard + +"@fluentui/react-theme@npm:^9.1.19, @fluentui/react-theme@npm:^9.1.24": + version: 9.1.24 + resolution: "@fluentui/react-theme@npm:9.1.24" + dependencies: + "@fluentui/tokens": "npm:1.0.0-alpha.21" + "@swc/helpers": "npm:^0.5.1" + checksum: 10c0/5e0e57f727e11ee054fbe554544cee3c59af51f422f785539be4a5c8cad5af3fef125a8805e0c174ec8ac1c0b1991f8c1fbf3a378082bf7a29927fa1671e8d24 + languageName: node + linkType: hard + +"@fluentui/react-theme@npm:^9.2.0": + version: 9.2.0 + resolution: "@fluentui/react-theme@npm:9.2.0" + dependencies: + "@fluentui/tokens": "npm:1.0.0-alpha.22" + "@swc/helpers": "npm:^0.5.1" + checksum: 10c0/d416c7e6c786d6ef3d6b999535edb8711503f0fa287c5f1866fd1e75b1b27f55b0041e723dec900c77f451637a24d9426cd9e5c1c997e847cb6bca7a73afc8dc + languageName: node + linkType: hard + +"@fluentui/react-toast@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-toast@npm:9.6.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-motion-components-preview": "npm:^0.8.1" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/0f97f43e0c60d5522e32fcccb18022539ca8167da7510e806bebc7a72d7b9cf77bb9a4ebc64c5c57571b68cf74d1b4b9291764dd18ecb61d38686a638df41e3d + languageName: node + linkType: hard + +"@fluentui/react-toolbar@npm:^9.6.2": + version: 9.6.2 + resolution: "@fluentui/react-toolbar@npm:9.6.2" + dependencies: + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-divider": "npm:^9.4.2" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-radio": "npm:^9.5.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/7579c95243c9480941d113fb95c96b956b69ab5f2925a733c142204e07ea8549234908b6102f7c33e5263defe470abc26144753591373317202ab50e7d021e3f + languageName: node + linkType: hard + +"@fluentui/react-tooltip@npm:^9.8.0": + version: 9.8.0 + resolution: "@fluentui/react-tooltip@npm:9.8.0" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-jsx-runtime": "npm:^9.1.2" + "@fluentui/react-portal": "npm:^9.7.0" + "@fluentui/react-positioning": "npm:^9.20.0" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@fluentui/react-tabster": "npm:^9.26.0" + "@fluentui/react-theme": "npm:^9.1.24" + "@fluentui/react-utilities": "npm:^9.22.0" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/e76af2faed81998e50b6cad614bb424d2f7eae4e5b3338a33b4229e791e740f25c19547cb81f02e8299a80c7422f05d482c48812a69961b8d4d8f58b02e7143c + languageName: node + linkType: hard + +"@fluentui/react-tooltip@npm:^9.8.2": + version: 9.8.2 + resolution: "@fluentui/react-tooltip@npm:9.8.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-portal": "npm:^9.7.2" + "@fluentui/react-positioning": "npm:^9.20.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/cf73fbde2bd7276136c45c0feae51ceca1aff37af6ab90687fa4eace38088085852c7cd755c1abc5ae8b9e2ded3550e1e38947364b60a9e448c301cf6ba11591 + languageName: node + linkType: hard + +"@fluentui/react-tree@npm:^9.12.2": + version: 9.12.2 + resolution: "@fluentui/react-tree@npm:9.12.2" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-aria": "npm:^9.16.1" + "@fluentui/react-avatar": "npm:^9.9.2" + "@fluentui/react-button": "npm:^9.6.2" + "@fluentui/react-checkbox": "npm:^9.5.2" + "@fluentui/react-context-selector": "npm:^9.2.4" + "@fluentui/react-icons": "npm:^2.0.245" + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-motion": "npm:^9.10.1" + "@fluentui/react-motion-components-preview": "npm:^0.8.1" + "@fluentui/react-radio": "npm:^9.5.2" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-tabster": "npm:^9.26.2" + "@fluentui/react-theme": "npm:^9.2.0" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/f74cc819f086bab86cb2d34d27eded8af745c6d59936e726c7954518b967017964479459a43d7e0fee552fb86c3d8512b918ca8fab78d1659532a7236e2918f3 + languageName: node + linkType: hard + +"@fluentui/react-utilities@npm:^9.18.10, @fluentui/react-utilities@npm:^9.18.7, @fluentui/react-utilities@npm:^9.22.0": + version: 9.22.0 + resolution: "@fluentui/react-utilities@npm:9.22.0" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-shared-contexts": "npm:^9.24.0" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + checksum: 10c0/3aca683bb5af9abe64d7a427b90b34aa29241202d79ec1da5f3b4e814802b4fbce8b539ba691c5f0aa58c1e9bef82f8751c0069d64325c12eb6fb8ea30545d9f + languageName: node + linkType: hard + +"@fluentui/react-utilities@npm:^9.23.1": + version: 9.23.1 + resolution: "@fluentui/react-utilities@npm:9.23.1" + dependencies: + "@fluentui/keyboard-keys": "npm:^9.0.8" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + checksum: 10c0/1830694e21ae71322b94ec4596d6129cc609b2a8cfea9dfa35ee74d83ad811cca719fcc1c250b2b79fab6a8dc34a5ab3910ee7f2683736a0d4ec2d7f84b0a3b7 + languageName: node + linkType: hard + +"@fluentui/react-virtualizer@npm:9.0.0-alpha.102": + version: 9.0.0-alpha.102 + resolution: "@fluentui/react-virtualizer@npm:9.0.0-alpha.102" + dependencies: + "@fluentui/react-jsx-runtime": "npm:^9.1.4" + "@fluentui/react-shared-contexts": "npm:^9.24.1" + "@fluentui/react-utilities": "npm:^9.23.1" + "@griffel/react": "npm:^1.5.22" + "@swc/helpers": "npm:^0.5.1" + peerDependencies: + "@types/react": ">=16.14.0 <19.0.0" + "@types/react-dom": ">=16.9.0 <19.0.0" + react: ">=16.14.0 <19.0.0" + react-dom: ">=16.14.0 <19.0.0" + checksum: 10c0/f2783b072f42ac1893a5b994e73e3b7cb829ce1892ce2db09eb816b2821691466e34b8c338a0eb673b7bc0ee88a947c0fc36a4005569d9758c8cdd1489853e42 + languageName: node + linkType: hard + +"@fluentui/tokens@npm:1.0.0-alpha.21": + version: 1.0.0-alpha.21 + resolution: "@fluentui/tokens@npm:1.0.0-alpha.21" + dependencies: + "@swc/helpers": "npm:^0.5.1" + checksum: 10c0/601f8bbd35f9f4bcf2c09b65554664336ca344e124405b6a0361181b61d401ebd81e2ac7d652d7b4643a64ab02c927e719571eadf7f76ee81a513555d75e156e + languageName: node + linkType: hard + +"@fluentui/tokens@npm:1.0.0-alpha.22": + version: 1.0.0-alpha.22 + resolution: "@fluentui/tokens@npm:1.0.0-alpha.22" + dependencies: + "@swc/helpers": "npm:^0.5.1" + checksum: 10c0/0997afc948c1fd542cbbeae76793911589401b8c119c88622deb8a0e592501e0c4f5cbc7f9bb6f5032e6ce145e35cad604252831bd5400ff0e53dba5aa77ac13 + languageName: node + linkType: hard + +"@griffel/core@npm:^1.16.0, @griffel/core@npm:^1.19.2": + version: 1.19.2 + resolution: "@griffel/core@npm:1.19.2" + dependencies: + "@emotion/hash": "npm:^0.9.0" + "@griffel/style-types": "npm:^1.3.0" + csstype: "npm:^3.1.3" + rtl-css-js: "npm:^1.16.1" + stylis: "npm:^4.2.0" + tslib: "npm:^2.1.0" + checksum: 10c0/6e086ba2132e130360e14d1ab0493f6b7e34e3dba82c928a0a438ac7f48628555845ba5366a8dfaaf163ee67b3bca1776906347d02dc0ab32c03ef85bd113ba2 + languageName: node + linkType: hard + +"@griffel/react@npm:^1.0.0, @griffel/react@npm:^1.5.14, @griffel/react@npm:^1.5.22": + version: 1.5.30 + resolution: "@griffel/react@npm:1.5.30" + dependencies: + "@griffel/core": "npm:^1.19.2" + tslib: "npm:^2.1.0" + peerDependencies: + react: ">=16.8.0 <20.0.0" + checksum: 10c0/c09ddb46e755fc183c807d4c51d93bc4dc21b6a62302597a5cde66c44d0868f91af32d020599de1fd9ae8d4095eb3fea365dcd2d4fb6b0e2bc6734f4fbcf080b + languageName: node + linkType: hard + +"@griffel/style-types@npm:^1.3.0": + version: 1.3.0 + resolution: "@griffel/style-types@npm:1.3.0" + dependencies: + csstype: "npm:^3.1.3" + checksum: 10c0/94c63ed89398e154f5ced769b25ff7e14471c991672f32f69ac27daa463f880650cb2d7d5ef7e00811e3cd3a6cd2694b9ef861d9bc294cf72838e31761d72ef8 + languageName: node + linkType: hard + +"@humanfs/core@npm:^0.19.1": + version: 0.19.1 + resolution: "@humanfs/core@npm:0.19.1" + checksum: 10c0/aa4e0152171c07879b458d0e8a704b8c3a89a8c0541726c6b65b81e84fd8b7564b5d6c633feadc6598307d34564bd53294b533491424e8e313d7ab6c7bc5dc67 + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.6": + version: 0.16.6 + resolution: "@humanfs/node@npm:0.16.6" + dependencies: + "@humanfs/core": "npm:^0.19.1" + "@humanwhocodes/retry": "npm:^0.3.0" + checksum: 10c0/8356359c9f60108ec204cbd249ecd0356667359b2524886b357617c4a7c3b6aace0fd5a369f63747b926a762a88f8a25bc066fa1778508d110195ce7686243e1 + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529 + languageName: node + linkType: hard + +"@humanwhocodes/momoa@npm:^3.3.8": + version: 3.3.9 + resolution: "@humanwhocodes/momoa@npm:3.3.9" + checksum: 10c0/591e1ef6b6e299a57006a052199a0b5f854dfbd0016c7862eb21fe7441a1426ef2ea75c006863693eb98fc46d4a60a82319546adb0277df2814b543a8dad7a7b + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.3.0": + version: 0.3.1 + resolution: "@humanwhocodes/retry@npm:0.3.1" + checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.4.2": + version: 0.4.3 + resolution: "@humanwhocodes/retry@npm:0.4.3" + checksum: 10c0/3775bb30087d4440b3f7406d5a057777d90e4b9f435af488a4923ef249e93615fb78565a85f173a186a076c7706a81d0d57d563a2624e4de2c5c9c66c486ce42 + languageName: node + linkType: hard + +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10c0/7da011805b259ec5c955f01cee903da72ad97c5e6f01ca96197267d3f33103d5b2f8a1af192140f3aa64526c593c8d098ae366c2b11f7f17645d12387c2fd420 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10c0/b4d4812f4be53afc2c5b6c545001ff7a4659af68d4484804e9d514e183d20269bb81def8682c01a22b17c4d6aed14292c8494f7d2ac664e547101c1a905aa977 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.12 + resolution: "@jridgewell/gen-mapping@npm:0.3.12" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/32f771ae2467e4d440be609581f7338d786d3d621bac3469e943b9d6d116c23c4becb36f84898a92bbf2f3c0511365c54a945a3b86a83141547a2a360a5ec0c7 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": + version: 1.5.4 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.4" + checksum: 10c0/c5aab3e6362a8dd94ad80ab90845730c825fc4c8d9cf07ebca7a2eb8a832d155d62558800fc41d42785f989ddbb21db6df004d1786e8ecb65e428ab8dff71309 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.29 + resolution: "@jridgewell/trace-mapping@npm:0.3.29" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/fb547ba31658c4d74eb17e7389f4908bf7c44cef47acb4c5baa57289daf68e6fe53c639f41f751b3923aca67010501264f70e7b49978ad1f040294b22c37b333 + languageName: node + linkType: hard + +"@lukeed/csprng@npm:^1.0.0": + version: 1.1.0 + resolution: "@lukeed/csprng@npm:1.1.0" + checksum: 10c0/5d6dcf478af732972083ab2889c294b57f1028fa13c2c240d7a4aaa079c2c75df7ef0dcbdda5419147fc6704b4adf96b2de92f1a9a72ac21c6350c4014fffe6c + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@npmcli/agent@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/agent@npm:3.0.0" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^10.0.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/fs@npm:4.0.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@pnpm/config.env-replace@npm:^1.1.0": + version: 1.1.0 + resolution: "@pnpm/config.env-replace@npm:1.1.0" + checksum: 10c0/4cfc4a5c49ab3d0c6a1f196cfd4146374768b0243d441c7de8fa7bd28eaab6290f514b98490472cc65dbd080d34369447b3e9302585e1d5c099befd7c8b5e55f + languageName: node + linkType: hard + +"@pnpm/network.ca-file@npm:^1.0.1": + version: 1.0.2 + resolution: "@pnpm/network.ca-file@npm:1.0.2" + dependencies: + graceful-fs: "npm:4.2.10" + checksum: 10c0/95f6e0e38d047aca3283550719155ce7304ac00d98911e4ab026daedaf640a63bd83e3d13e17c623fa41ac72f3801382ba21260bcce431c14fbbc06430ecb776 + languageName: node + linkType: hard + +"@pnpm/npm-conf@npm:^2.1.0": + version: 2.3.1 + resolution: "@pnpm/npm-conf@npm:2.3.1" + dependencies: + "@pnpm/config.env-replace": "npm:^1.1.0" + "@pnpm/network.ca-file": "npm:^1.0.1" + config-chain: "npm:^1.1.11" + checksum: 10c0/778a3a34ff7d6000a2594d2a9821f873f737bc56367865718b2cf0ba5d366e49689efe7975148316d7afd8e6f1dcef7d736fbb6ea7ef55caadd1dc93a36bb302 + languageName: node + linkType: hard + +"@rolldown/pluginutils@npm:1.0.0-beta.27": + version: 1.0.0-beta.27 + resolution: "@rolldown/pluginutils@npm:1.0.0-beta.27" + checksum: 10c0/9658f235b345201d4f6bfb1f32da9754ca164f892d1cb68154fe5f53c1df42bd675ecd409836dff46884a7847d6c00bdc38af870f7c81e05bba5c2645eb4ab9c + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.1.4": + version: 5.2.0 + resolution: "@rollup/pluginutils@npm:5.2.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-walker: "npm:^2.0.2" + picomatch: "npm:^4.0.2" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/794890d512751451bcc06aa112366ef47ea8f9125dac49b1abf72ff8b079518b09359de9c60a013b33266541634e765ae61839c749fae0edb59a463418665c55 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.45.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-android-arm64@npm:4.45.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.45.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.45.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.45.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-freebsd-x64@npm:4.45.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.45.1" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.45.1" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.45.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.45.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loongarch64-gnu@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.45.1" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.45.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.45.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.45.1" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.45.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.40.0": + version: 4.40.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.40.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.45.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.45.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.45.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.45.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.45.1": + version: 4.45.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.45.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@stylistic/eslint-plugin@npm:^5.2.2": + version: 5.2.2 + resolution: "@stylistic/eslint-plugin@npm:5.2.2" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.7.0" + "@typescript-eslint/types": "npm:^8.37.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" + estraverse: "npm:^5.3.0" + picomatch: "npm:^4.0.3" + peerDependencies: + eslint: ">=9.0.0" + checksum: 10c0/00c75290823340d2234d29a56caad0f66117bcc9e9ad0d8b830f19cfeb522fe5e0fb4828d1bf3cc29eda68095682d0c43e6eb8c17cd282a4872f972cf605cdbd + languageName: node + linkType: hard + +"@swc/helpers@npm:^0.5.1": + version: 0.5.17 + resolution: "@swc/helpers@npm:0.5.17" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10c0/fe1f33ebb968558c5a0c595e54f2e479e4609bff844f9ca9a2d1ffd8dd8504c26f862a11b031f48f75c95b0381c2966c3dd156e25942f90089badd24341e7dbb + languageName: node + linkType: hard + +"@types/babel__core@npm:^7.20.5": + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" + dependencies: + "@babel/parser": "npm:^7.20.7" + "@babel/types": "npm:^7.20.7" + "@types/babel__generator": "npm:*" + "@types/babel__template": "npm:*" + "@types/babel__traverse": "npm:*" + checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff + languageName: node + linkType: hard + +"@types/babel__generator@npm:*": + version: 7.27.0 + resolution: "@types/babel__generator@npm:7.27.0" + dependencies: + "@babel/types": "npm:^7.0.0" + checksum: 10c0/9f9e959a8792df208a9d048092fda7e1858bddc95c6314857a8211a99e20e6830bdeb572e3587ae8be5429e37f2a96fcf222a9f53ad232f5537764c9e13a2bbd + languageName: node + linkType: hard + +"@types/babel__template@npm:*": + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" + dependencies: + "@babel/parser": "npm:^7.1.0" + "@babel/types": "npm:^7.0.0" + checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b + languageName: node + linkType: hard + +"@types/babel__traverse@npm:*": + version: 7.20.7 + resolution: "@types/babel__traverse@npm:7.20.7" + dependencies: + "@babel/types": "npm:^7.20.7" + checksum: 10c0/5386f0af44f8746b063b87418f06129a814e16bb2686965a575e9d7376b360b088b89177778d8c426012abc43dd1a2d8ec3218bfc382280c898682746ce2ffbd + languageName: node + linkType: hard + +"@types/chrome@npm:^0.0.280": + version: 0.0.280 + resolution: "@types/chrome@npm:0.0.280" + dependencies: + "@types/filesystem": "npm:*" + "@types/har-format": "npm:*" + checksum: 10c0/fb4ee8d67d9a306c2be826da8516f9e584cfbbed5d5693b809aabaf1e5a777c728fe6239e9bfcb1065a38f80b59a4db59e5576df521cc0b9bf0db445722efe67 + languageName: node + linkType: hard + +"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 + languageName: node + linkType: hard + +"@types/filesystem@npm:*": + version: 0.0.36 + resolution: "@types/filesystem@npm:0.0.36" + dependencies: + "@types/filewriter": "npm:*" + checksum: 10c0/3ebec32f0494b0a2612187d148e9f253ff55672c53f566d9a1e6d891eb6e2372df93c252b594b2775bc53e6660c4c37fdb05dc1b26e72b60a31010da8e1f7317 + languageName: node + linkType: hard + +"@types/filewriter@npm:*": + version: 0.0.33 + resolution: "@types/filewriter@npm:0.0.33" + checksum: 10c0/363ef9a658a961ceae04f52934562e4ebdcdc3a2564dd8544f593d77113c16574938b6ba4fea0bee418c37bda0668c1e03dfedb6adf00d55853f51fb3a59247b + languageName: node + linkType: hard + +"@types/har-format@npm:*": + version: 1.2.16 + resolution: "@types/har-format@npm:1.2.16" + checksum: 10c0/77e952bc219db0c1f0588cab3b95865bc343b922e8423a76fbbd6a757c40709a256933fa415eb8fefda6ea5897c8e3dd3191bb8a82b37c13d9232467d31ae485 + languageName: node + linkType: hard + +"@types/json-schema@npm:^7.0.15": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db + languageName: node + linkType: hard + +"@types/minimatch@npm:^3.0.5": + version: 3.0.5 + resolution: "@types/minimatch@npm:3.0.5" + checksum: 10c0/a1a19ba342d6f39b569510f621ae4bbe972dc9378d15e9a5e47904c440ee60744f5b09225bc73be1c6490e3a9c938eee69eb53debf55ce1f15761201aa965f97 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 24.0.15 + resolution: "@types/node@npm:24.0.15" + dependencies: + undici-types: "npm:~7.8.0" + checksum: 10c0/39ead0c0ff25dde29357630b5eaa7dd73cf3af796dbd0f01ed439a8af01cbddfa6b68aa9d67fb3243962836170a4463ff856c47fa822250c585987f707eb42b3 + languageName: node + linkType: hard + +"@types/prop-types@npm:*": + version: 15.7.15 + resolution: "@types/prop-types@npm:15.7.15" + checksum: 10c0/b59aad1ad19bf1733cf524fd4e618196c6c7690f48ee70a327eb450a42aab8e8a063fbe59ca0a5701aebe2d92d582292c0fb845ea57474f6a15f6994b0e260b2 + languageName: node + linkType: hard + +"@types/react-dom@npm:^18.3.1": + version: 18.3.7 + resolution: "@types/react-dom@npm:18.3.7" + peerDependencies: + "@types/react": ^18.0.0 + checksum: 10c0/8bd309e2c3d1604a28a736a24f96cbadf6c05d5288cfef8883b74f4054c961b6b3a5e997fd5686e492be903c8f3380dba5ec017eff3906b1256529cd2d39603e + languageName: node + linkType: hard + +"@types/react@npm:^18.3.1": + version: 18.3.23 + resolution: "@types/react@npm:18.3.23" + dependencies: + "@types/prop-types": "npm:*" + csstype: "npm:^3.0.2" + checksum: 10c0/49331800b76572eb2992a5c44801dbf8c612a5f99c8f4e4200f06c7de6f3a6e9455c661784a6c5469df96fa45622cb4a9d0982c44e6a0d5719be5f2ef1f545ed + languageName: node + linkType: hard + +"@types/scheduler@npm:0.23.0": + version: 0.23.0 + resolution: "@types/scheduler@npm:0.23.0" + checksum: 10c0/5cf7f2ba3732b74877559eb20b19f95fcd0a20c17dcb20e75a7ca7c7369cd455aeb2d406b3ff5a38168a9750da3bad78dd20d96d11118468b78f4959b8e56090 + languageName: node + linkType: hard + +"@types/webextension-polyfill@npm:^0.12.1": + version: 0.12.3 + resolution: "@types/webextension-polyfill@npm:0.12.3" + checksum: 10c0/0f0cea5ab70a3958f7eca61168b8b431288c35ee7e8f052f5b3bf6a6aa101183aa760addd4dcf408027d558731aa8a8f107e47a373bb7c347cb7cba807fe46e6 + languageName: node + linkType: hard + +"@types/yauzl@npm:^2.9.1": + version: 2.10.3 + resolution: "@types/yauzl@npm:2.10.3" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/f1b7c1b99fef9f2fe7f1985ef7426d0cebe48cd031f1780fcdc7451eec7e31ac97028f16f50121a59bcf53086a1fc8c856fd5b7d3e00970e43d92ae27d6b43dc + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.38.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.10.0" + "@typescript-eslint/scope-manager": "npm:8.38.0" + "@typescript-eslint/type-utils": "npm:8.38.0" + "@typescript-eslint/utils": "npm:8.38.0" + "@typescript-eslint/visitor-keys": "npm:8.38.0" + graphemer: "npm:^1.4.0" + ignore: "npm:^7.0.0" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + "@typescript-eslint/parser": ^8.38.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/199b82e9f0136baecf515df7c31bfed926a7c6d4e6298f64ee1a77c8bdd7a8cb92a2ea55a5a345c9f2948a02f7be6d72530efbe803afa1892b593fbd529d0c27 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/parser@npm:8.38.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.38.0" + "@typescript-eslint/types": "npm:8.38.0" + "@typescript-eslint/typescript-estree": "npm:8.38.0" + "@typescript-eslint/visitor-keys": "npm:8.38.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/5580c2a328f0c15f85e4a0961a07584013cc0aca85fe868486187f7c92e9e3f6602c6e3dab917b092b94cd492ed40827c6f5fea42730bef88eb17592c947adf4 + languageName: node + linkType: hard + +"@typescript-eslint/project-service@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/project-service@npm:8.38.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.38.0" + "@typescript-eslint/types": "npm:^8.38.0" + debug: "npm:^4.3.4" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/87d2f55521e289bbcdc666b1f4587ee2d43039cee927310b05abaa534b528dfb1b5565c1545bb4996d7fbdf9d5a3b0aa0e6c93a8f1289e3fcfd60d246364a884 + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/scope-manager@npm:8.38.0" + dependencies: + "@typescript-eslint/types": "npm:8.38.0" + "@typescript-eslint/visitor-keys": "npm:8.38.0" + checksum: 10c0/ceaf489ea1f005afb187932a7ee363dfe1e0f7cc3db921283991e20e4c756411a5e25afbec72edd2095d6a4384f73591f4c750cf65b5eaa650c90f64ef9fe809 + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.38.0, @typescript-eslint/tsconfig-utils@npm:^8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.38.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/1a90da16bf1f7cfbd0303640a8ead64a0080f2b1d5969994bdac3b80abfa1177f0c6fbf61250bae082e72cf5014308f2f5cc98edd6510202f13420a7ffd07a84 + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/type-utils@npm:8.38.0" + dependencies: + "@typescript-eslint/types": "npm:8.38.0" + "@typescript-eslint/typescript-estree": "npm:8.38.0" + "@typescript-eslint/utils": "npm:8.38.0" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/27795c4bd0be395dda3424e57d746639c579b7522af1c17731b915298a6378fd78869e8e141526064b6047db2c86ba06444469ace19c98cda5779d06f4abd37c + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.38.0, @typescript-eslint/types@npm:^8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/types@npm:8.38.0" + checksum: 10c0/f0ac0060c98c0f3d1871f107177b6ae25a0f1846ca8bd8cfc7e1f1dd0ddce293cd8ac4a5764d6a767de3503d5d01defcd68c758cb7ba6de52f82b209a918d0d2 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:^8.37.0": + version: 8.37.0 + resolution: "@typescript-eslint/types@npm:8.37.0" + checksum: 10c0/0caa649ba242d384e935eef9badbb352a3e640c3842104a6a562af69e0f680ec8e6c0c55c069d4d714f05208f6d07811417ca6179745128a60c45fa92794e6dd + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.38.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.38.0" + "@typescript-eslint/tsconfig-utils": "npm:8.38.0" + "@typescript-eslint/types": "npm:8.38.0" + "@typescript-eslint/visitor-keys": "npm:8.38.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/00a00f6549877f4ae5c2847fa5ac52bf42cbd59a87533856c359e2746e448ed150b27a6137c92fd50c06e6a4b39e386d6b738fac97d80d05596e81ce55933230 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/utils@npm:8.38.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.7.0" + "@typescript-eslint/scope-manager": "npm:8.38.0" + "@typescript-eslint/types": "npm:8.38.0" + "@typescript-eslint/typescript-estree": "npm:8.38.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/e97a45bf44f315f9ed8c2988429e18c88e3369c9ee3227ee86446d2d49f7325abebbbc9ce801e178f676baa986d3e1fd4b5391f1640c6eb8944c123423ae43bb + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:8.38.0": + version: 8.38.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.38.0" + dependencies: + "@typescript-eslint/types": "npm:8.38.0" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/071a756e383f41a6c9e51d78c8c64bd41cd5af68b0faef5fbaec4fa5dbd65ec9e4cd610c2e2cdbe9e2facc362995f202850622b78e821609a277b5b601a1d4ec + languageName: node + linkType: hard + +"@vitejs/plugin-react@npm:^4.3.4": + version: 4.7.0 + resolution: "@vitejs/plugin-react@npm:4.7.0" + dependencies: + "@babel/core": "npm:^7.28.0" + "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1" + "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1" + "@rolldown/pluginutils": "npm:1.0.0-beta.27" + "@types/babel__core": "npm:^7.20.5" + react-refresh: "npm:^0.17.0" + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/692f23960972879485d647713663ec299c478222c96567d60285acf7c7dc5c178e71abfe9d2eefddef1eeb01514dacbc2ed68aad84628debf9c7116134734253 + languageName: node + linkType: hard + +"@webext-core/fake-browser@npm:^1.3.1": + version: 1.3.2 + resolution: "@webext-core/fake-browser@npm:1.3.2" + dependencies: + lodash.merge: "npm:^4.6.2" + checksum: 10c0/92a8608846034d1ccf3be4f0fa3bf329f33f5905f6097cf066e05f51119669c49bffcabe36e84ccb971502ace648fa2090b74a57efe114deab935c048cf3a12e + languageName: node + linkType: hard + +"@webext-core/isolated-element@npm:^1.1.2": + version: 1.1.2 + resolution: "@webext-core/isolated-element@npm:1.1.2" + dependencies: + is-potential-custom-element-name: "npm:^1.0.1" + checksum: 10c0/fa3c9f5877c650cfb8bf9324f4892d51cf8085a7a8bbd5e0b6694dfed9e82871dfd1b3a9c21c65a43085927999cb0098f3ea78f84dfe43a73c926c3b461df230 + languageName: node + linkType: hard + +"@webext-core/match-patterns@npm:^1.0.3": + version: 1.0.3 + resolution: "@webext-core/match-patterns@npm:1.0.3" + checksum: 10c0/eb219006f03420ef2221d17b07b28923078ca817cf2f4f5cf031856137c102b0c0f75ce1c18627514e767fcc672cf6a673c2bbb3a0a20c322ff3c1d6b6bb9920 + languageName: node + linkType: hard + +"@webext-core/messaging@npm:^2.3.0": + version: 2.3.0 + resolution: "@webext-core/messaging@npm:2.3.0" + dependencies: + serialize-error: "npm:^11.0.0" + uid: "npm:^2.0.2" + webextension-polyfill: "npm:^0.10.0" + checksum: 10c0/9ec51725d09c74209def5c92c699d30fe35ce323f7e21dc7d5f3e367a897e497a95aca6d8d544e054f5bdc1059e5fa77e76b2c89a33829be0c56cc9431288af2 + languageName: node + linkType: hard + +"@wxt-dev/analytics@npm:^0.4.1": + version: 0.4.1 + resolution: "@wxt-dev/analytics@npm:0.4.1" + dependencies: + ua-parser-js: "npm:^1.0.38" + peerDependencies: + wxt: ">=0.19.23" + checksum: 10c0/e430759c7f2d91152e82029a78bc8ce1ec20194637c70517b19d924889ae37a8f56ef70c2eecb4f3d9945513a9c7398109bbb98d9feb6dd44d6aead54087dfab + languageName: node + linkType: hard + +"@wxt-dev/browser@npm:^0.0.318": + version: 0.0.318 + resolution: "@wxt-dev/browser@npm:0.0.318" + dependencies: + "@types/filesystem": "npm:*" + "@types/har-format": "npm:*" + checksum: 10c0/633076cfbba831d04d27ae1e70747d572c8b06c2bae0cb5ba38ecb82b3339b6c1b2aa56a52922b598aff403c502b4ffc07f826b2ffcf2bf60036cbda8481734d + languageName: node + linkType: hard + +"@wxt-dev/i18n@npm:^0.2.4": + version: 0.2.4 + resolution: "@wxt-dev/i18n@npm:0.2.4" + dependencies: + "@wxt-dev/browser": "npm:^0.0.318" + chokidar: "npm:^4.0.3" + confbox: "npm:^0.1.8 || ^0.2.2" + fast-glob: "npm:^3.3.3" + peerDependencies: + wxt: ">=0.19.7" + peerDependenciesMeta: + wxt: + optional: true + checksum: 10c0/dc8c55ec8774fef4fd53196c473e86ee1cce07380c2734ecc687d083e74ddaeb2ac85c70d0bcc62a2efbd7966b0b7b3cc0245372dbb13a76ff67f026f931dcb2 + languageName: node + linkType: hard + +"@wxt-dev/module-react@npm:^1.1.3": + version: 1.1.3 + resolution: "@wxt-dev/module-react@npm:1.1.3" + dependencies: + "@vitejs/plugin-react": "npm:^4.3.4" + peerDependencies: + wxt: ">=0.19.16" + checksum: 10c0/77067147c54ece1d237a1128763a380a11a946066c53dd6db50e26389513b268e1f50599dc11d077935e329e0dc541bd40c4e34eb234e8589e09fb6f5ea01c6a + languageName: node + linkType: hard + +"@wxt-dev/storage@npm:^1.0.0": + version: 1.1.1 + resolution: "@wxt-dev/storage@npm:1.1.1" + dependencies: + async-mutex: "npm:^0.5.0" + dequal: "npm:^2.0.3" + checksum: 10c0/15c21fe0e5657ecf73d2118ee1a32206eb6935946b8835c065396d303461e50ca2b12830d75213fdb0dca2e998aa1ac77afff6d9cda20d7b1bce57ccf2c23f0f + languageName: node + linkType: hard + +"abbrev@npm:^3.0.0": + version: 3.0.1 + resolution: "abbrev@npm:3.0.1" + checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf + languageName: node + linkType: hard + +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: "npm:^5.0.0" + checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.2": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1 + languageName: node + linkType: hard + +"acorn@npm:^8.14.0, acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec + languageName: node + linkType: hard + +"adm-zip@npm:~0.5.x": + version: 0.5.16 + resolution: "adm-zip@npm:0.5.16" + checksum: 10c0/6f10119d4570c7ba76dcf428abb8d3f69e63f92e51f700a542b43d4c0130373dd2ddfc8f85059f12d4a843703a90c3970cfd17876844b4f3f48bf042bfa6b49f + languageName: node + linkType: hard + +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 10c0/c2c9ab7599692d594b6a161559ada307b7a624fa4c7b03e3afdb5a5e31cd0e53269115b620fcab024c5ac6a6f37fa5eb2e004f076ad30f5f7e6b8b671f7b35fe + languageName: node + linkType: hard + +"ajv@npm:^6.12.4": + version: 6.12.6 + resolution: "ajv@npm:6.12.6" + dependencies: + fast-deep-equal: "npm:^3.1.1" + fast-json-stable-stringify: "npm:^2.0.0" + json-schema-traverse: "npm:^0.4.1" + uri-js: "npm:^4.2.2" + checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 + languageName: node + linkType: hard + +"ansi-align@npm:^3.0.1": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: "npm:^4.1.0" + checksum: 10c0/ad8b755a253a1bc8234eb341e0cec68a857ab18bf97ba2bda529e86f6e30460416523e0ec58c32e5c21f0ca470d779503244892873a5895dbd0c39c788e82467 + languageName: node + linkType: hard + +"ansi-escapes@npm:^7.0.0": + version: 7.0.0 + resolution: "ansi-escapes@npm:7.0.0" + dependencies: + environment: "npm:^1.0.0" + checksum: 10c0/86e51e36fabef18c9c004af0a280573e828900641cea35134a124d2715e0c5a473494ab4ce396614505da77638ae290ff72dd8002d9747d2ee53f5d6bbe336be + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.1.0 + resolution: "ansi-regex@npm:6.1.0" + checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c + languageName: node + linkType: hard + +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "array-buffer-byte-length@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.3" + is-array-buffer: "npm:^3.0.5" + checksum: 10c0/74e1d2d996941c7a1badda9cabb7caab8c449db9086407cad8a1b71d2604cc8abf105db8ca4e02c04579ec58b7be40279ddb09aea4784832984485499f48432d + languageName: node + linkType: hard + +"array-differ@npm:^4.0.0": + version: 4.0.0 + resolution: "array-differ@npm:4.0.0" + checksum: 10c0/72c035c505a7629d2983827a16654d73db6a9a2d6340ba9d0803aed516f46a202f3b7042c5a4a57534952f7477ca5394f3b65ecb9be5192e5d269f445f066d75 + languageName: node + linkType: hard + +"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8": + version: 3.1.9 + resolution: "array-includes@npm:3.1.9" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.24.0" + es-object-atoms: "npm:^1.1.1" + get-intrinsic: "npm:^1.3.0" + is-string: "npm:^1.1.1" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/0235fa69078abeac05ac4250699c44996bc6f774a9cbe45db48674ce6bd142f09b327d31482ff75cf03344db4ea03eae23edb862d59378b484b47ed842574856 + languageName: node + linkType: hard + +"array-union@npm:^3.0.1": + version: 3.0.1 + resolution: "array-union@npm:3.0.1" + checksum: 10c0/b5271d7e5688d2d1932928b271796dbbddc422448557ab05ef6f34a9f84fb645eb855384feec6234bf59c226053a0e21b8a00b0e6cd588874b90a5c13dbeb64e + languageName: node + linkType: hard + +"array.prototype.findlast@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.findlast@npm:1.2.5" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10c0/ddc952b829145ab45411b9d6adcb51a8c17c76bf89c9dd64b52d5dffa65d033da8c076ed2e17091779e83bc892b9848188d7b4b33453c5565e65a92863cb2775 + languageName: node + linkType: hard + +"array.prototype.flat@npm:^1.3.1": + version: 1.3.3 + resolution: "array.prototype.flat@npm:1.3.3" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10c0/d90e04dfbc43bb96b3d2248576753d1fb2298d2d972e29ca7ad5ec621f0d9e16ff8074dae647eac4f31f4fb7d3f561a7ac005fb01a71f51705a13b5af06a7d8a + languageName: node + linkType: hard + +"array.prototype.flatmap@npm:^1.3.3": + version: 1.3.3 + resolution: "array.prototype.flatmap@npm:1.3.3" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10c0/ba899ea22b9dc9bf276e773e98ac84638ed5e0236de06f13d63a90b18ca9e0ec7c97d622d899796e3773930b946cd2413d098656c0c5d8cc58c6f25c21e6bd54 + languageName: node + linkType: hard + +"array.prototype.tosorted@npm:^1.1.4": + version: 1.1.4 + resolution: "array.prototype.tosorted@npm:1.1.4" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.3" + es-errors: "npm:^1.3.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10c0/eb3c4c4fc0381b0bf6dba2ea4d48d367c2827a0d4236a5718d97caaccc6b78f11f4cadf090736e86301d295a6aa4967ed45568f92ced51be8cbbacd9ca410943 + languageName: node + linkType: hard + +"arraybuffer.prototype.slice@npm:^1.0.4": + version: 1.0.4 + resolution: "arraybuffer.prototype.slice@npm:1.0.4" + dependencies: + array-buffer-byte-length: "npm:^1.0.1" + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + is-array-buffer: "npm:^3.0.4" + checksum: 10c0/2f2459caa06ae0f7f615003f9104b01f6435cc803e11bd2a655107d52a1781dc040532dc44d93026b694cc18793993246237423e13a5337e86b43ed604932c06 + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73 + languageName: node + linkType: hard + +"async-mutex@npm:^0.5.0": + version: 0.5.0 + resolution: "async-mutex@npm:0.5.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/9096e6ad6b674c894d8ddd5aa4c512b09bb05931b8746ebd634952b05685608b2b0820ed5c406e6569919ff5fe237ab3c491e6f2887d6da6b6ba906db3ee9c32 + languageName: node + linkType: hard + +"async@npm:^3.2.0": + version: 3.2.6 + resolution: "async@npm:3.2.6" + checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70 + languageName: node + linkType: hard + +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: 10c0/e329a6665512736a9bbb073e1761b4ec102f7926cce35037753146a9db9c8104f5044c1662e4a863576ce544fb8be27cd2be6bc8c1a40147d03f31eb1cfb6e8a + languageName: node + linkType: hard + +"atomically@npm:^2.0.3": + version: 2.0.3 + resolution: "atomically@npm:2.0.3" + dependencies: + stubborn-fs: "npm:^1.2.5" + when-exit: "npm:^2.1.1" + checksum: 10c0/b9008a74f590d29be947f34b7583dab32034335fedfe340ac3e6458e2e315c770d8af6f15cd3947214702c523d91b5f989498348b1ab49c197bd645dc87d7a94 + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"big-integer@npm:^1.6.44": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 10c0/9604224b4c2ab3c43c075d92da15863077a9f59e5d4205f4e7e76acd0cd47e8d469ec5e5dba8d9b32aa233951893b29329ca56ac80c20ce094b4a647a66abae0 + languageName: node + linkType: hard + +"bl@npm:^5.0.0": + version: 5.1.0 + resolution: "bl@npm:5.1.0" + dependencies: + buffer: "npm:^6.0.3" + inherits: "npm:^2.0.4" + readable-stream: "npm:^3.4.0" + checksum: 10c0/528a9c3d7d6b87af98c46f10a887654d027c28c503c7f7de87440e643f0056d7a2319a967762b8ec18150c64799d2825a277147a752a0570a7407c0b705b0d01 + languageName: node + linkType: hard + +"bluebird@npm:~3.7": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 10c0/680de03adc54ff925eaa6c7bb9a47a0690e8b5de60f4792604aae8ed618c65e6b63a7893b57ca924beaf53eee69c5af4f8314148c08124c550fe1df1add897d2 + languageName: node + linkType: hard + +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf + languageName: node + linkType: hard + +"boxen@npm:^8.0.1": + version: 8.0.1 + resolution: "boxen@npm:8.0.1" + dependencies: + ansi-align: "npm:^3.0.1" + camelcase: "npm:^8.0.0" + chalk: "npm:^5.3.0" + cli-boxes: "npm:^3.0.0" + string-width: "npm:^7.2.0" + type-fest: "npm:^4.21.0" + widest-line: "npm:^5.0.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/8c54f9797bf59eec0b44c9043d9cb5d5b2783dc673e4650235e43a5155c43334e78ec189fd410cf92056c1054aee3758279809deed115b49e68f1a1c6b3faa32 + languageName: node + linkType: hard + +"bplist-parser@npm:^0.2.0": + version: 0.2.0 + resolution: "bplist-parser@npm:0.2.0" + dependencies: + big-integer: "npm:^1.6.44" + checksum: 10c0/ce79c69e0f6efe506281e7c84e3712f7d12978991675b6e3a58a295b16f13ca81aa9b845c335614a545e0af728c8311b6aa3142af76ba1cb616af9bbac5c4a9f + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.12 + resolution: "brace-expansion@npm:1.1.12" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"browserslist@npm:^4.24.0": + version: 4.25.1 + resolution: "browserslist@npm:4.25.1" + dependencies: + caniuse-lite: "npm:^1.0.30001726" + electron-to-chromium: "npm:^1.5.173" + node-releases: "npm:^2.0.19" + update-browserslist-db: "npm:^1.1.3" + bin: + browserslist: cli.js + checksum: 10c0/acba5f0bdbd5e72dafae1e6ec79235b7bad305ed104e082ed07c34c38c7cb8ea1bc0f6be1496958c40482e40166084458fc3aee15111f15faa79212ad9081b2a + languageName: node + linkType: hard + +"buffer-crc32@npm:~0.2.3": + version: 0.2.13 + resolution: "buffer-crc32@npm:0.2.13" + checksum: 10c0/cb0a8ddf5cf4f766466db63279e47761eb825693eeba6a5a95ee4ec8cb8f81ede70aa7f9d8aeec083e781d47154290eb5d4d26b3f7a465ec57fb9e7d59c47150 + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 + languageName: node + linkType: hard + +"bundle-name@npm:^3.0.0": + version: 3.0.0 + resolution: "bundle-name@npm:3.0.0" + dependencies: + run-applescript: "npm:^5.0.0" + checksum: 10c0/57bc7f8b025d83961b04db2f1eff6a87f2363c2891f3542a4b82471ff8ebb5d484af48e9784fcdb28ef1d48bb01f03d891966dc3ef58758e46ea32d750ce40f8 + languageName: node + linkType: hard + +"bundle-name@npm:^4.1.0": + version: 4.1.0 + resolution: "bundle-name@npm:4.1.0" + dependencies: + run-applescript: "npm:^7.0.0" + checksum: 10c0/8e575981e79c2bcf14d8b1c027a3775c095d362d1382312f444a7c861b0e21513c0bd8db5bd2b16e50ba0709fa622d4eab6b53192d222120305e68359daece29 + languageName: node + linkType: hard + +"c12@npm:^3.0.2": + version: 3.1.0 + resolution: "c12@npm:3.1.0" + dependencies: + chokidar: "npm:^4.0.3" + confbox: "npm:^0.2.2" + defu: "npm:^6.1.4" + dotenv: "npm:^16.6.1" + exsolve: "npm:^1.0.7" + giget: "npm:^2.0.0" + jiti: "npm:^2.4.2" + ohash: "npm:^2.0.11" + pathe: "npm:^2.0.3" + perfect-debounce: "npm:^1.0.0" + pkg-types: "npm:^2.2.0" + rc9: "npm:^2.1.2" + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + checksum: 10c0/a84d6cb5cb6171e9b5be67388b24c6945da8bf3d37b1e4db885ceb1db019da13b9af093d8bbed6b536fd9c4a9202a2ed8c14fb15d4d94fb2e5e7c83b6c88f05b + languageName: node + linkType: hard + +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + +"cacache@npm:^19.0.1": + version: 19.0.1 + resolution: "cacache@npm:19.0.1" + dependencies: + "@npmcli/fs": "npm:^4.0.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^10.2.2" + lru-cache: "npm:^10.0.1" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^7.0.2" + ssri: "npm:^12.0.0" + tar: "npm:^7.4.3" + unique-filename: "npm:^4.0.0" + checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.0, call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": + version: 1.0.8 + resolution: "call-bind@npm:1.0.8" + dependencies: + call-bind-apply-helpers: "npm:^1.0.0" + es-define-property: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.2" + checksum: 10c0/a13819be0681d915144467741b69875ae5f4eba8961eb0bf322aab63ec87f8250eb6d6b0dcbb2e1349876412a56129ca338592b3829ef4343527f5f18a0752d4 + languageName: node + linkType: hard + +"call-bound@npm:^1.0.2, call-bound@npm:^1.0.3, call-bound@npm:^1.0.4": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + get-intrinsic: "npm:^1.3.0" + checksum: 10c0/f4796a6a0941e71c766aea672f63b72bc61234c4f4964dc6d7606e3664c307e7d77845328a8f3359ce39ddb377fed67318f9ee203dea1d47e46165dcf2917644 + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + +"camelcase@npm:^8.0.0": + version: 8.0.0 + resolution: "camelcase@npm:8.0.0" + checksum: 10c0/56c5fe072f0523c9908cdaac21d4a3b3fb0f608fb2e9ba90a60e792b95dd3bb3d1f3523873ab17d86d146e94171305f73ef619e2f538bd759675bc4a14b4bff3 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001726": + version: 1.0.30001727 + resolution: "caniuse-lite@npm:1.0.30001727" + checksum: 10c0/f0a441c05d8925d728c2d02ce23b001935f52183a3bf669556f302568fe258d1657940c7ac0b998f92bc41383e185b390279a7d779e6d96a2b47881f56400221 + languageName: node + linkType: hard + +"chalk@npm:^4.0.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + +"chalk@npm:^5.0.0, chalk@npm:^5.3.0": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef + languageName: node + linkType: hard + +"chokidar@npm:^4.0.3": + version: 4.0.3 + resolution: "chokidar@npm:4.0.3" + dependencies: + readdirp: "npm:^4.0.1" + checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6 + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"chrome-launcher@npm:1.1.2": + version: 1.1.2 + resolution: "chrome-launcher@npm:1.1.2" + dependencies: + "@types/node": "npm:*" + escape-string-regexp: "npm:^4.0.0" + is-wsl: "npm:^2.2.0" + lighthouse-logger: "npm:^2.0.1" + bin: + print-chrome-path: bin/print-chrome-path.js + checksum: 10c0/518a6cb846b7187a692c510cc9d3f4d2a87ad3e21cec5eaefb3dcb7ce72ac6ab8b5465cb90510480b9f0b077c8fc340f57e2e078e1d7719aff576595800470b2 + languageName: node + linkType: hard + +"ci-info@npm:^4.1.0": + version: 4.3.0 + resolution: "ci-info@npm:4.3.0" + checksum: 10c0/60d3dfe95d75c01454ec1cfd5108617dd598a28a2a3e148bd7e1523c1c208b5f5a3007cafcbe293e6fd0a5a310cc32217c5dc54743eeabc0a2bec80072fc055c + languageName: node + linkType: hard + +"citty@npm:^0.1.6": + version: 0.1.6 + resolution: "citty@npm:0.1.6" + dependencies: + consola: "npm:^3.2.3" + checksum: 10c0/d26ad82a9a4a8858c7e149d90b878a3eceecd4cfd3e2ed3cd5f9a06212e451fb4f8cbe0fa39a3acb1b3e8f18e22db8ee5def5829384bad50e823d4b301609b48 + languageName: node + linkType: hard + +"cli-boxes@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-boxes@npm:3.0.0" + checksum: 10c0/4db3e8fbfaf1aac4fb3a6cbe5a2d3fa048bee741a45371b906439b9ffc821c6e626b0f108bdcd3ddf126a4a319409aedcf39a0730573ff050fdd7b6731e99fb9 + languageName: node + linkType: hard + +"cli-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-cursor@npm:4.0.0" + dependencies: + restore-cursor: "npm:^4.0.0" + checksum: 10c0/e776e8c3c6727300d0539b0d25160b2bb56aed1a63942753ba1826b012f337a6f4b7ace3548402e4f2f13b5e16bfd751be672c44b203205e7eca8be94afec42c + languageName: node + linkType: hard + +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" + dependencies: + restore-cursor: "npm:^5.0.0" + checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220 + languageName: node + linkType: hard + +"cli-highlight@npm:^2.1.11": + version: 2.1.11 + resolution: "cli-highlight@npm:2.1.11" + dependencies: + chalk: "npm:^4.0.0" + highlight.js: "npm:^10.7.1" + mz: "npm:^2.4.0" + parse5: "npm:^5.1.1" + parse5-htmlparser2-tree-adapter: "npm:^6.0.0" + yargs: "npm:^16.0.0" + bin: + highlight: bin/highlight + checksum: 10c0/b5b4af3b968aa9df77eee449a400fbb659cf47c4b03a395370bd98d5554a00afaa5819b41a9a8a1ca0d37b0b896a94e57c65289b37359a25b700b1f56eb04852 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.6.1, cli-spinners@npm:^2.9.2": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 + languageName: node + linkType: hard + +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" + dependencies: + slice-ansi: "npm:^5.0.0" + string-width: "npm:^7.0.0" + checksum: 10c0/d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c + languageName: node + linkType: hard + +"cliui@npm:^7.0.2": + version: 7.0.4 + resolution: "cliui@npm:7.0.4" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.0" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/6035f5daf7383470cef82b3d3db00bec70afb3423538c50394386ffbbab135e26c3689c41791f911fa71b62d13d3863c712fdd70f0fbdffd938a1e6fd09aac00 + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + +"clone@npm:^1.0.2": + version: 1.0.4 + resolution: "clone@npm:1.0.4" + checksum: 10c0/2176952b3649293473999a95d7bebfc9dc96410f6cbd3d2595cf12fd401f63a4bf41a7adbfd3ab2ff09ed60cb9870c58c6acdd18b87767366fabfc163700f13b + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"colorette@npm:^2.0.20": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 + languageName: node + linkType: hard + +"commander@npm:2.9.0": + version: 2.9.0 + resolution: "commander@npm:2.9.0" + dependencies: + graceful-readlink: "npm:>= 1.0.0" + checksum: 10c0/56bcda1e47f453016ed25d9f300bed9e622842a5515802658adb62792fa2ff9af6ee3f9ff16e058d7b20aacc78fb3baa3e02f982414bae1fb5f198c7cb41d5ad + languageName: node + linkType: hard + +"commander@npm:^9.1.0": + version: 9.5.0 + resolution: "commander@npm:9.5.0" + checksum: 10c0/5f7784fbda2aaec39e89eb46f06a999e00224b3763dc65976e05929ec486e174fe9aac2655f03ba6a5e83875bd173be5283dc19309b7c65954701c02025b3c1d + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"concat-stream@npm:^1.4.7": + version: 1.6.2 + resolution: "concat-stream@npm:1.6.2" + dependencies: + buffer-from: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^2.2.2" + typedarray: "npm:^0.0.6" + checksum: 10c0/2e9864e18282946dabbccb212c5c7cec0702745e3671679eb8291812ca7fd12023f7d8cb36493942a62f770ac96a7f90009dc5c82ad69893438371720fa92617 + languageName: node + linkType: hard + +"confbox@npm:^0.1.8": + version: 0.1.8 + resolution: "confbox@npm:0.1.8" + checksum: 10c0/fc2c68d97cb54d885b10b63e45bd8da83a8a71459d3ecf1825143dd4c7f9f1b696b3283e07d9d12a144c1301c2ebc7842380bdf0014e55acc4ae1c9550102418 + languageName: node + linkType: hard + +"confbox@npm:^0.1.8 || ^0.2.2, confbox@npm:^0.2.2": + version: 0.2.2 + resolution: "confbox@npm:0.2.2" + checksum: 10c0/7c246588d533d31e8cdf66cb4701dff6de60f9be77ab54c0d0338e7988750ac56863cc0aca1b3f2046f45ff223a765d3e5d4977a7674485afcd37b6edf3fd129 + languageName: node + linkType: hard + +"config-chain@npm:^1.1.11": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: "npm:^1.3.4" + proto-list: "npm:~1.2.1" + checksum: 10c0/39d1df18739d7088736cc75695e98d7087aea43646351b028dfabd5508d79cf6ef4c5bcd90471f52cd87ae470d1c5490c0a8c1a292fbe6ee9ff688061ea0963e + languageName: node + linkType: hard + +"configstore@npm:^7.0.0": + version: 7.0.0 + resolution: "configstore@npm:7.0.0" + dependencies: + atomically: "npm:^2.0.3" + dot-prop: "npm:^9.0.0" + graceful-fs: "npm:^4.2.11" + xdg-basedir: "npm:^5.1.0" + checksum: 10c0/46639ddcebe94e58ab903d1bcfaddf297585469ee11fb2900975531cf6e59f495fa1324bf594d6bf13c5daf02e1110e9f0634caecc11203c52283ff26e2a4d8b + languageName: node + linkType: hard + +"consola@npm:^3.2.3, consola@npm:^3.4.0": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10c0/90a0e40abbddfd7618f8ccd63a74d88deea94e77d0e8dbbea059fa7ebebb8fbb4e2909667fe26f3a467073de1a542ebe6ae4c73a73745ac5833786759cd906c9 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + +"css-select@npm:^5.1.0": + version: 5.2.2 + resolution: "css-select@npm:5.2.2" + dependencies: + boolbase: "npm:^1.0.0" + css-what: "npm:^6.1.0" + domhandler: "npm:^5.0.2" + domutils: "npm:^3.0.1" + nth-check: "npm:^2.0.1" + checksum: 10c0/d79fffa97106007f2802589f3ed17b8c903f1c961c0fc28aa8a051eee0cbad394d8446223862efd4c1b40445a6034f626bb639cf2035b0bfc468544177593c99 + languageName: node + linkType: hard + +"css-what@npm:^6.1.0": + version: 6.2.2 + resolution: "css-what@npm:6.2.2" + checksum: 10c0/91e24c26fb977b4ccef30d7007d2668c1c10ac0154cc3f42f7304410e9594fb772aea4f30c832d2993b132ca8d99338050866476210316345ec2e7d47b248a56 + languageName: node + linkType: hard + +"cssom@npm:^0.5.0": + version: 0.5.0 + resolution: "cssom@npm:0.5.0" + checksum: 10c0/8c4121c243baf0678c65dcac29b201ff0067dfecf978de9d5c83b2ff127a8fdefd2bfd54577f5ad8c80ed7d2c8b489ae01c82023545d010c4ecb87683fb403dd + languageName: node + linkType: hard + +"csstype@npm:^3.0.2, csstype@npm:^3.1.3": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 10c0/80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 + languageName: node + linkType: hard + +"data-view-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "data-view-buffer@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.2" + checksum: 10c0/7986d40fc7979e9e6241f85db8d17060dd9a71bd53c894fa29d126061715e322a4cd47a00b0b8c710394854183d4120462b980b8554012acc1c0fa49df7ad38c + languageName: node + linkType: hard + +"data-view-byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "data-view-byte-length@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.2" + checksum: 10c0/f8a4534b5c69384d95ac18137d381f18a5cfae1f0fc1df0ef6feef51ef0d568606d970b69e02ea186c6c0f0eac77fe4e6ad96fec2569cc86c3afcc7475068c55 + languageName: node + linkType: hard + +"data-view-byte-offset@npm:^1.0.1": + version: 1.0.1 + resolution: "data-view-byte-offset@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + is-data-view: "npm:^1.0.1" + checksum: 10c0/fa7aa40078025b7810dcffc16df02c480573b7b53ef1205aa6a61533011005c1890e5ba17018c692ce7c900212b547262d33279fde801ad9843edc0863bf78c4 + languageName: node + linkType: hard + +"debounce@npm:1.2.1": + version: 1.2.1 + resolution: "debounce@npm:1.2.1" + checksum: 10c0/6c9320aa0973fc42050814621a7a8a78146c1975799b5b3cc1becf1f77ba9a5aa583987884230da0842a03f385def452fad5d60db97c3d1c8b824e38a8edf500 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.1": + version: 4.4.1 + resolution: "debug@npm:4.4.1" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55 + languageName: node + linkType: hard + +"debug@npm:^2.6.9": + version: 2.6.9 + resolution: "debug@npm:2.6.9" + dependencies: + ms: "npm:2.0.0" + checksum: 10c0/121908fb839f7801180b69a7e218a40b5a0b718813b886b7d6bdb82001b931c938e2941d1e4450f33a1b1df1da653f5f7a0440c197f29fbf8a6e9d45ff6ef589 + languageName: node + linkType: hard + +"debug@npm:~4.3.1": + version: 4.3.7 + resolution: "debug@npm:4.3.7" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b + languageName: node + linkType: hard + +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c + languageName: node + linkType: hard + +"default-browser-id@npm:^3.0.0": + version: 3.0.0 + resolution: "default-browser-id@npm:3.0.0" + dependencies: + bplist-parser: "npm:^0.2.0" + untildify: "npm:^4.0.0" + checksum: 10c0/8db3ab882eb3e1e8b59d84c8641320e6c66d8eeb17eb4bb848b7dd549b1e6fd313988e4a13542e95fbaeff03f6e9dedc5ad191ad4df7996187753eb0d45c00b7 + languageName: node + linkType: hard + +"default-browser-id@npm:^5.0.0": + version: 5.0.0 + resolution: "default-browser-id@npm:5.0.0" + checksum: 10c0/957fb886502594c8e645e812dfe93dba30ed82e8460d20ce39c53c5b0f3e2afb6ceaec2249083b90bdfbb4cb0f34e1f73fde3d68cac00becdbcfd894156b5ead + languageName: node + linkType: hard + +"default-browser@npm:^4.0.0": + version: 4.0.0 + resolution: "default-browser@npm:4.0.0" + dependencies: + bundle-name: "npm:^3.0.0" + default-browser-id: "npm:^3.0.0" + execa: "npm:^7.1.1" + titleize: "npm:^3.0.0" + checksum: 10c0/7c8848badc139ecf9d878e562bc4e7ab4301e51ba120b24d8dcb14739c30152115cc612065ac3ab73c02aace4afa29db5a044257b2f0cf234f16e3a58f6c925e + languageName: node + linkType: hard + +"default-browser@npm:^5.2.1": + version: 5.2.1 + resolution: "default-browser@npm:5.2.1" + dependencies: + bundle-name: "npm:^4.1.0" + default-browser-id: "npm:^5.0.0" + checksum: 10c0/73f17dc3c58026c55bb5538749597db31f9561c0193cd98604144b704a981c95a466f8ecc3c2db63d8bfd04fb0d426904834cfc91ae510c6aeb97e13c5167c4d + languageName: node + linkType: hard + +"defaults@npm:^1.0.3": + version: 1.0.4 + resolution: "defaults@npm:1.0.4" + dependencies: + clone: "npm:^1.0.2" + checksum: 10c0/9cfbe498f5c8ed733775db62dfd585780387d93c17477949e1670bfcfb9346e0281ce8c4bf9f4ac1fc0f9b851113bd6dc9e41182ea1644ccd97de639fa13c35a + languageName: node + linkType: hard + +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + +"define-lazy-prop@npm:^2.0.0": + version: 2.0.0 + resolution: "define-lazy-prop@npm:2.0.0" + checksum: 10c0/db6c63864a9d3b7dc9def55d52764968a5af296de87c1b2cc71d8be8142e445208071953649e0386a8cc37cfcf9a2067a47207f1eb9ff250c2a269658fdae422 + languageName: node + linkType: hard + +"define-lazy-prop@npm:^3.0.0": + version: 3.0.0 + resolution: "define-lazy-prop@npm:3.0.0" + checksum: 10c0/5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 + languageName: node + linkType: hard + +"define-properties@npm:^1.1.3, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" + dependencies: + define-data-property: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 + languageName: node + linkType: hard + +"defu@npm:^6.1.4": + version: 6.1.4 + resolution: "defu@npm:6.1.4" + checksum: 10c0/2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5 + languageName: node + linkType: hard + +"dequal@npm:^2.0.3": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 10c0/f98860cdf58b64991ae10205137c0e97d384c3a4edc7f807603887b7c4b850af1224a33d88012009f150861cbee4fa2d322c4cc04b9313bee312e47f6ecaa888 + languageName: node + linkType: hard + +"destr@npm:^2.0.3": + version: 2.0.5 + resolution: "destr@npm:2.0.5" + checksum: 10c0/efabffe7312a45ad90d79975376be958c50069f1156b94c181199763a7f971e113bd92227c26b94a169c71ca7dbc13583b7e96e5164743969fc79e1ff153e646 + languageName: node + linkType: hard + +"doctrine@npm:^2.1.0": + version: 2.1.0 + resolution: "doctrine@npm:2.1.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10c0/b6416aaff1f380bf56c3b552f31fdf7a69b45689368deca72d28636f41c16bb28ec3ebc40ace97db4c1afc0ceeb8120e8492fe0046841c94c2933b2e30a7d5ac + languageName: node + linkType: hard + +"dom-helpers@npm:^5.0.1": + version: 5.2.1 + resolution: "dom-helpers@npm:5.2.1" + dependencies: + "@babel/runtime": "npm:^7.8.7" + csstype: "npm:^3.0.2" + checksum: 10c0/f735074d66dd759b36b158fa26e9d00c9388ee0e8c9b16af941c38f014a37fc80782de83afefd621681b19ac0501034b4f1c4a3bff5caa1b8667f0212b5e124c + languageName: node + linkType: hard + +"dom-serializer@npm:^2.0.0": + version: 2.0.0 + resolution: "dom-serializer@npm:2.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.2" + entities: "npm:^4.2.0" + checksum: 10c0/d5ae2b7110ca3746b3643d3ef60ef823f5f078667baf530cec096433f1627ec4b6fa8c072f09d079d7cda915fd2c7bc1b7b935681e9b09e591e1e15f4040b8e2 + languageName: node + linkType: hard + +"domelementtype@npm:^2.3.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9 + languageName: node + linkType: hard + +"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10c0/bba1e5932b3e196ad6862286d76adc89a0dbf0c773e5ced1eb01f9af930c50093a084eff14b8de5ea60b895c56a04d5de8bbc4930c5543d029091916770b2d2a + languageName: node + linkType: hard + +"domutils@npm:^3.0.1, domutils@npm:^3.2.1": + version: 3.2.2 + resolution: "domutils@npm:3.2.2" + dependencies: + dom-serializer: "npm:^2.0.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + checksum: 10c0/47938f473b987ea71cd59e59626eb8666d3aa8feba5266e45527f3b636c7883cca7e582d901531961f742c519d7514636b7973353b648762b2e3bedbf235fada + languageName: node + linkType: hard + +"dot-prop@npm:^9.0.0": + version: 9.0.0 + resolution: "dot-prop@npm:9.0.0" + dependencies: + type-fest: "npm:^4.18.2" + checksum: 10c0/4bac49a2f559156811862ac92813906f70529c50da918eaab81b38dd869743c667d578e183607f5ae11e8ae2a02e43e98e32c8a37bc4cae76b04d5b576e3112f + languageName: node + linkType: hard + +"dotenv-expand@npm:^12.0.1": + version: 12.0.2 + resolution: "dotenv-expand@npm:12.0.2" + dependencies: + dotenv: "npm:^16.4.5" + checksum: 10c0/c96fd1b74d1dbb72fda6ad5df4f0ae83cba438c8090231c4da4344f8aa0448d67eb70f7fcb317c8a9bf44191545b393e8cdc1fa9f301a66b6414a22d1135f81e + languageName: node + linkType: hard + +"dotenv@npm:^16.3.1, dotenv@npm:^16.4.5, dotenv@npm:^16.6.1": + version: 16.6.1 + resolution: "dotenv@npm:16.6.1" + checksum: 10c0/15ce56608326ea0d1d9414a5c8ee6dcf0fffc79d2c16422b4ac2268e7e2d76ff5a572d37ffe747c377de12005f14b3cc22361e79fc7f1061cce81f77d2c973dc + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.173": + version: 1.5.187 + resolution: "electron-to-chromium@npm:1.5.187" + checksum: 10c0/c83153010b786deac926fb128e0e0a68202312a25b4896892bcf166acb2ccb6357ec918ba38d44e16294cd85bf7e345198e1809e1848295221a8753beb658241 + languageName: node + linkType: hard + +"embla-carousel-autoplay@npm:^8.5.1": + version: 8.6.0 + resolution: "embla-carousel-autoplay@npm:8.6.0" + peerDependencies: + embla-carousel: 8.6.0 + checksum: 10c0/d7e5cb79c1474b3d27621302e63dedf783a281ae0464b21b87183d58103f938d22be9f6a4735de4b1b19806606006da43f6151497df91c3edbbc99a4dae4a94f + languageName: node + linkType: hard + +"embla-carousel-fade@npm:^8.5.1": + version: 8.6.0 + resolution: "embla-carousel-fade@npm:8.6.0" + peerDependencies: + embla-carousel: 8.6.0 + checksum: 10c0/00b44b6bd8df7d6854c7ca00e46a25996d58904c790043067e85210b29972fe0a602f0d36e98a27043fd92c229d9f46069be178b8462b7fcedfe13b14d3009b5 + languageName: node + linkType: hard + +"embla-carousel@npm:^8.5.1": + version: 8.6.0 + resolution: "embla-carousel@npm:8.6.0" + checksum: 10c0/f4c598e7be28b70340d31ffd2bebb2472db370b0c81d9b089bf9555cf618695f35dc4a0694565c994c9ab972731123063f945aa09ff485df0df761d79c6a08ef + languageName: node + linkType: hard + +"emoji-regex@npm:^10.3.0": + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 + languageName: node + linkType: hard + +"end-of-stream@npm:^1.1.0": + version: 1.4.5 + resolution: "end-of-stream@npm:1.4.5" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/b0701c92a10b89afb1cb45bf54a5292c6f008d744eb4382fa559d54775ff31617d1d7bc3ef617575f552e24fad2c7c1a1835948c66b3f3a4be0a6c1f35c883d8 + languageName: node + linkType: hard + +"entities@npm:^4.2.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 + languageName: node + linkType: hard + +"entities@npm:^6.0.0": + version: 6.0.1 + resolution: "entities@npm:6.0.1" + checksum: 10c0/ed836ddac5acb34341094eb495185d527bd70e8632b6c0d59548cbfa23defdbae70b96f9a405c82904efa421230b5b3fd2283752447d737beffd3f3e6ee74414 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"environment@npm:^1.0.0": + version: 1.1.0 + resolution: "environment@npm:1.1.0" + checksum: 10c0/fb26434b0b581ab397039e51ff3c92b34924a98b2039dcb47e41b7bca577b9dbf134a8eadb364415c74464b682e2d3afe1a4c0eb9873dc44ea814c5d3103331d + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 + languageName: node + linkType: hard + +"error-ex@npm:^1.3.2": + version: 1.3.2 + resolution: "error-ex@npm:1.3.2" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10c0/ba827f89369b4c93382cfca5a264d059dfefdaa56ecc5e338ffa58a6471f5ed93b71a20add1d52290a4873d92381174382658c885ac1a2305f7baca363ce9cce + languageName: node + linkType: hard + +"es-abstract@npm:^1.17.5, es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.3, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.0": + version: 1.24.0 + resolution: "es-abstract@npm:1.24.0" + dependencies: + array-buffer-byte-length: "npm:^1.0.2" + arraybuffer.prototype.slice: "npm:^1.0.4" + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + data-view-buffer: "npm:^1.0.2" + data-view-byte-length: "npm:^1.0.2" + data-view-byte-offset: "npm:^1.0.1" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + es-set-tostringtag: "npm:^2.1.0" + es-to-primitive: "npm:^1.3.0" + function.prototype.name: "npm:^1.1.8" + get-intrinsic: "npm:^1.3.0" + get-proto: "npm:^1.0.1" + get-symbol-description: "npm:^1.1.0" + globalthis: "npm:^1.0.4" + gopd: "npm:^1.2.0" + has-property-descriptors: "npm:^1.0.2" + has-proto: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + internal-slot: "npm:^1.1.0" + is-array-buffer: "npm:^3.0.5" + is-callable: "npm:^1.2.7" + is-data-view: "npm:^1.0.2" + is-negative-zero: "npm:^2.0.3" + is-regex: "npm:^1.2.1" + is-set: "npm:^2.0.3" + is-shared-array-buffer: "npm:^1.0.4" + is-string: "npm:^1.1.1" + is-typed-array: "npm:^1.1.15" + is-weakref: "npm:^1.1.1" + math-intrinsics: "npm:^1.1.0" + object-inspect: "npm:^1.13.4" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.7" + own-keys: "npm:^1.0.1" + regexp.prototype.flags: "npm:^1.5.4" + safe-array-concat: "npm:^1.1.3" + safe-push-apply: "npm:^1.0.0" + safe-regex-test: "npm:^1.1.0" + set-proto: "npm:^1.0.0" + stop-iteration-iterator: "npm:^1.1.0" + string.prototype.trim: "npm:^1.2.10" + string.prototype.trimend: "npm:^1.0.9" + string.prototype.trimstart: "npm:^1.0.8" + typed-array-buffer: "npm:^1.0.3" + typed-array-byte-length: "npm:^1.0.3" + typed-array-byte-offset: "npm:^1.0.4" + typed-array-length: "npm:^1.0.7" + unbox-primitive: "npm:^1.1.0" + which-typed-array: "npm:^1.1.19" + checksum: 10c0/b256e897be32df5d382786ce8cce29a1dd8c97efbab77a26609bd70f2ed29fbcfc7a31758cb07488d532e7ccccdfca76c1118f2afe5a424cdc05ca007867c318 + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-iterator-helpers@npm:^1.2.1": + version: 1.2.1 + resolution: "es-iterator-helpers@npm:1.2.1" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.6" + es-errors: "npm:^1.3.0" + es-set-tostringtag: "npm:^2.0.3" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.6" + globalthis: "npm:^1.0.4" + gopd: "npm:^1.2.0" + has-property-descriptors: "npm:^1.0.2" + has-proto: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + internal-slot: "npm:^1.1.0" + iterator.prototype: "npm:^1.1.4" + safe-array-concat: "npm:^1.1.3" + checksum: 10c0/97e3125ca472d82d8aceea11b790397648b52c26d8768ea1c1ee6309ef45a8755bb63225a43f3150c7591cffc17caf5752459f1e70d583b4184370a8f04ebd2f + languageName: node + linkType: hard + +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.0.3, es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af + languageName: node + linkType: hard + +"es-shim-unscopables@npm:^1.0.2": + version: 1.1.0 + resolution: "es-shim-unscopables@npm:1.1.0" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10c0/1b9702c8a1823fc3ef39035a4e958802cf294dd21e917397c561d0b3e195f383b978359816b1732d02b255ccf63e1e4815da0065b95db8d7c992037be3bbbcdb + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.3.0": + version: 1.3.0 + resolution: "es-to-primitive@npm:1.3.0" + dependencies: + is-callable: "npm:^1.2.7" + is-date-object: "npm:^1.0.5" + is-symbol: "npm:^1.0.4" + checksum: 10c0/c7e87467abb0b438639baa8139f701a06537d2b9bc758f23e8622c3b42fd0fdb5bde0f535686119e446dd9d5e4c0f238af4e14960f4771877cf818d023f6730b + languageName: node + linkType: hard + +"es6-error@npm:4.1.1": + version: 4.1.1 + resolution: "es6-error@npm:4.1.1" + checksum: 10c0/357663fb1e845c047d548c3d30f86e005db71e122678f4184ced0693f634688c3f3ef2d7de7d4af732f734de01f528b05954e270f06aa7d133679fb9fe6600ef + languageName: node + linkType: hard + +"esbuild@npm:^0.25.0": + version: 0.25.8 + resolution: "esbuild@npm:0.25.8" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.8" + "@esbuild/android-arm": "npm:0.25.8" + "@esbuild/android-arm64": "npm:0.25.8" + "@esbuild/android-x64": "npm:0.25.8" + "@esbuild/darwin-arm64": "npm:0.25.8" + "@esbuild/darwin-x64": "npm:0.25.8" + "@esbuild/freebsd-arm64": "npm:0.25.8" + "@esbuild/freebsd-x64": "npm:0.25.8" + "@esbuild/linux-arm": "npm:0.25.8" + "@esbuild/linux-arm64": "npm:0.25.8" + "@esbuild/linux-ia32": "npm:0.25.8" + "@esbuild/linux-loong64": "npm:0.25.8" + "@esbuild/linux-mips64el": "npm:0.25.8" + "@esbuild/linux-ppc64": "npm:0.25.8" + "@esbuild/linux-riscv64": "npm:0.25.8" + "@esbuild/linux-s390x": "npm:0.25.8" + "@esbuild/linux-x64": "npm:0.25.8" + "@esbuild/netbsd-arm64": "npm:0.25.8" + "@esbuild/netbsd-x64": "npm:0.25.8" + "@esbuild/openbsd-arm64": "npm:0.25.8" + "@esbuild/openbsd-x64": "npm:0.25.8" + "@esbuild/openharmony-arm64": "npm:0.25.8" + "@esbuild/sunos-x64": "npm:0.25.8" + "@esbuild/win32-arm64": "npm:0.25.8" + "@esbuild/win32-ia32": "npm:0.25.8" + "@esbuild/win32-x64": "npm:0.25.8" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/43747a25e120d5dd9ce75c82f57306580d715647c8db4f4a0a84e73b04cf16c27572d3937d3cfb95d5ac3266a4d1bbd3913e3d76ae719693516289fc86f8a5fd + languageName: node + linkType: hard + +"escalade@npm:^3.1.1, escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"escape-goat@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-goat@npm:4.0.0" + checksum: 10c0/9d2a8314e2370f2dd9436d177f6b3b1773525df8f895c8f3e1acb716f5fd6b10b336cb1cd9862d4709b36eb207dbe33664838deca9c6d55b8371be4eebb972f6 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^5.0.0": + version: 5.0.0 + resolution: "escape-string-regexp@npm:5.0.0" + checksum: 10c0/6366f474c6f37a802800a435232395e04e9885919873e382b157ab7e8f0feb8fed71497f84a6f6a81a49aab41815522f5839112bd38026d203aea0c91622df95 + languageName: node + linkType: hard + +"eslint-plugin-react@npm:^7.37.5": + version: 7.37.5 + resolution: "eslint-plugin-react@npm:7.37.5" + dependencies: + array-includes: "npm:^3.1.8" + array.prototype.findlast: "npm:^1.2.5" + array.prototype.flatmap: "npm:^1.3.3" + array.prototype.tosorted: "npm:^1.1.4" + doctrine: "npm:^2.1.0" + es-iterator-helpers: "npm:^1.2.1" + estraverse: "npm:^5.3.0" + hasown: "npm:^2.0.2" + jsx-ast-utils: "npm:^2.4.1 || ^3.0.0" + minimatch: "npm:^3.1.2" + object.entries: "npm:^1.1.9" + object.fromentries: "npm:^2.0.8" + object.values: "npm:^1.2.1" + prop-types: "npm:^15.8.1" + resolve: "npm:^2.0.0-next.5" + semver: "npm:^6.3.1" + string.prototype.matchall: "npm:^4.0.12" + string.prototype.repeat: "npm:^1.0.0" + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + checksum: 10c0/c850bfd556291d4d9234f5ca38db1436924a1013627c8ab1853f77cac73ec19b020e861e6c7b783436a48b6ffcdfba4547598235a37ad4611b6739f65fd8ad57 + languageName: node + linkType: hard + +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 + languageName: node + linkType: hard + +"eslint@npm:^9.32.0": + version: 9.32.0 + resolution: "eslint@npm:9.32.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.21.0" + "@eslint/config-helpers": "npm:^0.3.0" + "@eslint/core": "npm:^0.15.0" + "@eslint/eslintrc": "npm:^3.3.1" + "@eslint/js": "npm:9.32.0" + "@eslint/plugin-kit": "npm:^0.3.4" + "@humanfs/node": "npm:^0.16.6" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@humanwhocodes/retry": "npm:^0.4.2" + "@types/estree": "npm:^1.0.6" + "@types/json-schema": "npm:^7.0.15" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.3.2" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" + esquery: "npm:^1.5.0" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^8.0.0" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: 10c0/e8a23924ec5f8b62e95483002ca25db74e25c23bd9c6d98a9f656ee32f820169bee3bfdf548ec728b16694f198b3db857d85a49210ee4a035242711d08fdc602 + languageName: node + linkType: hard + +"espree@npm:^10.0.1, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" + dependencies: + acorn: "npm:^8.15.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b + languageName: node + linkType: hard + +"esquery@npm:^1.5.0": + version: 1.6.0 + resolution: "esquery@npm:1.6.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10c0/cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: "npm:^5.2.0" + checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5 + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0, estraverse@npm:^5.3.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107 + languageName: node + linkType: hard + +"estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af + languageName: node + linkType: hard + +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b + languageName: node + linkType: hard + +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 10c0/4ba5c00c506e6c786b4d6262cfbce90ddc14c10d4667e5c83ae993c9de88aa856033994dd2b35b83e8dc1170e224e66a319fa80adc4c32adcd2379bbc75da814 + languageName: node + linkType: hard + +"events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: 10c0/d6b6f2adbccbcda74ddbab52ed07db727ef52e31a61ed26db9feb7dc62af7fc8e060defa65e5f8af9449b86b52cc1a1f6a79f2eafcf4e62add2b7a1fa4a432f6 + languageName: node + linkType: hard + +"execa@npm:^5.0.0": + version: 5.1.1 + resolution: "execa@npm:5.1.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^6.0.0" + human-signals: "npm:^2.1.0" + is-stream: "npm:^2.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^4.0.1" + onetime: "npm:^5.1.2" + signal-exit: "npm:^3.0.3" + strip-final-newline: "npm:^2.0.0" + checksum: 10c0/c8e615235e8de4c5addf2fa4c3da3e3aa59ce975a3e83533b4f6a71750fb816a2e79610dc5f1799b6e28976c9ae86747a36a606655bf8cb414a74d8d507b304f + languageName: node + linkType: hard + +"execa@npm:^7.1.1": + version: 7.2.0 + resolution: "execa@npm:7.2.0" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^6.0.1" + human-signals: "npm:^4.3.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^3.0.7" + strip-final-newline: "npm:^3.0.0" + checksum: 10c0/098cd6a1bc26d509e5402c43f4971736450b84d058391820c6f237aeec6436963e006fd8423c9722f148c53da86aa50045929c7278b5522197dff802d10f9885 + languageName: node + linkType: hard + +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.2 + resolution: "exponential-backoff@npm:3.1.2" + checksum: 10c0/d9d3e1eafa21b78464297df91f1776f7fbaa3d5e3f7f0995648ca5b89c069d17055033817348d9f4a43d1c20b0eab84f75af6991751e839df53e4dfd6f22e844 + languageName: node + linkType: hard + +"exsolve@npm:^1.0.7": + version: 1.0.7 + resolution: "exsolve@npm:1.0.7" + checksum: 10c0/4479369d0bd84bb7e0b4f5d9bc18d26a89b6dbbbccd73f9d383d14892ef78ddbe159e01781055342f83dc00ebe90044036daf17ddf55cc21e2cac6609aa15631 + languageName: node + linkType: hard + +"extract-zip@npm:^2.0.1": + version: 2.0.1 + resolution: "extract-zip@npm:2.0.1" + dependencies: + "@types/yauzl": "npm:^2.9.1" + debug: "npm:^4.1.1" + get-stream: "npm:^5.1.0" + yauzl: "npm:^2.10.0" + dependenciesMeta: + "@types/yauzl": + optional: true + bin: + extract-zip: cli.js + checksum: 10c0/9afbd46854aa15a857ae0341a63a92743a7b89c8779102c3b4ffc207516b2019337353962309f85c66ee3d9092202a83cdc26dbf449a11981272038443974aee + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 + languageName: node + linkType: hard + +"fast-glob@npm:^3.3.2, fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 + languageName: node + linkType: hard + +"fast-redact@npm:^3.1.1": + version: 3.5.0 + resolution: "fast-redact@npm:3.5.0" + checksum: 10c0/7e2ce4aad6e7535e0775bf12bd3e4f2e53d8051d8b630e0fa9e67f68cb0b0e6070d2f7a94b1d0522ef07e32f7c7cda5755e2b677a6538f1e9070ca053c42343a + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.19.1 + resolution: "fastq@npm:1.19.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/ebc6e50ac7048daaeb8e64522a1ea7a26e92b3cee5cd1c7f2316cdca81ba543aa40a136b53891446ea5c3a67ec215fbaca87ad405f102dd97012f62916905630 + languageName: node + linkType: hard + +"fd-slicer@npm:~1.1.0": + version: 1.1.0 + resolution: "fd-slicer@npm:1.1.0" + dependencies: + pend: "npm:~1.2.0" + checksum: 10c0/304dd70270298e3ffe3bcc05e6f7ade2511acc278bc52d025f8918b48b6aa3b77f10361bddfadfe2a28163f7af7adbdce96f4d22c31b2f648ba2901f0c5fc20e + languageName: node + linkType: hard + +"fdir@npm:^6.4.4, fdir@npm:^6.4.6": + version: 6.4.6 + resolution: "fdir@npm:6.4.6" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9 + languageName: node + linkType: hard + +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" + dependencies: + flat-cache: "npm:^4.0.0" + checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 + languageName: node + linkType: hard + +"filesize@npm:^10.1.6": + version: 10.1.6 + resolution: "filesize@npm:10.1.6" + checksum: 10c0/9a196d64da4e947b8c0d294be09a3dfa7a634434a1fc5fb3465f1c9acc1237ea0363f245ba6e24477ea612754d942bc964d86e0e500905a72e9e0e17ae1bbdbc + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: "npm:^6.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a + languageName: node + linkType: hard + +"firefox-profile@npm:4.7.0": + version: 4.7.0 + resolution: "firefox-profile@npm:4.7.0" + dependencies: + adm-zip: "npm:~0.5.x" + fs-extra: "npm:^11.2.0" + ini: "npm:^4.1.3" + minimist: "npm:^1.2.8" + xml2js: "npm:^0.6.2" + bin: + firefox-profile: lib/cli.js + checksum: 10c0/032949c923336f843015757f1aba90d19b9f0d7277ba91705958a5579d55c98a424700388ac263a1dc67d6a942403e25090443703ff0f38347a20e9dbc40e1a3 + languageName: node + linkType: hard + +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.4" + checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.3.3 + resolution: "flatted@npm:3.3.3" + checksum: 10c0/e957a1c6b0254aa15b8cce8533e24165abd98fadc98575db082b786b5da1b7d72062b81bfdcd1da2f4d46b6ed93bec2434e62333e9b4261d79ef2e75a10dd538 + languageName: node + linkType: hard + +"for-each@npm:^0.3.3, for-each@npm:^0.3.5": + version: 0.3.5 + resolution: "for-each@npm:0.3.5" + dependencies: + is-callable: "npm:^1.2.7" + checksum: 10c0/0e0b50f6a843a282637d43674d1fb278dda1dd85f4f99b640024cfb10b85058aac0cc781bf689d5fe50b4b7f638e91e548560723a4e76e04fe96ae35ef039cee + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" + dependencies: + cross-spawn: "npm:^7.0.6" + signal-exit: "npm:^4.0.1" + checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 + languageName: node + linkType: hard + +"formdata-node@npm:^6.0.3": + version: 6.0.3 + resolution: "formdata-node@npm:6.0.3" + checksum: 10c0/9b8ada280c7b0c7314bed57fd50b3562f8825bd3ede6f6231b1bc7683b649e7f3ffb7b0f13d8e9e6cae8042ea21eaf497a7c676d2fe6dc63daefefaea4838240 + languageName: node + linkType: hard + +"fs-extra@npm:^11.2.0": + version: 11.3.0 + resolution: "fs-extra@npm:11.3.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/5f95e996186ff45463059feb115a22fb048bdaf7e487ecee8a8646c78ed8fdca63630e3077d4c16ce677051f5e60d3355a06f3cd61f3ca43f48cc58822a44d0a + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"function.prototype.name@npm:^1.1.6, function.prototype.name@npm:^1.1.8": + version: 1.1.8 + resolution: "function.prototype.name@npm:1.1.8" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + functions-have-names: "npm:^1.2.3" + hasown: "npm:^2.0.2" + is-callable: "npm:^1.2.7" + checksum: 10c0/e920a2ab52663005f3cbe7ee3373e3c71c1fb5558b0b0548648cdf3e51961085032458e26c71ff1a8c8c20e7ee7caeb03d43a5d1fa8610c459333323a2e71253 + languageName: node + linkType: hard + +"functions-have-names@npm:^1.2.3": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: 10c0/33e77fd29bddc2d9bb78ab3eb854c165909201f88c75faa8272e35899e2d35a8a642a15e7420ef945e1f64a9670d6aa3ec744106b2aa42be68ca5114025954ca + languageName: node + linkType: hard + +"fx-runner@npm:1.4.0": + version: 1.4.0 + resolution: "fx-runner@npm:1.4.0" + dependencies: + commander: "npm:2.9.0" + shell-quote: "npm:1.7.3" + spawn-sync: "npm:1.0.15" + when: "npm:3.7.7" + which: "npm:1.2.4" + winreg: "npm:0.0.12" + bin: + fx-runner: bin/fx-runner + checksum: 10c0/32ab32c5b9f92deced7103ed03de0dee1dca2c51f2e1d545ad34bafe600fb7f634f717b4a2c2fdab20058341846682f4d867a7081f6a75e66d658425a551d37c + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + +"get-east-asian-width@npm:^1.0.0": + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": + version: 1.3.0 + resolution: "get-intrinsic@npm:1.3.0" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/52c81808af9a8130f581e6a6a83e1ba4a9f703359e7a438d1369a5267a25412322f03dcbd7c549edaef0b6214a0630a28511d7df0130c93cfd380f4fa0b5b66a + languageName: node + linkType: hard + +"get-port-please@npm:^3.1.2": + version: 3.2.0 + resolution: "get-port-please@npm:3.2.0" + checksum: 10c0/7e48443110b463e76ef47efc381c9f16d78798f9ea9f6d928dad2b5cee53a199cf64e6e2f22603e5f8a1f742e3d4a144cd367f6ef82ac48759bfd2beb48ee9e5 + languageName: node + linkType: hard + +"get-proto@npm:^1.0.0, get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c + languageName: node + linkType: hard + +"get-stream@npm:^5.1.0": + version: 5.2.0 + resolution: "get-stream@npm:5.2.0" + dependencies: + pump: "npm:^3.0.0" + checksum: 10c0/43797ffd815fbb26685bf188c8cfebecb8af87b3925091dd7b9a9c915993293d78e3c9e1bce125928ff92f2d0796f3889b92b5ec6d58d1041b574682132e0a80 + languageName: node + linkType: hard + +"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": + version: 6.0.1 + resolution: "get-stream@npm:6.0.1" + checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 + languageName: node + linkType: hard + +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + +"get-symbol-description@npm:^1.1.0": + version: 1.1.0 + resolution: "get-symbol-description@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/d6a7d6afca375779a4b307738c9e80dbf7afc0bdbe5948768d54ab9653c865523d8920e670991a925936eb524b7cb6a6361d199a760b21d0ca7620194455aa4b + languageName: node + linkType: hard + +"giget@npm:^1.2.3": + version: 1.2.5 + resolution: "giget@npm:1.2.5" + dependencies: + citty: "npm:^0.1.6" + consola: "npm:^3.4.0" + defu: "npm:^6.1.4" + node-fetch-native: "npm:^1.6.6" + nypm: "npm:^0.5.4" + pathe: "npm:^2.0.3" + tar: "npm:^6.2.1" + bin: + giget: dist/cli.mjs + checksum: 10c0/0c541589b8a10274f5adb6cd34a568829939182f50b3d80f8bb891e974b889f0fc629a5d702920456037cc9c90fba84cf3860bad7a22a46bc51a5c55998f24a9 + languageName: node + linkType: hard + +"giget@npm:^2.0.0": + version: 2.0.0 + resolution: "giget@npm:2.0.0" + dependencies: + citty: "npm:^0.1.6" + consola: "npm:^3.4.0" + defu: "npm:^6.1.4" + node-fetch-native: "npm:^1.6.6" + nypm: "npm:^0.6.0" + pathe: "npm:^2.0.3" + bin: + giget: dist/cli.mjs + checksum: 10c0/606d81652643936ee7f76653b4dcebc09703524ff7fd19692634ce69e3fc6775a377760d7508162379451c03bf43cc6f46716aeadeb803f7cef3fc53d0671396 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 + languageName: node + linkType: hard + +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: 10c0/0486925072d7a916f052842772b61c3e86247f0a80cc0deb9b5a3e8a1a9faad5b04fb6f58986a09f34d3e96cd2a22a24b7e9882fb1cf904c31e9a310de96c429 + languageName: node + linkType: hard + +"glob@npm:^10.2.2": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + languageName: node + linkType: hard + +"global-directory@npm:^4.0.1": + version: 4.0.1 + resolution: "global-directory@npm:4.0.1" + dependencies: + ini: "npm:4.1.1" + checksum: 10c0/f9cbeef41db4876f94dd0bac1c1b4282a7de9c16350ecaaf83e7b2dd777b32704cc25beeb1170b5a63c42a2c9abfade74d46357fe0133e933218bc89e613d4b2 + languageName: node + linkType: hard + +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d + languageName: node + linkType: hard + +"globals@npm:^16.3.0": + version: 16.3.0 + resolution: "globals@npm:16.3.0" + checksum: 10c0/c62dc20357d1c0bf2be4545d6c4141265d1a229bf1c3294955efb5b5ef611145391895e3f2729f8603809e81b30b516c33e6c2597573844449978606aad6eb38 + languageName: node + linkType: hard + +"globalthis@npm:^1.0.4": + version: 1.0.4 + resolution: "globalthis@npm:1.0.4" + dependencies: + define-properties: "npm:^1.2.1" + gopd: "npm:^1.0.1" + checksum: 10c0/9d156f313af79d80b1566b93e19285f481c591ad6d0d319b4be5e03750d004dde40a39a0f26f7e635f9007a3600802f53ecd85a759b86f109e80a5f705e01846 + languageName: node + linkType: hard + +"gopd@npm:^1.0.1, gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead + languageName: node + linkType: hard + +"graceful-fs@npm:4.2.10": + version: 4.2.10 + resolution: "graceful-fs@npm:4.2.10" + checksum: 10c0/4223a833e38e1d0d2aea630c2433cfb94ddc07dfc11d511dbd6be1d16688c5be848acc31f9a5d0d0ddbfb56d2ee5a6ae0278aceeb0ca6a13f27e06b9956fb952 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"graceful-readlink@npm:>= 1.0.0": + version: 1.0.1 + resolution: "graceful-readlink@npm:1.0.1" + checksum: 10c0/c53e703257e77f8a4495ff0d476c09aa413251acd26684f4544771b15e0ad361d1075b8f6d27b52af6942ea58155a9bbdb8125d717c70df27117460fee295a54 + languageName: node + linkType: hard + +"graphemer@npm:^1.4.0": + version: 1.4.0 + resolution: "graphemer@npm:1.4.0" + checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 + languageName: node + linkType: hard + +"growly@npm:^1.3.0": + version: 1.3.0 + resolution: "growly@npm:1.3.0" + checksum: 10c0/3043bd5c064e87f89e8c9b66894ed09fd882c7fa645621a543b45b72f040c7241e25061207a858ab191be2fbdac34795ff57c2a40962b154a6b2908a5e509252 + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.2": + version: 1.1.0 + resolution: "has-bigints@npm:1.1.0" + checksum: 10c0/2de0cdc4a1ccf7a1e75ffede1876994525ac03cc6f5ae7392d3415dd475cd9eee5bceec63669ab61aa997ff6cceebb50ef75561c7002bed8988de2b9d1b40788 + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.2.0": + version: 1.2.0 + resolution: "has-proto@npm:1.2.0" + dependencies: + dunder-proto: "npm:^1.0.0" + checksum: 10c0/46538dddab297ec2f43923c3d35237df45d8c55a6fc1067031e04c13ed8a9a8f94954460632fd4da84c31a1721eefee16d901cbb1ae9602bab93bb6e08f93b95 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + languageName: node + linkType: hard + +"highlight.js@npm:^10.7.1": + version: 10.7.3 + resolution: "highlight.js@npm:10.7.3" + checksum: 10c0/073837eaf816922427a9005c56c42ad8786473dc042332dfe7901aa065e92bc3d94ebf704975257526482066abb2c8677cc0326559bb8621e046c21c5991c434 + languageName: node + linkType: hard + +"hookable@npm:^5.5.3": + version: 5.5.3 + resolution: "hookable@npm:5.5.3" + checksum: 10c0/275f4cc84d27f8d48c5a5cd5685b6c0fea9291be9deea5bff0cfa72856ed566abde1dcd8cb1da0f9a70b4da3d7ec0d60dc3554c4edbba647058cc38816eced3d + languageName: node + linkType: hard + +"html-escaper@npm:^3.0.3": + version: 3.0.3 + resolution: "html-escaper@npm:3.0.3" + checksum: 10c0/a042fa4139127ff7546513e90ea39cc9161a1938ce90122dbc4260d4b7252c9aa8452f4509c0c2889901b8ae9a8699179150f1f99d3f80bcf7317573c5f08f4e + languageName: node + linkType: hard + +"htmlparser2@npm:^10.0.0": + version: 10.0.0 + resolution: "htmlparser2@npm:10.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.2.1" + entities: "npm:^6.0.0" + checksum: 10c0/47cfa37e529c86a7ba9a1e0e6f951ad26ef8ca5af898ab6e8916fa02c0264c1453b4a65f28b7b8a7f9d0d29b5a70abead8203bf8b3f07bc69407e85e7d9a68e4 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.2.0 + resolution: "http-cache-semantics@npm:4.2.0" + checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac + languageName: node + linkType: hard + +"human-signals@npm:^2.1.0": + version: 2.1.0 + resolution: "human-signals@npm:2.1.0" + checksum: 10c0/695edb3edfcfe9c8b52a76926cd31b36978782062c0ed9b1192b36bebc75c4c87c82e178dfcb0ed0fc27ca59d434198aac0bd0be18f5781ded775604db22304a + languageName: node + linkType: hard + +"human-signals@npm:^4.3.0": + version: 4.3.1 + resolution: "human-signals@npm:4.3.1" + checksum: 10c0/40498b33fe139f5cc4ef5d2f95eb1803d6318ac1b1c63eaf14eeed5484d26332c828de4a5a05676b6c83d7b9e57727c59addb4b1dea19cb8d71e83689e5b336c + languageName: node + linkType: hard + +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 + languageName: node + linkType: hard + +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + +"ignore@npm:^5.2.0": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 + languageName: node + linkType: hard + +"ignore@npm:^7.0.0": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d + languageName: node + linkType: hard + +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: 10c0/f8ba7ede69bee9260241ad078d2d535848745ff5f6995c7c7cb41cfdc9ccc213f66e10fa5afb881f90298b24a3f7344b637b592beb4f54e582770cdce3f1f039 + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec + languageName: node + linkType: hard + +"import-meta-resolve@npm:^4.1.0": + version: 4.1.0 + resolution: "import-meta-resolve@npm:4.1.0" + checksum: 10c0/42f3284b0460635ddf105c4ad99c6716099c3ce76702602290ad5cbbcd295700cbc04e4bdf47bacf9e3f1a4cec2e1ff887dabc20458bef398f9de22ddff45ef5 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + languageName: node + linkType: hard + +"inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ini@npm:4.1.1": + version: 4.1.1 + resolution: "ini@npm:4.1.1" + checksum: 10c0/7fddc8dfd3e63567d4fdd5d999d1bf8a8487f1479d0b34a1d01f28d391a9228d261e19abc38e1a6a1ceb3400c727204fce05725d5eb598dfcf2077a1e3afe211 + languageName: node + linkType: hard + +"ini@npm:^1.3.4, ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + +"ini@npm:^4.1.3": + version: 4.1.3 + resolution: "ini@npm:4.1.3" + checksum: 10c0/0d27eff094d5f3899dd7c00d0c04ea733ca03a8eb6f9406ce15daac1a81de022cb417d6eaff7e4342451ffa663389c565ffc68d6825eaf686bf003280b945764 + languageName: node + linkType: hard + +"internal-slot@npm:^1.1.0": + version: 1.1.0 + resolution: "internal-slot@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + hasown: "npm:^2.0.2" + side-channel: "npm:^1.1.0" + checksum: 10c0/03966f5e259b009a9bf1a78d60da920df198af4318ec004f57b8aef1dd3fe377fbc8cce63a96e8c810010302654de89f9e19de1cd8ad0061d15be28a695465c7 + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc + languageName: node + linkType: hard + +"is-absolute@npm:^0.1.7": + version: 0.1.7 + resolution: "is-absolute@npm:0.1.7" + dependencies: + is-relative: "npm:^0.1.0" + checksum: 10c0/ffa42b79866c16e54c00a98a94f1eaf4b5bf1debae5e321b80b24d529d9a1e8f47ec1bcdc2dd0773ea814c8facbe76680582d099a57c3d5775720adcc4071850 + languageName: node + linkType: hard + +"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": + version: 3.0.5 + resolution: "is-array-buffer@npm:3.0.5" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/c5c9f25606e86dbb12e756694afbbff64bc8b348d1bc989324c037e1068695131930199d6ad381952715dad3a9569333817f0b1a72ce5af7f883ce802e49c83d + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 + languageName: node + linkType: hard + +"is-async-function@npm:^2.0.0": + version: 2.1.1 + resolution: "is-async-function@npm:2.1.1" + dependencies: + async-function: "npm:^1.0.0" + call-bound: "npm:^1.0.3" + get-proto: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/d70c236a5e82de6fc4d44368ffd0c2fee2b088b893511ce21e679da275a5ecc6015ff59a7d7e1bdd7ca39f71a8dbdd253cf8cce5c6b3c91cdd5b42b5ce677298 + languageName: node + linkType: hard + +"is-bigint@npm:^1.1.0": + version: 1.1.0 + resolution: "is-bigint@npm:1.1.0" + dependencies: + has-bigints: "npm:^1.0.2" + checksum: 10c0/f4f4b905ceb195be90a6ea7f34323bf1c18e3793f18922e3e9a73c684c29eeeeff5175605c3a3a74cc38185fe27758f07efba3dbae812e5c5afbc0d2316b40e4 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.2.1": + version: 1.2.2 + resolution: "is-boolean-object@npm:1.2.2" + dependencies: + call-bound: "npm:^1.0.3" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/36ff6baf6bd18b3130186990026f5a95c709345c39cd368468e6c1b6ab52201e9fd26d8e1f4c066357b4938b0f0401e1a5000e08257787c1a02f3a719457001e + languageName: node + linkType: hard + +"is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f + languageName: node + linkType: hard + +"is-core-module@npm:^2.13.0": + version: 2.16.1 + resolution: "is-core-module@npm:2.16.1" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10c0/898443c14780a577e807618aaae2b6f745c8538eca5c7bc11388a3f2dc6de82b9902bcc7eb74f07be672b11bbe82dd6a6edded44a00cb3d8f933d0459905eedd + languageName: node + linkType: hard + +"is-data-view@npm:^1.0.1, is-data-view@npm:^1.0.2": + version: 1.0.2 + resolution: "is-data-view@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.6" + is-typed-array: "npm:^1.1.13" + checksum: 10c0/ef3548a99d7e7f1370ce21006baca6d40c73e9f15c941f89f0049c79714c873d03b02dae1c64b3f861f55163ecc16da06506c5b8a1d4f16650b3d9351c380153 + languageName: node + linkType: hard + +"is-date-object@npm:^1.0.5, is-date-object@npm:^1.1.0": + version: 1.1.0 + resolution: "is-date-object@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/1a4d199c8e9e9cac5128d32e6626fa7805175af9df015620ac0d5d45854ccf348ba494679d872d37301032e35a54fc7978fba1687e8721b2139aea7870cafa2f + languageName: node + linkType: hard + +"is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 10c0/e828365958d155f90c409cdbe958f64051d99e8aedc2c8c4cd7c89dcf35329daed42f7b99346f7828df013e27deb8f721cf9408ba878c76eb9e8290235fbcdcc + languageName: node + linkType: hard + +"is-docker@npm:^3.0.0": + version: 3.0.0 + resolution: "is-docker@npm:3.0.0" + bin: + is-docker: cli.js + checksum: 10c0/d2c4f8e6d3e34df75a5defd44991b6068afad4835bb783b902fa12d13ebdb8f41b2a199dcb0b5ed2cb78bfee9e4c0bbdb69c2d9646f4106464674d3e697a5856 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-finalizationregistry@npm:^1.1.0": + version: 1.1.1 + resolution: "is-finalizationregistry@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + checksum: 10c0/818dff679b64f19e228a8205a1e2d09989a98e98def3a817f889208cfcbf918d321b251aadf2c05918194803ebd2eb01b14fc9d0b2bea53d984f4137bfca5e97 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^4.0.0": + version: 4.0.0 + resolution: "is-fullwidth-code-point@npm:4.0.0" + checksum: 10c0/df2a717e813567db0f659c306d61f2f804d480752526886954a2a3e2246c7745fd07a52b5fecf2b68caf0a6c79dcdace6166fdf29cc76ed9975cc334f0a018b8 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^5.0.0": + version: 5.0.0 + resolution: "is-fullwidth-code-point@npm:5.0.0" + dependencies: + get-east-asian-width: "npm:^1.0.0" + checksum: 10c0/cd591b27d43d76b05fa65ed03eddce57a16e1eca0b7797ff7255de97019bcaf0219acfc0c4f7af13319e13541f2a53c0ace476f442b13267b9a6a7568f2b65c8 + languageName: node + linkType: hard + +"is-generator-function@npm:^1.0.10": + version: 1.1.0 + resolution: "is-generator-function@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.3" + get-proto: "npm:^1.0.0" + has-tostringtag: "npm:^1.0.2" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/fdfa96c8087bf36fc4cd514b474ba2ff404219a4dd4cfa6cf5426404a1eed259bdcdb98f082a71029a48d01f27733e3436ecc6690129a7ec09cb0434bee03a2a + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-in-ci@npm:^1.0.0": + version: 1.0.0 + resolution: "is-in-ci@npm:1.0.0" + bin: + is-in-ci: cli.js + checksum: 10c0/98f9cec4c35aece4cf731abf35ebf28359a9b0324fac810da05b842515d9ccb33b8999c1d9a679f0362e1a4df3292065c38d7f86327b1387fa667bc0150f4898 + languageName: node + linkType: hard + +"is-inside-container@npm:^1.0.0": + version: 1.0.0 + resolution: "is-inside-container@npm:1.0.0" + dependencies: + is-docker: "npm:^3.0.0" + bin: + is-inside-container: cli.js + checksum: 10c0/a8efb0e84f6197e6ff5c64c52890fa9acb49b7b74fed4da7c95383965da6f0fa592b4dbd5e38a79f87fc108196937acdbcd758fcefc9b140e479b39ce1fcd1cd + languageName: node + linkType: hard + +"is-installed-globally@npm:^1.0.0": + version: 1.0.0 + resolution: "is-installed-globally@npm:1.0.0" + dependencies: + global-directory: "npm:^4.0.1" + is-path-inside: "npm:^4.0.0" + checksum: 10c0/5f57745b6e75b2e9e707a26470d0cb74291d9be33c0fe0dc06c6955fe086bc2ca0a8960631b1ecb9677100eac90af33e911aec7a2c0b88097d702bfa3b76486d + languageName: node + linkType: hard + +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: 10c0/801c8f6064f85199dc6bf99b5dd98db3282e930c3bc197b32f2c5b89313bb578a07d1b8a01365c4348c2927229234f3681eb861b9c2c92bee72ff397390fa600 + languageName: node + linkType: hard + +"is-map@npm:^2.0.3": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc + languageName: node + linkType: hard + +"is-negative-zero@npm:^2.0.3": + version: 2.0.3 + resolution: "is-negative-zero@npm:2.0.3" + checksum: 10c0/bcdcf6b8b9714063ffcfa9929c575ac69bfdabb8f4574ff557dfc086df2836cf07e3906f5bbc4f2a5c12f8f3ba56af640c843cdfc74da8caed86c7c7d66fd08e + languageName: node + linkType: hard + +"is-npm@npm:^6.0.0": + version: 6.0.0 + resolution: "is-npm@npm:6.0.0" + checksum: 10c0/1f064c66325cba6e494783bee4e635caa2655aad7f853a0e045d086e0bb7d83d2d6cdf1745dc9a7c7c93dacbf816fbee1f8d9179b02d5d01674d4f92541dc0d9 + languageName: node + linkType: hard + +"is-number-object@npm:^1.1.1": + version: 1.1.1 + resolution: "is-number-object@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/97b451b41f25135ff021d85c436ff0100d84a039bb87ffd799cbcdbea81ef30c464ced38258cdd34f080be08fc3b076ca1f472086286d2aa43521d6ec6a79f53 + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-path-inside@npm:^4.0.0": + version: 4.0.0 + resolution: "is-path-inside@npm:4.0.0" + checksum: 10c0/51188d7e2b1d907a9a5f7c18d99a90b60870b951ed87cf97595d9aaa429d4c010652c3350bcbf31182e7f4b0eab9a1860b43e16729b13cb1a44baaa6cdb64c46 + languageName: node + linkType: hard + +"is-plain-object@npm:^2.0.4": + version: 2.0.4 + resolution: "is-plain-object@npm:2.0.4" + dependencies: + isobject: "npm:^3.0.1" + checksum: 10c0/f050fdd5203d9c81e8c4df1b3ff461c4bc64e8b5ca383bcdde46131361d0a678e80bcf00b5257646f6c636197629644d53bd8e2375aea633de09a82d57e942f4 + languageName: node + linkType: hard + +"is-potential-custom-element-name@npm:^1.0.1": + version: 1.0.1 + resolution: "is-potential-custom-element-name@npm:1.0.1" + checksum: 10c0/b73e2f22bc863b0939941d369486d308b43d7aef1f9439705e3582bfccaa4516406865e32c968a35f97a99396dac84e2624e67b0a16b0a15086a785e16ce7db9 + languageName: node + linkType: hard + +"is-primitive@npm:^3.0.1": + version: 3.0.1 + resolution: "is-primitive@npm:3.0.1" + checksum: 10c0/2e3b6f029fabbdda467ea51ea4fdd00e6552434108b863a08f296638072c506a7c195089e3e31f83e7fc14bebbd1c5c9f872fe127c9284a7665c8227b47ffdd6 + languageName: node + linkType: hard + +"is-regex@npm:^1.2.1": + version: 1.2.1 + resolution: "is-regex@npm:1.2.1" + dependencies: + call-bound: "npm:^1.0.2" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/1d3715d2b7889932349241680032e85d0b492cfcb045acb75ffc2c3085e8d561184f1f7e84b6f8321935b4aea39bc9c6ba74ed595b57ce4881a51dfdbc214e04 + languageName: node + linkType: hard + +"is-relative@npm:^0.1.0": + version: 0.1.3 + resolution: "is-relative@npm:0.1.3" + checksum: 10c0/91a4fe81b3b93ee220562e56e817b16c243a265d6c2daf9872ee583718db506b3b54036e852aedbb14ed693d7fc439e8836d0a5e44c56f450f730d074600c3ab + languageName: node + linkType: hard + +"is-set@npm:^2.0.3": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.4": + version: 1.0.4 + resolution: "is-shared-array-buffer@npm:1.0.4" + dependencies: + call-bound: "npm:^1.0.3" + checksum: 10c0/65158c2feb41ff1edd6bbd6fd8403a69861cf273ff36077982b5d4d68e1d59278c71691216a4a64632bd76d4792d4d1d2553901b6666d84ade13bba5ea7bc7db + languageName: node + linkType: hard + +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 + languageName: node + linkType: hard + +"is-string@npm:^1.1.1": + version: 1.1.1 + resolution: "is-string@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/2f518b4e47886bb81567faba6ffd0d8a8333cf84336e2e78bf160693972e32ad00fe84b0926491cc598dee576fdc55642c92e62d0cbe96bf36f643b6f956f94d + languageName: node + linkType: hard + +"is-symbol@npm:^1.0.4, is-symbol@npm:^1.1.1": + version: 1.1.1 + resolution: "is-symbol@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.2" + has-symbols: "npm:^1.1.0" + safe-regex-test: "npm:^1.1.0" + checksum: 10c0/f08f3e255c12442e833f75a9e2b84b2d4882fdfd920513cf2a4a2324f0a5b076c8fd913778e3ea5d258d5183e9d92c0cd20e04b03ab3df05316b049b2670af1e + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.14, is-typed-array@npm:^1.1.15": + version: 1.1.15 + resolution: "is-typed-array@npm:1.1.15" + dependencies: + which-typed-array: "npm:^1.1.16" + checksum: 10c0/415511da3669e36e002820584e264997ffe277ff136643a3126cc949197e6ca3334d0f12d084e83b1994af2e9c8141275c741cf2b7da5a2ff62dd0cac26f76c4 + languageName: node + linkType: hard + +"is-unicode-supported@npm:^1.1.0, is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + +"is-weakmap@npm:^2.0.2": + version: 2.0.2 + resolution: "is-weakmap@npm:2.0.2" + checksum: 10c0/443c35bb86d5e6cc5929cd9c75a4024bb0fff9586ed50b092f94e700b89c43a33b186b76dbc6d54f3d3d09ece689ab38dcdc1af6a482cbe79c0f2da0a17f1299 + languageName: node + linkType: hard + +"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.1": + version: 1.1.1 + resolution: "is-weakref@npm:1.1.1" + dependencies: + call-bound: "npm:^1.0.3" + checksum: 10c0/8e0a9c07b0c780949a100e2cab2b5560a48ecd4c61726923c1a9b77b6ab0aa0046c9e7fb2206042296817045376dee2c8ab1dabe08c7c3dfbf195b01275a085b + languageName: node + linkType: hard + +"is-weakset@npm:^2.0.3": + version: 2.0.4 + resolution: "is-weakset@npm:2.0.4" + dependencies: + call-bound: "npm:^1.0.3" + get-intrinsic: "npm:^1.2.6" + checksum: 10c0/6491eba08acb8dc9532da23cb226b7d0192ede0b88f16199e592e4769db0a077119c1f5d2283d1e0d16d739115f70046e887e477eb0e66cd90e1bb29f28ba647 + languageName: node + linkType: hard + +"is-wsl@npm:^2.2.0": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: "npm:^2.0.0" + checksum: 10c0/a6fa2d370d21be487c0165c7a440d567274fbba1a817f2f0bfa41cc5e3af25041d84267baa22df66696956038a43973e72fca117918c91431920bdef490fa25e + languageName: node + linkType: hard + +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: "npm:^1.0.0" + checksum: 10c0/d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 + languageName: node + linkType: hard + +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10c0/18b5be6669be53425f0b84098732670ed4e727e3af33bc7f948aac01782110eb9a18b3b329c5323bcdd3acdaae547ee077d3951317e7f133bff7105264b3003d + languageName: node + linkType: hard + +"isexe@npm:^1.1.1": + version: 1.1.2 + resolution: "isexe@npm:1.1.2" + checksum: 10c0/a61c79949c6198046d147df44693dc645f3605f8d3078e3720cf048daa7d966c8b4890a39924cec8e948395a9b6b33051af9fd7264d8ad96a4a3f562a592e33f + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 + languageName: node + linkType: hard + +"isobject@npm:^3.0.1": + version: 3.0.1 + resolution: "isobject@npm:3.0.1" + checksum: 10c0/03344f5064a82f099a0cd1a8a407f4c0d20b7b8485e8e816c39f249e9416b06c322e8dec5b842b6bb8a06de0af9cb48e7bc1b5352f0fadc2f0abac033db3d4db + languageName: node + linkType: hard + +"iterator.prototype@npm:^1.1.4": + version: 1.1.5 + resolution: "iterator.prototype@npm:1.1.5" + dependencies: + define-data-property: "npm:^1.1.4" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.6" + get-proto: "npm:^1.0.0" + has-symbols: "npm:^1.1.0" + set-function-name: "npm:^2.0.2" + checksum: 10c0/f7a262808e1b41049ab55f1e9c29af7ec1025a000d243b83edf34ce2416eedd56079b117fa59376bb4a724110690f13aa8427f2ee29a09eec63a7e72367626d0 + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"jiti@npm:^2.4.2": + version: 2.4.2 + resolution: "jiti@npm:2.4.2" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/4ceac133a08c8faff7eac84aabb917e85e8257f5ad659e843004ce76e981c457c390a220881748ac67ba1b940b9b729b30fb85cbaf6e7989f04b6002c94da331 + languageName: node + linkType: hard + +"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f + languageName: node + linkType: hard + +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 + languageName: node + linkType: hard + +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 + languageName: node + linkType: hard + +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^3.0.0": + version: 3.0.2 + resolution: "json-parse-even-better-errors@npm:3.0.2" + checksum: 10c0/147f12b005768abe9fab78d2521ce2b7e1381a118413d634a40e6d907d7d10f5e9a05e47141e96d6853af7cc36d2c834d0a014251be48791e037ff2f13d2b94b + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5 + languageName: node + linkType: hard + +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + languageName: node + linkType: hard + +"jsx-ast-utils@npm:^2.4.1 || ^3.0.0": + version: 3.3.5 + resolution: "jsx-ast-utils@npm:3.3.5" + dependencies: + array-includes: "npm:^3.1.6" + array.prototype.flat: "npm:^1.3.1" + object.assign: "npm:^4.1.4" + object.values: "npm:^1.1.6" + checksum: 10c0/a32679e9cb55469cb6d8bbc863f7d631b2c98b7fc7bf172629261751a6e7bc8da6ae374ddb74d5fbd8b06cf0eb4572287b259813d92b36e384024ed35e4c13e1 + languageName: node + linkType: hard + +"jszip@npm:^3.10.1, jszip@npm:^3.2.2": + version: 3.10.1 + resolution: "jszip@npm:3.10.1" + dependencies: + lie: "npm:~3.3.0" + pako: "npm:~1.0.2" + readable-stream: "npm:~2.3.6" + setimmediate: "npm:^1.0.5" + checksum: 10c0/58e01ec9c4960383fb8b38dd5f67b83ccc1ec215bf74c8a5b32f42b6e5fb79fada5176842a11409c4051b5b94275044851814a31076bf49e1be218d3ef57c863 + languageName: node + linkType: hard + +"keyborg@npm:2.6.0, keyborg@npm:^2.6.0": + version: 2.6.0 + resolution: "keyborg@npm:2.6.0" + checksum: 10c0/a5e3fe28a845b25b8025c9d170c39f9f53814b3b2535128e623873a330dd295cccd38a98779994b37ca67b33539fadeee5929173fbb1ee0967b745f54a59f886 + languageName: node + linkType: hard + +"keyv@npm:^4.5.4": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: "npm:3.0.1" + checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e + languageName: node + linkType: hard + +"kleur@npm:^3.0.3": + version: 3.0.3 + resolution: "kleur@npm:3.0.3" + checksum: 10c0/cd3a0b8878e7d6d3799e54340efe3591ca787d9f95f109f28129bdd2915e37807bf8918bb295ab86afb8c82196beec5a1adcaf29042ce3f2bd932b038fe3aa4b + languageName: node + linkType: hard + +"ky@npm:^1.2.0": + version: 1.8.2 + resolution: "ky@npm:1.8.2" + checksum: 10c0/81edb33f4397b4a23a085ab0c54c0efd7baad0c8eeaabbbec4e7147202627a20d80114595d03647fc1ae465d682de30e2e04805e6888e5650e5a93684cf86e93 + languageName: node + linkType: hard + +"latest-version@npm:^9.0.0": + version: 9.0.0 + resolution: "latest-version@npm:9.0.0" + dependencies: + package-json: "npm:^10.0.0" + checksum: 10c0/643cfda3a58dfb3af221a2950e433393d28a5adbe225d1cbbb358dbcbb04e9f8dce15b892f8ae3e3156f50693428dbd7ca13a69edfbdfcd94e62519480d7041e + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: "npm:^1.2.1" + type-check: "npm:~0.4.0" + checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e + languageName: node + linkType: hard + +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: "npm:~3.0.5" + checksum: 10c0/56dd113091978f82f9dc5081769c6f3b947852ecf9feccaf83e14a123bc630c2301439ce6182521e5fbafbde88e88ac38314327a4e0493a1bea7e0699a7af808 + languageName: node + linkType: hard + +"lighthouse-logger@npm:^2.0.1": + version: 2.0.1 + resolution: "lighthouse-logger@npm:2.0.1" + dependencies: + debug: "npm:^2.6.9" + marky: "npm:^1.2.2" + checksum: 10c0/414743d9b1491ad127c78741edfe88bd1c2411b267274c973036b90f56a268c3b8c3e02498bce04b560083da34a149bc3f81d2c47b6c6ad592202354cf781c43 + languageName: node + linkType: hard + +"lines-and-columns@npm:^2.0.3": + version: 2.0.4 + resolution: "lines-and-columns@npm:2.0.4" + checksum: 10c0/4db28bf065cd7ad897c0700f22d3d0d7c5ed6777e138861c601c496d545340df3fc19e18bd04ff8d95a246a245eb55685b82ca2f8c2ca53a008e9c5316250379 + languageName: node + linkType: hard + +"linkedom@npm:^0.18.5": + version: 0.18.11 + resolution: "linkedom@npm:0.18.11" + dependencies: + css-select: "npm:^5.1.0" + cssom: "npm:^0.5.0" + html-escaper: "npm:^3.0.3" + htmlparser2: "npm:^10.0.0" + uhyphen: "npm:^0.2.0" + checksum: 10c0/20bd02f77bd35609185c578d5e14c4f6754894dfec17d5d1df2756c18314e4250cebc6521f9bdf437c4e94b61b5f7c2fa52af98f805c6b0ec459ec05d7f70756 + languageName: node + linkType: hard + +"listr2@npm:^8.0.1": + version: 8.3.3 + resolution: "listr2@npm:8.3.3" + dependencies: + cli-truncate: "npm:^4.0.0" + colorette: "npm:^2.0.20" + eventemitter3: "npm:^5.0.1" + log-update: "npm:^6.1.0" + rfdc: "npm:^1.4.1" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/0792f8a7fd482fa516e21689e012e07081cab3653172ca606090622cfa0024c784a1eba8095a17948a0e9a4aa98a80f7c9c90f78a0dd35173d6802f9cc123a82 + languageName: node + linkType: hard + +"local-pkg@npm:^1.0.0": + version: 1.1.1 + resolution: "local-pkg@npm:1.1.1" + dependencies: + mlly: "npm:^1.7.4" + pkg-types: "npm:^2.0.1" + quansync: "npm:^0.2.8" + checksum: 10c0/fe8f9d0443fb066c3f28a4c89d587dd7cba3ab02645cd16598f8d5f30968acf60af1b0ec2d6ad768475ec9f52baad124f31a93d2fbc034f645bcc02bf3a84882 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: "npm:^5.0.0" + checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 + languageName: node + linkType: hard + +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: 10c0/fcba15d21a458076dd309fce6b1b4bf611d84a0ec252cb92447c948c533ac250b95d2e00955801ebc367e5af5ed288b996d75d37d2035260a937008e14eaf432 + languageName: node + linkType: hard + +"lodash.kebabcase@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.kebabcase@npm:4.1.1" + checksum: 10c0/da5d8f41dbb5bc723d4bf9203d5096ca8da804d6aec3d2b56457156ba6c8d999ff448d347ebd97490da853cb36696ea4da09a431499f1ee8deb17b094ecf4e33 + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 + languageName: node + linkType: hard + +"lodash.snakecase@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.snakecase@npm:4.1.1" + checksum: 10c0/f0b3f2497eb20eea1a1cfc22d645ecaeb78ac14593eb0a40057977606d2f35f7aaff0913a06553c783b535aafc55b718f523f9eb78f8d5293f492af41002eaf9 + languageName: node + linkType: hard + +"log-symbols@npm:^5.1.0": + version: 5.1.0 + resolution: "log-symbols@npm:5.1.0" + dependencies: + chalk: "npm:^5.0.0" + is-unicode-supported: "npm:^1.1.0" + checksum: 10c0/c14f8567c6618a7f96209c4c4b9fb3b794187116904712f7b526e465a5c9535728aec983735a5bef919247d0e54b9b72b6680a7fb9fc72d76b945dac4865e669 + languageName: node + linkType: hard + +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 10c0/36636cacedba8f067d2deb4aad44e91a89d9efb3ead27e1846e7b82c9a10ea2e3a7bd6ce28a7ca616bebc60954ff25c67b0f92d20a6a746bb3cc52c3701891f6 + languageName: node + linkType: hard + +"log-update@npm:^6.1.0": + version: 6.1.0 + resolution: "log-update@npm:6.1.0" + dependencies: + ansi-escapes: "npm:^7.0.0" + cli-cursor: "npm:^5.0.0" + slice-ansi: "npm:^7.1.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/4b350c0a83d7753fea34dcac6cd797d1dc9603291565de009baa4aa91c0447eab0d3815a05c8ec9ac04fdfffb43c82adcdb03ec1fceafd8518e1a8c1cff4ff89 + languageName: node + linkType: hard + +"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": + version: 1.4.0 + resolution: "loose-envify@npm:1.4.0" + dependencies: + js-tokens: "npm:^3.0.0 || ^4.0.0" + bin: + loose-envify: cli.js + checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"lzutf8@npm:^0.6.3": + version: 0.6.3 + resolution: "lzutf8@npm:0.6.3" + dependencies: + readable-stream: "npm:^4.0.0" + checksum: 10c0/5abd0db31d5b7b336335b907117670bfa3e7c654d640b3e3cb70004998a224b8babecbf303b5ce1f08e7fa0989f4b6128ed41ab8beaf8e524b84137474edd073 + languageName: node + linkType: hard + +"magic-string@npm:^0.30.17": + version: 0.30.17 + resolution: "magic-string@npm:0.30.17" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + checksum: 10c0/16826e415d04b88378f200fe022b53e638e3838b9e496edda6c0e086d7753a44a6ed187adc72d19f3623810589bf139af1a315541cd6a26ae0771a0193eaf7b8 + languageName: node + linkType: hard + +"magicast@npm:^0.3.5": + version: 0.3.5 + resolution: "magicast@npm:0.3.5" + dependencies: + "@babel/parser": "npm:^7.25.4" + "@babel/types": "npm:^7.25.4" + source-map-js: "npm:^1.2.0" + checksum: 10c0/a6cacc0a848af84f03e3f5bda7b0de75e4d0aa9ddce5517fd23ed0f31b5ddd51b2d0ff0b7e09b51f7de0f4053c7a1107117edda6b0732dca3e9e39e6c5a68c64 + languageName: node + linkType: hard + +"make-error@npm:^1.3.2": + version: 1.3.6 + resolution: "make-error@npm:1.3.6" + checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f + languageName: node + linkType: hard + +"make-fetch-happen@npm:^14.0.3": + version: 14.0.3 + resolution: "make-fetch-happen@npm:14.0.3" + dependencies: + "@npmcli/agent": "npm:^3.0.0" + cacache: "npm:^19.0.1" + http-cache-semantics: "npm:^4.1.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^4.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^1.0.0" + proc-log: "npm:^5.0.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^12.0.0" + checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0 + languageName: node + linkType: hard + +"many-keys-map@npm:^2.0.1": + version: 2.0.1 + resolution: "many-keys-map@npm:2.0.1" + checksum: 10c0/cba5a8f67e847441fdff77cf584301e8f2bd91851690dbfabd0561f083c6acccbf34655a951b1e793d63547599b2c279ddf1185547ea0fc1d169e95b38ba2634 + languageName: node + linkType: hard + +"marky@npm:^1.2.2": + version: 1.3.0 + resolution: "marky@npm:1.3.0" + checksum: 10c0/6619cdb132fdc4f7cd3e2bed6eebf81a38e50ff4b426bbfb354db68731e4adfebf35ebfd7c8e5a6e846cbf9b872588c4f76db25782caee8c1529ec9d483bf98b + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f + languageName: node + linkType: hard + +"mdn-data@npm:2.21.0": + version: 2.21.0 + resolution: "mdn-data@npm:2.21.0" + checksum: 10c0/cd26902551af2cc29f06f130893cb04bca9ee278939fce3ffbcb759497cc80d53a6f4abdef2ae2f3ed3c95ac8d651f53fc141defd580ebf4ae2f93aea325957b + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + +"merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 + languageName: node + linkType: hard + +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4 + languageName: node + linkType: hard + +"mimic-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-fn@npm:4.0.0" + checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf + languageName: node + linkType: hard + +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + +"minimatch@npm:^10.0.1": + version: 10.0.3 + resolution: "minimatch@npm:10.0.3" + dependencies: + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10c0/e43e4a905c5d70ac4cec8530ceaeccb9c544b1ba8ac45238e2a78121a01c17ff0c373346472d221872563204eabe929ad02669bb575cb1f0cc30facab369f70f + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + +"minimist@npm:^1.2.0, minimist@npm:^1.2.8": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e + languageName: node + linkType: hard + +"minipass-fetch@npm:^4.0.0": + version: 4.0.1 + resolution: "minipass-fetch@npm:4.0.1" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^3.0.1" + dependenciesMeta: + encoding: + optional: true + checksum: 10c0/a3147b2efe8e078c9bf9d024a0059339c5a09c5b1dded6900a219c218cc8b1b78510b62dae556b507304af226b18c3f1aeb1d48660283602d5b6586c399eed5c + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: "npm:^3.0.0" + yallist: "npm:^4.0.0" + checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78 + languageName: node + linkType: hard + +"minizlib@npm:^3.0.1": + version: 3.0.2 + resolution: "minizlib@npm:3.0.2" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf + languageName: node + linkType: hard + +"mkdirp@npm:^3.0.1": + version: 3.0.1 + resolution: "mkdirp@npm:3.0.1" + bin: + mkdirp: dist/cjs/src/bin.js + checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d + languageName: node + linkType: hard + +"mlly@npm:^1.7.4": + version: 1.7.4 + resolution: "mlly@npm:1.7.4" + dependencies: + acorn: "npm:^8.14.0" + pathe: "npm:^2.0.1" + pkg-types: "npm:^1.3.0" + ufo: "npm:^1.5.4" + checksum: 10c0/69e738218a13d6365caf930e0ab4e2b848b84eec261597df9788cefb9930f3e40667be9cb58a4718834ba5f97a6efeef31d3b5a95f4388143fd4e0d0deff72ff + languageName: node + linkType: hard + +"ms@npm:2.0.0": + version: 2.0.0 + resolution: "ms@npm:2.0.0" + checksum: 10c0/f8fda810b39fd7255bbdc451c46286e549794fcc700dc9cd1d25658bbc4dc2563a5de6fe7c60f798a16a60c6ceb53f033cb353f493f0cf63e5199b702943159d + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"multimatch@npm:6.0.0": + version: 6.0.0 + resolution: "multimatch@npm:6.0.0" + dependencies: + "@types/minimatch": "npm:^3.0.5" + array-differ: "npm:^4.0.0" + array-union: "npm:^3.0.1" + minimatch: "npm:^3.0.4" + checksum: 10c0/e303c3d83a66bdffbe6bb50b7be000dd299f1928a602323adc92daef3c1028ef1ee4cabf7d2ac458e32096c5dac2a263209835464348faf8a8332d076b58c35a + languageName: node + linkType: hard + +"mz@npm:^2.4.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + +"nano-spawn@npm:^0.2.0": + version: 0.2.1 + resolution: "nano-spawn@npm:0.2.1" + checksum: 10c0/7019909f07425b33bd77569b940ee8a6d8fd3acec8a6b6dbf24449fd3ab220990b0b4dbd995c6cf7cd717f55c95e25d8fcd2b67d3eb661b0b35dbfb18e761847 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.11": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 + languageName: node + linkType: hard + +"negotiator@npm:^1.0.0": + version: 1.0.0 + resolution: "negotiator@npm:1.0.0" + checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b + languageName: node + linkType: hard + +"node-fetch-native@npm:^1.6.4, node-fetch-native@npm:^1.6.6": + version: 1.6.6 + resolution: "node-fetch-native@npm:1.6.6" + checksum: 10c0/8c12dab0e640d8bc126a03d604af9cf3fc1b87f2cda5af0c71601079d5ed835c1dc149c7042b61c83f252a382e1cf1e541788f4c9e8e6c089af77497190f5dc3 + languageName: node + linkType: hard + +"node-forge@npm:^1.3.1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 10c0/e882819b251a4321f9fc1d67c85d1501d3004b4ee889af822fd07f64de3d1a8e272ff00b689570af0465d65d6bf5074df9c76e900e0aff23e60b847f2a46fbe8 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 11.2.0 + resolution: "node-gyp@npm:11.2.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^14.0.3" + nopt: "npm:^8.0.0" + proc-log: "npm:^5.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.4.3" + tinyglobby: "npm:^0.2.12" + which: "npm:^5.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/bd8d8c76b06be761239b0c8680f655f6a6e90b48e44d43415b11c16f7e8c15be346fba0cbf71588c7cdfb52c419d928a7d3db353afc1d952d19756237d8f10b9 + languageName: node + linkType: hard + +"node-notifier@npm:10.0.1": + version: 10.0.1 + resolution: "node-notifier@npm:10.0.1" + dependencies: + growly: "npm:^1.3.0" + is-wsl: "npm:^2.2.0" + semver: "npm:^7.3.5" + shellwords: "npm:^0.1.1" + uuid: "npm:^8.3.2" + which: "npm:^2.0.2" + checksum: 10c0/8888f6c4c277c588e6be991019e32ebbf4abdd598151683de59b9f70c31e6bbbddf0e443ea373da44338ab82a958695dcf73035c96e336a398940228d59399eb + languageName: node + linkType: hard + +"node-releases@npm:^2.0.19": + version: 2.0.19 + resolution: "node-releases@npm:2.0.19" + checksum: 10c0/52a0dbd25ccf545892670d1551690fe0facb6a471e15f2cfa1b20142a5b255b3aa254af5f59d6ecb69c2bec7390bc643c43aa63b13bf5e64b6075952e716b1aa + languageName: node + linkType: hard + +"nopt@npm:^8.0.0": + version: 8.1.0 + resolution: "nopt@npm:8.1.0" + dependencies: + abbrev: "npm:^3.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + +"npm-run-path@npm:^4.0.1": + version: 4.0.1 + resolution: "npm-run-path@npm:4.0.1" + dependencies: + path-key: "npm:^3.0.0" + checksum: 10c0/6f9353a95288f8455cf64cbeb707b28826a7f29690244c1e4bb61ec573256e021b6ad6651b394eb1ccfd00d6ec50147253aba2c5fe58a57ceb111fad62c519ac + languageName: node + linkType: hard + +"npm-run-path@npm:^5.1.0": + version: 5.3.0 + resolution: "npm-run-path@npm:5.3.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba + languageName: node + linkType: hard + +"nth-check@npm:^2.0.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: "npm:^1.0.0" + checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479 + languageName: node + linkType: hard + +"nypm@npm:^0.3.12": + version: 0.3.12 + resolution: "nypm@npm:0.3.12" + dependencies: + citty: "npm:^0.1.6" + consola: "npm:^3.2.3" + execa: "npm:^8.0.1" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.2.0" + ufo: "npm:^1.5.4" + bin: + nypm: dist/cli.mjs + checksum: 10c0/1455abc1521e2d307736ef7c3b79eb4c2ef1fb2e471b90d16a440f2f7863cbcfe40708061015fd7056ebdba48359845192b749882b12a6659798244a5936ece6 + languageName: node + linkType: hard + +"nypm@npm:^0.5.4": + version: 0.5.4 + resolution: "nypm@npm:0.5.4" + dependencies: + citty: "npm:^0.1.6" + consola: "npm:^3.4.0" + pathe: "npm:^2.0.3" + pkg-types: "npm:^1.3.1" + tinyexec: "npm:^0.3.2" + ufo: "npm:^1.5.4" + bin: + nypm: dist/cli.mjs + checksum: 10c0/4b4661d2e460f4f8e96338669776dc3be4ed895bd34208ac188b5b8b438553aab737d41a5699cdc716f078fba9048b3d40b7d8a55c2544f9453536f837d323dc + languageName: node + linkType: hard + +"nypm@npm:^0.6.0": + version: 0.6.0 + resolution: "nypm@npm:0.6.0" + dependencies: + citty: "npm:^0.1.6" + consola: "npm:^3.4.0" + pathe: "npm:^2.0.3" + pkg-types: "npm:^2.0.0" + tinyexec: "npm:^0.3.2" + bin: + nypm: dist/cli.mjs + checksum: 10c0/899f16c2df1bdf3ef4de5f7d4ed5530e2e1ca097cc7dedbaa25abb6b8e44bb470c25cd26639f6e3e4f5734867e61f7f77c4ed5dfbe86b2a1bdef4525a2dc0026 + languageName: node + linkType: hard + +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 10c0/d7f8711e803b96ea3191c745d6f8056ce1f2496e530e6a19a0e92d89b0fa3c76d910c31f0aa270432db6bd3b2f85500a376a83aaba849a8d518c8845b3211692 + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: 10c0/b11f7ccdbc6d406d1f186cdadb9d54738e347b2692a14439ca5ac70c225fa6db46db809711b78589866d47b25fc3e8dee0b4c722ac751e11180f9380e3d8601d + languageName: node + linkType: hard + +"object.assign@npm:^4.1.4, object.assign@npm:^4.1.7": + version: 4.1.7 + resolution: "object.assign@npm:4.1.7" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + has-symbols: "npm:^1.1.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/3b2732bd860567ea2579d1567525168de925a8d852638612846bd8082b3a1602b7b89b67b09913cbb5b9bd6e95923b2ae73580baa9d99cb4e990564e8cbf5ddc + languageName: node + linkType: hard + +"object.entries@npm:^1.1.9": + version: 1.1.9 + resolution: "object.entries@npm:1.1.9" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.1.1" + checksum: 10c0/d4b8c1e586650407da03370845f029aa14076caca4e4d4afadbc69cfb5b78035fd3ee7be417141abdb0258fa142e59b11923b4c44d8b1255b28f5ffcc50da7db + languageName: node + linkType: hard + +"object.fromentries@npm:^2.0.8": + version: 2.0.8 + resolution: "object.fromentries@npm:2.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/cd4327e6c3369cfa805deb4cbbe919bfb7d3aeebf0bcaba291bb568ea7169f8f8cdbcabe2f00b40db0c20cd20f08e11b5f3a5a36fb7dd3fe04850c50db3bf83b + languageName: node + linkType: hard + +"object.values@npm:^1.1.6, object.values@npm:^1.2.1": + version: 1.2.1 + resolution: "object.values@npm:1.2.1" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/3c47814fdc64842ae3d5a74bc9d06bdd8d21563c04d9939bf6716a9c00596a4ebc342552f8934013d1ec991c74e3671b26710a0c51815f0b603795605ab6b2c9 + languageName: node + linkType: hard + +"ofetch@npm:^1.3.3": + version: 1.4.1 + resolution: "ofetch@npm:1.4.1" + dependencies: + destr: "npm:^2.0.3" + node-fetch-native: "npm:^1.6.4" + ufo: "npm:^1.5.4" + checksum: 10c0/fd712e84058ad5058a5880fe805e9bb1c2084fb7f9c54afa99a2c7e84065589b4312fa6e2dcca4432865e44ad1ec13fcd055c1bf7977ced838577a45689a04fa + languageName: node + linkType: hard + +"ohash@npm:^1.1.4": + version: 1.1.6 + resolution: "ohash@npm:1.1.6" + checksum: 10c0/3c25dde77662fffa3fa956b8975d7324311e3df15d9cf8d79b466ac3508f313e02b8ed3041d967810f68e00af344a5a3c4288be0208143c1844ba0f3cc83f777 + languageName: node + linkType: hard + +"ohash@npm:^2.0.11": + version: 2.0.11 + resolution: "ohash@npm:2.0.11" + checksum: 10c0/d07c8d79cc26da082c1a7c8d5b56c399dd4ed3b2bd069fcae6bae78c99a9bcc3ad813b1e1f49ca2f335292846d689c6141a762cf078727d2302a33d414e69c79 + languageName: node + linkType: hard + +"on-exit-leak-free@npm:^2.1.0": + version: 2.1.2 + resolution: "on-exit-leak-free@npm:2.1.2" + checksum: 10c0/faea2e1c9d696ecee919026c32be8d6a633a7ac1240b3b87e944a380e8a11dc9c95c4a1f8fb0568de7ab8db3823e790f12bda45296b1d111e341aad3922a0570 + languageName: node + linkType: hard + +"once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 + languageName: node + linkType: hard + +"onetime@npm:^5.1.0, onetime@npm:^5.1.2": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: "npm:^2.1.0" + checksum: 10c0/ffcef6fbb2692c3c40749f31ea2e22677a876daea92959b8a80b521d95cca7a668c884d8b2045d1d8ee7d56796aa405c405462af112a1477594cc63531baeb8f + languageName: node + linkType: hard + +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c + languageName: node + linkType: hard + +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221 + languageName: node + linkType: hard + +"open@npm:^10.1.0": + version: 10.2.0 + resolution: "open@npm:10.2.0" + dependencies: + default-browser: "npm:^5.2.1" + define-lazy-prop: "npm:^3.0.0" + is-inside-container: "npm:^1.0.0" + wsl-utils: "npm:^0.1.0" + checksum: 10c0/5a36d0c1fd2f74ce553beb427ca8b8494b623fc22c6132d0c1688f246a375e24584ea0b44c67133d9ab774fa69be8e12fbe1ff12504b1142bd960fb09671948f + languageName: node + linkType: hard + +"open@npm:^8.4.0": + version: 8.4.2 + resolution: "open@npm:8.4.2" + dependencies: + define-lazy-prop: "npm:^2.0.0" + is-docker: "npm:^2.1.1" + is-wsl: "npm:^2.2.0" + checksum: 10c0/bb6b3a58401dacdb0aad14360626faf3fb7fba4b77816b373495988b724fb48941cad80c1b65d62bb31a17609b2cd91c41a181602caea597ca80dfbcc27e84c9 + languageName: node + linkType: hard + +"open@npm:^9.1.0": + version: 9.1.0 + resolution: "open@npm:9.1.0" + dependencies: + default-browser: "npm:^4.0.0" + define-lazy-prop: "npm:^3.0.0" + is-inside-container: "npm:^1.0.0" + is-wsl: "npm:^2.2.0" + checksum: 10c0/8073ec0dd8994a7a7d9bac208bd17d093993a65ce10f2eb9b62b6d3a91c9366ae903938a237c275493c130171d339f6dcbdd2a2de7e32953452c0867b97825af + languageName: node + linkType: hard + +"optionator@npm:^0.9.3": + version: 0.9.4 + resolution: "optionator@npm:0.9.4" + dependencies: + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + word-wrap: "npm:^1.2.5" + checksum: 10c0/4afb687a059ee65b61df74dfe87d8d6815cd6883cb8b3d5883a910df72d0f5d029821f37025e4bccf4048873dbdb09acc6d303d27b8f76b1a80dd5a7d5334675 + languageName: node + linkType: hard + +"ora@npm:^6.3.1": + version: 6.3.1 + resolution: "ora@npm:6.3.1" + dependencies: + chalk: "npm:^5.0.0" + cli-cursor: "npm:^4.0.0" + cli-spinners: "npm:^2.6.1" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^1.1.0" + log-symbols: "npm:^5.1.0" + stdin-discarder: "npm:^0.1.0" + strip-ansi: "npm:^7.0.1" + wcwidth: "npm:^1.0.1" + checksum: 10c0/f8753e234c9967c86cfb73e7396e1a51ed8771c4921d539af8e8962b32c7928cefef7b3c4ce730a504be72b1437f91cc0523f468927b9fe322498c4edcc50203 + languageName: node + linkType: hard + +"ora@npm:^8.1.1": + version: 8.2.0 + resolution: "ora@npm:8.2.0" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/7d9291255db22e293ea164f520b6042a3e906576ab06c9cf408bf9ef5664ba0a9f3bd258baa4ada058cfcc2163ef9b6696d51237a866682ce33295349ba02c3a + languageName: node + linkType: hard + +"os-shim@npm:^0.1.2": + version: 0.1.3 + resolution: "os-shim@npm:0.1.3" + checksum: 10c0/eaa09098c0f6a3115b2d0c027927cba9c2706e362b7767021b7ac83d159f18806ac1e95786b496d1912ce1aea8a6866e526d3f18f075c7c719eb08a0ffb9177f + languageName: node + linkType: hard + +"own-keys@npm:^1.0.1": + version: 1.0.1 + resolution: "own-keys@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.2.6" + object-keys: "npm:^1.1.1" + safe-push-apply: "npm:^1.0.0" + checksum: 10c0/6dfeb3455bff92ec3f16a982d4e3e65676345f6902d9f5ded1d8265a6318d0200ce461956d6d1c70053c7fe9f9fe65e552faac03f8140d37ef0fdd108e67013a + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a + languageName: node + linkType: hard + +"p-map@npm:^7.0.2": + version: 7.0.3 + resolution: "p-map@npm:7.0.3" + checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + +"package-json@npm:^10.0.0": + version: 10.0.1 + resolution: "package-json@npm:10.0.1" + dependencies: + ky: "npm:^1.2.0" + registry-auth-token: "npm:^5.0.2" + registry-url: "npm:^6.0.1" + semver: "npm:^7.6.0" + checksum: 10c0/4a55648d820496326730a7b149fd3fd8382e96f3d6def5ec687f46b75063894acf06b21f79832b40bb094c821d97f532cb0f009f85c4102d0084b488d4f492d3 + languageName: node + linkType: hard + +"pako@npm:~1.0.2": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 10c0/86dd99d8b34c3930345b8bbeb5e1cd8a05f608eeb40967b293f72fe469d0e9c88b783a8777e4cc7dc7c91ce54c5e93d88ff4b4f060e6ff18408fd21030d9ffbe + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse-json@npm:7.1.1": + version: 7.1.1 + resolution: "parse-json@npm:7.1.1" + dependencies: + "@babel/code-frame": "npm:^7.21.4" + error-ex: "npm:^1.3.2" + json-parse-even-better-errors: "npm:^3.0.0" + lines-and-columns: "npm:^2.0.3" + type-fest: "npm:^3.8.0" + checksum: 10c0/a85ebc7430af7763fa52eb456d7efd35c35be5b06f04d8d80c37d0d33312ac6cdff12647acb9c95448dcc8b907dfafa81fb126e094aa132b0abc2a71b9df51d5 + languageName: node + linkType: hard + +"parse5-htmlparser2-tree-adapter@npm:^6.0.0": + version: 6.0.1 + resolution: "parse5-htmlparser2-tree-adapter@npm:6.0.1" + dependencies: + parse5: "npm:^6.0.1" + checksum: 10c0/dfa5960e2aaf125707e19a4b1bc333de49232eba5a6ffffb95d313a7d6087c3b7a274b58bee8d3bd41bdf150638815d1d601a42bbf2a0345208c3c35b1279556 + languageName: node + linkType: hard + +"parse5@npm:^5.1.1": + version: 5.1.1 + resolution: "parse5@npm:5.1.1" + checksum: 10c0/b0f87a77a7fea5f242e3d76917c983bbea47703b9371801d51536b78942db6441cbda174bf84eb30e47315ddc6f8a0b57d68e562c790154430270acd76c1fa03 + languageName: node + linkType: hard + +"parse5@npm:^6.0.1": + version: 6.0.1 + resolution: "parse5@npm:6.0.1" + checksum: 10c0/595821edc094ecbcfb9ddcb46a3e1fe3a718540f8320eff08b8cf6742a5114cce2d46d45f95c26191c11b184dcaf4e2960abcd9c5ed9eb9393ac9a37efcfdecb + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b + languageName: node + linkType: hard + +"path-key@npm:^3.0.0, path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 10c0/794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + +"pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 + languageName: node + linkType: hard + +"pathe@npm:^2.0.1, pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 10c0/8a87e63f7a4afcfb0f9f77b39bb92374afc723418b9cb716ee4257689224171002e07768eeade4ecd0e86f1fa3d8f022994219fb45634f2dbd78c6803e452458 + languageName: node + linkType: hard + +"perfect-debounce@npm:^1.0.0": + version: 1.0.0 + resolution: "perfect-debounce@npm:1.0.0" + checksum: 10c0/e2baac416cae046ef1b270812cf9ccfb0f91c04ea36ac7f5b00bc84cb7f41bdbba087c0ab21b4e02a7ef3a1f1f6db399f137cecec46868bd7d8d88c2a9ee431f + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be + languageName: node + linkType: hard + +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3": + version: 4.0.3 + resolution: "picomatch@npm:4.0.3" + checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 + languageName: node + linkType: hard + +"pino-abstract-transport@npm:^2.0.0": + version: 2.0.0 + resolution: "pino-abstract-transport@npm:2.0.0" + dependencies: + split2: "npm:^4.0.0" + checksum: 10c0/02c05b8f2ffce0d7c774c8e588f61e8b77de8ccb5f8125afd4a7325c9ea0e6af7fb78168999657712ae843e4462bb70ac550dfd6284f930ee57f17f486f25a9f + languageName: node + linkType: hard + +"pino-std-serializers@npm:^7.0.0": + version: 7.0.0 + resolution: "pino-std-serializers@npm:7.0.0" + checksum: 10c0/73e694d542e8de94445a03a98396cf383306de41fd75ecc07085d57ed7a57896198508a0dec6eefad8d701044af21eb27253ccc352586a03cf0d4a0bd25b4133 + languageName: node + linkType: hard + +"pino@npm:9.6.0": + version: 9.6.0 + resolution: "pino@npm:9.6.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + fast-redact: "npm:^3.1.1" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:^2.0.0" + pino-std-serializers: "npm:^7.0.0" + process-warning: "npm:^4.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.2.0" + safe-stable-stringify: "npm:^2.3.1" + sonic-boom: "npm:^4.0.1" + thread-stream: "npm:^3.0.0" + bin: + pino: bin.js + checksum: 10c0/bcd1e9d9b301bea13b95689ca9ad7105ae9451928fb6c0b67b3e58c5fe37cea1d40665f3d6641e3da00be0bbc17b89031e67abbc8ea6aac6164f399309fd78e7 + languageName: node + linkType: hard + +"pkg-types@npm:^1.2.0, pkg-types@npm:^1.3.0, pkg-types@npm:^1.3.1": + version: 1.3.1 + resolution: "pkg-types@npm:1.3.1" + dependencies: + confbox: "npm:^0.1.8" + mlly: "npm:^1.7.4" + pathe: "npm:^2.0.1" + checksum: 10c0/19e6cb8b66dcc66c89f2344aecfa47f2431c988cfa3366bdfdcfb1dd6695f87dcce37fbd90fe9d1605e2f4440b77f391e83c23255347c35cf84e7fd774d7fcea + languageName: node + linkType: hard + +"pkg-types@npm:^2.0.0, pkg-types@npm:^2.0.1, pkg-types@npm:^2.2.0": + version: 2.2.0 + resolution: "pkg-types@npm:2.2.0" + dependencies: + confbox: "npm:^0.2.2" + exsolve: "npm:^1.0.7" + pathe: "npm:^2.0.3" + checksum: 10c0/df14eada1aeaaf73f72d3ec08d360bbfb44f2dfec5612358e0ce30f306a395a51fc7bfa96a2ca6ba005e9f56ddb1d2ee5b4cdd2e7b87ff075e5bf52e6fbc1cd6 + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0": + version: 1.1.0 + resolution: "possible-typed-array-names@npm:1.1.0" + checksum: 10c0/c810983414142071da1d644662ce4caebce890203eb2bc7bf119f37f3fe5796226e117e6cca146b521921fa6531072674174a3325066ac66fce089a53e1e5196 + languageName: node + linkType: hard + +"postcss@npm:^8.5.3, postcss@npm:^8.5.6": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" + dependencies: + nanoid: "npm:^3.3.11" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd + languageName: node + linkType: hard + +"proc-log@npm:^5.0.0": + version: 5.0.0 + resolution: "proc-log@npm:5.0.0" + checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3 + languageName: node + linkType: hard + +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10c0/bec089239487833d46b59d80327a1605e1c5287eaad770a291add7f45fda1bb5e28b38e0e061add0a1d0ee0984788ce74fa394d345eed1c420cacf392c554367 + languageName: node + linkType: hard + +"process-warning@npm:^4.0.0": + version: 4.0.1 + resolution: "process-warning@npm:4.0.1" + checksum: 10c0/577a268b9fd5c3d9f6dbb4348220099391d830905642845d591e7ee8b1e45043d98b7b9826a3c1379bdd1686cdfe0f6cf349cb812affc5853b662e6a9896579e + languageName: node + linkType: hard + +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 + languageName: node + linkType: hard + +"promise-toolbox@npm:0.21.0": + version: 0.21.0 + resolution: "promise-toolbox@npm:0.21.0" + dependencies: + make-error: "npm:^1.3.2" + checksum: 10c0/f1de739b200113f17b49017d8de43c4f2d579a60fbf696201e9ae68a3b78d4d4d9f7777ebbc3745f0c427bd6f065aec6a40b98d1a4351807d165d15281b8a3a9 + languageName: node + linkType: hard + +"prompts@npm:^2.4.2": + version: 2.4.2 + resolution: "prompts@npm:2.4.2" + dependencies: + kleur: "npm:^3.0.3" + sisteransi: "npm:^1.0.5" + checksum: 10c0/16f1ac2977b19fe2cf53f8411cc98db7a3c8b115c479b2ca5c82b5527cd937aa405fa04f9a5960abeb9daef53191b53b4d13e35c1f5d50e8718c76917c5f1ea4 + languageName: node + linkType: hard + +"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": + version: 15.8.1 + resolution: "prop-types@npm:15.8.1" + dependencies: + loose-envify: "npm:^1.4.0" + object-assign: "npm:^4.1.1" + react-is: "npm:^16.13.1" + checksum: 10c0/59ece7ca2fb9838031d73a48d4becb9a7cc1ed10e610517c7d8f19a1e02fa47f7c27d557d8a5702bec3cfeccddc853579832b43f449e54635803f277b1c78077 + languageName: node + linkType: hard + +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: 10c0/b9179f99394ec8a68b8afc817690185f3b03933f7b46ce2e22c1930dc84b60d09f5ad222beab4e59e58c6c039c7f7fcf620397235ef441a356f31f9744010e12 + languageName: node + linkType: hard + +"publish-browser-extension@npm:^2.3.0 || ^3.0.0": + version: 3.0.1 + resolution: "publish-browser-extension@npm:3.0.1" + dependencies: + cac: "npm:^6.7.14" + cli-highlight: "npm:^2.1.11" + consola: "npm:^3.2.3" + dotenv: "npm:^16.3.1" + extract-zip: "npm:^2.0.1" + formdata-node: "npm:^6.0.3" + listr2: "npm:^8.0.1" + lodash.camelcase: "npm:^4.3.0" + lodash.kebabcase: "npm:^4.1.1" + lodash.snakecase: "npm:^4.1.1" + ofetch: "npm:^1.3.3" + open: "npm:^9.1.0" + ora: "npm:^6.3.1" + prompts: "npm:^2.4.2" + zod: "npm:^3.22.4" + bin: + publish-extension: bin/publish-extension.cjs + checksum: 10c0/d8acc1a01fc596c88e94f03514c1aaa4d5c1716d55155a16f761efc6cab36c1de5efe7df6c5d0fd49dc6c9112ac410cf4be1c2dea1db2260c5edb243767318c3 + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.3 + resolution: "pump@npm:3.0.3" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10c0/ada5cdf1d813065bbc99aa2c393b8f6beee73b5de2890a8754c9f488d7323ffd2ca5f5a0943b48934e3fcbd97637d0337369c3c631aeb9614915db629f1c75c9 + languageName: node + linkType: hard + +"punycode@npm:^2.1.0": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 + languageName: node + linkType: hard + +"pupa@npm:^3.1.0": + version: 3.1.0 + resolution: "pupa@npm:3.1.0" + dependencies: + escape-goat: "npm:^4.0.0" + checksum: 10c0/02afa6e4547a733484206aaa8f8eb3fbfb12d3dd17d7ca4fa1ea390a7da2cb8f381e38868bbf68009c4d372f8f6059f553171b6a712d8f2802c7cd43d513f06c + languageName: node + linkType: hard + +"quansync@npm:^0.2.8": + version: 0.2.10 + resolution: "quansync@npm:0.2.10" + checksum: 10c0/f86f1d644f812a3a7c42de79eb401c47a5a67af82a9adff8a8afb159325e03e00f77cebbf42af6340a0bd47bd0c1fbe999e7caf7e1bbb30d7acb00c8729b7530 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 10c0/fe5acc6f775b172ca5b4373df26f7e4fd347975578199e7d74b2ae4077f0af05baa27d231de1e80e8f72d88275ccc6028568a7a8c9ee5e7368ace0e18eff93a4 + languageName: node + linkType: hard + +"rc9@npm:^2.1.2": + version: 2.1.2 + resolution: "rc9@npm:2.1.2" + dependencies: + defu: "npm:^6.1.4" + destr: "npm:^2.0.3" + checksum: 10c0/a2ead3b94bf033e35e4ea40d70062a09feddb8f589c3f5a8fe4e9342976974296aee9f6e9e72bd5e78e6ae4b7bc16dc244f63699fd7322c16314e3238db982c9 + languageName: node + linkType: hard + +"rc@npm:1.2.8": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: "npm:^0.6.0" + ini: "npm:~1.3.0" + minimist: "npm:^1.2.0" + strip-json-comments: "npm:~2.0.1" + bin: + rc: ./cli.js + checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 + languageName: node + linkType: hard + +"react-dom@npm:^18.3.1": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + scheduler: "npm:^0.23.2" + peerDependencies: + react: ^18.3.1 + checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 + languageName: node + linkType: hard + +"react-is@npm:^16.13.1": + version: 16.13.1 + resolution: "react-is@npm:16.13.1" + checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 + languageName: node + linkType: hard + +"react-is@npm:^17.0.2": + version: 17.0.2 + resolution: "react-is@npm:17.0.2" + checksum: 10c0/2bdb6b93fbb1820b024b496042cce405c57e2f85e777c9aabd55f9b26d145408f9f74f5934676ffdc46f3dcff656d78413a6e43968e7b3f92eea35b3052e9053 + languageName: node + linkType: hard + +"react-refresh@npm:^0.17.0": + version: 0.17.0 + resolution: "react-refresh@npm:0.17.0" + checksum: 10c0/002cba940384c9930008c0bce26cac97a9d5682bc623112c2268ba0c155127d9c178a9a5cc2212d560088d60dfd503edd808669a25f9b377f316a32361d0b23c + languageName: node + linkType: hard + +"react-transition-group@npm:^4.4.1": + version: 4.4.5 + resolution: "react-transition-group@npm:4.4.5" + dependencies: + "@babel/runtime": "npm:^7.5.5" + dom-helpers: "npm:^5.0.1" + loose-envify: "npm:^1.4.0" + prop-types: "npm:^15.6.2" + peerDependencies: + react: ">=16.6.0" + react-dom: ">=16.6.0" + checksum: 10c0/2ba754ba748faefa15f87c96dfa700d5525054a0141de8c75763aae6734af0740e77e11261a1e8f4ffc08fd9ab78510122e05c21c2d79066c38bb6861a886c82 + languageName: node + linkType: hard + +"react@npm:^18.3.1": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3 + languageName: node + linkType: hard + +"readable-stream@npm:^2.2.2, readable-stream@npm:~2.3.6": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10c0/7efdb01f3853bc35ac62ea25493567bf588773213f5f4a79f9c365e1ad13bab845ac0dae7bc946270dc40c3929483228415e92a3fc600cc7e4548992f41ee3fa + languageName: node + linkType: hard + +"readable-stream@npm:^3.4.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: "npm:^2.0.3" + string_decoder: "npm:^1.1.1" + util-deprecate: "npm:^1.0.1" + checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7 + languageName: node + linkType: hard + +"readable-stream@npm:^4.0.0": + version: 4.7.0 + resolution: "readable-stream@npm:4.7.0" + dependencies: + abort-controller: "npm:^3.0.0" + buffer: "npm:^6.0.3" + events: "npm:^3.3.0" + process: "npm:^0.11.10" + string_decoder: "npm:^1.3.0" + checksum: 10c0/fd86d068da21cfdb10f7a4479f2e47d9c0a9b0c862fc0c840a7e5360201580a55ac399c764b12a4f6fa291f8cee74d9c4b7562e0d53b3c4b2769f2c98155d957 + languageName: node + linkType: hard + +"readdirp@npm:^4.0.1": + version: 4.1.2 + resolution: "readdirp@npm:4.1.2" + checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62 + languageName: node + linkType: hard + +"real-require@npm:^0.2.0": + version: 0.2.0 + resolution: "real-require@npm:0.2.0" + checksum: 10c0/23eea5623642f0477412ef8b91acd3969015a1501ed34992ada0e3af521d3c865bb2fe4cdbfec5fe4b505f6d1ef6a03e5c3652520837a8c3b53decff7e74b6a0 + languageName: node + linkType: hard + +"reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9": + version: 1.0.10 + resolution: "reflect.getprototypeof@npm:1.0.10" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.9" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.7" + get-proto: "npm:^1.0.1" + which-builtin-type: "npm:^1.2.1" + checksum: 10c0/7facec28c8008876f8ab98e80b7b9cb4b1e9224353fd4756dda5f2a4ab0d30fa0a5074777c6df24e1e0af463a2697513b0a11e548d99cf52f21f7bc6ba48d3ac + languageName: node + linkType: hard + +"regenerator-runtime@npm:^0.14.0": + version: 0.14.1 + resolution: "regenerator-runtime@npm:0.14.1" + checksum: 10c0/1b16eb2c4bceb1665c89de70dcb64126a22bc8eb958feef3cd68fe11ac6d2a4899b5cd1b80b0774c7c03591dc57d16631a7f69d2daa2ec98100e2f29f7ec4cc4 + languageName: node + linkType: hard + +"regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": + version: 1.5.4 + resolution: "regexp.prototype.flags@npm:1.5.4" + dependencies: + call-bind: "npm:^1.0.8" + define-properties: "npm:^1.2.1" + es-errors: "npm:^1.3.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + set-function-name: "npm:^2.0.2" + checksum: 10c0/83b88e6115b4af1c537f8dabf5c3744032cb875d63bc05c288b1b8c0ef37cbe55353f95d8ca817e8843806e3e150b118bc624e4279b24b4776b4198232735a77 + languageName: node + linkType: hard + +"registry-auth-token@npm:^5.0.2": + version: 5.1.0 + resolution: "registry-auth-token@npm:5.1.0" + dependencies: + "@pnpm/npm-conf": "npm:^2.1.0" + checksum: 10c0/316229bd8a4acc29a362a7a3862ff809e608256f0fd9e0b133412b43d6a9ea18743756a0ec5ee1467a5384e1023602b85461b3d88d1336b11879e42f7cf02c12 + languageName: node + linkType: hard + +"registry-url@npm:^6.0.1": + version: 6.0.1 + resolution: "registry-url@npm:6.0.1" + dependencies: + rc: "npm:1.2.8" + checksum: 10c0/66e2221c8113fc35ee9d23fe58cb516fc8d556a189fb8d6f1011a02efccc846c4c9b5075b4027b99a5d5c9ad1345ac37f297bea3c0ca30d607ec8084bf561b90 + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"resolve@npm:^2.0.0-next.5": + version: 2.0.0-next.5 + resolution: "resolve@npm:2.0.0-next.5" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/a6c33555e3482ea2ec4c6e3d3bf0d78128abf69dca99ae468e64f1e30acaa318fd267fb66c8836b04d558d3e2d6ed875fe388067e7d8e0de647d3c21af21c43a + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^2.0.0-next.5#optional!builtin": + version: 2.0.0-next.5 + resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#optional!builtin::version=2.0.0-next.5&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/78ad6edb8309a2bfb720c2c1898f7907a37f858866ce11a5974643af1203a6a6e05b2fa9c53d8064a673a447b83d42569260c306d43628bff5bb101969708355 + languageName: node + linkType: hard + +"restore-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "restore-cursor@npm:4.0.0" + dependencies: + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/6f7da8c5e422ac26aa38354870b1afac09963572cf2879443540449068cb43476e9cbccf6f8de3e0171e0d6f7f533c2bc1a0a008003c9a525bbc098e89041318 + languageName: node + linkType: hard + +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" + dependencies: + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60 + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.1.0 + resolution: "reusify@npm:1.1.0" + checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa + languageName: node + linkType: hard + +"rfdc@npm:^1.4.1": + version: 1.4.1 + resolution: "rfdc@npm:1.4.1" + checksum: 10c0/4614e4292356cafade0b6031527eea9bc90f2372a22c012313be1dcc69a3b90c7338158b414539be863fa95bfcb2ddcd0587be696841af4e6679d85e62c060c7 + languageName: node + linkType: hard + +"rollup@npm:^4.34.9, rollup@npm:^4.40.0": + version: 4.45.1 + resolution: "rollup@npm:4.45.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.45.1" + "@rollup/rollup-android-arm64": "npm:4.45.1" + "@rollup/rollup-darwin-arm64": "npm:4.45.1" + "@rollup/rollup-darwin-x64": "npm:4.45.1" + "@rollup/rollup-freebsd-arm64": "npm:4.45.1" + "@rollup/rollup-freebsd-x64": "npm:4.45.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.45.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.45.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.45.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.45.1" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.45.1" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.45.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.45.1" + "@rollup/rollup-linux-riscv64-musl": "npm:4.45.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.45.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.45.1" + "@rollup/rollup-linux-x64-musl": "npm:4.45.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.45.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.45.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.45.1" + "@types/estree": "npm:1.0.8" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loongarch64-gnu": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/d641c283fe25d5db9e32c5bd977847cae1773f1021301d7baf936c65110e30e0608bb43cade46827df64a6149a67b853e72d3e92b46603c7ceb3b50bbd9ee1f8 + languageName: node + linkType: hard + +"rtl-css-js@npm:^1.16.1": + version: 1.16.1 + resolution: "rtl-css-js@npm:1.16.1" + dependencies: + "@babel/runtime": "npm:^7.1.2" + checksum: 10c0/4b81ef50e50c97455d61c9bb576e2892651c79bac5d0c52b4123ebb9d6a2c5144590a79c9db0a3212a81b4eb83bf317e03637220f20b387a37b96cbac324d3d2 + languageName: node + linkType: hard + +"run-applescript@npm:^5.0.0": + version: 5.0.0 + resolution: "run-applescript@npm:5.0.0" + dependencies: + execa: "npm:^5.0.0" + checksum: 10c0/f9977db5770929f3f0db434b8e6aa266498c70dec913c84320c0a06add510cf44e3a048c44da088abee312006f9cbf572fd065cdc8f15d7682afda8755f4114c + languageName: node + linkType: hard + +"run-applescript@npm:^7.0.0": + version: 7.0.0 + resolution: "run-applescript@npm:7.0.0" + checksum: 10c0/bd821bbf154b8e6c8ecffeaf0c33cebbb78eb2987476c3f6b420d67ab4c5301faa905dec99ded76ebb3a7042b4e440189ae6d85bbbd3fc6e8d493347ecda8bfe + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"safe-array-concat@npm:^1.1.3": + version: 1.1.3 + resolution: "safe-array-concat@npm:1.1.3" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.6" + has-symbols: "npm:^1.1.0" + isarray: "npm:^2.0.5" + checksum: 10c0/43c86ffdddc461fb17ff8a17c5324f392f4868f3c7dd2c6a5d9f5971713bc5fd755667212c80eab9567595f9a7509cc2f83e590ddaebd1bd19b780f9c79f9a8d + languageName: node + linkType: hard + +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 + languageName: node + linkType: hard + +"safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + +"safe-push-apply@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-push-apply@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + isarray: "npm:^2.0.5" + checksum: 10c0/831f1c9aae7436429e7862c7e46f847dfe490afac20d0ee61bae06108dbf5c745a0de3568ada30ccdd3eeb0864ca8331b2eef703abd69bfea0745b21fd320750 + languageName: node + linkType: hard + +"safe-regex-test@npm:^1.1.0": + version: 1.1.0 + resolution: "safe-regex-test@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + is-regex: "npm:^1.2.1" + checksum: 10c0/f2c25281bbe5d39cddbbce7f86fca5ea9b3ce3354ea6cd7c81c31b006a5a9fff4286acc5450a3b9122c56c33eba69c56b9131ad751457b2b4a585825e6a10665 + languageName: node + linkType: hard + +"safe-stable-stringify@npm:^2.3.1": + version: 2.5.0 + resolution: "safe-stable-stringify@npm:2.5.0" + checksum: 10c0/baea14971858cadd65df23894a40588ed791769db21bafb7fd7608397dbdce9c5aac60748abae9995e0fc37e15f2061980501e012cd48859740796bea2987f49 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"sax@npm:>=0.6.0": + version: 1.4.1 + resolution: "sax@npm:1.4.1" + checksum: 10c0/6bf86318a254c5d898ede6bd3ded15daf68ae08a5495a2739564eb265cd13bcc64a07ab466fb204f67ce472bb534eb8612dac587435515169593f4fffa11de7c + languageName: node + linkType: hard + +"scheduler@npm:0.23.0": + version: 0.23.0 + resolution: "scheduler@npm:0.23.0" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/b777f7ca0115e6d93e126ac490dbd82642d14983b3079f58f35519d992fa46260be7d6e6cede433a92db70306310c6f5f06e144f0e40c484199e09c1f7be53dd + languageName: node + linkType: hard + +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 + languageName: node + linkType: hard + +"scule@npm:^1.3.0": + version: 1.3.0 + resolution: "scule@npm:1.3.0" + checksum: 10c0/5d1736daa10622c420f2aa74e60d3c722e756bfb139fa784ae5c66669fdfe92932d30ed5072e4ce3107f9c3053e35ad73b2461cb18de45b867e1d4dea63f8823 + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.7.2 + resolution: "semver@npm:7.7.2" + bin: + semver: bin/semver.js + checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea + languageName: node + linkType: hard + +"serialize-error@npm:^11.0.0": + version: 11.0.3 + resolution: "serialize-error@npm:11.0.3" + dependencies: + type-fest: "npm:^2.12.2" + checksum: 10c0/7263603883b8936650819f0fd5150d41427b317432678b21722c54b85367ae15b8552865eb7f3f39ba71a32a003730a2e2e971e6909431eb54db70a3ef8eca17 + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.2": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.2": + version: 2.0.2 + resolution: "set-function-name@npm:2.0.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + functions-have-names: "npm:^1.2.3" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/fce59f90696c450a8523e754abb305e2b8c73586452619c2bad5f7bf38c7b6b4651895c9db895679c5bef9554339cf3ef1c329b66ece3eda7255785fbe299316 + languageName: node + linkType: hard + +"set-proto@npm:^1.0.0": + version: 1.0.0 + resolution: "set-proto@npm:1.0.0" + dependencies: + dunder-proto: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/ca5c3ccbba479d07c30460e367e66337cec825560b11e8ba9c5ebe13a2a0d6021ae34eddf94ff3dfe17a3104dc1f191519cb6c48378b503e5c3f36393938776a + languageName: node + linkType: hard + +"set-value@npm:4.1.0": + version: 4.1.0 + resolution: "set-value@npm:4.1.0" + dependencies: + is-plain-object: "npm:^2.0.4" + is-primitive: "npm:^3.0.1" + checksum: 10c0/dc186676b6cc0cfcf1656b8acdfe7a68591f0645dd2872250100817fb53e5e9298dc1727a95605ac03f82110e9b3820c90a0a02d84e0fb89f210922b08b37e02 + languageName: node + linkType: hard + +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: 10c0/5bae81bfdbfbd0ce992893286d49c9693c82b1bcc00dcaaf3a09c8f428fdeacf4190c013598b81875dfac2b08a572422db7df779a99332d0fce186d15a3e4d49 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"shell-quote@npm:1.7.3": + version: 1.7.3 + resolution: "shell-quote@npm:1.7.3" + checksum: 10c0/cf997c325f49c4393a859074f1ee9ca3da7d9e1940225bab24a86f0266504c7d7e356b83f13c74932cb243d53125b5c8c57b714017c53490bf1fe10540422014 + languageName: node + linkType: hard + +"shellwords@npm:^0.1.1": + version: 0.1.1 + resolution: "shellwords@npm:0.1.1" + checksum: 10c0/7d66b28927e0b524b71b2e185651fcd88a70473a077dd230fbf86188380e948ffb36cea00832d78fc13c93cd15f6f52286fb05f2746b7580623ca1ec619eb004 + languageName: node + linkType: hard + +"side-channel-list@npm:^1.0.0": + version: 1.0.0 + resolution: "side-channel-list@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + checksum: 10c0/644f4ac893456c9490ff388bf78aea9d333d5e5bfc64cfb84be8f04bf31ddc111a8d4b83b85d7e7e8a7b845bc185a9ad02c052d20e086983cf59f0be517d9b3d + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + checksum: 10c0/010584e6444dd8a20b85bc926d934424bd809e1a3af941cace229f7fdcb751aada0fb7164f60c2e22292b7fa3c0ff0bce237081fd4cdbc80de1dc68e95430672 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + side-channel-map: "npm:^1.0.1" + checksum: 10c0/71362709ac233e08807ccd980101c3e2d7efe849edc51455030327b059f6c4d292c237f94dc0685031dd11c07dd17a68afde235d6cf2102d949567f98ab58185 + languageName: node + linkType: hard + +"side-channel@npm:^1.1.0": + version: 1.1.0 + resolution: "side-channel@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + side-channel-list: "npm:^1.0.0" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10c0/cb20dad41eb032e6c24c0982e1e5a24963a28aa6122b4f05b3f3d6bf8ae7fd5474ef382c8f54a6a3ab86e0cac4d41a23bd64ede3970e5bfb50326ba02a7996e6 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"sisteransi@npm:^1.0.5": + version: 1.0.5 + resolution: "sisteransi@npm:1.0.5" + checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 + languageName: node + linkType: hard + +"slice-ansi@npm:^5.0.0": + version: 5.0.0 + resolution: "slice-ansi@npm:5.0.0" + dependencies: + ansi-styles: "npm:^6.0.0" + is-fullwidth-code-point: "npm:^4.0.0" + checksum: 10c0/2d4d40b2a9d5cf4e8caae3f698fe24ae31a4d778701724f578e984dcb485ec8c49f0c04dab59c401821e80fcdfe89cace9c66693b0244e40ec485d72e543914f + languageName: node + linkType: hard + +"slice-ansi@npm:^7.1.0": + version: 7.1.0 + resolution: "slice-ansi@npm:7.1.0" + dependencies: + ansi-styles: "npm:^6.2.1" + is-fullwidth-code-point: "npm:^5.0.0" + checksum: 10c0/631c971d4abf56cf880f034d43fcc44ff883624867bf11ecbd538c47343911d734a4656d7bc02362b40b89d765652a7f935595441e519b59e2ad3f4d5d6fe7ca + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.5 + resolution: "socks-proxy-agent@npm:8.0.5" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6 + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.6 + resolution: "socks@npm:2.8.6" + dependencies: + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/15b95db4caa359c80bfa880ff3e58f3191b9ffa4313570e501a60ee7575f51e4be664a296f4ee5c2c40544da179db6140be53433ce41ec745f9d51f342557514 + languageName: node + linkType: hard + +"sonic-boom@npm:^4.0.1": + version: 4.2.0 + resolution: "sonic-boom@npm:4.2.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + checksum: 10c0/ae897e6c2cd6d3cb7cdcf608bc182393b19c61c9413a85ce33ffd25891485589f39bece0db1de24381d0a38fc03d08c9862ded0c60f184f1b852f51f97af9684 + languageName: node + linkType: hard + +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"source-map-support@npm:0.5.21": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"source-map@npm:^0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 10c0/dc0cf3768fe23c345ea8760487f8c97ef6fca8a73c83cd7c9bf2fde8bc2c34adb9c0824d6feb14bc4f9e37fb522e18af621543f1289038a66ac7586da29aa7dc + languageName: node + linkType: hard + +"spawn-sync@npm:1.0.15": + version: 1.0.15 + resolution: "spawn-sync@npm:1.0.15" + dependencies: + concat-stream: "npm:^1.4.7" + os-shim: "npm:^0.1.2" + checksum: 10c0/7c4b626a075940c7ffccbcf612a0ff88316fdb2266be40a824e90e60092278025f055e6f9895eae45ff828bae0add181cc88c70e6c32ca3ee38823110055f6eb + languageName: node + linkType: hard + +"split2@npm:^4.0.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 10c0/b292beb8ce9215f8c642bb68be6249c5a4c7f332fc8ecadae7be5cbdf1ea95addc95f0459ef2e7ad9d45fd1064698a097e4eb211c83e772b49bc0ee423e91534 + languageName: node + linkType: hard + +"split@npm:~1.0.1": + version: 1.0.1 + resolution: "split@npm:1.0.1" + dependencies: + through: "npm:2" + checksum: 10c0/7f489e7ed5ff8a2e43295f30a5197ffcb2d6202c9cf99357f9690d645b19c812bccf0be3ff336fea5054cda17ac96b91d67147d95dbfc31fbb5804c61962af85 + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec + languageName: node + linkType: hard + +"ssri@npm:^12.0.0": + version: 12.0.0 + resolution: "ssri@npm:12.0.0" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d + languageName: node + linkType: hard + +"stdin-discarder@npm:^0.1.0": + version: 0.1.0 + resolution: "stdin-discarder@npm:0.1.0" + dependencies: + bl: "npm:^5.0.0" + checksum: 10c0/3bbf7f8107e49c05b4a46bd739afdd34605cf1f06a038c8b2a33d034bf146344fc0ebc5149df1e6422510dd219971a220f25b1102413ef5128fe267683fbef9d + languageName: node + linkType: hard + +"stdin-discarder@npm:^0.2.2": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 10c0/c78375e82e956d7a64be6e63c809c7f058f5303efcaf62ea48350af072bacdb99c06cba39209b45a071c1acbd49116af30df1df9abb448df78a6005b72f10537 + languageName: node + linkType: hard + +"stop-iteration-iterator@npm:^1.1.0": + version: 1.1.0 + resolution: "stop-iteration-iterator@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + internal-slot: "npm:^1.1.0" + checksum: 10c0/de4e45706bb4c0354a4b1122a2b8cc45a639e86206807ce0baf390ee9218d3ef181923fa4d2b67443367c491aa255c5fbaa64bb74648e3c5b48299928af86c09 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + +"string.prototype.matchall@npm:^4.0.12": + version: 4.0.12 + resolution: "string.prototype.matchall@npm:4.0.12" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.3" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.6" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.6" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + internal-slot: "npm:^1.1.0" + regexp.prototype.flags: "npm:^1.5.3" + set-function-name: "npm:^2.0.2" + side-channel: "npm:^1.1.0" + checksum: 10c0/1a53328ada73f4a77f1fdf1c79414700cf718d0a8ef6672af5603e709d26a24f2181208144aed7e858b1bcc1a0d08567a570abfb45567db4ae47637ed2c2f85c + languageName: node + linkType: hard + +"string.prototype.repeat@npm:^1.0.0": + version: 1.0.0 + resolution: "string.prototype.repeat@npm:1.0.0" + dependencies: + define-properties: "npm:^1.1.3" + es-abstract: "npm:^1.17.5" + checksum: 10c0/94c7978566cffa1327d470fd924366438af9b04b497c43a9805e476e2e908aa37a1fd34cc0911156c17556dab62159d12c7b92b3cc304c3e1281fe4c8e668f40 + languageName: node + linkType: hard + +"string.prototype.trim@npm:^1.2.10": + version: 1.2.10 + resolution: "string.prototype.trim@npm:1.2.10" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.2" + define-data-property: "npm:^1.1.4" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.5" + es-object-atoms: "npm:^1.0.0" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/8a8854241c4b54a948e992eb7dd6b8b3a97185112deb0037a134f5ba57541d8248dd610c966311887b6c2fd1181a3877bffb14d873ce937a344535dabcc648f8 + languageName: node + linkType: hard + +"string.prototype.trimend@npm:^1.0.9": + version: 1.0.9 + resolution: "string.prototype.trimend@npm:1.0.9" + dependencies: + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.2" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/59e1a70bf9414cb4c536a6e31bef5553c8ceb0cf44d8b4d0ed65c9653358d1c64dd0ec203b100df83d0413bbcde38b8c5d49e14bc4b86737d74adc593a0d35b6 + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.8": + version: 1.0.8 + resolution: "string.prototype.trimstart@npm:1.0.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/d53af1899959e53c83b64a5fd120be93e067da740e7e75acb433849aa640782fb6c7d4cd5b84c954c84413745a3764df135a8afeb22908b86a835290788d8366 + languageName: node + linkType: hard + +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: "npm:~5.2.0" + checksum: 10c0/810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d + languageName: node + linkType: hard + +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10c0/b4f89f3a92fd101b5653ca3c99550e07bdf9e13b35037e9e2a1c7b47cec4e55e06ff3fc468e314a0b5e80bfbaf65c1ca5a84978764884ae9413bec1fc6ca924e + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4 + languageName: node + linkType: hard + +"strip-bom@npm:5.0.0": + version: 5.0.0 + resolution: "strip-bom@npm:5.0.0" + checksum: 10c0/f87e212f8a41e08e77d3994b3d9c4112258bd3a13688f9c7c85a6705a87a8e526422bce0762326cc2d9655c32a8c0ff1a2b14936795384c353828e4637823bc6 + languageName: node + linkType: hard + +"strip-final-newline@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-final-newline@npm:2.0.0" + checksum: 10c0/bddf8ccd47acd85c0e09ad7375409d81653f645fda13227a9d459642277c253d877b68f2e5e4d819fe75733b0e626bac7e954c04f3236f6d196f79c94fa4a96f + languageName: node + linkType: hard + +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce + languageName: node + linkType: hard + +"strip-json-comments@npm:5.0.1": + version: 5.0.1 + resolution: "strip-json-comments@npm:5.0.1" + checksum: 10c0/c9d9d55a0167c57aa688df3aa20628cf6f46f0344038f189eaa9d159978e80b2bfa6da541a40d83f7bde8a3554596259bf6b70578b2172356536a0e3fa5a0982 + languageName: node + linkType: hard + +"strip-json-comments@npm:^3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd + languageName: node + linkType: hard + +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 + languageName: node + linkType: hard + +"strip-literal@npm:^2.1.1": + version: 2.1.1 + resolution: "strip-literal@npm:2.1.1" + dependencies: + js-tokens: "npm:^9.0.1" + checksum: 10c0/66a7353f5ba1ae6a4fb2805b4aba228171847200640083117c41512692e6b2c020e18580402984f55c0ae69c30f857f9a55abd672863e4ca8fdb463fdf93ba19 + languageName: node + linkType: hard + +"stubborn-fs@npm:^1.2.5": + version: 1.2.5 + resolution: "stubborn-fs@npm:1.2.5" + checksum: 10c0/0676befd9901d4dd4e162700fa0396f11d523998589cd6b61b06d1021db811dc4c1e6713869748c6cfa49d58beb9b6f0dc5b6aca6b075811b949e1602ce1e26f + languageName: node + linkType: hard + +"stylis@npm:^4.2.0": + version: 4.3.6 + resolution: "stylis@npm:4.3.6" + checksum: 10c0/e736d484983a34f7c65d362c67dc79b7bce388054b261c2b7b23d02eaaf280617033f65d44b1ea341854f4331a5074b885668ac8741f98c13a6cfd6443ae85d0 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + +"tabs-aside@workspace:.": + version: 0.0.0-use.local + resolution: "tabs-aside@workspace:." + dependencies: + "@dnd-kit/core": "npm:^6.3.1" + "@dnd-kit/modifiers": "npm:^9.0.0" + "@dnd-kit/sortable": "npm:^10.0.0" + "@dnd-kit/utilities": "npm:^3.2.2" + "@eslint/css": "npm:^0.10.0" + "@eslint/js": "npm:^9.32.0" + "@eslint/json": "npm:^0.13.1" + "@fluentui/react-components": "npm:^9.68.1" + "@fluentui/react-icons": "npm:^2.0.307" + "@stylistic/eslint-plugin": "npm:^5.2.2" + "@types/react": "npm:^18.3.1" + "@types/react-dom": "npm:^18.3.1" + "@types/scheduler": "npm:0.23.0" + "@webext-core/messaging": "npm:^2.3.0" + "@wxt-dev/analytics": "npm:^0.4.1" + "@wxt-dev/i18n": "npm:^0.2.4" + "@wxt-dev/module-react": "npm:^1.1.3" + eslint: "npm:^9.32.0" + eslint-plugin-react: "npm:^7.37.5" + globals: "npm:^16.3.0" + lzutf8: "npm:^0.6.3" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" + scheduler: "npm:0.23.0" + typescript: "npm:^5.8.3" + typescript-eslint: "npm:^8.38.0" + vite: "npm:^7.0.6" + wxt: "npm:~0.19.29" + languageName: unknown + linkType: soft + +"tabster@npm:^8.5.5": + version: 8.5.6 + resolution: "tabster@npm:8.5.6" + dependencies: + "@rollup/rollup-linux-x64-gnu": "npm:4.40.0" + keyborg: "npm:2.6.0" + tslib: "npm:^2.8.1" + dependenciesMeta: + "@rollup/rollup-linux-x64-gnu": + optional: true + checksum: 10c0/6b04b36bb511af1c79b0ad0cef8d6a90fa3cdabb969bd80b70aa73c139518b165cbb3df6d4770c80c8eecaac4b5d6160fb9fcf7ba8338e7831e26040cfadea48 + languageName: node + linkType: hard + +"tar@npm:^6.2.1": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: "npm:^2.0.0" + fs-minipass: "npm:^2.0.0" + minipass: "npm:^5.0.0" + minizlib: "npm:^2.1.1" + mkdirp: "npm:^1.0.3" + yallist: "npm:^4.0.0" + checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 + languageName: node + linkType: hard + +"tar@npm:^7.4.3": + version: 7.4.3 + resolution: "tar@npm:7.4.3" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.0.1" + mkdirp: "npm:^3.0.1" + yallist: "npm:^5.0.0" + checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d + languageName: node + linkType: hard + +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + +"thread-stream@npm:^3.0.0": + version: 3.1.0 + resolution: "thread-stream@npm:3.1.0" + dependencies: + real-require: "npm:^0.2.0" + checksum: 10c0/c36118379940b77a6ef3e6f4d5dd31e97b8210c3f7b9a54eb8fe6358ab173f6d0acfaf69b9c3db024b948c0c5fd2a7df93e2e49151af02076b35ada3205ec9a6 + languageName: node + linkType: hard + +"through@npm:2": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: 10c0/4b09f3774099de0d4df26d95c5821a62faee32c7e96fb1f4ebd54a2d7c11c57fe88b0a0d49cf375de5fee5ae6bf4eb56dbbf29d07366864e2ee805349970d3cc + languageName: node + linkType: hard + +"tinyexec@npm:^0.3.2": + version: 0.3.2 + resolution: "tinyexec@npm:0.3.2" + checksum: 10c0/3efbf791a911be0bf0821eab37a3445c2ba07acc1522b1fa84ae1e55f10425076f1290f680286345ed919549ad67527d07281f1c19d584df3b74326909eb1f90 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.14": + version: 0.2.14 + resolution: "tinyglobby@npm:0.2.14" + dependencies: + fdir: "npm:^6.4.4" + picomatch: "npm:^4.0.2" + checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6 + languageName: node + linkType: hard + +"titleize@npm:^3.0.0": + version: 3.0.0 + resolution: "titleize@npm:3.0.0" + checksum: 10c0/5ae6084ba299b5782f95e3fe85ea9f0fa4d74b8ae722b6b3208157e975589fbb27733aeba4e5080fa9314a856044ef52caa61b87caea4b1baade951a55c06336 + languageName: node + linkType: hard + +"tmp@npm:0.2.3": + version: 0.2.3 + resolution: "tmp@npm:0.2.3" + checksum: 10c0/3e809d9c2f46817475b452725c2aaa5d11985cf18d32a7a970ff25b568438e2c076c2e8609224feef3b7923fa9749b74428e3e634f6b8e520c534eef2fd24125 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"ts-api-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "ts-api-utils@npm:2.1.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10c0/9806a38adea2db0f6aa217ccc6bc9c391ddba338a9fe3080676d0d50ed806d305bb90e8cef0276e793d28c8a929f400abb184ddd7ff83a416959c0f4d2ce754f + languageName: node + linkType: hard + +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: "npm:^1.2.1" + checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58 + languageName: node + linkType: hard + +"type-fest@npm:^2.12.2": + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: 10c0/a5a7ecf2e654251613218c215c7493574594951c08e52ab9881c9df6a6da0aeca7528c213c622bc374b4e0cb5c443aa3ab758da4e3c959783ce884c3194e12cb + languageName: node + linkType: hard + +"type-fest@npm:^3.8.0": + version: 3.13.1 + resolution: "type-fest@npm:3.13.1" + checksum: 10c0/547d22186f73a8c04590b70dcf63baff390078c75ea8acd366bbd510fd0646e348bd1970e47ecf795b7cff0b41d26e9c475c1fedd6ef5c45c82075fbf916b629 + languageName: node + linkType: hard + +"type-fest@npm:^4.18.2, type-fest@npm:^4.21.0": + version: 4.41.0 + resolution: "type-fest@npm:4.41.0" + checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-buffer@npm:1.0.3" + dependencies: + call-bound: "npm:^1.0.3" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.14" + checksum: 10c0/1105071756eb248774bc71646bfe45b682efcad93b55532c6ffa4518969fb6241354e4aa62af679ae83899ec296d69ef88f1f3763657cdb3a4d29321f7b83079 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.3": + version: 1.0.3 + resolution: "typed-array-byte-length@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.8" + for-each: "npm:^0.3.3" + gopd: "npm:^1.2.0" + has-proto: "npm:^1.2.0" + is-typed-array: "npm:^1.1.14" + checksum: 10c0/6ae083c6f0354f1fce18b90b243343b9982affd8d839c57bbd2c174a5d5dc71be9eb7019ffd12628a96a4815e7afa85d718d6f1e758615151d5f35df841ffb3e + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.4": + version: 1.0.4 + resolution: "typed-array-byte-offset@npm:1.0.4" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + for-each: "npm:^0.3.3" + gopd: "npm:^1.2.0" + has-proto: "npm:^1.2.0" + is-typed-array: "npm:^1.1.15" + reflect.getprototypeof: "npm:^1.0.9" + checksum: 10c0/3d805b050c0c33b51719ee52de17c1cd8e6a571abdf0fffb110e45e8dd87a657e8b56eee94b776b13006d3d347a0c18a730b903cf05293ab6d92e99ff8f77e53 + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.7": + version: 1.0.7 + resolution: "typed-array-length@npm:1.0.7" + dependencies: + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + is-typed-array: "npm:^1.1.13" + possible-typed-array-names: "npm:^1.0.0" + reflect.getprototypeof: "npm:^1.0.6" + checksum: 10c0/e38f2ae3779584c138a2d8adfa8ecf749f494af3cd3cdafe4e688ce51418c7d2c5c88df1bd6be2bbea099c3f7cea58c02ca02ed438119e91f162a9de23f61295 + languageName: node + linkType: hard + +"typedarray@npm:^0.0.6": + version: 0.0.6 + resolution: "typedarray@npm:0.0.6" + checksum: 10c0/6005cb31df50eef8b1f3c780eb71a17925f3038a100d82f9406ac2ad1de5eb59f8e6decbdc145b3a1f8e5836e17b0c0002fb698b9fe2516b8f9f9ff602d36412 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.38.0": + version: 8.38.0 + resolution: "typescript-eslint@npm:8.38.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.38.0" + "@typescript-eslint/parser": "npm:8.38.0" + "@typescript-eslint/typescript-estree": "npm:8.38.0" + "@typescript-eslint/utils": "npm:8.38.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10c0/486b9862ee08f7827d808a2264ce03b58087b11c4c646c0da3533c192a67ae3fcb4e68d7a1e69d0f35a1edc274371a903a50ecfe74012d5eaa896cb9d5a81e0b + languageName: node + linkType: hard + +"typescript@npm:^5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb + languageName: node + linkType: hard + +"ua-parser-js@npm:^1.0.38": + version: 1.0.40 + resolution: "ua-parser-js@npm:1.0.40" + bin: + ua-parser-js: script/cli.js + checksum: 10c0/2b6ac642c74323957dae142c31f72287f2420c12dced9603d989b96c132b80232779c429b296d7de4012ef8b64e0d8fadc53c639ef06633ce13d785a78b5be6c + languageName: node + linkType: hard + +"ufo@npm:^1.5.4": + version: 1.6.1 + resolution: "ufo@npm:1.6.1" + checksum: 10c0/5a9f041e5945fba7c189d5410508cbcbefef80b253ed29aa2e1f8a2b86f4bd51af44ee18d4485e6d3468c92be9bf4a42e3a2b72dcaf27ce39ce947ec994f1e6b + languageName: node + linkType: hard + +"uhyphen@npm:^0.2.0": + version: 0.2.0 + resolution: "uhyphen@npm:0.2.0" + checksum: 10c0/1e7129fe7a5c86445d1adf04d5c58913b5992e4899ea5553d9ddf6e7ef88af0f807a47f1bf9673b92f705276e5cf1b2c1d3852f1ab5d08ecac3382bcc3a642f9 + languageName: node + linkType: hard + +"uid@npm:^2.0.2": + version: 2.0.2 + resolution: "uid@npm:2.0.2" + dependencies: + "@lukeed/csprng": "npm:^1.0.0" + checksum: 10c0/e9d02d0562c74e74b5a2519e586db9d7f8204978e476cddd191ee1a9efb85efafdbab2dbf3fc3dde0f5da01fd9da161f37d604dabf513447fd2c03d008f1324c + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.1.0": + version: 1.1.0 + resolution: "unbox-primitive@npm:1.1.0" + dependencies: + call-bound: "npm:^1.0.3" + has-bigints: "npm:^1.0.2" + has-symbols: "npm:^1.1.0" + which-boxed-primitive: "npm:^1.1.1" + checksum: 10c0/7dbd35ab02b0e05fe07136c72cb9355091242455473ec15057c11430129bab38b7b3624019b8778d02a881c13de44d63cd02d122ee782fb519e1de7775b5b982 + languageName: node + linkType: hard + +"undici-types@npm:~7.8.0": + version: 7.8.0 + resolution: "undici-types@npm:7.8.0" + checksum: 10c0/9d9d246d1dc32f318d46116efe3cfca5a72d4f16828febc1918d94e58f6ffcf39c158aa28bf5b4fc52f410446bc7858f35151367bd7a49f21746cab6497b709b + languageName: node + linkType: hard + +"unimport@npm:^3.13.1": + version: 3.14.6 + resolution: "unimport@npm:3.14.6" + dependencies: + "@rollup/pluginutils": "npm:^5.1.4" + acorn: "npm:^8.14.0" + escape-string-regexp: "npm:^5.0.0" + estree-walker: "npm:^3.0.3" + fast-glob: "npm:^3.3.3" + local-pkg: "npm:^1.0.0" + magic-string: "npm:^0.30.17" + mlly: "npm:^1.7.4" + pathe: "npm:^2.0.1" + picomatch: "npm:^4.0.2" + pkg-types: "npm:^1.3.0" + scule: "npm:^1.3.0" + strip-literal: "npm:^2.1.1" + unplugin: "npm:^1.16.1" + checksum: 10c0/041cd6d2c85483e68e900c3ae55ddfd60f20b1a43016f6f810e970aba552db2ea5e03817f7c79c16d8648e5757d289cffc6b01f141aa579dbbb4fab6f7a3a4b3 + languageName: node + linkType: hard + +"unique-filename@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-filename@npm:4.0.0" + dependencies: + unique-slug: "npm:^5.0.0" + checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc + languageName: node + linkType: hard + +"unique-slug@npm:^5.0.0": + version: 5.0.0 + resolution: "unique-slug@npm:5.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293 + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + +"unplugin@npm:^1.16.1": + version: 1.16.1 + resolution: "unplugin@npm:1.16.1" + dependencies: + acorn: "npm:^8.14.0" + webpack-virtual-modules: "npm:^0.6.2" + checksum: 10c0/dd5f8c5727d0135847da73cf03fb199107f1acf458167034886fda3405737dab871ad3926431b4f70e1e82cdac482ac1383cea4019d782a68515c8e3e611b6cc + languageName: node + linkType: hard + +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: 10c0/d758e624c707d49f76f7511d75d09a8eda7f2020d231ec52b67ff4896bcf7013be3f9522d8375f57e586e9a2e827f5641c7e06ee46ab9c435fc2b2b2e9de517a + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.1.3": + version: 1.1.3 + resolution: "update-browserslist-db@npm:1.1.3" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/682e8ecbf9de474a626f6462aa85927936cdd256fe584c6df2508b0df9f7362c44c957e9970df55dfe44d3623807d26316ea2c7d26b80bb76a16c56c37233c32 + languageName: node + linkType: hard + +"update-notifier@npm:7.3.1": + version: 7.3.1 + resolution: "update-notifier@npm:7.3.1" + dependencies: + boxen: "npm:^8.0.1" + chalk: "npm:^5.3.0" + configstore: "npm:^7.0.0" + is-in-ci: "npm:^1.0.0" + is-installed-globally: "npm:^1.0.0" + is-npm: "npm:^6.0.0" + latest-version: "npm:^9.0.0" + pupa: "npm:^3.1.0" + semver: "npm:^7.6.3" + xdg-basedir: "npm:^5.1.0" + checksum: 10c0/678839453840f46bb75e8cfebc0ff522262d2d3ece343fca722dd506039832e2a952d14ae39153f05f684467c8293ebc4c6479c9652c1bf97908fcaf300c2b31 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c + languageName: node + linkType: hard + +"use-disposable@npm:^1.0.1": + version: 1.0.4 + resolution: "use-disposable@npm:1.0.4" + peerDependencies: + "@types/react": ">=16.8.0 <19.0.0" + "@types/react-dom": ">=16.8.0 <19.0.0" + react: ">=16.8.0 <19.0.0" + react-dom: ">=16.8.0 <19.0.0" + checksum: 10c0/4ce459e9e90345618c1f77ceb7076e75b00a6fd9f7f9415dff8ca90b5c1d549135a4e4164c116888beab89e018c64baca374889624c64384f33e12b2bd979a44 + languageName: node + linkType: hard + +"use-sync-external-store@npm:^1.2.0": + version: 1.5.0 + resolution: "use-sync-external-store@npm:1.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/1b8663515c0be34fa653feb724fdcce3984037c78dd4a18f68b2c8be55cc1a1084c578d5b75f158d41b5ddffc2bf5600766d1af3c19c8e329bb20af2ec6f52f4 + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54 + languageName: node + linkType: hard + +"vite-node@npm:^2.1.4 || ^3.0.0": + version: 3.2.4 + resolution: "vite-node@npm:3.2.4" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.4.1" + es-module-lexer: "npm:^1.7.0" + pathe: "npm:^2.0.3" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/6ceca67c002f8ef6397d58b9539f80f2b5d79e103a18367288b3f00a8ab55affa3d711d86d9112fce5a7fa658a212a087a005a045eb8f4758947dd99af2a6c6b + languageName: node + linkType: hard + +"vite@npm:^5.0.0 || ^6.0.0": + version: 6.3.5 + resolution: "vite@npm:6.3.5" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.4" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" + postcss: "npm:^8.5.3" + rollup: "npm:^4.34.9" + tinyglobby: "npm:^0.2.13" + peerDependencies: + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: ">=1.21.0" + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/df70201659085133abffc6b88dcdb8a57ef35f742a01311fc56a4cfcda6a404202860729cc65a2c401a724f6e25f9ab40ce4339ed4946f550541531ced6fe41c + languageName: node + linkType: hard + +"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": + version: 7.0.5 + resolution: "vite@npm:7.0.5" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.6" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" + postcss: "npm:^8.5.6" + rollup: "npm:^4.40.0" + tinyglobby: "npm:^0.2.14" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/95fb7f393adc2d2428e0e033ed4ea599323118f645415c35db3df40bd2c5a2962b0b4c967f35cfb80ca04efb1eb51bdb5f4b63529bb5f9aa3603d9fbeb50543f + languageName: node + linkType: hard + +"vite@npm:^7.0.6": + version: 7.0.6 + resolution: "vite@npm:7.0.6" + dependencies: + esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.6" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.40.0" + tinyglobby: "npm:^0.2.14" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/3b14dfa661281b4843789884199ba2a9cca940a7666970036fe3fb1abff52b88e63e8be5ab419dd04d9f96c0415ee0f1e3ec8ebe357041648af7ccd8e348b6ad + languageName: node + linkType: hard + +"watchpack@npm:2.4.2": + version: 2.4.2 + resolution: "watchpack@npm:2.4.2" + dependencies: + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.1.2" + checksum: 10c0/ec60a5f0e9efaeca0102fd9126346b3b2d523e01c34030d3fddf5813a7125765121ebdc2552981136dcd2c852deb1af0b39340f2fcc235f292db5399d0283577 + languageName: node + linkType: hard + +"wcwidth@npm:^1.0.1": + version: 1.0.1 + resolution: "wcwidth@npm:1.0.1" + dependencies: + defaults: "npm:^1.0.3" + checksum: 10c0/5b61ca583a95e2dd85d7078400190efd452e05751a64accb8c06ce4db65d7e0b0cde9917d705e826a2e05cc2548f61efde115ffa374c3e436d04be45c889e5b4 + languageName: node + linkType: hard + +"web-ext-run@npm:^0.2.1": + version: 0.2.3 + resolution: "web-ext-run@npm:0.2.3" + dependencies: + "@babel/runtime": "npm:7.27.0" + "@devicefarmer/adbkit": "npm:3.3.8" + chrome-launcher: "npm:1.1.2" + debounce: "npm:1.2.1" + es6-error: "npm:4.1.1" + firefox-profile: "npm:4.7.0" + fx-runner: "npm:1.4.0" + multimatch: "npm:6.0.0" + node-notifier: "npm:10.0.1" + parse-json: "npm:7.1.1" + pino: "npm:9.6.0" + promise-toolbox: "npm:0.21.0" + set-value: "npm:4.1.0" + source-map-support: "npm:0.5.21" + strip-bom: "npm:5.0.0" + strip-json-comments: "npm:5.0.1" + tmp: "npm:0.2.3" + update-notifier: "npm:7.3.1" + watchpack: "npm:2.4.2" + ws: "npm:8.18.1" + zip-dir: "npm:2.0.0" + checksum: 10c0/fe14fb1b3d2a6eebf7d553c1f692c77f3d091f3d55b15ca5e471483da54e4b2602275a44dead7427a500c1147d8ab1ced48261e9eaddcd8a3e34e967314c20bd + languageName: node + linkType: hard + +"webextension-polyfill@npm:^0.10.0": + version: 0.10.0 + resolution: "webextension-polyfill@npm:0.10.0" + checksum: 10c0/6a45278f1fed8fbd5355f9b19a7b0b3fadc91fa3a6eef69125a1706bb3efa2181235eefbfb3f538443bb396cfcb97512361551888ce8465c08914431cb2d5b6d + languageName: node + linkType: hard + +"webextension-polyfill@npm:^0.12.0": + version: 0.12.0 + resolution: "webextension-polyfill@npm:0.12.0" + checksum: 10c0/5ace2aaaf6a203515bdd2fb948622f186a5fbb50099b539ce9c0ad54896f9cc1fcc3c0e2a71d1f7071dd7236d7daebba1e0cbcf43bfdfe54361addf0333ee7d1 + languageName: node + linkType: hard + +"webpack-virtual-modules@npm:^0.6.2": + version: 0.6.2 + resolution: "webpack-virtual-modules@npm:0.6.2" + checksum: 10c0/5ffbddf0e84bf1562ff86cf6fcf039c74edf09d78358a6904a09bbd4484e8bb6812dc385fe14330b715031892dcd8423f7a88278b57c9f5002c84c2860179add + languageName: node + linkType: hard + +"when-exit@npm:^2.1.1": + version: 2.1.4 + resolution: "when-exit@npm:2.1.4" + checksum: 10c0/d8ffba54afca880de6f366ab06a32e8fab99fa298a3f79b2d5304bab19d290f55c5f081336cdaa65d0b6e9a842b46a46bab0800e94e755ea599a2082224a8cc0 + languageName: node + linkType: hard + +"when@npm:3.7.7": + version: 3.7.7 + resolution: "when@npm:3.7.7" + checksum: 10c0/2385c08ea86e74060248acf607526e75addf64ad7c5bae5563a42b7afa2dbf181d7fd8a247f27fdb7ccac9768e765805489f47242f99082ece765805f5cb3e3d + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": + version: 1.1.1 + resolution: "which-boxed-primitive@npm:1.1.1" + dependencies: + is-bigint: "npm:^1.1.0" + is-boolean-object: "npm:^1.2.1" + is-number-object: "npm:^1.1.1" + is-string: "npm:^1.1.1" + is-symbol: "npm:^1.1.1" + checksum: 10c0/aceea8ede3b08dede7dce168f3883323f7c62272b49801716e8332ff750e7ae59a511ae088840bc6874f16c1b7fd296c05c949b0e5b357bfe3c431b98c417abe + languageName: node + linkType: hard + +"which-builtin-type@npm:^1.2.1": + version: 1.2.1 + resolution: "which-builtin-type@npm:1.2.1" + dependencies: + call-bound: "npm:^1.0.2" + function.prototype.name: "npm:^1.1.6" + has-tostringtag: "npm:^1.0.2" + is-async-function: "npm:^2.0.0" + is-date-object: "npm:^1.1.0" + is-finalizationregistry: "npm:^1.1.0" + is-generator-function: "npm:^1.0.10" + is-regex: "npm:^1.2.1" + is-weakref: "npm:^1.0.2" + isarray: "npm:^2.0.5" + which-boxed-primitive: "npm:^1.1.0" + which-collection: "npm:^1.0.2" + which-typed-array: "npm:^1.1.16" + checksum: 10c0/8dcf323c45e5c27887800df42fbe0431d0b66b1163849bb7d46b5a730ad6a96ee8bfe827d078303f825537844ebf20c02459de41239a0a9805e2fcb3cae0d471 + languageName: node + linkType: hard + +"which-collection@npm:^1.0.2": + version: 1.0.2 + resolution: "which-collection@npm:1.0.2" + dependencies: + is-map: "npm:^2.0.3" + is-set: "npm:^2.0.3" + is-weakmap: "npm:^2.0.2" + is-weakset: "npm:^2.0.3" + checksum: 10c0/3345fde20964525a04cdf7c4a96821f85f0cc198f1b2ecb4576e08096746d129eb133571998fe121c77782ac8f21cbd67745a3d35ce100d26d4e684c142ea1f2 + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": + version: 1.1.19 + resolution: "which-typed-array@npm:1.1.19" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" + for-each: "npm:^0.3.5" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/702b5dc878addafe6c6300c3d0af5983b175c75fcb4f2a72dfc3dd38d93cf9e89581e4b29c854b16ea37e50a7d7fca5ae42ece5c273d8060dcd603b2404bbb3f + languageName: node + linkType: hard + +"which@npm:1.2.4": + version: 1.2.4 + resolution: "which@npm:1.2.4" + dependencies: + is-absolute: "npm:^0.1.7" + isexe: "npm:^1.1.1" + bin: + which: ./bin/which + checksum: 10c0/618944508e04fefa02fa811b1a68d8a27b4f712f2f8332c27ed8bf8d1dc7e469bb9bbe20b4e197311ce798c16bb96b5c5e32ceaf275a3b5388bd8144536f5247 + languageName: node + linkType: hard + +"which@npm:^2.0.1, which@npm:^2.0.2": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"which@npm:^5.0.0": + version: 5.0.0 + resolution: "which@npm:5.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/e556e4cd8b7dbf5df52408c9a9dd5ac6518c8c5267c8953f5b0564073c66ed5bf9503b14d876d0e9c7844d4db9725fb0dcf45d6e911e17e26ab363dc3965ae7b + languageName: node + linkType: hard + +"widest-line@npm:^5.0.0": + version: 5.0.0 + resolution: "widest-line@npm:5.0.0" + dependencies: + string-width: "npm:^7.0.0" + checksum: 10c0/6bd6cca8cda502ef50e05353fd25de0df8c704ffc43ada7e0a9cf9a5d4f4e12520485d80e0b77cec8a21f6c3909042fcf732aa9281e5dbb98cc9384a138b2578 + languageName: node + linkType: hard + +"winreg@npm:0.0.12": + version: 0.0.12 + resolution: "winreg@npm:0.0.12" + checksum: 10c0/148b6aca1c3e88badd0d2b77ee0a71f1033e22e1cfcb41b71a5bba9e97cb3e7b6a2ec6b00cf0397959a13d65577d9173932588b3cd57b3f2e774b77ad14394ba + languageName: node + linkType: hard + +"word-wrap@npm:^1.2.5": + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20 + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"wrap-ansi@npm:^9.0.0": + version: 9.0.0 + resolution: "wrap-ansi@npm:9.0.0" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/a139b818da9573677548dd463bd626a5a5286271211eb6e4e82f34a4f643191d74e6d4a9bb0a3c26ec90e6f904f679e0569674ac099ea12378a8b98e20706066 + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 + languageName: node + linkType: hard + +"ws@npm:8.18.1": + version: 8.18.1 + resolution: "ws@npm:8.18.1" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/e498965d6938c63058c4310ffb6967f07d4fa06789d3364829028af380d299fe05762961742971c764973dce3d1f6a2633fe8b2d9410c9b52e534b4b882a99fa + languageName: node + linkType: hard + +"wsl-utils@npm:^0.1.0": + version: 0.1.0 + resolution: "wsl-utils@npm:0.1.0" + dependencies: + is-wsl: "npm:^3.1.0" + checksum: 10c0/44318f3585eb97be994fc21a20ddab2649feaf1fbe893f1f866d936eea3d5f8c743bec6dc02e49fbdd3c0e69e9b36f449d90a0b165a4f47dd089747af4cf2377 + languageName: node + linkType: hard + +"wxt@npm:~0.19.29": + version: 0.19.29 + resolution: "wxt@npm:0.19.29" + dependencies: + "@1natsu/wait-element": "npm:^4.1.2" + "@aklinker1/rollup-plugin-visualizer": "npm:5.12.0" + "@types/chrome": "npm:^0.0.280" + "@types/webextension-polyfill": "npm:^0.12.1" + "@webext-core/fake-browser": "npm:^1.3.1" + "@webext-core/isolated-element": "npm:^1.1.2" + "@webext-core/match-patterns": "npm:^1.0.3" + "@wxt-dev/storage": "npm:^1.0.0" + async-mutex: "npm:^0.5.0" + c12: "npm:^3.0.2" + cac: "npm:^6.7.14" + chokidar: "npm:^4.0.3" + ci-info: "npm:^4.1.0" + consola: "npm:^3.2.3" + defu: "npm:^6.1.4" + dotenv: "npm:^16.4.5" + dotenv-expand: "npm:^12.0.1" + esbuild: "npm:^0.25.0" + fast-glob: "npm:^3.3.2" + filesize: "npm:^10.1.6" + fs-extra: "npm:^11.2.0" + get-port-please: "npm:^3.1.2" + giget: "npm:^1.2.3" + hookable: "npm:^5.5.3" + import-meta-resolve: "npm:^4.1.0" + is-wsl: "npm:^3.1.0" + jiti: "npm:^2.4.2" + json5: "npm:^2.2.3" + jszip: "npm:^3.10.1" + linkedom: "npm:^0.18.5" + magicast: "npm:^0.3.5" + minimatch: "npm:^10.0.1" + nano-spawn: "npm:^0.2.0" + normalize-path: "npm:^3.0.0" + nypm: "npm:^0.3.12" + ohash: "npm:^1.1.4" + open: "npm:^10.1.0" + ora: "npm:^8.1.1" + perfect-debounce: "npm:^1.0.0" + picocolors: "npm:^1.1.1" + prompts: "npm:^2.4.2" + publish-browser-extension: "npm:^2.3.0 || ^3.0.0" + scule: "npm:^1.3.0" + unimport: "npm:^3.13.1" + vite: "npm:^5.0.0 || ^6.0.0" + vite-node: "npm:^2.1.4 || ^3.0.0" + web-ext-run: "npm:^0.2.1" + webextension-polyfill: "npm:^0.12.0" + peerDependenciesMeta: + "@types/chrome": + optional: true + bin: + wxt: bin/wxt.mjs + wxt-publish-extension: bin/wxt-publish-extension.cjs + checksum: 10c0/c59d456bb112302694a94326b01fc345cfddf25ede449d91c72a48c6aed6120e259790738b7fba16bd3803f75f2c03c9ae07a21abb32826c06df81bcf87647f7 + languageName: node + linkType: hard + +"xdg-basedir@npm:^5.1.0": + version: 5.1.0 + resolution: "xdg-basedir@npm:5.1.0" + checksum: 10c0/c88efabc71ffd996ba9ad8923a8cc1c7c020a03e2c59f0ffa72e06be9e724ad2a0fccef488757bc6ed3d8849d753dd25082d1035d95cb179e79eae4d034d0b80 + languageName: node + linkType: hard + +"xml2js@npm:^0.6.2": + version: 0.6.2 + resolution: "xml2js@npm:0.6.2" + dependencies: + sax: "npm:>=0.6.0" + xmlbuilder: "npm:~11.0.0" + checksum: 10c0/e98a84e9c172c556ee2c5afa0fc7161b46919e8b53ab20de140eedea19903ed82f7cd5b1576fb345c84f0a18da1982ddf65908129b58fc3d7cbc658ae232108f + languageName: node + linkType: hard + +"xmlbuilder@npm:~11.0.0": + version: 11.0.1 + resolution: "xmlbuilder@npm:11.0.1" + checksum: 10c0/74b979f89a0a129926bc786b913459bdbcefa809afaa551c5ab83f89b1915bdaea14c11c759284bb9b931e3b53004dbc2181e21d3ca9553eeb0b2a7b4e40c35b + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yargs-parser@npm:^20.2.2": + version: 20.2.9 + resolution: "yargs-parser@npm:20.2.9" + checksum: 10c0/0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72 + languageName: node + linkType: hard + +"yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs@npm:^16.0.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" + dependencies: + cliui: "npm:^7.0.2" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^20.2.2" + checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + languageName: node + linkType: hard + +"yargs@npm:^17.5.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard + +"yauzl@npm:^2.10.0": + version: 2.10.0 + resolution: "yauzl@npm:2.10.0" + dependencies: + buffer-crc32: "npm:~0.2.3" + fd-slicer: "npm:~1.1.0" + checksum: 10c0/f265002af7541b9ec3589a27f5fb8f11cf348b53cc15e2751272e3c062cd73f3e715bc72d43257de71bbaecae446c3f1b14af7559e8ab0261625375541816422 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f + languageName: node + linkType: hard + +"zip-dir@npm:2.0.0": + version: 2.0.0 + resolution: "zip-dir@npm:2.0.0" + dependencies: + async: "npm:^3.2.0" + jszip: "npm:^3.2.2" + checksum: 10c0/3bc6f84caeaaa19e7a65be01b5f042332eb09ec4a609d4ebebd93f854dfd2deb635f4b4486de224c6bdcb7e4e88b5e98792ffd14f1c58ce9b196061a83560be6 + languageName: node + linkType: hard + +"zod@npm:^3.22.4": + version: 3.25.76 + resolution: "zod@npm:3.25.76" + checksum: 10c0/5718ec35e3c40b600316c5b4c5e4976f7fee68151bc8f8d90ec18a469be9571f072e1bbaace10f1e85cf8892ea12d90821b200e980ab46916a6166a4260a983c + languageName: node + linkType: hard