diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000..64d713b2 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,18 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in the changesets repo](https://github.com/changesets/changesets). + +## How to contribute a changeset + +Before merging your PR, you should add a changeset if your change affects any of the published packages. + +1. Run `bun run changeset` in the root of the repository +2. Select the packages that are affected by your change +3. Choose the type of version bump (major, minor, or patch) +4. Write a summary of your changes that will be included in the changelog + +All published `@docsearch/*` packages use a single version (fixed versioning). This means when one package is bumped, all packages get the same version. + +## Releasing + +When changesets are present in the `main` branch, the CI will create a "Version Packages" PR that bumps versions and updates changelogs. Merging that PR will trigger the publish workflow. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000..41606b0a --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [ + [ + "typesense-docsearch-css", + "typesense-docsearch-core", + "typesense-docsearch.js", + "typesense-docsearch-modal", + "typesense-docsearch-react", + "typesense-docsearch-sidepanel", + "typesense-docsearch-sidepanel-js", + "typesense-docsearch-docusaurus-adapter" + ] + ], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [ + "@docsearch/website", + "@docsearch/react-example", + "@docsearch/js-example" + ] +} diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index f92b0c19..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,190 +0,0 @@ -version: 2.1 - -aliases: - - &install_yarn_version - name: Install specific Yarn version - command: | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.5 - echo 'export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"' >> $BASH_ENV - - - &restore_yarn_cache - name: Restore Yarn cache - keys: - - yarn-packages-{{ checksum "yarn.lock" }} - - - &save_yarn_cache - name: Save Yarn cache - key: yarn-packages-{{ checksum "yarn.lock" }} - paths: - - ~/.cache/yarn - - - &run_yarn_install - name: Install dependencies - command: yarn install --frozen-lockfile - - - &restore_dist_folders - name: Restore dist folders - command: | - set -exu - - mkdir -p packages/docsearch-react/dist - mkdir -p packages/docsearch-js/dist - mkdir -p packages/docsearch-css/dist - - cp -R /tmp/workspace/packages/docsearch-react/dist packages/docsearch-react - cp -R /tmp/workspace/packages/docsearch-js/dist packages/docsearch-js - cp -R /tmp/workspace/packages/docsearch-css/dist packages/docsearch-css - -defaults: &defaults - working_directory: ~/docsearch - docker: - - image: cimg/node:16.19.1 - -cypress: &cypress - working_directory: ~/docsearch - docker: - - image: cypress/browsers:node16.14.0-chrome99-ff97 - environment: - ## this enables colors in the output - TERM: xterm - -references: - workspace_root: &workspace_root /tmp/workspace - attach_workspace: &attach_workspace - attach_workspace: - at: *workspace_root - -jobs: - build: - <<: *defaults - steps: - - checkout - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: - name: Build and Size - command: | - yarn run build - - run: - name: Move dist folders to workspace - command: | - set -exu - - mkdir -p /tmp/workspace/packages/docsearch-react/dist - mkdir -p /tmp/workspace/packages/docsearch-js/dist - mkdir -p /tmp/workspace/packages/docsearch-css/dist - - cp -R packages/docsearch-react/dist /tmp/workspace/packages/docsearch-react - cp -R packages/docsearch-js/dist /tmp/workspace/packages/docsearch-js - cp -R packages/docsearch-css/dist /tmp/workspace/packages/docsearch-css - - persist_to_workspace: - root: *workspace_root - paths: - - packages - test_lint: - <<: *defaults - steps: - - checkout - - *attach_workspace - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: *restore_dist_folders - - run: - name: Linting - command: yarn run lint - test_types: - <<: *defaults - steps: - - checkout - - *attach_workspace - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: *restore_dist_folders - - run: - name: Type checking - command: yarn run test:types - test_size: - <<: *defaults - steps: - - checkout - - *attach_workspace - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: *restore_dist_folders - - run: - name: Size checking - command: yarn run test:size - test_unit: - <<: *defaults - steps: - - checkout - - *attach_workspace - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: *restore_dist_folders - - run: - name: Unit tests - command: yarn run test --maxWorkers=4 - release: - <<: *defaults - steps: - - checkout - - *attach_workspace - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: *restore_dist_folders - - run: - name: Release if needed - command: yarn run shipjs trigger - test_cypress: - <<: *cypress - steps: - - checkout - - *attach_workspace - - run: *install_yarn_version - - restore_cache: *restore_yarn_cache - - run: *run_yarn_install - - save_cache: *save_yarn_cache - - run: *restore_dist_folders - - run: - name: Cypress test Actions - command: yarn run cy:run - -workflows: - version: 2 - ci: - jobs: - - build - - test_lint: - requires: - - build - - test_types: - requires: - - build - - test_size: - requires: - - build - - test_unit: - requires: - - build - - test_cypress: - requires: - - build - - release: - requires: - - build - filters: - branches: - only: main diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 00000000..c8797033 --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "algolia-docsearch": { + "type": "http", + "url": "https://mcp.algolia.com/1/docsearch/mcp" + } + } +} diff --git a/.cursor/rules/use-algolia-docsearch.mdc b/.cursor/rules/use-algolia-docsearch.mdc new file mode 120000 index 00000000..d64ae41b --- /dev/null +++ b/.cursor/rules/use-algolia-docsearch.mdc @@ -0,0 +1 @@ +../../mcp/plugins/docsearch/cursor/algolia-docsearch/rules/use-algolia-docsearch.mdc \ No newline at end of file diff --git a/.cursor/settings.json b/.cursor/settings.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/.cursor/settings.json @@ -0,0 +1 @@ +{} diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4f5f0870..00000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -dist -/examples -build -.cache -.parcel-cache diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 67b0a180..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,94 +0,0 @@ -/* eslint-disable import/no-commonjs */ -module.exports = { - extends: ['algolia', 'algolia/jest', 'algolia/react', 'algolia/typescript'], - globals: { - __DEV__: false, - }, - settings: { - react: { - pragma: 'React', - version: 'detect', - }, - 'import/resolver': { - node: { - extensions: ['.js', '.ts', '.tsx'], - }, - }, - }, - rules: { - 'no-param-reassign': 0, - 'valid-jsdoc': 0, - 'no-shadow': 0, - 'prefer-template': 0, - 'jest/no-disabled-tests': 0, - 'react/prop-types': 0, - 'react/no-unescaped-entities': 0, - 'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], - 'import/extensions': 0, - 'no-unused-expressions': 0, - complexity: 0, - 'import/order': [ - 'error', - { - alphabetize: { - order: 'asc', - caseInsensitive: true, - }, - 'newlines-between': 'always', - groups: ['builtin', 'external', 'parent', 'sibling', 'index'], - pathGroups: [ - { - pattern: '@/**/*', - group: 'parent', - position: 'before', - }, - ], - pathGroupsExcludedImportTypes: ['builtin'], - }, - ], - - // TMP - '@typescript-eslint/explicit-function-return-type': ['off'], - 'react/function-component-definition': ['off'], - 'react/jsx-filename-extension': ['off'], - 'jsdoc/check-examples': ['off'], - }, - overrides: [ - { - files: ['**/rollup.config.js', 'stories/**/*', '**/__tests__/**'], - rules: { - 'import/no-extraneous-dependencies': 0, - }, - }, - { - files: ['cypress/**/*'], - plugins: ['cypress'], - env: { - 'cypress/globals': true, - }, - rules: { - 'jest/expect-expect': 0, - 'spaced-comment': 0, - '@typescript-eslint/triple-slash-reference': 0, - }, - }, - { - files: [ - 'scripts/**/*', - '*.config.js', - 'packages/website/plugins/**/*', - 'packages/website/sidebars.js', - ], - rules: { - 'import/no-commonjs': 0, - }, - }, - { - files: ['packages/website/**/*'], - rules: { - 'import/no-unresolved': 0, - 'import/no-extraneous-dependencies': 0, - }, - }, - ], -}; diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md deleted file mode 100644 index 5997ef4c..00000000 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: Bug report -about: Help us improve DocSearch. ---- - -## Description - - - -## Steps to reproduce - -1. Go to `...` -2. Click on `...` -3. Scroll down to `...` -4. See error - - - -**Live reproduction:** - -- JavaScript: https://codesandbox.io/s/github/algolia/docsearch/tree/main/examples/js-demo -- React: https://codesandbox.io/s/github/algolia/docsearch/tree/main/examples/demo - -## Expected behavior - - - -## Environment - -- OS: [e.g. Windows / Linux / macOS / iOS / Android] -- Browser: [e.g. Chrome, Safari] -- DocSearch version: [e.g. 3.0.0] diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index cf9b4043..00000000 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for DocSearch. ---- - -## Describe the problem - - - -## Describe the solution - - - -## Alternatives you've considered - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index ef2e6d34..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -blank_issues_enabled: false - -contact_links: - - name: Documentation feedback - url: https://github.com/algolia/docsearch/issues/new - about: Share with us issues about the DocSearch documentation. - - - name: Apply for DocSearch - url: https://docsearch.algolia.com/apply - about: Apply to integrate DocSearch to your website. diff --git a/.github/ISSUE_TEMPLATE/docsearch_ui.md b/.github/ISSUE_TEMPLATE/docsearch_ui.md new file mode 100644 index 00000000..24bad293 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docsearch_ui.md @@ -0,0 +1,25 @@ +--- +name: Enhance the search-UI +about: Everything related to the front end library +--- + + + +**Do you want to request a _feature_ or report a _bug_?** + +**What is the current behavior?** + +**If the current behavior is a bug, please provide all the steps to reproduce and a minimal +[JSFiddle][3] example or a repository on GitHub that we can `npm install` +and `npm start`.** + +**What is the expected behavior?** + +[1]: https://typesense.org/docs/latest/guide/docsearch.html +[2]: https://github.com/typesense/typesense-docsearch-scraper +[3]: https://jsfiddle.net/ diff --git a/.github/ISSUE_TEMPLATE/docsearch_user.md b/.github/ISSUE_TEMPLATE/docsearch_user.md new file mode 100644 index 00000000..b18ee6e7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docsearch_user.md @@ -0,0 +1,20 @@ +--- +name: Issue with your own DocSearch implementation +about: Everything related to your own custom DocSearch index having a configuration +--- + +**Is your issue related to your own DocSearch implementation?** + +- If it is a styling issue regarding your search-UI, you are in the good place, you can remove this section. +- If the issue is due to a wrong individual crawl: + + Please [try to update your configuration][1]. Our [dedicated documentation welcomes any feedback][2]. If it is not solving it, please open an issue on the scraper repository. + + If you are using our DocSearch on your own, please note that we will help you in the best effort possible. + + - What is the dropdown version you are using? + - What is the scraper version you are using? + - Could you share us the configuration file you are using? + +[1]: https://typesense.org/docs/latest/guide/docsearch.html +[2]: https://github.com/typesense/typesense-docsearch-scraper diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..d62725d5 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ + + +**Summary** + + + +**Result** + + diff --git a/.github/logo.svg b/.github/logo.svg deleted file mode 100644 index 4983b5a7..00000000 --- a/.github/logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/.github/preview-dark.png b/.github/preview-dark.png deleted file mode 100644 index 8ad239b5..00000000 Binary files a/.github/preview-dark.png and /dev/null differ diff --git a/.github/preview-light.png b/.github/preview-light.png deleted file mode 100644 index 650744b6..00000000 Binary files a/.github/preview-light.png and /dev/null differ diff --git a/.github/screencast.gif b/.github/screencast.gif deleted file mode 100644 index 2340c1be..00000000 Binary files a/.github/screencast.gif and /dev/null differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..f050b555 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: + - '**' + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Node + uses: actions/setup-node@v5 + with: + node-version-file: .tool-versions + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .tool-versions + + - name: Install + run: bun install --frozen-lockfile + + - name: Build + run: bun run build + + - name: Typecheck + run: bun run test:types + + - name: Unit Tests + run: bun run test --run diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml deleted file mode 100644 index ba2b1654..00000000 --- a/.github/workflows/renovate.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Renovate - -on: - schedule: - - cron: '0 14 * * 5' # At 14:00 on Friday. - workflow_dispatch: - -jobs: - renovate: - runs-on: ubuntu-latest - steps: - - name: Renovate Automatic Branch - uses: bodinsamuel/renovate-automatic-branch@v1 - with: - github-token: ${{ secrets.ALGOLIA_BOT_TOKEN }} - repo-owner: algolia - repo-name: docsearch - branch-base: main - pull-request-title: 'chore(deps): dependencies' diff --git a/.gitignore b/.gitignore index a641a4d8..083c7bfe 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,8 @@ node_modules/ *.log coverage/ .cache -.parcel-cache +.nx +scratchpad/ # Bundle build files dist/ @@ -15,10 +16,18 @@ dist/ # IDE .vscode/ +.idea # Environment files .env -# Cypress Video and Screenshots output -cypress/screenshots/ -cypress/videos/ \ No newline at end of file +# Playwright output +playwright-report/ +test-results/ + +**/.yarn/* +!**/.yarn/releases +!**/.yarn/plugins +!**/.yarn/patches + +rollup.config-*.cjs diff --git a/.nvmrc b/.nvmrc index 8b3ed1b2..32f8c50d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.19.1 +24.13.1 diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 00000000..467ed673 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,23 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "proseWrap": "never", + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 80, + "sortPackageJson": false, + "sortImports": { + "groups": ["builtin", "external", "parent", "sibling", "index"], + "newlinesBetween": true + }, + "jsdoc": true, + "ignorePatterns": [ + "node_modules", + "dist", + "build", + ".cache", + "**/*.mdx", + "**/*.md", + "adapters/docusaurus-theme-search-algolia/lib", + "!adapters/docusaurus-theme-search-algolia/lib/theme/**/*" + ] +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..c1c45128 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,341 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": [ + "import", + "jsdoc", + "unicorn", + "react", + "jsx-a11y", + "typescript", + "react-perf", + "oxc", + "eslint", + "vitest" + ], + "jsPlugins": [ + "@stylistic/eslint-plugin", + "@eslint-community/eslint-plugin-eslint-comments" + ], + "env": { + "builtin": true, + "es2026": true, + "commonjs": true + }, + "ignorePatterns": [ + "**/node_modules/", + "**/dist/", + "**/build/", + "**/.docusaurus", + "adapters/**/lib/**", + "examples/**/*" + ], + "rules": { + "react/react-compiler": "warn", + "constructor-super": ["error"], + "for-direction": "error", + "getter-return": [ + "error", + { + "allowImplicit": true + } + ], + "no-console": ["error"], + "no-promise-executor-return": ["error"], + "no-template-curly-in-string": ["error"], + "accessor-pairs": ["error"], + "array-callback-return": ["error"], + "block-scoped-var": ["error"], + "default-case": ["error"], + "default-case-last": ["error"], + "default-param-last": ["error"], + "eqeqeq": ["error"], + "grouped-accessor-pairs": ["error"], + "guard-for-in": ["error"], + "max-classes-per-file": ["warn", 1], + "no-alert": ["error"], + "no-caller": ["error"], + "no-constructor-return": ["error"], + "no-div-regex": ["error"], + "no-else-return": [ + "error", + { + "allowElseIf": false + } + ], + "no-eq-null": ["error"], + "no-eval": ["error"], + "no-extend-native": ["error"], + "no-extra-bind": ["error"], + "no-extra-label": ["error"], + "no-implicit-coercion": [ + "error", + { + "string": false + } + ], + "no-implicit-globals": ["error"], + "no-implied-eval": ["error"], + "no-iterator": ["error"], + "no-labels": ["error"], + "no-lone-blocks": ["error"], + "no-loop-func": ["error"], + "no-multi-assign": ["error"], + "no-multi-str": ["error"], + "no-new": ["error"], + "no-new-func": ["error"], + "no-new-wrappers": ["error"], + "no-proto": ["error"], + "no-return-assign": ["error"], + "no-script-url": ["error"], + "no-self-compare": ["error"], + "no-sequences": ["error"], + "no-throw-literal": ["error"], + "no-unmodified-loop-condition": ["error"], + "no-useless-call": ["error"], + "no-useless-concat": ["error"], + "no-useless-return": ["error"], + "no-void": ["error"], + "no-warning-comments": ["error"], + "prefer-promise-reject-errors": ["error"], + "radix": ["error"], + "require-await": ["error"], + "vars-on-top": ["error"], + "yoda": ["error"], + "no-label-var": ["error"], + "no-restricted-globals": ["error", "event"], + "no-shadow": ["error"], + "no-use-before-define": [ + "error", + { + "functions": false + } + ], + "@stylistic/spaced-comment": ["error"], + "max-depth": ["error"], + "max-nested-callbacks": ["error"], + "max-params": ["error", 5], + "new-cap": ["error"], + "no-array-constructor": ["error"], + "no-bitwise": ["error"], + "no-lonely-if": ["error"], + "no-nested-ternary": ["error"], + "no-unneeded-ternary": ["error"], + "unicode-bom": ["error"], + "no-useless-computed-key": ["error"], + "no-useless-constructor": ["error"], + "no-useless-rename": ["error"], + "no-var": ["error"], + "object-shorthand": ["error"], + "prefer-const": ["error"], + "prefer-rest-params": ["error"], + "prefer-spread": ["error"], + "symbol-description": ["error"], + "@eslint-community/eslint-comments/disable-enable-pair": [ + "error", + { + "allowWholeFile": true + } + ], + "@eslint-community/eslint-comments/no-aggregating-enable": ["error"], + "@eslint-community/eslint-comments/no-duplicate-disable": ["error"], + "@eslint-community/eslint-comments/no-unlimited-disable": ["error"], + "@eslint-community/eslint-comments/no-unused-disable": ["error"], + "@eslint-community/eslint-comments/no-unused-enable": ["error"], + "import/first": "error", + "import/newline-after-import": "error", + "import/no-amd": ["error"], + "import/no-commonjs": ["error"], + "import/no-duplicates": ["error"], + "import/no-named-as-default": "error", + "jsdoc/check-tag-names": [ + "error", + { + "definedTags": ["required", "swagger"] + } + ], + "jsdoc/implements-on-classes": ["error"], + "jsdoc/require-param-description": ["error"], + "jsdoc/require-param-name": ["error"], + "jsdoc/require-returns-description": ["error"], + "react/jsx-key": ["error"], + "react/jsx-no-comment-textnodes": "error", + "react/jsx-no-duplicate-props": "error", + "react/jsx-no-target-blank": ["error"], + "react/jsx-no-undef": "error", + "react/no-children-prop": "error", + "react/no-danger-with-children": "error", + "react/no-direct-mutation-state": ["error"], + "react/no-find-dom-node": ["error"], + "react/no-is-mounted": ["error"], + "react/no-render-return-value": "error", + "react/no-string-refs": ["error"], + "react/no-unknown-property": "error", + "react/react-in-jsx-scope": "error", + "react/button-has-type": ["error"], + "react/jsx-boolean-value": ["error", "always"], + "react/jsx-pascal-case": ["error"], + "react/jsx-fragments": ["error"], + "react/jsx-handler-names": ["error"], + "react/no-array-index-key": ["warn"], + "react/no-did-mount-set-state": ["warn"], + "react/no-did-update-set-state": ["warn"], + "react/no-multi-comp": [ + "warn", + { + "ignoreStateless": true + } + ], + "react/no-redundant-should-component-update": ["error"], + "react/no-this-in-sfc": ["error"], + "react/self-closing-comp": [ + "error", + { + "html": false + } + ], + "jsx-a11y/alt-text": ["error"], + "jsx-a11y/anchor-is-valid": ["error"], + "jsx-a11y/anchor-has-content": "error", + "jsx-a11y/aria-activedescendant-has-tabindex": ["error"], + "jsx-a11y/aria-props": ["error"], + "jsx-a11y/aria-proptypes": ["error"], + "jsx-a11y/aria-role": [ + "error", + { + "ignoreNonDOM": false + } + ], + "jsx-a11y/html-has-lang": ["error"], + "jsx-a11y/iframe-has-title": ["error"], + "jsx-a11y/interactive-supports-focus": ["error"], + "jsx-a11y/lang": ["error"], + "jsx-a11y/media-has-caption": ["error"], + "jsx-a11y/mouse-events-have-key-events": ["error"], + "jsx-a11y/no-access-key": ["error"], + "jsx-a11y/no-autofocus": [ + "error", + { + "ignoreNonDOM": true + } + ], + "jsx-a11y/no-distracting-elements": [ + "error", + { + "elements": ["marquee", "blink"] + } + ], + "jsx-a11y/no-interactive-element-to-noninteractive-role": ["error"], + "jsx-a11y/no-noninteractive-element-interactions": ["error"], + "jsx-a11y/no-noninteractive-element-to-interactive-role": ["error"], + "jsx-a11y/no-noninteractive-tabindex": ["error"], + "jsx-a11y/no-redundant-roles": ["error"], + "jsx-a11y/no-static-element-interactions": ["error"], + "jsx-a11y/role-has-required-aria-props": ["error"], + "jsx-a11y/role-supports-aria-props": ["error"], + "jsx-a11y/scope": ["error"], + "jsx-a11y/tabindex-no-positive": ["error"], + "@stylistic/comma-spacing": ["error"], + "@stylistic/func-call-spacing": ["error"], + "@stylistic/type-annotation-spacing": ["error"], + "react/rules-of-hooks": "error", + "react/exhaustive-deps": "error", + "typescript/adjacent-overload-signatures": ["error"], + "typescript/array-type": [ + "error", + { + "default": "array-simple" + } + ], + "typescript/no-restricted-types": [ + "error", + { + "types": { + "String": { + "message": "Use `string` instead.", + "fixWith": "string" + }, + "Number": { + "message": "Use `number` instead.", + "fixWith": "number" + }, + "Boolean": { + "message": "Use `boolean` instead.", + "fixWith": "boolean" + }, + "Symbol": { + "message": "Use `symbol` instead.", + "fixWith": "symbol" + }, + "Object": { + "message": "The `Object` type is mostly the same as `unknown`. You probably want `Record` instead. See https://github.com/typescript-eslint/typescript-eslint/pull/848", + "fixWith": "Record" + }, + "{}": { + "message": "The `{}` type is mostly the same as `unknown`. You probably want `Record` instead.", + "fixWith": "Record" + }, + "object": { + "message": "The `object` type is hard to use. Use `Record` instead. See: https://github.com/typescript-eslint/typescript-eslint/pull/848", + "fixWith": "Record" + }, + "Function": "Use a specific function type instead, like `() => void`." + } + } + ], + "typescript/consistent-type-imports": [ + "error", + { + "prefer": "type-imports" + } + ], + "typescript/consistent-type-assertions": [ + "error", + { + "assertionStyle": "as", + "objectLiteralTypeAssertions": "allow-as-parameter" + } + ], + "typescript/no-empty-interface": ["error"], + "typescript/no-misused-new": ["error"], + "typescript/no-namespace": [ + "error", + { + "allowDeclarations": true + } + ], + "typescript/triple-slash-reference": ["error"], + "typescript/no-confusing-non-null-assertion": ["error"], + "typescript/no-extra-non-null-assertion": ["error"], + "typescript/no-non-null-asserted-optional-chain": ["error"], + "typescript/no-unnecessary-type-constraint": ["error"], + "typescript/no-var-requires": ["error"], + "typescript/prefer-as-const": ["error"], + "typescript/prefer-enum-initializers": ["error"], + "typescript/prefer-function-type": ["error"], + "typescript/prefer-literal-enum-member": ["error"], + "typescript/prefer-namespace-keyword": ["error"], + "typescript/prefer-ts-expect-error": ["error"], + "typescript/unified-signatures": ["error"], + "vitest/require-mock-type-parameters": "off", + "no-debugger": ["error"] + }, + "overrides": [ + { + "files": ["**/*.ts", "**/*.tsx"], + "rules": { + "typescript/explicit-member-accessibility": [ + "error", + { + "accessibility": "no-public" + } + ] + } + }, + { + "files": ["packages/website/**/*"], + "rules": { + "react/react-compiler": "off" + } + } + ] +} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 6eea1699..00000000 --- a/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -dist -build -.cache -.parcel-cache \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 6e93c0d5..00000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "proseWrap": "never", - "singleQuote": true, - "trailingComma": "es5" -} diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index bd9668bd..00000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "plugins": [ - "stylelint-prettier", - "stylelint-no-unsupported-browser-features" - ], - "extends": [ - "stylelint-config-standard", - "stylelint-config-sass-guidelines", - "stylelint-order", - "stylelint-a11y/recommended", - "stylelint-prettier/recommended" - ], - "rules": { - "selector-class-pattern": [ - "^DocSearch-[A-Za-z0-9-]*$" - ], - "prettier/prettier": true, - "max-nesting-depth": [ - 2, - { - "ignore": [ - "pseudo-classes" - ], - "ignoreAtRules": [ - "media" - ] - } - ], - "plugin/no-unsupported-browser-features": [ - null, - { - "severity": "warning" - } - ] - } -} \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..61ef9ce5 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +bun 1.3.10 +nodejs 24.13.1 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..5c95ba15 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,243 @@ +# AGENTS.md - DocSearch Development Guide + +This document provides guidelines for AI agents working on the DocSearch codebase. + +## Project Overview + +DocSearch is an Algolia-powered search widget for documentation sites. It's a TypeScript/React monorepo using Bun workspaces. + +### Packages + +- `typesense-docsearch-core` - Core logic and hooks +- `typesense-docsearch-react` - React components +- `typesense-docsearch.js` - Vanilla JavaScript wrapper +- `typesense-docsearch-css` - Styles +- `typesense-docsearch-modal` - Modal component +- `typesense-docsearch-sidepanel` - Side panel React component +- `typesense-docsearch-sidepanel-js` - Side panel vanilla JS wrapper +- `website` - Documentation site (Docusaurus) + +## Build Commands + +```bash +# Install dependencies +bun install + +# Build all packages +bun run build + +# Build specific package +bun run --filter typesense-docsearch-react build + +# Watch mode (all packages) +bun run watch +``` + +## Test Commands + +```bash +# Run all unit tests +bun run test + +# Run a single test file +bun run test --run packages/docsearch-react/src/__tests__/utils.test.ts + +# Type checking +bun run test:types + +# Bundle size check +bun run test:size +``` + +When running tests, prefer to run specific files with the `--run` flag to prevent running with watch mode. + +## Lint Commands + +```bash +# Run oxlint +bun run lint --format=agent + +# Perform oxfmt formatting +bun run fmt + +# Lint CSS +bun run lint:css +``` + +## E2E Testing (Playwright) + +```bash +# Run Cypress tests +bun run pw:run + +# Run with specific browser +bun run pw:run:chromium +bun run pw:run:firefox +bun run pw:run:webkit +``` + +## Code Style Guidelines + +### TypeScript + +- Use `type` imports for type-only imports: `import type { Foo } from './types'` +- Prefer interfaces for object shapes, types for unions/primitives +- Avoid `any`; use `unknown` when type is truly unknown + +```typescript +// Good +export type DocSearchHit = { + objectID: string; + content: string | null; +}; + +// Return type annotation +function createStorage(key: string): StorageInterface { + // ... +} +``` + +### Naming Conventions + +- **Components**: PascalCase (`DocSearchModal.tsx`) +- **Hooks**: camelCase with `use` prefix (`useDocSearchKeyboardEvents.ts`) +- **Utilities**: camelCase (`removeHighlightTags.ts`) +- **Types**: PascalCase (`InternalDocSearchHit`) +- **Constants**: SCREAMING_SNAKE_CASE (`MAX_QUERY_SIZE`) +- **CSS classes**: `DocSearch-` prefix + +### React Components + +- Use function components with explicit JSX return type +- Forward refs when needed using `React.forwardRef` +- Use `React.useCallback` for callbacks passed as props +- Use `React.useMemo` for expensive computations +- Prefer destructuring props in function signature + +### React Component Structure + +- `src/components/ui/` contains reusable rendering components. +- Prefer domain-light primitives in `src/components/ui/` when possible. +- Feature-scoped UI components may live in `src/components/ui/` when their feature scope is explicit in the filename, such as `RecentConversationsResults.tsx`. +- `src/components/` contains scoped composition components that own feature flow, branching, and state orchestration. +- Keep AI-specific behavior out of generic UI primitives. If a UI component is AI-specific, make that scope clear in its name. + +```typescript +function DocSearchComponent( + props: DocSearchProps, + ref: React.ForwardedRef +): JSX.Element { + // ... +} + +export const DocSearch = React.forwardRef(DocSearchComponent); +``` + +### Error Handling + +- Use try-catch for async operations that may fail +- Check for specific error types when handling errors +- Fail silently for non-critical localStorage operations +- Throw descriptive errors for configuration issues + +```typescript +try { + window.localStorage.setItem(key, JSON.stringify(value)); +} catch (error) { + if (error instanceof DOMException && error.name === 'QuotaExceededError') { + cleanupDocSearchStorage(); + } + // Silently fail for other errors +} +``` + +### Formatting (oxfmt) + +- Single quotes for strings +- Trailing commas (ES5 style) +- No prose wrapping + +### CSS (Stylelint) + +- Selector pattern: `^DocSearch-[A-Za-z0-9-]*$` +- Max nesting depth: 2 (excluding pseudo-classes) +- Follow `stylelint-config-standard` and `stylelint-config-sass-guidelines` + +## Commit Conventions + +Follow conventional changelog format: + +``` +type(scope): description +``` + +Types: `fix`, `feat`, `refactor`, `docs`, `chore` + +Examples: + +- `fix(modal): increase default height` +- `feat(searchbox): add type input property` +- `chore(deps): update dependency rollup-plugin-babel to v3.0.7` + +## Testing Patterns + +Tests use Vitest with Testing Library: + +```typescript +import { describe, it, expect, beforeEach } from 'vitest'; +import { render, act, fireEvent, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/vitest'; + +describe('ComponentName', () => { + it('describes expected behavior', () => { + // Arrange + render(); + + // Act + fireEvent.click(screen.getByText('Button')); + + // Assert + expect(screen.getByText('Result')).toBeInTheDocument(); + }); +}); +``` + +## File Structure + +``` +packages/ + docsearch-react/ + src/ + components/ # Scoped React composition components + ui/ # Reusable rendering components and explicitly scoped UI pieces + __tests__/ # Test files + icons/ # Icon components + types/ # Type definitions + utils/ # Utility functions + Sidepanel/ # Sidepanel subcomponents + DocSearch.tsx # Main component + index.ts # Public exports +``` + +## Key Dependencies + +- `@algolia/autocomplete-core` - Autocomplete engine +- `algoliasearch` - Algolia search client +- `ai` / `@ai-sdk/react` - AI/streaming support +- `marked` - Markdown rendering +- `rollup` - Build bundling +- `vitest` - Test runner + +## Cursor Cloud specific instructions + +Toolchain is pinned in `.tool-versions`: Node `24.13.1` (managed via `fnm`) and Bun `1.3.10`. These are preinstalled in the Cloud VM and available on `PATH` in new shells; the startup update script only runs `bun install`. + +Non-obvious caveats: + +- **To run/demo the widget, use the React playground:** `bun run playground:start` serves at `http://localhost:5173` (Vite). `bun run playground-js:start` serves the vanilla-JS demo. These connect to Algolia's hosted index using public credentials baked into the demo, so **outbound internet is required** for live search results. +- Run unit tests non-interactively with `bun run test --run` (plain `bun run test` starts Vitest watch mode). +- `bun run lint:css` reports many pre-existing CSS lint violations in the repo; these are not environment problems. + +## Documentation + +- When writing or working on the documentation website (`packages/website`), MUST adhere to the writing guidelines in @packages/website/WRITING_GUIDE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index ec9a3fdd..27d6eb02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,174 +1,484 @@ -# [3.4.0-7](https://github.com/typesense/docsearch.js/compare/v3.4.0-5...v3.4.0-7) (2023-03-21) +# [4.6.0](https://github.com/algolia/docsearch/compare/v4.5.4...v4.6.0) (2026-02-17) +### Bug Fixes -# [3.4.0-5](https://github.com/typesense/docsearch.js/compare/v0.4.0-3...v3.4.0-5) (2023-03-21) +* **sidepanel:** autofocus prompt input on open for desktop only ([#2857](https://github.com/algolia/docsearch/issues/2857)) ([b3fb7f5](https://github.com/algolia/docsearch/commit/b3fb7f50f879c70f6d4cd4f3d16e34f22fb08e37)) +### Features -# [0.4.0-3](https://github.com/typesense/docsearch.js/compare/v3.4.0-4...v0.4.0-3) (2023-03-21) +* **docusaurus-adapter:** support docsearch key and adapter docs ([#2858](https://github.com/algolia/docsearch/issues/2858)) ([4f68ebb](https://github.com/algolia/docsearch/commit/4f68ebbc06e515f54de3986464bcb633acc7337d)) -# [3.4.0-4](https://github.com/typesense/docsearch.js/compare/v0.4.0-2...v3.4.0-4) (2023-03-21) +## [4.5.4](https://github.com/algolia/docsearch/compare/v4.5.4...v4.5.4) (2026-02-05) +### Bug Fixes -# [0.4.0-2](https://github.com/typesense/docsearch.js/compare/v3.4.0-3...v0.4.0-2) (2023-03-21) +* include ac in package ([#2853](https://github.com/algolia/docsearch/issues/2853)) ([343115d](https://github.com/algolia/docsearch/commit/343115dc7c179bf2e238a88477f88e7172060813)) +* Missing result title would trigger an error ([#2852](https://github.com/algolia/docsearch/issues/2852)) ([6ef6505](https://github.com/algolia/docsearch/commit/6ef6505e871ad5bd57b9cac8829df3b764567047)) -# [3.4.0-3](https://github.com/typesense/docsearch.js/compare/v3.4.0-2...v3.4.0-3) (2023-03-20) +## [4.5.4](https://github.com/algolia/docsearch/compare/v4.5.4...v4.5.4) (2026-01-21) +### Bug Fixes -# [3.4.0-2](https://github.com/typesense/docsearch.js/compare/v0.4.0-1...v3.4.0-2) (2023-03-20) +* make typesense-docsearch-core a real dependency. ([#2848](https://github.com/algolia/docsearch/issues/2848)) ([73eddb7](https://github.com/algolia/docsearch/commit/73eddb76fca7a6091f12c977caff76934bd508f1)) -# [0.4.0-1](https://github.com/typesense/docsearch.js/compare/v3.4.0-1...v0.4.0-1) (2023-03-20) +## [4.5.4](https://github.com/algolia/docsearch/compare/v4.5.4...v4.5.4) (2026-01-21) +### Reverts -# [3.4.0-1](https://github.com/typesense/docsearch.js/compare/v0.4.0-0...v3.4.0-1) (2023-03-20) +* changelog ([221fe8e](https://github.com/algolia/docsearch/commit/221fe8ec6c7ead0b2170b50eb9eb46846fcc1acc)) -# [0.4.0-0](https://github.com/typesense/docsearch.js/compare/v3.4.0-0...v0.4.0-0) (2023-03-20) +# [4.5.0](https://github.com/algolia/docsearch/compare/v4.5.0...v4.5.0) (2026-01-20) +# [4.5.0](https://github.com/algolia/docsearch/compare/v4.5.0-beta.2...v4.5.0) (2026-01-16) +### Features -# [3.4.0-0](https://github.com/typesense/docsearch.js/compare/v0.3.0...v3.4.0-0) (2023-03-20) +* **askai:** Allow Agent Studio specific search params ([#2842](https://github.com/algolia/docsearch/issues/2842)) ([f1aaf91](https://github.com/algolia/docsearch/commit/f1aaf911d70a27935d628898bc7c12e2a85c4c2d)) +* **website:** new features section ([1015eb8](https://github.com/algolia/docsearch/commit/1015eb808bc92d9580a678307d780d0a106593e6)) +* **website:** new features section ([b4764c1](https://github.com/algolia/docsearch/commit/b4764c1552f19eda136f29b4609525cc6db0acab)) +# [4.5.0-beta.2](https://github.com/algolia/docsearch/compare/v4.5.0-beta.1...v4.5.0-beta.2) (2026-01-13) +### Bug Fixes -# [3.4.0-6](https://github.com/typesense/docsearch.js/compare/v3.4.0-5...v3.4.0-6) (2023-03-21) +* pin preact version ([a90f5b1](https://github.com/algolia/docsearch/commit/a90f5b10ce95b5717ce07df01cab49a5e74f457f)) +# [4.5.0-beta.1](https://github.com/algolia/docsearch/compare/v4.5.0-beta.0...v4.5.0-beta.1) (2026-01-13) +### Bug Fixes -# [3.4.0-5](https://github.com/typesense/docsearch.js/compare/v0.4.0-3...v3.4.0-5) (2023-03-21) +* **hybrid:** Fixes modal on mobile in hybrid mode ([#2835](https://github.com/algolia/docsearch/issues/2835)) ([8c750e5](https://github.com/algolia/docsearch/commit/8c750e5102763dd71cb7aab4ef40bdd5d0cb2ea5)) +* **js:** preact & types versions ([#2839](https://github.com/algolia/docsearch/issues/2839)) ([851cfae](https://github.com/algolia/docsearch/commit/851cfae380f58bf43778ef81e17785dc5c2100ba)) +* **modal:** Fixes modal opening on every key press ([#2837](https://github.com/algolia/docsearch/issues/2837)) ([7319f54](https://github.com/algolia/docsearch/commit/7319f54898af50883f66bedc0fa5a66cc38e07c0)) +* **sidepanel:** Fixes some issues while conversation is streaming ([#2828](https://github.com/algolia/docsearch/issues/2828)) ([8ad60d9](https://github.com/algolia/docsearch/commit/8ad60d9bab4e8a46b499375779996cbd9f76dc9a)) +### Features +* **askai:** Allow use of Agent Studio for chat backend ([#2832](https://github.com/algolia/docsearch/issues/2832)) ([ffa2835](https://github.com/algolia/docsearch/commit/ffa283519411173306b9c4729bf26ffc736a1487)) +* **docs:** update Ask AI errors docs ([#2833](https://github.com/algolia/docsearch/issues/2833)) ([5aac293](https://github.com/algolia/docsearch/commit/5aac293d3e263e1206592a08484b8f00ea8107e9)) -# [0.4.0-3](https://github.com/typesense/docsearch.js/compare/v3.4.0-4...v0.4.0-3) (2023-03-21) +# [4.5.0-beta.0](https://github.com/algolia/docsearch/compare/v4.4.0...v4.5.0-beta.0) (2026-01-08) +### Bug Fixes +* **deps:** Move dependencies to devDependencies to fix peer dependency issues ([#2831](https://github.com/algolia/docsearch/issues/2831)) ([ed59c7b](https://github.com/algolia/docsearch/commit/ed59c7baa6644ff0a87a71cf84a912219e80ffb3)) +* prevent XSS vulnerability in AskAI conversation history ([#2822](https://github.com/algolia/docsearch/issues/2822)) ([f9ba0a0](https://github.com/algolia/docsearch/commit/f9ba0a01a48e35fd675b6e90e629728f699ce175)) -# [3.4.0-4](https://github.com/typesense/docsearch.js/compare/v0.4.0-2...v3.4.0-4) (2023-03-21) +### Features +* **website:** Add Sidepanel to DocSearch website ([#2821](https://github.com/algolia/docsearch/issues/2821)) ([54bac9c](https://github.com/algolia/docsearch/commit/54bac9cf54e4613a363a6df2fe7e5f3835009bdf)) +## [4.4.0](https://github.com/algolia/docsearch/compare/v4.3.2...v4.4.0) (2025-12-18) -# [0.4.0-2](https://github.com/typesense/docsearch.js/compare/v3.4.0-3...v0.4.0-2) (2023-03-21) +### Bug Fixes +* **button:** Fixes default button styling accessibility ([#2814](https://github.com/algolia/docsearch/pull/2814)) ([b0a28f5](https://github.com/algolia/docsearch/commit/b0a28f57f74a903aef4ad6620f0d34eb5be9e7a4)), closes [#2804](https://github.com/algolia/docsearch/issues/2804) +* **modal:** Fixes modal opening on every keypress ([#2813](https://github.com/algolia/docsearch/pull/2813)) ([373d139](https://github.com/algolia/docsearch/commit/373d1394e1e789f451a55a6377798705351856ca)), closes [#2809](https://github.com/algolia/docsearch/issues/2809) +* **modal:** dropdown menu styles ([#2801](https://github.com/algolia/docsearch/pull/2801)) ([5ef01ec](https://github.com/algolia/docsearch/commit/5ef01ecd4dbda1bfb89e03ddbad4be6e30dba89a)) +### Features -# [3.4.0-3](https://github.com/typesense/docsearch.js/compare/v3.4.0-2...v3.4.0-3) (2023-03-20) +* **sidepanel:** Introduce Ask AI Sidepanel ([#2816](https://github.com/algolia/docsearch/issues/2816)) ([171c504](https://github.com/algolia/docsearch/commit/171c504f11d63e9a3a3abfc69b779594ef2c0b67)) +* **askai:** add thread depth limit logic ([#2812](https://github.com/algolia/docsearch/pull/2812)) ([fd0de3d](https://github.com/algolia/docsearch/commit/fd0de3d16f8c434b7f75793fae69fe8e2c2d159d)) +* **askai:** Handle errors better for Ask AI ([#2803](https://github.com/algolia/docsearch/pull/2803)) ([d531137](https://github.com/algolia/docsearch/commit/d531137f14accb82732804452bf324093a827cea)) +* **modal:** Native RTL support ([#2802](https://github.com/algolia/docsearch/pull/2802)) ([9ed6c5a](https://github.com/algolia/docsearch/commit/9ed6c5a9bad1bcbaf46f551506dccdd9342c03c8)), closes [#1512](https://github.com/algolia/docsearch/issues/1512) +* **docs:** Add utm_ params to apply links ([#2817](https://github.com/algolia/docsearch/pull/2817)) ([8a9c451](https://github.com/algolia/docsearch/commit/8a9c4519eb04254d64fad8432c4fc395cf848cc1)) +## [4.3.2](https://github.com/algolia/docsearch/compare/v4.3.1...v4.3.2) (2025-11-10) +### Bug Fixes -# [3.4.0-2](https://github.com/typesense/docsearch.js/compare/v0.4.0-1...v3.4.0-2) (2023-03-20) +* **suggested-questions:** Fixes Suggested Questions on dark theme ([#2797](https://github.com/algolia/docsearch/issues/2797)) ([d9c4220](https://github.com/algolia/docsearch/commit/d9c42204572141a042ea93abf6d4aa73571c1354)) +## [4.3.1](https://github.com/algolia/docsearch/compare/v4.3.0...v4.3.1) (2025-11-04) +### Bug Fixes -# [0.4.0-1](https://github.com/typesense/docsearch.js/compare/v3.4.0-1...v0.4.0-1) (2023-03-20) +* **npm:** Publish new packages with public access ([#2795](https://github.com/algolia/docsearch/issues/2795)) ([2d045b5](https://github.com/algolia/docsearch/commit/2d045b59f29df9300e466b4701c3378022895e4a)) +# [4.3.0](https://github.com/algolia/docsearch/compare/v4.2.0...v4.3.0) (2025-11-04) +### Bug Fixes -# [3.4.0-1](https://github.com/typesense/docsearch.js/compare/v0.4.0-0...v3.4.0-1) (2023-03-20) +* **docsearch-js:** resultsFooterComponent not working in JS CDN version ([#2786](https://github.com/algolia/docsearch/issues/2786)) ([5381e76](https://github.com/algolia/docsearch/commit/5381e76126d6111039407d5aea06f8710917515f)) +* **website:** Fix API Reference docs page formatting ([#2783](https://github.com/algolia/docsearch/issues/2783)) ([8980d47](https://github.com/algolia/docsearch/commit/8980d47dbc7981e34fa3c15a7ce4ba42ed795eed)) +* **website:** updated askai markdown documentation ([#2788](https://github.com/algolia/docsearch/issues/2788)) ([f7be2a6](https://github.com/algolia/docsearch/commit/f7be2a65fcfaadf8ea192c0d4ef796a6635ee30d)) +### Features +* Composable API packages versions ([#2789](https://github.com/algolia/docsearch/issues/2789)) ([b91e309](https://github.com/algolia/docsearch/commit/b91e3099c63090003ddc656e40ac356dc6c64efa)) +* **composable-api:** Document Composable API ([#2793](https://github.com/algolia/docsearch/issues/2793)) ([e5ae33f](https://github.com/algolia/docsearch/commit/e5ae33fe905703cb7211c73cc22c98cd48e33285)) +* **composable-api:** Introduce new Composable API ([#2779](https://github.com/algolia/docsearch/issues/2779)) ([5e9bdfe](https://github.com/algolia/docsearch/commit/5e9bdfed70fe9d3a93583836fff62e52e387b25f)), closes [#2771](https://github.com/algolia/docsearch/issues/2771) [#2772](https://github.com/algolia/docsearch/issues/2772) [#2774](https://github.com/algolia/docsearch/issues/2774) +* **suggested-questions:** Add suggested questions ([#2787](https://github.com/algolia/docsearch/issues/2787)) ([2b4779a](https://github.com/algolia/docsearch/commit/2b4779ad06d6a132e82e0e43540550e580c6b498)) -# [0.4.0-0](https://github.com/typesense/docsearch.js/compare/v3.4.0-0...v0.4.0-0) (2023-03-20) +# [4.2.0](https://github.com/algolia/docsearch/compare/v4.1.0...v4.2.0) (2025-10-06) +### Bug Fixes +* **askai:** Fixes issue with askai state being outside DocSearchModal ([#2778](https://github.com/algolia/docsearch/issues/2778)) ([5d8943b](https://github.com/algolia/docsearch/commit/5d8943b5c6d84410b1ff9ad5604646609c6c9e63)) +* remove hit favorite and hit deletion animations ([#2777](https://github.com/algolia/docsearch/issues/2777)) ([cd9e6fb](https://github.com/algolia/docsearch/commit/cd9e6fb3fb2eb4a618c7037983ad9a563d96cf5b)) +* typo ([1ff1198](https://github.com/algolia/docsearch/commit/1ff1198da31e0ee39ddfe61168f9ac3d86beb164)) -# [3.4.0-0](https://github.com/typesense/docsearch.js/compare/v0.3.0...v3.4.0-0) (2023-03-20) +### Features +* curate AskAI search parameters ([#2768](https://github.com/algolia/docsearch/issues/2768)) ([d8ad7f6](https://github.com/algolia/docsearch/commit/d8ad7f6d5f4ba1784cf9fdd2f25cbd58cc27015d)) +* dark theme support for Menu ([#2776](https://github.com/algolia/docsearch/issues/2776)) ([01517d9](https://github.com/algolia/docsearch/commit/01517d94dec49ac2533425c70d9a02ab3fc70a8d)) +* Modal and AskAI UX tweaks ([#2765](https://github.com/algolia/docsearch/issues/2765)) ([0267f55](https://github.com/algolia/docsearch/commit/0267f554532a754a776f8125d16949ba2d4ff327)) +* Modal UI/UX fixes and updates ([#2780](https://github.com/algolia/docsearch/issues/2780)) ([a3ab7a8](https://github.com/algolia/docsearch/commit/a3ab7a8176fd9ec32e879237b887dce8a138a7cc)) +* update Ask AI docs ([#2775](https://github.com/algolia/docsearch/issues/2775)) ([9986cbf](https://github.com/algolia/docsearch/commit/9986cbf4266e1fecb44ee3658d556c9315852540)) +# [4.1.0](https://github.com/algolia/docsearch/compare/v4.0.1...v4.1.0) (2025-09-22) -# [0.3.0](https://github.com/typesense/docsearch.js/compare/v3.3.3...v0.3.0) (2023-03-08) +### Bug Fixes +* add docsearch 4 to website ([#2767](https://github.com/algolia/docsearch/issues/2767)) ([9a908b8](https://github.com/algolia/docsearch/commit/9a908b89b08972f7eb30fdce3435e7adff8fbdfa)) +### Features -## [3.0.1](https://github.com/typesense/docsearch.js/compare/v0.2.3...v3.0.1) (2022-07-25) +* Split docsearch-react package exports ([#2764](https://github.com/algolia/docsearch/issues/2764)) ([b15f302](https://github.com/algolia/docsearch/commit/b15f302ee3c262e79c3bba92a936cd8163fe406d)) +## [4.0.1](https://github.com/algolia/docsearch/compare/v4.0.0...v4.0.1) (2025-09-16) +### Bug Fixes -## [0.2.3](https://github.com/typesense/docsearch.js/compare/v0.2.2...v0.2.3) (2022-07-25) +* **deps:** Adds zod to dependency list ([#2759](https://github.com/algolia/docsearch/issues/2759)) ([4011294](https://github.com/algolia/docsearch/commit/40112944e8a7018568b50cddaf694312f26e167c)) +* **modal:** Fixes issue with index typings on DocSearchModal ([#2760](https://github.com/algolia/docsearch/issues/2760)) ([55934d8](https://github.com/algolia/docsearch/commit/55934d8f9804959e7ae8e2407fc306552dbadfbc)) +# [4.0.0](https://github.com/algolia/docsearch/compare/v4.0.0-beta.8...v4.0.0) (2025-09-15) +### Bug Fixes -## [0.2.2](https://github.com/typesense/docsearch.js/compare/v3.0.0...v0.2.2) (2022-07-25) +* add gif ([2f7bd27](https://github.com/algolia/docsearch/commit/2f7bd2748ca95b51331786c0c24e893c1a245409)) +* docs ([f74199f](https://github.com/algolia/docsearch/commit/f74199f492b5ae4fdb9fa37adffbfca0e2222b83)) +* update readme ([441cef0](https://github.com/algolia/docsearch/commit/441cef0356ec842ce9b1e43e540fca1cb12b7c4d)) +### Features +* **askai:** ai-sdk v5 ([#2746](https://github.com/algolia/docsearch/issues/2746)) ([df20e6c](https://github.com/algolia/docsearch/commit/df20e6c791815c7f3f69fe197aa7111b3a194f0b)) +* **v4:** Add support for multi-index search ([#2736](https://github.com/algolia/docsearch/issues/2736)) ([905e26d](https://github.com/algolia/docsearch/commit/905e26d693c60c1acc6b8b93fa152dbb660a63c0)) -# [3.0.0](https://github.com/typesense/docsearch.js/compare/v0.2.1...v3.0.0) (2022-07-25) +# [4.0.0-beta.8](https://github.com/algolia/docsearch/compare/v4.0.0-beta.7...v4.0.0-beta.8) (2025-08-28) +### Bug Fixes +* **docsearch-js:** provide unmounting function ([#2602](https://github.com/algolia/docsearch/issues/2602)) ([ba1573f](https://github.com/algolia/docsearch/commit/ba1573f2a124a9db8942875d02ce6091ce3fc85b)) +* **docsearch:** use correct index name for Ask AI localStorage keys ([#2733](https://github.com/algolia/docsearch/issues/2733)) ([e2fe65e](https://github.com/algolia/docsearch/commit/e2fe65e739a5222042441b849270b63d177affb2)) +* document open in new tab ([#2737](https://github.com/algolia/docsearch/issues/2737)) ([d26e13d](https://github.com/algolia/docsearch/commit/d26e13de289662560aadf892c7af0c686a6922ee)) +* ensure content items are saved to recent searches ([#2738](https://github.com/algolia/docsearch/issues/2738)) ([e8ba175](https://github.com/algolia/docsearch/commit/e8ba175868822888f3dc9bf7da46623d560a4353)) +* modal layout issues with footer positioning ([#2734](https://github.com/algolia/docsearch/issues/2734)) ([4d042d6](https://github.com/algolia/docsearch/commit/4d042d68a3f1512c342cae4726cda361044e7211)) -## [0.2.1](https://github.com/typesense/docsearch.js/compare/v0.2.1-0...v0.2.1) (2022-07-25) +### Features +* **docs:** Add Ask AI error documentation ([#2726](https://github.com/algolia/docsearch/issues/2726)) ([f065790](https://github.com/algolia/docsearch/commit/f065790ce6796c46aa23593fa65667423c9f6bd7)) +# [4.0.0-beta.7](https://github.com/algolia/docsearch/compare/v4.0.0-beta.6...v4.0.0-beta.7) (2025-08-14) -## [0.2.1-0](https://github.com/typesense/docsearch.js/compare/v3.0.0-3...v0.2.1-0) (2022-07-25) +### Features + +* a11y and ux tweaks ([#2712](https://github.com/algolia/docsearch/issues/2712)) ([d5c9c52](https://github.com/algolia/docsearch/commit/d5c9c527d7cd887ebce407163ad58ca302736be0)) +* **askai:** Dont display links from code blocks in sources ([#2709](https://github.com/algolia/docsearch/issues/2709)) ([2e4c114](https://github.com/algolia/docsearch/commit/2e4c11459e5edbe820a638b506f5ebfb57f66c09)) +* **docsearch:** added documentation for crawler automated markdown configuration ([#2714](https://github.com/algolia/docsearch/issues/2714)) ([69983f9](https://github.com/algolia/docsearch/commit/69983f95848ab79e5ac3ffff73c01f849a5637db)) +# [4.0.0-beta.6](https://github.com/algolia/docsearch/compare/v4.0.0-beta.5...v4.0.0-beta.6) (2025-08-08) + +### Bug Fixes + +* cater for `transformItems` to use DS with custom indices ([#2705](https://github.com/algolia/docsearch/issues/2705)) ([3478262](https://github.com/algolia/docsearch/commit/3478262b3e6970e9bf2e63c7a656a4c801b42059)) +* **docs:** better crawler config for markdown ([5690471](https://github.com/algolia/docsearch/commit/5690471c2ad22e8b9df5dd5177d8f2d25671e84a)) +* revert css change ([175a7d6](https://github.com/algolia/docsearch/commit/175a7d6bf48f13b4bb9bb2fc983b8b9e5189de09)) +* tailwind styles ([0fd05af](https://github.com/algolia/docsearch/commit/0fd05af65917ed9d29b2b9dc6f4832bc8dc05c9f)) ### Features -* **ui:** use Typesense color as primary color ([ad7e045](https://github.com/typesense/docsearch.js/commit/ad7e045f64e0baedef1cc6e014d8519c3d3e246e)) -* **ui:** use Typesense color in logo ([fe45232](https://github.com/typesense/docsearch.js/commit/fe452323e5537e2bcb84e3725b6bc0e918b30f2a)) +* Add recent searches limit param ([#2704](https://github.com/algolia/docsearch/issues/2704)) ([95af410](https://github.com/algolia/docsearch/commit/95af410f8202ee704b9187d8a4e6d1e15eae2e37)) +* **askai:** Add providers table to docs ([#2684](https://github.com/algolia/docsearch/issues/2684)) ([942c1ca](https://github.com/algolia/docsearch/commit/942c1ca353f90b196748e2ffeda551a13be8421b)) +* **askai:** pubic beta launch ([5626830](https://github.com/algolia/docsearch/commit/5626830883e60232924e85ab94270b1136e4678d)) +* **docs:** api documentation and better examples ([#2686](https://github.com/algolia/docsearch/issues/2686)) ([920e392](https://github.com/algolia/docsearch/commit/920e3926b7812e29dad635243c0bbfb144de28e6)) +# [4.0.0-beta.5](https://github.com/algolia/docsearch/compare/v4.0.0-beta.4...v4.0.0-beta.5) (2025-07-24) +### Bug Fixes -# [3.0.0-3](https://github.com/typesense/docsearch.js/compare/v3.0.0-2...v3.0.0-3) (2022-07-22) +* **js:** make `typesense-docsearch-react` devDep ([9967436](https://github.com/algolia/docsearch/commit/9967436993da24cf5340c03c1681350d7bb787c2)) +* update doc ([a9b3d89](https://github.com/algolia/docsearch/commit/a9b3d893f6571da936c986d3e9b701650ab30ac9)) +* update website package version ([1c5070b](https://github.com/algolia/docsearch/commit/1c5070b2063810428afe30cb4b9504f3e4b75138)) +* upload tarballs ([9a6f1a0](https://github.com/algolia/docsearch/commit/9a6f1a0cfa33a35970691e25e82914f551d649ca)) +### Features +* **docs:** added tips for facetFilters ([#2680](https://github.com/algolia/docsearch/issues/2680)) ([4210e3b](https://github.com/algolia/docsearch/commit/4210e3b503676206ee9dc22d0f6606e0119ef878)) +* **docs:** Enhance AskAI with markdown indexes ([#2683](https://github.com/algolia/docsearch/issues/2683)) ([4bcdedf](https://github.com/algolia/docsearch/commit/4bcdedf8f10694dc114fe3c6706190e2310c2c2e)) -# [3.0.0-2](https://github.com/typesense/docsearch.js/compare/v3.0.0-1...v3.0.0-2) (2022-07-22) +# [4.0.0-beta.4](https://github.com/algolia/docsearch/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2025-07-22) +### Bug Fixes +* revert to internal package ([#2678](https://github.com/algolia/docsearch/issues/2678)) ([414cdfe](https://github.com/algolia/docsearch/commit/414cdfeac47a46a3d222aee6e66d4a4f76a8289d)) -# [3.0.0-1](https://github.com/typesense/docsearch.js/compare/v3.1.1...v3.0.0-1) (2022-07-22) +# [4.0.0-beta.3](https://github.com/algolia/docsearch/compare/v3.9.0...v4.0.0-beta.3) (2025-07-22) +### Features +* **askai:** add facet filtering ([#2676](https://github.com/algolia/docsearch/issues/2676)) ([55e3eb3](https://github.com/algolia/docsearch/commit/55e3eb301c551323737c902f674382907a4da421)) +* **v4:** release v4 ([#2555](https://github.com/algolia/docsearch/issues/2555)) ([#2666](https://github.com/algolia/docsearch/issues/2666)) ([19c1ef9](https://github.com/algolia/docsearch/commit/19c1ef91928b6f64c1da48c727a4c5f91d6de1a4)), closes [#2574](https://github.com/algolia/docsearch/issues/2574) [#2587](https://github.com/algolia/docsearch/issues/2587) [#2654](https://github.com/algolia/docsearch/issues/2654) [#2659](https://github.com/algolia/docsearch/issues/2659) [#2660](https://github.com/algolia/docsearch/issues/2660) [#2661](https://github.com/algolia/docsearch/issues/2661) [#2662](https://github.com/algolia/docsearch/issues/2662) [#2665](https://github.com/algolia/docsearch/issues/2665) [#2667](https://github.com/algolia/docsearch/issues/2667) [#2668](https://github.com/algolia/docsearch/issues/2668) -## [3.3.3](https://github.com/algolia/docsearch/compare/v3.3.2...v3.3.3) (2023-02-06) +# [4.0.0-beta.2](https://github.com/algolia/docsearch/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2025-07-16) + +### Bug Fixes + +* add a section on models ([c3c5691](https://github.com/algolia/docsearch/commit/c3c5691cf6186f21a28984ab697b70d72e75b124)) +* Add more styling ([505c214](https://github.com/algolia/docsearch/commit/505c2147da072d7307b02d05e833ad910a6ea44c)) +* beta in readme ([b1dad74](https://github.com/algolia/docsearch/commit/b1dad744f4dc9fb84ff1b4101e4a54bd200ec28a)) +* polishing v4 ([#2667](https://github.com/algolia/docsearch/issues/2667)) ([2fe7b1d](https://github.com/algolia/docsearch/commit/2fe7b1d0d753954ee9a4ffbeade8fbc8ba5d4cc9)) +* update to `[@beta](https://github.com/beta)` ([0cebaa6](https://github.com/algolia/docsearch/commit/0cebaa67491557c4f8d803b1f22c38cd13106168)) + +### Features + +* add glow around the search bar and copy above the keyboard ([11716ac](https://github.com/algolia/docsearch/commit/11716ac88225b749b075f08b03aaf9b8608a248f)) +* added glow around the search bar and a little copy above the keyboard ([7942f3f](https://github.com/algolia/docsearch/commit/7942f3f5e914e87a12e6f85348f920891d52491e)) +* **docsearch-website:** Updated docs ([#2662](https://github.com/algolia/docsearch/issues/2662)) ([1b09a56](https://github.com/algolia/docsearch/commit/1b09a569f6cd9ffedaaf3d4965fbc8667053d4f0)) +* **v4:** update the landing page ([#2665](https://github.com/algolia/docsearch/issues/2665)) ([3861789](https://github.com/algolia/docsearch/commit/386178966e497fc1dd2a07b46582c2577ee0e229)) +# [4.0.0-beta.1](https://github.com/algolia/docsearch/compare/v4.0.0-beta.0...v4.0.0-beta.1) (2025-07-09) + +# [4.0.0-beta.0](https://github.com/algolia/docsearch/compare/v3.9.0...v4.0.0-beta.0) (2025-07-09) ### Bug Fixes -* Correctly type `getMissingResultsUrl` query as `string` ([#1781](https://github.com/algolia/docsearch/issues/1781)) ([379f163](https://github.com/algolia/docsearch/commit/379f163a72665ab464b73a1ef92db5e154628dc5)) +* `` in ShadowDOM will open docsearch ([#2212](https://github.com/algolia/docsearch/issues/2212)) ([ec35171](https://github.com/algolia/docsearch/commit/ec351710b134c57bdbcad39e009dae8516f6e571)) +* **a11y:** have unique ids for favorite/recent lists ([#2485](https://github.com/algolia/docsearch/issues/2485)) ([c591f00](https://github.com/algolia/docsearch/commit/c591f004423a9ead953409f3d4a89643fa84b994)) +* recaptcha import ([#2621](https://github.com/algolia/docsearch/issues/2621)) ([ba5bdd8](https://github.com/algolia/docsearch/commit/ba5bdd8a84544b1f92e0d9a88eb12620ce573528)) +* size and background scroll fixes ([#2603](https://github.com/algolia/docsearch/issues/2603)) ([fe03b8e](https://github.com/algolia/docsearch/commit/fe03b8e04e71b328837bb6f0461a0ca0949cf171)) +* **v4:** ask ai updates ([#2654](https://github.com/algolia/docsearch/issues/2654)) ([a64e762](https://github.com/algolia/docsearch/commit/a64e7626cc57330a9998b46e3116da196b693a5d)) + +### Features +* **a11y:** add `aria-keyshortcuts` to search button ([#2365](https://github.com/algolia/docsearch/issues/2365)) ([b68a792](https://github.com/algolia/docsearch/commit/b68a79261e139385cdaf3d0321f25428aa90b242)) +* add theme prop ([#2651](https://github.com/algolia/docsearch/issues/2651)) ([85568ce](https://github.com/algolia/docsearch/commit/85568ce0952007f94e0649d752c25e1c61c36d99)) +* **apply:** Update rest of apply links ([#2655](https://github.com/algolia/docsearch/issues/2655)) ([29fde53](https://github.com/algolia/docsearch/commit/29fde5332033d2766d7648332aeb9a92656dccf5)) +* **onboarding:** Docsearch new sign up page ([#2606](https://github.com/algolia/docsearch/issues/2606)) ([2874789](https://github.com/algolia/docsearch/commit/2874789fad17d3cc8020a8c69109f61408b41460)) +* **onboarding:** Docsearch who can apply updated page ([#2607](https://github.com/algolia/docsearch/issues/2607)) ([161dcdc](https://github.com/algolia/docsearch/commit/161dcdca00e2c578a7a6831335849fe5479737c2)) +* **v4:** add new UI ([#2555](https://github.com/algolia/docsearch/issues/2555)) ([6a160ae](https://github.com/algolia/docsearch/commit/6a160aecf6cb8cd67782dc77ef2f2deb95d7277d)) +* **v4:** ask-ai foundations :sparkles: ([#2574](https://github.com/algolia/docsearch/issues/2574)) ([5ab4d3d](https://github.com/algolia/docsearch/commit/5ab4d3d215242857c4dc0a99d05a1c978852ac4e)) +* **v4:** docsearch askAI context ([#2587](https://github.com/algolia/docsearch/issues/2587)) ([d8bddd1](https://github.com/algolia/docsearch/commit/d8bddd119682d03d17505bc1f85161e57f318d9c)) +* **v4:** update beta documentation ([#2659](https://github.com/algolia/docsearch/issues/2659)) ([e3c0005](https://github.com/algolia/docsearch/commit/e3c0005e471d3c2a05095d5f9d5903573785fa19)) +# [3.9.0](https://github.com/algolia/docsearch/compare/v3.8.3...v3.9.0) (2025-02-17) -## [3.3.2](https://github.com/algolia/docsearch/compare/v3.3.1...v3.3.2) (2023-01-10) +### Features +* **deps:** update react to 19 ([#2487](https://github.com/algolia/docsearch/issues/2487)) ([698c00c](https://github.com/algolia/docsearch/commit/698c00c95543a3e8407f05a4493890fdf2ffaf5a)) + +## [3.8.3](https://github.com/algolia/docsearch/compare/v3.8.2...v3.8.3) (2025-01-21) ### Bug Fixes -* dependencies 2023-01-02 ([#1736](https://github.com/algolia/docsearch/issues/1736)) ([c867c8b](https://github.com/algolia/docsearch/commit/c867c8b6493fec9f184b852f1405247da5173f58)) +* **css:** add transition effects to pressed key buttons ([#2447](https://github.com/algolia/docsearch/issues/2447)) ([7577045](https://github.com/algolia/docsearch/commit/7577045fe81313ccd614455806f87599c55449c6)) +* **modal:** add scroll-margin-top for DocSearch-Hit ([#2434](https://github.com/algolia/docsearch/issues/2434)) ([50de4d9](https://github.com/algolia/docsearch/commit/50de4d9de075368b1ab4b26c13ceeb9def7181eb)), closes [#2433](https://github.com/algolia/docsearch/issues/2433) +* **script:** use mjs for shipjs ([#2462](https://github.com/algolia/docsearch/issues/2462)) ([212814f](https://github.com/algolia/docsearch/commit/212814f121570ddfe5fa9129dc023231a640e9ce)) +* **ui:** compensate for scrollbar ([#2413](https://github.com/algolia/docsearch/issues/2413)) ([db8edeb](https://github.com/algolia/docsearch/commit/db8edeb818f8a788efe89b97f954308a1b1e2b32)), closes [#2378](https://github.com/algolia/docsearch/issues/2378) +## [3.8.2](https://github.com/algolia/docsearch/compare/v3.8.1...v3.8.2) (2024-12-17) -## [3.3.1](https://github.com/algolia/docsearch/compare/v3.3.0...v3.3.1) (2022-12-27) +### Bug Fixes +* **css:** older sass supported syntax ([#2415](https://github.com/algolia/docsearch/issues/2415)) ([0658d31](https://github.com/algolia/docsearch/commit/0658d3190d022954af21ff021da42d08d4db7463)) + +## [3.8.1](https://github.com/algolia/docsearch/compare/v3.8.0...v3.8.1) (2024-12-16) ### Bug Fixes -* **modal:** enable outline for focus state ([#1732](https://github.com/algolia/docsearch/issues/1732)) ([f7a8bde](https://github.com/algolia/docsearch/commit/f7a8bde7af783cb15f0c83346f3699069ad50ff6)) +* use SearchParamsObject for searchParameters ([#2362](https://github.com/algolia/docsearch/issues/2362)) ([24c6b4d](https://github.com/algolia/docsearch/commit/24c6b4dbf7bd476683ddea6ba14f641dcca31895)) +# [3.8.0](https://github.com/algolia/docsearch/compare/v3.7.0...v3.8.0) (2024-11-12) +This version introduces a new type [DocSearchTransformClient](https://github.com/algolia/docsearch/blob/main/packages/docsearch-react/src/DocSearch.tsx#L19) to allow the `transformSearchClient` to use either the lite client, or the full algoliasearch client. +Users are encouraged to use the [lite client](https://www.algolia.com/doc/libraries/javascript/v5/methods/search/) (`import { liteClient } from 'algoliasearch/lite`) to reduce bundle size. -# [3.3.0](https://github.com/algolia/docsearch/compare/v3.2.2...v3.3.0) (2022-10-25) +### Bug Fixes +* **types:** use a minimal type for transformSearchClient ([#2348](https://github.com/algolia/docsearch/issues/2348)) ([08d9a37](https://github.com/algolia/docsearch/commit/08d9a37807a136da99ca2dd39f4cbc86ade9c5bf)) ### Features -* enable `Cmd+K` shortcut when caps lock is on ([#1590](https://github.com/algolia/docsearch/issues/1590)) ([d13ded1](https://github.com/algolia/docsearch/commit/d13ded1cbd8b3fe78ced2ac2f9d97c0fc5c806db)) +* **a11y:** add hotkeys to search input aria-label ([#2337](https://github.com/algolia/docsearch/issues/2337)) ([44aa7dc](https://github.com/algolia/docsearch/commit/44aa7dcf3831b533d9546c95a916d6acdee9d6d8)) +# [3.7.0](https://github.com/algolia/docsearch/compare/v3.6.3...v3.7.0) (2024-11-04) +### Bug Fixes -## [3.2.2](https://github.com/algolia/docsearch/compare/v3.2.1...v3.2.2) (2022-10-20) +* **types:** export the types folder ([#2332](https://github.com/algolia/docsearch/issues/2332)) ([90ca5fa](https://github.com/algolia/docsearch/commit/90ca5fa8ed73d01d4ca22ed0de0e38652e56b5bb)) +## 3.6.3 (2024-10-31) + +* fix: upgrade algoliasearch to v5 (#2326) ([93578a4](https://github.com/algolia/docsearch/commit/93578a4)), closes [#2326](https://github.com/algolia/docsearch/issues/2326) +* fix: use vite instead of parcel in the examples (#2329) ([d360855](https://github.com/algolia/docsearch/commit/d360855)), closes [#2329](https://github.com/algolia/docsearch/issues/2329) +* docs(showcase): Fix logos for ScalarDB and ScalarDL in showcase (#2317) ([90f3c6a](https://github.com/algolia/docsearch/commit/90f3c6a)), closes [#2317](https://github.com/algolia/docsearch/issues/2317) + +## [3.6.2](https://github.com/algolia/docsearch/compare/v3.6.1...v3.6.2) (2024-09-27) + +### Bug Fixes + +* **email:** is being retired ([#2280](https://github.com/algolia/docsearch/issues/2280)) ([698c1cd](https://github.com/algolia/docsearch/commit/698c1cd8cd3848c10941dbdde01661311efd29f9)) +* **hits:** default to hierarchy lvl0 if highlights present ([#2309](https://github.com/algolia/docsearch/issues/2309)) ([5260d1e](https://github.com/algolia/docsearch/commit/5260d1e5c280468bb08a20918c7858e695d11cd8)) + +## [3.6.1](https://github.com/algolia/docsearch/compare/v3.6.0...v3.6.1) (2024-07-16) + +### Bug Fixes + +* updating the templates page ([#2273](https://github.com/algolia/docsearch/issues/2273)) ([4aa0c2c](https://github.com/algolia/docsearch/commit/4aa0c2c67a5ba4d5d786c63cbce749d674b8ce44)) +* **apply form:** Update ApplyForm.js ([#2270](https://github.com/algolia/docsearch/issues/2270)) ([069a3de](https://github.com/algolia/docsearch/commit/069a3de0b5e94b1186315a90e5724a9a3d8b97b1)) +* **logos:** removed logos no longer using algolia ([#2261](https://github.com/algolia/docsearch/issues/2261)) ([8587f58](https://github.com/algolia/docsearch/commit/8587f58b0361699bb9ea26624052b201e69e5531)) +* **logos:** they will not be fixing the search ([#2269](https://github.com/algolia/docsearch/issues/2269)) ([cdfa993](https://github.com/algolia/docsearch/commit/cdfa993990ff8caa323719ff8205db38f2ba06fe)) +* **logos:** update URL and remove logos no longer using DocSearch ([#2260](https://github.com/algolia/docsearch/issues/2260)) ([1081660](https://github.com/algolia/docsearch/commit/108166016d91d8fcc26df15ca52c386d9334ab5d)) + +# [3.6.0](https://github.com/algolia/docsearch/compare/v3.5.2...v3.6.0) (2024-03-06) + +### Bug Fixes + +* add accessible label to SearchBox input ([#2193](https://github.com/algolia/docsearch/issues/2193)) ([df60b8e](https://github.com/algolia/docsearch/commit/df60b8ebe89c80ce3d00b80d94e95b6fd86edd19)) +* **docs:** sidebar logo size ([#2125](https://github.com/algolia/docsearch/issues/2125)) ([572b117](https://github.com/algolia/docsearch/commit/572b117ce6ab3f131b18e345e7e8384685a2a8e0)) +* **website:** update discord link ([#2141](https://github.com/algolia/docsearch/issues/2141)) ([874e16a](https://github.com/algolia/docsearch/commit/874e16a5d42e8657e6ab2653e9638cd2282ba408)) + +### Features + +* add additional scss output ([#2201](https://github.com/algolia/docsearch/issues/2201)) ([fe3ac9e](https://github.com/algolia/docsearch/commit/fe3ac9e782c2bc84e39b61e3749f067c59f56129)) +* add keypress effect to DocSearchButton ([#2087](https://github.com/algolia/docsearch/issues/2087)) ([4c82640](https://github.com/algolia/docsearch/commit/4c82640022154dd32cc9d3e28a7ae2fba786da8d)) + +## [3.5.2](https://github.com/algolia/docsearch/compare/v3.5.1...v3.5.2) (2023-08-11) + +### Fixes + +* Updates `algoliasearch` package version ([cc90789](https://github.com/algolia/docsearch/commit/cc9078929032e7360fd629ed43071e698a54bab6)) +* Adds `search-insights` as options peer dependency ([cc90789](https://github.com/algolia/docsearch/commit/cc9078929032e7360fd629ed43071e698a54bab6)) + +## [3.5.1](https://github.com/algolia/docsearch/compare/v3.5.0...v3.5.1) (2023-06-15) + +### Features + +* **api:** support Insights on search results click ([#1916](https://github.com/algolia/docsearch/issues/1916)) ([cacade4](https://github.com/algolia/docsearch/commit/cacade4c65878877c5c7433281299e3a5f9a2541)) + +# [3.5.0](https://github.com/algolia/docsearch/compare/v3.4.0...v3.5.0) (2023-06-08) + +### Features + +* **api:** allow insights options to be forwarded to Autocomplete ([#1904](https://github.com/algolia/docsearch/issues/1904)) ([26691a5](https://github.com/algolia/docsearch/commit/26691a5c8abfe107a6737d66d3f02c2cac50553b)) + +# [3.4.0](https://github.com/algolia/docsearch/compare/v3.3.5...v3.4.0) (2023-05-24) + +### Features + +* implement maxResultsPerGroup prop ([#1891](https://github.com/algolia/docsearch/issues/1891)) ([f82a733](https://github.com/algolia/docsearch/commit/f82a73367a02453609454dc26d3cf2b6d901dd75)) + +## [3.3.5](https://github.com/algolia/docsearch/compare/v3.3.4...v3.3.5) (2023-05-17) + +### Bug Fixes + +* support Chrome autofill when `event.key` is undefined ([#1888](https://github.com/algolia/docsearch/issues/1888)) ([3f15fdb](https://github.com/algolia/docsearch/commit/3f15fdb3192b654dda14c36a5780f49fe309329c)), closes [#1887](https://github.com/algolia/docsearch/issues/1887) + +## [3.3.4](https://github.com/algolia/docsearch/compare/v3.3.3...v3.3.4) (2023-04-26) + +### Bug Fixes + +* prevent calling `onClose()` when `shiftKey`, `ctrlKey` or `metaKey` is pressed ([#1870](https://github.com/algolia/docsearch/issues/1870)) ([b9a3899](https://github.com/algolia/docsearch/commit/b9a38991e411c969459c2bac8a75f29576b46906)) +* type of lvl0 ([#1859](https://github.com/algolia/docsearch/issues/1859)) ([8f48ebb](https://github.com/algolia/docsearch/commit/8f48ebb330c01be426f06d0dea2855e4ca65834b)) + +--- + +## Typesense fork releases (2022–2023) + +> The entries below are preserved from the changelog of the Typesense DocSearch fork ([typesense/typesense-docsearch.js](https://github.com/typesense/typesense-docsearch.js)), which tracked its own release trains before v5: `typesense-docsearch.js` / `typesense-docsearch-react` 3.x and `typesense-docsearch-css` 0.x. Later fork releases (`3.4.0-8`, `3.4.0-9`, `3.4.0`, `3.4.1`, and `typesense-docsearch-css` `0.4.0` / `0.4.1`) were published to npm without changelog entries. + +# [3.4.0-7](https://github.com/typesense/docsearch.js/compare/v3.4.0-5...v3.4.0-7) (2023-03-21) + +# [3.4.0-6](https://github.com/typesense/docsearch.js/compare/v3.4.0-5...v3.4.0-6) (2023-03-21) + +# [3.4.0-5](https://github.com/typesense/docsearch.js/compare/v0.4.0-3...v3.4.0-5) (2023-03-21) + +# [0.4.0-3](https://github.com/typesense/docsearch.js/compare/v3.4.0-4...v0.4.0-3) (2023-03-21) + +# [3.4.0-4](https://github.com/typesense/docsearch.js/compare/v0.4.0-2...v3.4.0-4) (2023-03-21) + +# [0.4.0-2](https://github.com/typesense/docsearch.js/compare/v3.4.0-3...v0.4.0-2) (2023-03-21) + +# [3.4.0-3](https://github.com/typesense/docsearch.js/compare/v3.4.0-2...v3.4.0-3) (2023-03-20) + +# [3.4.0-2](https://github.com/typesense/docsearch.js/compare/v0.4.0-1...v3.4.0-2) (2023-03-20) + +# [0.4.0-1](https://github.com/typesense/docsearch.js/compare/v3.4.0-1...v0.4.0-1) (2023-03-20) + +# [3.4.0-1](https://github.com/typesense/docsearch.js/compare/v0.4.0-0...v3.4.0-1) (2023-03-20) + +# [0.4.0-0](https://github.com/typesense/docsearch.js/compare/v3.4.0-0...v0.4.0-0) (2023-03-20) + +# [3.4.0-0](https://github.com/typesense/docsearch.js/compare/v0.3.0...v3.4.0-0) (2023-03-20) + +# [0.3.0](https://github.com/typesense/docsearch.js/compare/v3.3.3...v0.3.0) (2023-03-08) + +## [3.0.1](https://github.com/typesense/docsearch.js/compare/v0.2.3...v3.0.1) (2022-07-25) + +## [0.2.3](https://github.com/typesense/docsearch.js/compare/v0.2.2...v0.2.3) (2022-07-25) + +## [0.2.2](https://github.com/typesense/docsearch.js/compare/v3.0.0...v0.2.2) (2022-07-25) + +# [3.0.0](https://github.com/typesense/docsearch.js/compare/v0.2.1...v3.0.0) (2022-07-25) + +## [0.2.1](https://github.com/typesense/docsearch.js/compare/v0.2.1-0...v0.2.1) (2022-07-25) + +## [0.2.1-0](https://github.com/typesense/docsearch.js/compare/v3.0.0-3...v0.2.1-0) (2022-07-25) + + +### Features + +* **ui:** use Typesense color as primary color ([ad7e045](https://github.com/typesense/docsearch.js/commit/ad7e045f64e0baedef1cc6e014d8519c3d3e246e)) +* **ui:** use Typesense color in logo ([fe45232](https://github.com/typesense/docsearch.js/commit/fe452323e5537e2bcb84e3725b6bc0e918b30f2a)) + +# [3.0.0-3](https://github.com/typesense/docsearch.js/compare/v3.0.0-2...v3.0.0-3) (2022-07-22) + +# [3.0.0-2](https://github.com/typesense/docsearch.js/compare/v3.0.0-1...v3.0.0-2) (2022-07-22) + +# [3.0.0-1](https://github.com/typesense/docsearch.js/compare/v3.1.1...v3.0.0-1) (2022-07-22) + +--- + +## [3.3.3](https://github.com/algolia/docsearch/compare/v3.3.2...v3.3.3) (2023-02-06) + +### Bug Fixes + +* Correctly type `getMissingResultsUrl` query as `string` ([#1781](https://github.com/algolia/docsearch/issues/1781)) ([379f163](https://github.com/algolia/docsearch/commit/379f163a72665ab464b73a1ef92db5e154628dc5)) + +## [3.3.2](https://github.com/algolia/docsearch/compare/v3.3.1...v3.3.2) (2023-01-10) + +### Bug Fixes + +* dependencies 2023-01-02 ([#1736](https://github.com/algolia/docsearch/issues/1736)) ([c867c8b](https://github.com/algolia/docsearch/commit/c867c8b6493fec9f184b852f1405247da5173f58)) + +## [3.3.1](https://github.com/algolia/docsearch/compare/v3.3.0...v3.3.1) (2022-12-27) + +### Bug Fixes + +* **modal:** enable outline for focus state ([#1732](https://github.com/algolia/docsearch/issues/1732)) ([f7a8bde](https://github.com/algolia/docsearch/commit/f7a8bde7af783cb15f0c83346f3699069ad50ff6)) + +# [3.3.0](https://github.com/algolia/docsearch/compare/v3.2.2...v3.3.0) (2022-10-25) + +### Features + +* enable `Cmd+K` shortcut when caps lock is on ([#1590](https://github.com/algolia/docsearch/issues/1590)) ([d13ded1](https://github.com/algolia/docsearch/commit/d13ded1cbd8b3fe78ced2ac2f9d97c0fc5c806db)) + +## [3.2.2](https://github.com/algolia/docsearch/compare/v3.2.1...v3.2.2) (2022-10-20) ### Bug Fixes @@ -176,179 +486,177 @@ * dependencies 2022-08-22 ([#1509](https://github.com/algolia/docsearch/issues/1509)) ([0da9cf8](https://github.com/algolia/docsearch/commit/0da9cf8b754e3ca66d2a20c5a67ab5a4a094713a)) * dependencies 2022-08-29 ([#1523](https://github.com/algolia/docsearch/issues/1523)) ([f1ce39e](https://github.com/algolia/docsearch/commit/f1ce39e31a44ad26bae1ad3b2b70bfcfe88189af)), closes [#1530](https://github.com/algolia/docsearch/issues/1530) [#1533](https://github.com/algolia/docsearch/issues/1533) - - ## [3.2.1](https://github.com/algolia/docsearch/compare/v3.2.0...v3.2.1) (2022-08-16) ### Bug Fixes -* **deps:** make react an optional peer dependency in @docsearch/react ([#1273](https://github.com/algolia/docsearch/issues/1273)) ([eb44d0c](https://github.com/algolia/docsearch/commit/eb44d0c2b72f700b81cf1390539592156b2580b6)) +* **deps:** make react an optional peer dependency in typesense-docsearch-react ([#1273](https://github.com/algolia/docsearch/issues/1273)) ([eb44d0c](https://github.com/algolia/docsearch/commit/eb44d0c2b72f700b81cf1390539592156b2580b6)) * dependencies 2022-08-08 ([#1480](https://github.com/algolia/docsearch/issues/1480)) ([587ccd1](https://github.com/algolia/docsearch/commit/587ccd1cf9ba5529c8c7ded3547e21849321a8d0)) # [3.2.0](https://github.com/algolia/docsearch/compare/v3.1.1...v3.2.0) (2022-08-02) ### Bug Fixes -- dependencies 2022-07-25 ([#1451](https://github.com/algolia/docsearch/issues/1451)) ([2c64362](https://github.com/algolia/docsearch/commit/2c64362067376a76575b7b5131972f71c920a406)) -- dependencies 2022-08-01 ([#1471](https://github.com/algolia/docsearch/issues/1471)) ([8f67c47](https://github.com/algolia/docsearch/commit/8f67c474881c088ae5a8c1595fa844c210940140)) -- **form:** validate URL correctly ([26b0af9](https://github.com/algolia/docsearch/commit/26b0af98e01b36e5238ab8e2b1ba9d5212a1e1fe)) +* dependencies 2022-07-25 ([#1451](https://github.com/algolia/docsearch/issues/1451)) ([2c64362](https://github.com/algolia/docsearch/commit/2c64362067376a76575b7b5131972f71c920a406)) +* dependencies 2022-08-01 ([#1471](https://github.com/algolia/docsearch/issues/1471)) ([8f67c47](https://github.com/algolia/docsearch/commit/8f67c474881c088ae5a8c1595fa844c210940140)) +* **form:** validate URL correctly ([26b0af9](https://github.com/algolia/docsearch/commit/26b0af98e01b36e5238ab8e2b1ba9d5212a1e1fe)) ### Features -- **styles:** set medium breakpoint to standarized value ([#1446](https://github.com/algolia/docsearch/issues/1446)) ([8e2e90e](https://github.com/algolia/docsearch/commit/8e2e90e980b03e99ec7ae3231dbe79e44db643da)) +* **styles:** set medium breakpoint to standarized value ([#1446](https://github.com/algolia/docsearch/issues/1446)) ([8e2e90e](https://github.com/algolia/docsearch/commit/8e2e90e980b03e99ec7ae3231dbe79e44db643da)) ## [3.1.1](https://github.com/algolia/docsearch/compare/v3.1.0...v3.1.1) (2022-06-30) ### Bug Fixes -- **js:** use outDir to avoid ambient type definition creation ([#1396](https://github.com/algolia/docsearch/issues/1396)) ([4c924bc](https://github.com/algolia/docsearch/commit/4c924bc3c19c295d24ef13ccffd58ae51a37b68f)) -- **searchbutton:** fix react production hydration error ([#1384](https://github.com/algolia/docsearch/issues/1384)) ([448192e](https://github.com/algolia/docsearch/commit/448192e07b482724e581a0181d85e871ec27befa)) -- **deps:** bump and fix autocomplete deps ([#1416](https://github.com/algolia/docsearch/issues/1416)) ([448192e](https://github.com/algolia/docsearch/commit/d7be594fc62c8ee8573dbaf5ffad3f4bb9149002)) +* **js:** use outDir to avoid ambient type definition creation ([#1396](https://github.com/algolia/docsearch/issues/1396)) ([4c924bc](https://github.com/algolia/docsearch/commit/4c924bc3c19c295d24ef13ccffd58ae51a37b68f)) +* **searchbutton:** fix react production hydration error ([#1384](https://github.com/algolia/docsearch/issues/1384)) ([448192e](https://github.com/algolia/docsearch/commit/448192e07b482724e581a0181d85e871ec27befa)) +* **deps:** bump and fix autocomplete deps ([#1416](https://github.com/algolia/docsearch/issues/1416)) ([448192e](https://github.com/algolia/docsearch/commit/d7be594fc62c8ee8573dbaf5ffad3f4bb9149002)) # [3.1.0](https://github.com/algolia/docsearch/compare/v3.0.0...v3.1.0) (2022-05-18) ### Bug Fixes -- **button:** use kbd instead of span ([#1335](https://github.com/algolia/docsearch/issues/1335)) ([7459a42](https://github.com/algolia/docsearch/commit/7459a424d9435f46f3f6115921f1b7528e0c10da)) -- **css:** prevent `kbd` unwanted style overriding ([#1389](https://github.com/algolia/docsearch/issues/1389)) ([613f2a8](https://github.com/algolia/docsearch/commit/613f2a882d73a967d738971376fc30e799cc61eb)) +* **button:** use kbd instead of span ([#1335](https://github.com/algolia/docsearch/issues/1335)) ([7459a42](https://github.com/algolia/docsearch/commit/7459a424d9435f46f3f6115921f1b7528e0c10da)) +* **css:** prevent `kbd` unwanted style overriding ([#1389](https://github.com/algolia/docsearch/issues/1389)) ([613f2a8](https://github.com/algolia/docsearch/commit/613f2a882d73a967d738971376fc30e799cc61eb)) ### Features -- **deps:** bump peer to React 18 ([#1385](https://github.com/algolia/docsearch/issues/1385)) ([eb372f2](https://github.com/algolia/docsearch/commit/eb372f2743e9f74e2546cde59e676c32b903bece)) +* **deps:** bump peer to React 18 ([#1385](https://github.com/algolia/docsearch/issues/1385)) ([eb372f2](https://github.com/algolia/docsearch/commit/eb372f2743e9f74e2546cde59e676c32b903bece)) # [3.0.0](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.50...v3.0.0) (2022-02-22) ### Breaking Change -- **v3:** require appId ([#1299](https://github.com/algolia/docsearch/issues/1299)) ([80630cb](https://github.com/algolia/docsearch/commit/80630cb36dcfe486eff4f7b859deef5431775045)) +* **v3:** require appId ([#1299](https://github.com/algolia/docsearch/issues/1299)) ([80630cb](https://github.com/algolia/docsearch/commit/80630cb36dcfe486eff4f7b859deef5431775045)) ### Bug Fixes -- **footer:** make SVG accessible ([#1306](https://github.com/algolia/docsearch/issues/1306)) ([59a3ebf](https://github.com/algolia/docsearch/commit/59a3ebf5ecfca951da5139faa08812d450fab1e0)) +* **footer:** make SVG accessible ([#1306](https://github.com/algolia/docsearch/issues/1306)) ([59a3ebf](https://github.com/algolia/docsearch/commit/59a3ebf5ecfca951da5139faa08812d450fab1e0)) # [3.0.0-alpha.50](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.42...v3.0.0-alpha.50) (2022-02-03) ### Bug Fixes -- **deps:** update algoliasearch-autocomplete monorepo to v1.5.2 ([#1225](https://github.com/algolia/docsearch/issues/1225)) ([aa309f5](https://github.com/algolia/docsearch/commit/aa309f54afddaca54b38f571e367c2ad2830fb3e)) -- **deps:** update dependency @svgr/webpack to v6 ([#1219](https://github.com/algolia/docsearch/issues/1219)) ([52d6927](https://github.com/algolia/docsearch/commit/52d6927c7e4c840f6201ce64793a811a2e17ff2e)) -- **deps:** update dependency postcss to v8.4.5 ([#1205](https://github.com/algolia/docsearch/issues/1205)) ([d348589](https://github.com/algolia/docsearch/commit/d34858949e7f6dd0e2ff3af68f459e00a1d6a337)) -- **deps:** update dependency postcss-preset-env to v7.0.2 ([#1228](https://github.com/algolia/docsearch/issues/1228)) ([1a7093b](https://github.com/algolia/docsearch/commit/1a7093b93ea03c20f1f1efaff2a450ce6aa007af)) -- **deps:** update dependency postcss-preset-env to v7.1.0 ([#1240](https://github.com/algolia/docsearch/issues/1240)) ([cbb14eb](https://github.com/algolia/docsearch/commit/cbb14eb11451c291299ceee38e39aae527196232)) -- **deps:** update dependency postcss-preset-env to v7.2.0 ([#1252](https://github.com/algolia/docsearch/issues/1252)) ([b3288fe](https://github.com/algolia/docsearch/commit/b3288feb31138bd61a0cc0953e6dc281870eac58)) -- **deps:** update docusaurus monorepo to v2.0.0-beta.13 ([#1226](https://github.com/algolia/docsearch/issues/1226)) ([02025e5](https://github.com/algolia/docsearch/commit/02025e5a2c0477eb39676a73f7cf2e17b761594b)) -- **deps:** update docusaurus monorepo to v2.0.0-beta.14 ([#1238](https://github.com/algolia/docsearch/issues/1238)) ([77f785d](https://github.com/algolia/docsearch/commit/77f785d66de0a9527b5a572855e3d8e938b0b7d4)) -- **logo:** empty utm campaign ([#1259](https://github.com/algolia/docsearch/issues/1259)) ([30ee161](https://github.com/algolia/docsearch/commit/30ee161921c61bf994a6be2da212fd999171fe47)) +* **deps:** update algoliasearch-autocomplete monorepo to v1.5.2 ([#1225](https://github.com/algolia/docsearch/issues/1225)) ([aa309f5](https://github.com/algolia/docsearch/commit/aa309f54afddaca54b38f571e367c2ad2830fb3e)) +* **deps:** update dependency @svgr/webpack to v6 ([#1219](https://github.com/algolia/docsearch/issues/1219)) ([52d6927](https://github.com/algolia/docsearch/commit/52d6927c7e4c840f6201ce64793a811a2e17ff2e)) +* **deps:** update dependency postcss to v8.4.5 ([#1205](https://github.com/algolia/docsearch/issues/1205)) ([d348589](https://github.com/algolia/docsearch/commit/d34858949e7f6dd0e2ff3af68f459e00a1d6a337)) +* **deps:** update dependency postcss-preset-env to v7.0.2 ([#1228](https://github.com/algolia/docsearch/issues/1228)) ([1a7093b](https://github.com/algolia/docsearch/commit/1a7093b93ea03c20f1f1efaff2a450ce6aa007af)) +* **deps:** update dependency postcss-preset-env to v7.1.0 ([#1240](https://github.com/algolia/docsearch/issues/1240)) ([cbb14eb](https://github.com/algolia/docsearch/commit/cbb14eb11451c291299ceee38e39aae527196232)) +* **deps:** update dependency postcss-preset-env to v7.2.0 ([#1252](https://github.com/algolia/docsearch/issues/1252)) ([b3288fe](https://github.com/algolia/docsearch/commit/b3288feb31138bd61a0cc0953e6dc281870eac58)) +* **deps:** update docusaurus monorepo to v2.0.0-beta.13 ([#1226](https://github.com/algolia/docsearch/issues/1226)) ([02025e5](https://github.com/algolia/docsearch/commit/02025e5a2c0477eb39676a73f7cf2e17b761594b)) +* **deps:** update docusaurus monorepo to v2.0.0-beta.14 ([#1238](https://github.com/algolia/docsearch/issues/1238)) ([77f785d](https://github.com/algolia/docsearch/commit/77f785d66de0a9527b5a572855e3d8e938b0b7d4)) +* **logo:** empty utm campaign ([#1259](https://github.com/algolia/docsearch/issues/1259)) ([30ee161](https://github.com/algolia/docsearch/commit/30ee161921c61bf994a6be2da212fd999171fe47)) ### Features -- **noResults:** allow providing URL to report missing results ([#1289](https://github.com/algolia/docsearch/issues/1289)) ([2c1fe7c](https://github.com/algolia/docsearch/commit/2c1fe7c77c5d5ab28875aa86c9a8ef3b4ffdb254)) +* **noResults:** allow providing URL to report missing results ([#1289](https://github.com/algolia/docsearch/issues/1289)) ([2c1fe7c](https://github.com/algolia/docsearch/commit/2c1fe7c77c5d5ab28875aa86c9a8ef3b4ffdb254)) # [v3.0.0-alpha.42](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.41...v3.0.0-alpha.42) (2021-12-03) ### Bug Fixes -- **deps:** update dependency postcss to v8.3.11 ([#1153](https://github.com/algolia/docsearch/issues/1153)) ([0ddad32](https://github.com/algolia/docsearch/commit/0ddad32aaff34c7660d3f0e3a71fe88eafa3dfb8)) -- **deps:** update dependency postcss-import to v14 ([#1155](https://github.com/algolia/docsearch/issues/1155)) ([ebaf66e](https://github.com/algolia/docsearch/commit/ebaf66e3ef26f74152ce25ea062f7010a45ad05a)) -- **deps:** update dependency postcss-preset-env to v7 ([#1194](https://github.com/algolia/docsearch/issues/1194)) ([96af398](https://github.com/algolia/docsearch/commit/96af3984110825baa4b048b9fef4fdfec44148bc)) -- **deps:** update docusaurus monorepo to v2.0.0-beta.9 ([#1166](https://github.com/algolia/docsearch/issues/1166)) ([5aed646](https://github.com/algolia/docsearch/commit/5aed646ba8d5f1adf3d5a6671387a8595bc7347a)) -- **discord:** update invite link ([510cbc6](https://github.com/algolia/docsearch/commit/510cbc679954919d58148f14d55d416b49d3009f)) -- **DocSearchProps:** type `searchParameters` ([#1200](https://github.com/algolia/docsearch/issues/1200)) ([80677fd](https://github.com/algolia/docsearch/commit/80677fd7d36a85b6a205383a7ca3d68969fbe3bf)) -- **examples:** use new infra credentials ([65d9414](https://github.com/algolia/docsearch/commit/65d9414fce6c26389e9e86c93b46a28d49f8102c)) -- **issue template:** update codesandbox link ([891fd88](https://github.com/algolia/docsearch/commit/891fd88d1bd3670533fc4a8b41d21678a6f6b832)) +* **deps:** update dependency postcss to v8.3.11 ([#1153](https://github.com/algolia/docsearch/issues/1153)) ([0ddad32](https://github.com/algolia/docsearch/commit/0ddad32aaff34c7660d3f0e3a71fe88eafa3dfb8)) +* **deps:** update dependency postcss-import to v14 ([#1155](https://github.com/algolia/docsearch/issues/1155)) ([ebaf66e](https://github.com/algolia/docsearch/commit/ebaf66e3ef26f74152ce25ea062f7010a45ad05a)) +* **deps:** update dependency postcss-preset-env to v7 ([#1194](https://github.com/algolia/docsearch/issues/1194)) ([96af398](https://github.com/algolia/docsearch/commit/96af3984110825baa4b048b9fef4fdfec44148bc)) +* **deps:** update docusaurus monorepo to v2.0.0-beta.9 ([#1166](https://github.com/algolia/docsearch/issues/1166)) ([5aed646](https://github.com/algolia/docsearch/commit/5aed646ba8d5f1adf3d5a6671387a8595bc7347a)) +* **discord:** update invite link ([510cbc6](https://github.com/algolia/docsearch/commit/510cbc679954919d58148f14d55d416b49d3009f)) +* **DocSearchProps:** type `searchParameters` ([#1200](https://github.com/algolia/docsearch/issues/1200)) ([80677fd](https://github.com/algolia/docsearch/commit/80677fd7d36a85b6a205383a7ca3d68969fbe3bf)) +* **examples:** use new infra credentials ([65d9414](https://github.com/algolia/docsearch/commit/65d9414fce6c26389e9e86c93b46a28d49f8102c)) +* **issue template:** update codesandbox link ([891fd88](https://github.com/algolia/docsearch/commit/891fd88d1bd3670533fc4a8b41d21678a6f6b832)) ### Features -- **translations:** expose Button and Modal translations ([#1183](https://github.com/algolia/docsearch/issues/1183)) ([ef13dea](https://github.com/algolia/docsearch/commit/ef13dea393e02ec459c8adcb5883e896fbd64bb0)) +* **translations:** expose Button and Modal translations ([#1183](https://github.com/algolia/docsearch/issues/1183)) ([ef13dea](https://github.com/algolia/docsearch/commit/ef13dea393e02ec459c8adcb5883e896fbd64bb0)) # [v3.0.0-alpha.41](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.40...v3.0.0-alpha.41) (2021-10-18) ### Bug Fixes -- **build:** remove occurences of `process` in UMD build ([#1047](https://github.com/algolia/docsearch/issues/1047)) ([20f54c9](https://github.com/algolia/docsearch/commit/20f54c968cf02e95d8d85e5d6efdc163f2236edf)) -- **ci:** update docker image ([#1052](https://github.com/algolia/docsearch/issues/1052)) ([6b4944f](https://github.com/algolia/docsearch/commit/6b4944f4027e8a7107ab5b1f44cb7fe028d43a05)) -- **docs:** relative apply link ([#1060](https://github.com/algolia/docsearch/issues/1060)) ([7b48078](https://github.com/algolia/docsearch/commit/7b48078c277772ebb12f782602a66d14fc58d329)) -- **docs:** typo in `Home.js` ([#1054](https://github.com/algolia/docsearch/issues/1054)) ([2942246](https://github.com/algolia/docsearch/commit/29422464cc9c67a94e28ef7f1014221b5a17594c)) -- **v3:** pass `initialQuery` ([#1051](https://github.com/algolia/docsearch/issues/1051)) ([6e194c7](https://github.com/algolia/docsearch/commit/6e194c73bf604611a0b975554590efd748f1c2d3)) -- **website:** apply feedbacks from beta testers ([#1053](https://github.com/algolia/docsearch/issues/1053)) ([33d78e7](https://github.com/algolia/docsearch/commit/33d78e7d328bc17da259f7a83c84cd2612e6cd8b)) +* **build:** remove occurences of `process` in UMD build ([#1047](https://github.com/algolia/docsearch/issues/1047)) ([20f54c9](https://github.com/algolia/docsearch/commit/20f54c968cf02e95d8d85e5d6efdc163f2236edf)) +* **ci:** update docker image ([#1052](https://github.com/algolia/docsearch/issues/1052)) ([6b4944f](https://github.com/algolia/docsearch/commit/6b4944f4027e8a7107ab5b1f44cb7fe028d43a05)) +* **docs:** relative apply link ([#1060](https://github.com/algolia/docsearch/issues/1060)) ([7b48078](https://github.com/algolia/docsearch/commit/7b48078c277772ebb12f782602a66d14fc58d329)) +* **docs:** typo in `Home.js` ([#1054](https://github.com/algolia/docsearch/issues/1054)) ([2942246](https://github.com/algolia/docsearch/commit/29422464cc9c67a94e28ef7f1014221b5a17594c)) +* **v3:** pass `initialQuery` ([#1051](https://github.com/algolia/docsearch/issues/1051)) ([6e194c7](https://github.com/algolia/docsearch/commit/6e194c73bf604611a0b975554590efd748f1c2d3)) +* **website:** apply feedbacks from beta testers ([#1053](https://github.com/algolia/docsearch/issues/1053)) ([33d78e7](https://github.com/algolia/docsearch/commit/33d78e7d328bc17da259f7a83c84cd2612e6cd8b)) ### Features -- **website:** new documentation ([#1048](https://github.com/algolia/docsearch/issues/1048)) ([9a89cde](https://github.com/algolia/docsearch/commit/9a89cde74605a1011dd3dcf8a02807228017b965)) +* **website:** new documentation ([#1048](https://github.com/algolia/docsearch/issues/1048)) ([9a89cde](https://github.com/algolia/docsearch/commit/9a89cde74605a1011dd3dcf8a02807228017b965)) # [v3.0.0-alpha.40](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.39...v3.0.0-alpha.40) (2021-08-23) ### Bug Fixes -- **ci:** update config file ([#1040](https://github.com/algolia/docsearch/issues/1040)) ([4673f00](https://github.com/algolia/docsearch/commit/4673f00bf3feb114e5e1dd043c56c78177c7292a)) -- **removeHighlightTags:** prevent destructuring `undefined` object ([#1039](https://github.com/algolia/docsearch/issues/1039)) ([6a6f7f6](https://github.com/algolia/docsearch/commit/6a6f7f6e8d0f803062c42b0c4521a0501bb287a1)) +* **ci:** update config file ([#1040](https://github.com/algolia/docsearch/issues/1040)) ([4673f00](https://github.com/algolia/docsearch/commit/4673f00bf3feb114e5e1dd043c56c78177c7292a)) +* **removeHighlightTags:** prevent destructuring `undefined` object ([#1039](https://github.com/algolia/docsearch/issues/1039)) ([6a6f7f6](https://github.com/algolia/docsearch/commit/6a6f7f6e8d0f803062c42b0c4521a0501bb287a1)) # [v3.0.0-alpha.39](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.38...v3.0.0-alpha.39) (2021-07-23) ### Bug Fixes -- **button:** prevent container keys ui flash ([#1033](https://github.com/algolia/docsearch/issues/1033)) ([7fad6a4](https://github.com/algolia/docsearch/commit/7fad6a46c18f2e08befc6ddf02817bb1d5930828)) +* **button:** prevent container keys ui flash ([#1033](https://github.com/algolia/docsearch/issues/1033)) ([7fad6a4](https://github.com/algolia/docsearch/commit/7fad6a46c18f2e08befc6ddf02817bb1d5930828)) # [v3.0.0-alpha.38](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.37...v3.0.0-alpha.38) (2021-07-23) ### Bug Fixes -- eliminate keyboard shortcuts UI flash ([#1021](https://github.com/algolia/docsearch/issues/1021)) ([c8e0c27](https://github.com/algolia/docsearch/commit/c8e0c270f3ab2c3ddf27f2247e69c31ed4a732f3)) -- **searchClient:** use client `search` method ([#1019](https://github.com/algolia/docsearch/issues/1019)) ([9da72a8](https://github.com/algolia/docsearch/commit/9da72a8909c1403396f0fe147ecf7bc768f1bab9)) +* eliminate keyboard shortcuts UI flash ([#1021](https://github.com/algolia/docsearch/issues/1021)) ([c8e0c27](https://github.com/algolia/docsearch/commit/c8e0c270f3ab2c3ddf27f2247e69c31ed4a732f3)) +* **searchClient:** use client `search` method ([#1019](https://github.com/algolia/docsearch/issues/1019)) ([9da72a8](https://github.com/algolia/docsearch/commit/9da72a8909c1403396f0fe147ecf7bc768f1bab9)) # [v3.0.0-alpha.37](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.36...v3.0.0-alpha.37) (2021-07-09) ### Bug Fixes -- **dependencies:** upgrade dependencies ([#1015](https://github.com/algolia/docsearch/issues/1015)) ([9300cb4](https://github.com/algolia/docsearch/commit/9300cb4bd6fe09d9649eb3664a6fa6b40412cd6a)) +* **dependencies:** upgrade dependencies ([#1015](https://github.com/algolia/docsearch/issues/1015)) ([9300cb4](https://github.com/algolia/docsearch/commit/9300cb4bd6fe09d9649eb3664a6fa6b40412cd6a)) # [v3.0.0-alpha.36](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.35...v3.0.0-alpha.36) (2021-04-14) ### Bug Fixes -- **escapedResults:** fix undefined `_highlightResult` ([#1003](https://github.com/algolia/docsearch/issues/1003)) ([16e6558](https://github.com/algolia/docsearch/commit/16e6558da735e014c809f1b549fdb7fccd682dbc)) +* **escapedResults:** fix undefined `_highlightResult` ([#1003](https://github.com/algolia/docsearch/issues/1003)) ([16e6558](https://github.com/algolia/docsearch/commit/16e6558da735e014c809f1b549fdb7fccd682dbc)) # [3.0.0-alpha.35](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.34...v3.0.0-alpha.35) (2021-04-12) ### Bug Fixes -- **results:** unescaped lvl0 ([#1001](https://github.com/algolia/docsearch/issues/1001)) ([117228b](https://github.com/algolia/docsearch/commit/117228b19b57755c1d1cc2762f73a5b1e76a1da4)) +* **results:** unescaped lvl0 ([#1001](https://github.com/algolia/docsearch/issues/1001)) ([117228b](https://github.com/algolia/docsearch/commit/117228b19b57755c1d1cc2762f73a5b1e76a1da4)) # [3.0.0-alpha.34](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.33...v3.0.0-alpha.34) (2021-03-09) ### Bug Fixes -- **deps:** Update and pin Autocomplete to the latest version ([#999](https://github.com/algolia/docsearch/issues/999)) ([1421077](https://github.com/algolia/docsearch/commit/1421077637ca52284e33f95e1745df2796039891)) +* **deps:** Update and pin Autocomplete to the latest version ([#999](https://github.com/algolia/docsearch/issues/999)) ([1421077](https://github.com/algolia/docsearch/commit/1421077637ca52284e33f95e1745df2796039891)) # [3.0.0-alpha.33](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.32...v3.0.0-alpha.33) (2021-02-05) ### Bug Fixes -- use spans inside button tag instead of divs ([#994](https://github.com/algolia/docsearch/issues/994)) ([f5c2a27](https://github.com/algolia/docsearch/commit/f5c2a2713651985412cb57fc37aeac787270c77c)) -- **css:** remove `sideEffects` package field ([#991](https://github.com/algolia/docsearch/issues/991)) ([9216ffa](https://github.com/algolia/docsearch/commit/9216ffa0a86724144d1b34d83fc288711eb22322)) -- **website:** Sync tailwind darkmode class with docusaurus theme context ([3ade29f](https://github.com/algolia/docsearch/commit/3ade29f4e1569c05c3dcff430a8317611e471c28)) +* use spans inside button tag instead of divs ([#994](https://github.com/algolia/docsearch/issues/994)) ([f5c2a27](https://github.com/algolia/docsearch/commit/f5c2a2713651985412cb57fc37aeac787270c77c)) +* **css:** remove `sideEffects` package field ([#991](https://github.com/algolia/docsearch/issues/991)) ([9216ffa](https://github.com/algolia/docsearch/commit/9216ffa0a86724144d1b34d83fc288711eb22322)) +* **website:** Sync tailwind darkmode class with docusaurus theme context ([3ade29f](https://github.com/algolia/docsearch/commit/3ade29f4e1569c05c3dcff430a8317611e471c28)) ### Features -- add `translations` prop to search button ([#995](https://github.com/algolia/docsearch/issues/995)) ([44f5c7b](https://github.com/algolia/docsearch/commit/44f5c7b5f8ef647a221cf4d41ffe9f3485ad842d)) +* add `translations` prop to search button ([#995](https://github.com/algolia/docsearch/issues/995)) ([44f5c7b](https://github.com/algolia/docsearch/commit/44f5c7b5f8ef647a221cf4d41ffe9f3485ad842d)) # [3.0.0-alpha.32](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.31...v3.0.0-alpha.32) (2020-12-01) ### Bug Fixes -- highlight first item by default ([777bc8e](https://github.com/algolia/docsearch/commit/777bc8ef6896bded5e0c97d07d8a55a77bcb37ad)) +* highlight first item by default ([777bc8e](https://github.com/algolia/docsearch/commit/777bc8ef6896bded5e0c97d07d8a55a77bcb37ad)) # [3.0.0-alpha.31](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.30...v3.0.0-alpha.31) (2020-11-25) ### Bug Fixes -- display keyboard shortcuts when platform exists ([79f5b27](https://github.com/algolia/docsearch/commit/79f5b274be9214adb36bf143eecaa6634275ddc0)) +* display keyboard shortcuts when platform exists ([79f5b27](https://github.com/algolia/docsearch/commit/79f5b274be9214adb36bf143eecaa6634275ddc0)) # [3.0.0-alpha.30](https://github.com/algolia/docsearch/compare/v3.0.0-alpha.29...v3.0.0-alpha.30) (2020-11-25) ### Bug Fixes -- **react:** lower `algoliasearch` dependency ([64fbf75c040aa4ca23497395ad6cbdec8df5b8a5](https://github.com/algolia/docsearch/commit/64fbf75c040aa4ca23497395ad6cbdec8df5b8a5)) +* **react:** lower `algoliasearch` dependency ([64fbf75c040aa4ca23497395ad6cbdec8df5b8a5](https://github.com/algolia/docsearch/commit/64fbf75c040aa4ca23497395ad6cbdec8df5b8a5)) # [3.0.0-alpha.29](https://github.com/algolia/docsearch/compare/v2.6.3...v3.0.0-alpha.29) (2020-11-25) @@ -356,149 +664,149 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **core:** add type `search` to `getInputProps` ([9c1b040](https://github.com/algolia/docsearch/commit/9c1b04003a72b76c4d05151d3e003c620e52c6c6)) -- **css:** fixed Modal height undefined on Gecko ([77a39a6](https://github.com/algolia/docsearch/commit/77a39a6a5dc2755f5ca5b2c308f35515f7dccde8)) -- **css:** don't display key shortcuts on mobile ([1957f5a](https://github.com/algolia/docsearch/commit/1957f5ad5f7901a59e6d200a7cb497f98d75d21e)) -- **css:** enforce no margin on DocSearch form ([f5947a9](https://github.com/algolia/docsearch/commit/f5947a98cd1f601862144d1643a413621e9bf193)) -- **css:** firefox placeholder opacity ([43ea375](https://github.com/algolia/docsearch/commit/43ea3752fd7c813575748d03316e36ff4fb8e42d)) -- **css:** overflow overlay not supported on gecko ([cca7700](https://github.com/algolia/docsearch/commit/cca770052acd103d47a4ae33c069e6e0ac6c105d)) -- **css:** scroll windows ([c0baaa6](https://github.com/algolia/docsearch/commit/c0baaa63e1f989cd4784b9a33572893ad7a53d3c)) -- **css:** separate docusaurus css variables ([faf6ef1](https://github.com/algolia/docsearch/commit/faf6ef13bff37f767a46e91fb79e7a6c940c9362)) -- **deps:** update dependency autocomplete.js to v0.37.0 ([#806](https://github.com/algolia/docsearch/issues/806)) ([26e45c7](https://github.com/algolia/docsearch/commit/26e45c7be8bb849ec61c1d2abcd861d380a9694d)) -- **deps:** update failling yarn.lock ([#957](https://github.com/algolia/docsearch/issues/957)) ([36ba461](https://github.com/algolia/docsearch/commit/36ba461e057be075426d395bb9ab71c24bdd7738)) -- **design:** animation + spacing search button ([a73d050](https://github.com/algolia/docsearch/commit/a73d050c80a76797b5f351e70664ce6a3e103449)) -- **design:** contrast + darkmode ([ba6bdde](https://github.com/algolia/docsearch/commit/ba6bdde0418f90cf28190de4448b674670fa9ad7)) -- **design:** error screen, loading indicator, no results links ([67a2b3c](https://github.com/algolia/docsearch/commit/67a2b3c3e0105dd710462913d19ff3acaa8713a2)) -- **design:** footer, hit spacing ([7ef73c3](https://github.com/algolia/docsearch/commit/7ef73c3a967e1f6cc2eb45dd8039a278040dbf5b)) -- **design:** hovers + tree icons ([92fa12d](https://github.com/algolia/docsearch/commit/92fa12d780119a42d1524727ffeeeb1320819a7a)) -- **design:** icons ([3219559](https://github.com/algolia/docsearch/commit/321955979f44bf63f24cd2537a2200a4e325460f)) -- **design:** improvements from feedbacks ([c9273bf](https://github.com/algolia/docsearch/commit/c9273bfbca2d1fc41ec4451a530736e1a4e8bfb2)) -- **design:** mobile ([3e1b503](https://github.com/algolia/docsearch/commit/3e1b5037fcbae09cdcafb2e58ca802e0c85ab76f)) -- **design:** mobile scroll and source zindex ([f2e9eb9](https://github.com/algolia/docsearch/commit/f2e9eb95f8fd952e1a6d00412b7447a9f16ca709)) -- **design:** modal height, no results screen ([b884838](https://github.com/algolia/docsearch/commit/b88483851e4e0a9ffdd784b6e41777fbf18eef67)) -- **design:** No results cosmetics ([aa71abd](https://github.com/algolia/docsearch/commit/aa71abdbe819b047ada265513226e0816a4335db)) -- **design:** no results, empty screen + details ([cf7864d](https://github.com/algolia/docsearch/commit/cf7864d7f5057d7c2682522b8cc17f1811843d64)) -- **design:** tablet responsive ([a97199a](https://github.com/algolia/docsearch/commit/a97199a381dd2e63e76fba041037432612da2c50)) -- **docsearch:** add index name to localStorage key ([294c7cf](https://github.com/algolia/docsearch/commit/294c7cf2215ef72c7ffee2fc46cc744ea35966e6)) -- **docsearch:** add padding to dropdown when no recent searches ([f214d8d](https://github.com/algolia/docsearch/commit/f214d8d9afa37287815bfdd0ec3d35d672908c59)) -- **docsearch:** allow a single instance to open ([928b9ca](https://github.com/algolia/docsearch/commit/928b9ca7b899f22cf3e40bf497d5bd671f2f613f)) -- **docsearch:** always use `aria-expanded` to `true` ([262ddcb](https://github.com/algolia/docsearch/commit/262ddcb2f3685280f45af82c3c9e1e7489437d9c)) -- **docsearch:** capture `mousedown` event to close modal ([f9247b9](https://github.com/algolia/docsearch/commit/f9247b9caec89e72f56e76b37861d188b87c0f69)), closes [/github.com/facebook/react-native-website/pull/2139#issuecomment-678330203](https://github.com//github.com/facebook/react-native-website/pull/2139/issues/issuecomment-678330203) -- **docsearch:** do not render Screen when loading or stalled ([39e72c4](https://github.com/algolia/docsearch/commit/39e72c4fb417138208cba0d5bd2e6e1c3866bc52)) -- **docsearch:** don't add search to recent searches if favorited ([f00672a](https://github.com/algolia/docsearch/commit/f00672ae554141fb585cd3efaaef614c4c61f6b0)) -- **docsearch:** don't blur input on submit ([ecda3d3](https://github.com/algolia/docsearch/commit/ecda3d3da8796bf56925458aecc2925c7681468a)) -- **docsearch:** don't display items when no suggestions ([4f96cdb](https://github.com/algolia/docsearch/commit/4f96cdb622d68f6c16e25faeafae78a9f387cfa3)) -- **docsearch:** don't focus input if initial query ([80983f5](https://github.com/algolia/docsearch/commit/80983f524565813edc1a931f7ee330b7ed177db5)) -- **docsearch:** don't open modal on `/` when editing text ([45cf5c3](https://github.com/algolia/docsearch/commit/45cf5c325b9c05ff88b50ac93811b940d3dabb7f)) -- **docsearch:** don't request Algolia on start screen ([e661435](https://github.com/algolia/docsearch/commit/e6614359ab666b77d9adf29b2d5903c502e4978a)) -- **docsearch:** drop the "..." ([bf86a1d](https://github.com/algolia/docsearch/commit/bf86a1d09f5c6e5d9e464307568f8856bf5006d7)) -- **docsearch:** fix vanilla DocSearch types ([5ccaead](https://github.com/algolia/docsearch/commit/5ccaead2d209dbd2a538b497786c2d5b11fa3f08)) -- **docsearch:** focus input on Selection Search ([dfa63df](https://github.com/algolia/docsearch/commit/dfa63df4d08563ca0ecd758ac9b437797ac4f4ed)) -- **docsearch:** group search button elements in containers ([#74](https://github.com/algolia/docsearch/issues/74)) ([8640418](https://github.com/algolia/docsearch/commit/8640418995ef2eb590949f6cbb6989118d3eacb8)) -- **docsearch:** hide search suggestions if none ([d7d6984](https://github.com/algolia/docsearch/commit/d7d698476246d76f1038415c960877774314eb8a)) -- **docsearch:** hoist `transformItems` default value ([1b9952f](https://github.com/algolia/docsearch/commit/1b9952fa9d7a85506e0d4b288feb464b13c90eeb)) -- **docsearch:** limit number of searches retrieved ([9bac38c](https://github.com/algolia/docsearch/commit/9bac38cc9678f05713ffb2a1065c89949275f789)) -- **docsearch:** limit query size ([49c6347](https://github.com/algolia/docsearch/commit/49c6347d8a960e71676ee237c7e234ff3b4cc501)) -- **docsearch:** make sure there are search suggestions before showing them ([ef3566f](https://github.com/algolia/docsearch/commit/ef3566f5b76fb816e93043228bb059985fa884ed)) -- **docsearch:** pass `autoFocus` prop to autocomplete for mobiles ([4ea64b3](https://github.com/algolia/docsearch/commit/4ea64b31aa5633195bc7991d2659b2e1417d3c16)) -- **docsearch:** provide autocomplete ID for HTML attributes ([a86a368](https://github.com/algolia/docsearch/commit/a86a36805d94b28a761260b1b33039e000f848a7)) -- **docsearch:** remove `data-cy` attributes ([a9f13dd](https://github.com/algolia/docsearch/commit/a9f13dd080f69070371dc413beedbbceb41adfdf)) -- **docsearch:** remove blur effect to avoid performance issues ([ea9c700](https://github.com/algolia/docsearch/commit/ea9c7002de95ea9899c588e005823cfc76919534)) -- **docsearch:** remove Docusaurus style ([2ab1d0a](https://github.com/algolia/docsearch/commit/2ab1d0a0fdbc88789b4dc02e727e5f51d8d0a272)) -- **docsearch:** remove recent search when favorited ([858a498](https://github.com/algolia/docsearch/commit/858a49820f93141be1bbee4ed58d645d35a86e8a)) -- **docsearch:** remove theme media query ([dde883e](https://github.com/algolia/docsearch/commit/dde883ebe3334d0f359fdab0453384a3eeee6d67)) -- **docsearch:** rename `DocSearch-Button` CSS class ([a738af0](https://github.com/algolia/docsearch/commit/a738af0a9a39a76c9b212e7fc84e97636b181f90)) -- **docsearch:** rename `DocSearch-Button` CSS class ([8588b96](https://github.com/algolia/docsearch/commit/8588b965e995bc50d79efd318c7142685cd3cd15)) -- **docsearch:** support initial query ([cf84a25](https://github.com/algolia/docsearch/commit/cf84a256a4541b4bcee7df6129a6007a6c034dc5)) -- **docsearch:** update local storage key ([db4bb37](https://github.com/algolia/docsearch/commit/db4bb37d434e0aeb1ca20867f3407016f601000e)) -- **docsearch:** update search suggestions when new lvl0s are encountered ([3513478](https://github.com/algolia/docsearch/commit/3513478fb6949f49580ee8b8c2497a806a65fba4)) -- **docsearch:** use `scrollTo` when unmounting modal ([1f9deb1](https://github.com/algolia/docsearch/commit/1f9deb13225d7fefb72ea8692634bd56c12bd748)) -- **docsearch:** use `scrollTop` on body ([1ef1947](https://github.com/algolia/docsearch/commit/1ef1947721684657ec0346dea5d2e91c28f3eeab)) -- **docsearch:** use absolute URLs ([3d95664](https://github.com/algolia/docsearch/commit/3d9566422b03bd7b303e8e41621326311bbdb344)) -- **docsearch:** use Preact alias in Babel config ([4acf723](https://github.com/algolia/docsearch/commit/4acf723becb8be4708cd5edd2b9ee675ff6d19c0)) -- **DocSearch:** extend dropdown to fill the viewport height ([#72](https://github.com/algolia/docsearch/issues/72)) ([a8fb063](https://github.com/algolia/docsearch/commit/a8fb06305a4a015f102b876a34ae09f93f3718e4)) -- **dosearch:** don't add `distinct` search parameter ([9893bb3](https://github.com/algolia/docsearch/commit/9893bb3c49ce06f2125c29e20f1d1c1b6088c3ea)) -- **react:** make `navigator` optional ([f228c11](https://github.com/algolia/docsearch/commit/f228c1178fbb55867014d43db8f9049cff4a4047)) -- **react:** update `enterkeyhint` to `enterKeyHint` ([0fadc8b](https://github.com/algolia/docsearch/commit/0fadc8b7dd9d133de7e182f3df796744915ebe3b)) -- **search:** hide content when `disableUserPersonalization ([1cec65b](https://github.com/algolia/docsearch/commit/1cec65bbe0cb60cb6ad5d78d2039acbe88dbd5fd)) -- re-export styles from style entry ([5db7483](https://github.com/algolia/docsearch/commit/5db74830e362d81b2f439288f47417c3acf1164a)) -- use correct url ([#936](https://github.com/algolia/docsearch/issues/936)) ([978df0e](https://github.com/algolia/docsearch/commit/978df0eb8e80f301cc64f6d3ad3443b1219c2e41)) -- **homepage:** correct small issues ([#921](https://github.com/algolia/docsearch/issues/921)) ([7fb619b](https://github.com/algolia/docsearch/commit/7fb619b7916b9b0d0999c8896f028d7f04f900b1)) -- **homepage:** make logo inline block ([#887](https://github.com/algolia/docsearch/issues/887)) ([7a6f0a1](https://github.com/algolia/docsearch/commit/7a6f0a17278116a54d9411c06c6e8039b68d74ed)) -- **website:** memoize `onInput` callback ([5411367](https://github.com/algolia/docsearch/commit/5411367582502611ae0191fbf2464d23fafa789f)) - -### Features - -- push keyboard buttons till client ([#983](https://github.com/algolia/docsearch/issues/983)) ([9e4c69d](https://github.com/algolia/docsearch/commit/9e4c69dec762ab1cf0ea469a705c9156f206b1b8)), closes [facebook/docusaurus#3682](https://github.com/facebook/docusaurus/issues/3682) -- select query when coming from a selection search ([b3ee1cc](https://github.com/algolia/docsearch/commit/b3ee1cc2023097b77ebf254fbbc7cd569922c85f)) -- update Autocomplete version ([9a38842](https://github.com/algolia/docsearch/commit/9a388427cada71cae9c92ee72242fc9c051e0436)) -- **design:** icon actions ([6eca639](https://github.com/algolia/docsearch/commit/6eca63971eb5157271cf172b027a9187c5c3ad8f)) -- **design:** new error icons + update icons + update light shadows / searchbox ([2520895](https://github.com/algolia/docsearch/commit/25208954c37da26e4bae69bae688e078b5d31e48)) -- **design:** new icons ([3cc88fa](https://github.com/algolia/docsearch/commit/3cc88fa471fca79495566d41d70b19a69429742b)) -- **docsearch:** add `/` keyboard shortcut ([5725023](https://github.com/algolia/docsearch/commit/5725023ae7afe9e9ebd1dab4641a1bdb3c986659)) -- **docsearch:** add `aria-label` to search button ([f75e1cc](https://github.com/algolia/docsearch/commit/f75e1cc7a1a339a66322edf6ee3409d596f61663)) -- **docsearch:** add `DocSearch` component ([65a98b0](https://github.com/algolia/docsearch/commit/65a98b07ed791f935caeb4115f4d341bafcf7281)) -- **docsearch:** add `enterkeyhint` to `go` ([1a0cbbf](https://github.com/algolia/docsearch/commit/1a0cbbfdbf857ab5949fc71f8b7c7f2bd8dd8a92)) -- **docsearch:** add `hitComponent` and `transformItems` options ([6ad1848](https://github.com/algolia/docsearch/commit/6ad18485fddff0dd9b8e4bd6b4d70a059930a8c5)) -- **docsearch:** add `useDocSearchKeyboardEvents` API ([adadacd](https://github.com/algolia/docsearch/commit/adadacd53132a981d74c0764ea50b383383f42e4)) -- **docsearch:** add DocSearch for Docusaurus ([#39](https://github.com/algolia/docsearch/issues/39)) ([fa42ab2](https://github.com/algolia/docsearch/commit/fa42ab2bd9d8ae405c724b25b7c97050ddf7106b)) -- **docsearch:** add recent searches ([#40](https://github.com/algolia/docsearch/issues/40)) ([4c0aa3b](https://github.com/algolia/docsearch/commit/4c0aa3b71ee451eeeb4df8b87ee3f8f30d47d397)) -- **docsearch:** add search suggestions ([a448e9e](https://github.com/algolia/docsearch/commit/a448e9ee728a157af6e672e8123d051c754375a3)) -- **docsearch:** allow placeholder customization ([7d62c6f](https://github.com/algolia/docsearch/commit/7d62c6fd0fe2c1ae4b67edb3982ad334dd053c79)) -- **docsearch:** animate cards on action ([a917e60](https://github.com/algolia/docsearch/commit/a917e607abf6dce0c4870d43a8c6eda51a368cdc)) -- **docsearch:** append modal to body ([935aa81](https://github.com/algolia/docsearch/commit/935aa81ae7807f559a4edc3778feb79c7451283d)) -- **docsearch:** attach `docsearch.js` user agent to vanilla renderer ([f9398c4](https://github.com/algolia/docsearch/commit/f9398c45a50501b0cf7f16c0f3b7fb92f7d55b15)) -- **docsearch:** attach `docsearch.js` user agent to vanilla renderer ([267c22f](https://github.com/algolia/docsearch/commit/267c22f27f5b1a7512492ee45b302abd8c05a6a8)) -- **docsearch:** catch retry errors in the search client ([ca8e410](https://github.com/algolia/docsearch/commit/ca8e410e8405f1084d7562cdc5c9906fc0c7a801)) -- **docsearch:** display 5 hits per category maximum ([e589657](https://github.com/algolia/docsearch/commit/e5896579be18e65b2f44918f125800e40d4c66bb)) -- **docsearch:** display more recent searches when no favorites ([164a0ce](https://github.com/algolia/docsearch/commit/164a0ce8f053f90ec82897caac208847bede9be7)) -- **docsearch:** export DocSearch types ([159ef8b](https://github.com/algolia/docsearch/commit/159ef8ba89a873aca2f6a9d9afeb784883cb8fc5)) -- **docsearch:** forward props to autocomplete-core ([26a4cc0](https://github.com/algolia/docsearch/commit/26a4cc073c199aec3a9408840853df5cb14196fd)) -- **docsearch:** introduce `disableUserPersonalization` API ([a40cc03](https://github.com/algolia/docsearch/commit/a40cc0351e2605e4afd7ee72bdcc8bff8f72243b)) -- **docsearch:** introduce `initialScrollY` option ([bf4dc9a](https://github.com/algolia/docsearch/commit/bf4dc9a17075420e99dc20c8c6a892aede0cadc7)) -- **docsearch:** introduce `resultsFooterComponent` option ([9eaf18a](https://github.com/algolia/docsearch/commit/9eaf18a81ffff04b7fb8d8389463b56d823f7e9d)) -- **docsearch:** introduce `transformSearchClient` API ([70b4de6](https://github.com/algolia/docsearch/commit/70b4de690d2dcecb15f7c0f3664d56b87352a335)) -- **docsearch:** introduce DocSearch.js v3 ([#56](https://github.com/algolia/docsearch/issues/56)) ([86a6a5d](https://github.com/algolia/docsearch/commit/86a6a5df6c231d013e4ac00ace32467f6a1de2d8)) -- **docsearch:** introduce DocSearch.js v3 ([#56](https://github.com/algolia/docsearch/issues/56)) ([c7f2645](https://github.com/algolia/docsearch/commit/c7f2645d3fbaf7fc3c33d88ccb1b462c00652e0a)) -- **docsearch:** introduce DocSearch.js v3 ([#56](https://github.com/algolia/docsearch/issues/56)) ([a358695](https://github.com/algolia/docsearch/commit/a358695c256595bd256a029f3e3cd1a3e9f84026)) -- **docsearch:** introduce favorite searches ([708b470](https://github.com/algolia/docsearch/commit/708b470cc11dc9f266a4a7f635aa93fa18a370ed)) -- **docsearch:** introduce Selection Search ([f944f30](https://github.com/algolia/docsearch/commit/f944f30af7e20dd5ac28ebc6f42c4d8b3cb9fa02)) -- **docsearch:** save content record hit parent in recent searches ([3c5b4ff](https://github.com/algolia/docsearch/commit/3c5b4fff8f0fe3d5202825f607dd87abc875be6d)) -- **docsearch:** save content record hit parent in recent searches ([4d743bc](https://github.com/algolia/docsearch/commit/4d743bc48c169dff177ee55e14fca0b17d8cb6ec)) -- **docsearch:** support `initialQuery` ([8109206](https://github.com/algolia/docsearch/commit/81092060d5c3416deb3bbf7414cace09313f3189)), closes [#51](https://github.com/algolia/docsearch/issues/51) -- **docsearch:** support keyboard on focus on default integration ([92e2565](https://github.com/algolia/docsearch/commit/92e2565d459e3aeed40767a79d79502273c8e807)) -- **docsearch:** support typing query when search button is focused ([#54](https://github.com/algolia/docsearch/issues/54)) ([929b1c6](https://github.com/algolia/docsearch/commit/929b1c6a72019e78e7e0f0de507d33a7c217e111)) -- **docsearch:** track `docsearch-react` UA ([715bc4f](https://github.com/algolia/docsearch/commit/715bc4f92592521b0096308dfa54d56b34186c37)) -- **docsearch:** track `docsearch-react` UA ([79a972b](https://github.com/algolia/docsearch/commit/79a972b520ad6c45d1c4070035c3588c839ca966)) -- **docsearch:** trap focus in modal ([ec7df43](https://github.com/algolia/docsearch/commit/ec7df43e80d2fa35aa7adef8de2c4ce481df3caf)) -- **docsearch:** update missing results issue link ([b904ff6](https://github.com/algolia/docsearch/commit/b904ff6fc7250a75979a037deb401865d2cc7720)) -- **docsearch:** use relative URLs ([352592e](https://github.com/algolia/docsearch/commit/352592ecc9458a51cf764a2268e393755738ff0a)) -- **DocSearch:** add `DocSearch` CSS class to DocSearch elements ([26bc858](https://github.com/algolia/docsearch/commit/26bc858de664e871235c739d58d647bb9b2947f7)) -- **docusaurus:** import DocSearch modal on hover ([4b007b6](https://github.com/algolia/docsearch/commit/4b007b67c8c0a2883265d8293b407bea71fbd1b6)) -- **website:** lazy load DocSearch styles ([8733274](https://github.com/algolia/docsearch/commit/873327413cf6eb713b6212604bf5f75cef306f35)) -- **website:** lazy load DocSearch styles ([9a21885](https://github.com/algolia/docsearch/commit/9a2188548c446fc195bd009f220a1958a4a3e934)) -- create clean exports ([dbcebed](https://github.com/algolia/docsearch/commit/dbcebed048bcee6de146d3273eb42333fa0e52fa)) -- redirect to https://docsearch.algolia.com ([#926](https://github.com/algolia/docsearch/issues/926)) ([4c32b6f](https://github.com/algolia/docsearch/commit/4c32b6f80b753f592de83351116664bf74b10297)) +* **core:** add type `search` to `getInputProps` ([9c1b040](https://github.com/algolia/docsearch/commit/9c1b04003a72b76c4d05151d3e003c620e52c6c6)) +* **css:** fixed Modal height undefined on Gecko ([77a39a6](https://github.com/algolia/docsearch/commit/77a39a6a5dc2755f5ca5b2c308f35515f7dccde8)) +* **css:** don't display key shortcuts on mobile ([1957f5a](https://github.com/algolia/docsearch/commit/1957f5ad5f7901a59e6d200a7cb497f98d75d21e)) +* **css:** enforce no margin on DocSearch form ([f5947a9](https://github.com/algolia/docsearch/commit/f5947a98cd1f601862144d1643a413621e9bf193)) +* **css:** firefox placeholder opacity ([43ea375](https://github.com/algolia/docsearch/commit/43ea3752fd7c813575748d03316e36ff4fb8e42d)) +* **css:** overflow overlay not supported on gecko ([cca7700](https://github.com/algolia/docsearch/commit/cca770052acd103d47a4ae33c069e6e0ac6c105d)) +* **css:** scroll windows ([c0baaa6](https://github.com/algolia/docsearch/commit/c0baaa63e1f989cd4784b9a33572893ad7a53d3c)) +* **css:** separate docusaurus css variables ([faf6ef1](https://github.com/algolia/docsearch/commit/faf6ef13bff37f767a46e91fb79e7a6c940c9362)) +* **deps:** update dependency autocomplete.js to v0.37.0 ([#806](https://github.com/algolia/docsearch/issues/806)) ([26e45c7](https://github.com/algolia/docsearch/commit/26e45c7be8bb849ec61c1d2abcd861d380a9694d)) +* **deps:** update failling yarn.lock ([#957](https://github.com/algolia/docsearch/issues/957)) ([36ba461](https://github.com/algolia/docsearch/commit/36ba461e057be075426d395bb9ab71c24bdd7738)) +* **design:** animation + spacing search button ([a73d050](https://github.com/algolia/docsearch/commit/a73d050c80a76797b5f351e70664ce6a3e103449)) +* **design:** contrast + darkmode ([ba6bdde](https://github.com/algolia/docsearch/commit/ba6bdde0418f90cf28190de4448b674670fa9ad7)) +* **design:** error screen, loading indicator, no results links ([67a2b3c](https://github.com/algolia/docsearch/commit/67a2b3c3e0105dd710462913d19ff3acaa8713a2)) +* **design:** footer, hit spacing ([7ef73c3](https://github.com/algolia/docsearch/commit/7ef73c3a967e1f6cc2eb45dd8039a278040dbf5b)) +* **design:** hovers + tree icons ([92fa12d](https://github.com/algolia/docsearch/commit/92fa12d780119a42d1524727ffeeeb1320819a7a)) +* **design:** icons ([3219559](https://github.com/algolia/docsearch/commit/321955979f44bf63f24cd2537a2200a4e325460f)) +* **design:** improvements from feedbacks ([c9273bf](https://github.com/algolia/docsearch/commit/c9273bfbca2d1fc41ec4451a530736e1a4e8bfb2)) +* **design:** mobile ([3e1b503](https://github.com/algolia/docsearch/commit/3e1b5037fcbae09cdcafb2e58ca802e0c85ab76f)) +* **design:** mobile scroll and source zindex ([f2e9eb9](https://github.com/algolia/docsearch/commit/f2e9eb95f8fd952e1a6d00412b7447a9f16ca709)) +* **design:** modal height, no results screen ([b884838](https://github.com/algolia/docsearch/commit/b88483851e4e0a9ffdd784b6e41777fbf18eef67)) +* **design:** No results cosmetics ([aa71abd](https://github.com/algolia/docsearch/commit/aa71abdbe819b047ada265513226e0816a4335db)) +* **design:** no results, empty screen + details ([cf7864d](https://github.com/algolia/docsearch/commit/cf7864d7f5057d7c2682522b8cc17f1811843d64)) +* **design:** tablet responsive ([a97199a](https://github.com/algolia/docsearch/commit/a97199a381dd2e63e76fba041037432612da2c50)) +* **docsearch:** add index name to localStorage key ([294c7cf](https://github.com/algolia/docsearch/commit/294c7cf2215ef72c7ffee2fc46cc744ea35966e6)) +* **docsearch:** add padding to dropdown when no recent searches ([f214d8d](https://github.com/algolia/docsearch/commit/f214d8d9afa37287815bfdd0ec3d35d672908c59)) +* **docsearch:** allow a single instance to open ([928b9ca](https://github.com/algolia/docsearch/commit/928b9ca7b899f22cf3e40bf497d5bd671f2f613f)) +* **docsearch:** always use `aria-expanded` to `true` ([262ddcb](https://github.com/algolia/docsearch/commit/262ddcb2f3685280f45af82c3c9e1e7489437d9c)) +* **docsearch:** capture `mousedown` event to close modal ([f9247b9](https://github.com/algolia/docsearch/commit/f9247b9caec89e72f56e76b37861d188b87c0f69)), closes [/github.com/facebook/react-native-website/pull/2139#issuecomment-678330203](https://github.com//github.com/facebook/react-native-website/pull/2139/issues/issuecomment-678330203) +* **docsearch:** do not render Screen when loading or stalled ([39e72c4](https://github.com/algolia/docsearch/commit/39e72c4fb417138208cba0d5bd2e6e1c3866bc52)) +* **docsearch:** don't add search to recent searches if favorited ([f00672a](https://github.com/algolia/docsearch/commit/f00672ae554141fb585cd3efaaef614c4c61f6b0)) +* **docsearch:** don't blur input on submit ([ecda3d3](https://github.com/algolia/docsearch/commit/ecda3d3da8796bf56925458aecc2925c7681468a)) +* **docsearch:** don't display items when no suggestions ([4f96cdb](https://github.com/algolia/docsearch/commit/4f96cdb622d68f6c16e25faeafae78a9f387cfa3)) +* **docsearch:** don't focus input if initial query ([80983f5](https://github.com/algolia/docsearch/commit/80983f524565813edc1a931f7ee330b7ed177db5)) +* **docsearch:** don't open modal on `/` when editing text ([45cf5c3](https://github.com/algolia/docsearch/commit/45cf5c325b9c05ff88b50ac93811b940d3dabb7f)) +* **docsearch:** don't request Algolia on start screen ([e661435](https://github.com/algolia/docsearch/commit/e6614359ab666b77d9adf29b2d5903c502e4978a)) +* **docsearch:** drop the "..." ([bf86a1d](https://github.com/algolia/docsearch/commit/bf86a1d09f5c6e5d9e464307568f8856bf5006d7)) +* **docsearch:** fix vanilla DocSearch types ([5ccaead](https://github.com/algolia/docsearch/commit/5ccaead2d209dbd2a538b497786c2d5b11fa3f08)) +* **docsearch:** focus input on Selection Search ([dfa63df](https://github.com/algolia/docsearch/commit/dfa63df4d08563ca0ecd758ac9b437797ac4f4ed)) +* **docsearch:** group search button elements in containers ([#74](https://github.com/algolia/docsearch/issues/74)) ([8640418](https://github.com/algolia/docsearch/commit/8640418995ef2eb590949f6cbb6989118d3eacb8)) +* **docsearch:** hide search suggestions if none ([d7d6984](https://github.com/algolia/docsearch/commit/d7d698476246d76f1038415c960877774314eb8a)) +* **docsearch:** hoist `transformItems` default value ([1b9952f](https://github.com/algolia/docsearch/commit/1b9952fa9d7a85506e0d4b288feb464b13c90eeb)) +* **docsearch:** limit number of searches retrieved ([9bac38c](https://github.com/algolia/docsearch/commit/9bac38cc9678f05713ffb2a1065c89949275f789)) +* **docsearch:** limit query size ([49c6347](https://github.com/algolia/docsearch/commit/49c6347d8a960e71676ee237c7e234ff3b4cc501)) +* **docsearch:** make sure there are search suggestions before showing them ([ef3566f](https://github.com/algolia/docsearch/commit/ef3566f5b76fb816e93043228bb059985fa884ed)) +* **docsearch:** pass `autoFocus` prop to autocomplete for mobiles ([4ea64b3](https://github.com/algolia/docsearch/commit/4ea64b31aa5633195bc7991d2659b2e1417d3c16)) +* **docsearch:** provide autocomplete ID for HTML attributes ([a86a368](https://github.com/algolia/docsearch/commit/a86a36805d94b28a761260b1b33039e000f848a7)) +* **docsearch:** remove `data-cy` attributes ([a9f13dd](https://github.com/algolia/docsearch/commit/a9f13dd080f69070371dc413beedbbceb41adfdf)) +* **docsearch:** remove blur effect to avoid performance issues ([ea9c700](https://github.com/algolia/docsearch/commit/ea9c7002de95ea9899c588e005823cfc76919534)) +* **docsearch:** remove Docusaurus style ([2ab1d0a](https://github.com/algolia/docsearch/commit/2ab1d0a0fdbc88789b4dc02e727e5f51d8d0a272)) +* **docsearch:** remove recent search when favorited ([858a498](https://github.com/algolia/docsearch/commit/858a49820f93141be1bbee4ed58d645d35a86e8a)) +* **docsearch:** remove theme media query ([dde883e](https://github.com/algolia/docsearch/commit/dde883ebe3334d0f359fdab0453384a3eeee6d67)) +* **docsearch:** rename `DocSearch-Button` CSS class ([a738af0](https://github.com/algolia/docsearch/commit/a738af0a9a39a76c9b212e7fc84e97636b181f90)) +* **docsearch:** rename `DocSearch-Button` CSS class ([8588b96](https://github.com/algolia/docsearch/commit/8588b965e995bc50d79efd318c7142685cd3cd15)) +* **docsearch:** support initial query ([cf84a25](https://github.com/algolia/docsearch/commit/cf84a256a4541b4bcee7df6129a6007a6c034dc5)) +* **docsearch:** update local storage key ([db4bb37](https://github.com/algolia/docsearch/commit/db4bb37d434e0aeb1ca20867f3407016f601000e)) +* **docsearch:** update search suggestions when new lvl0s are encountered ([3513478](https://github.com/algolia/docsearch/commit/3513478fb6949f49580ee8b8c2497a806a65fba4)) +* **docsearch:** use `scrollTo` when unmounting modal ([1f9deb1](https://github.com/algolia/docsearch/commit/1f9deb13225d7fefb72ea8692634bd56c12bd748)) +* **docsearch:** use `scrollTop` on body ([1ef1947](https://github.com/algolia/docsearch/commit/1ef1947721684657ec0346dea5d2e91c28f3eeab)) +* **docsearch:** use absolute URLs ([3d95664](https://github.com/algolia/docsearch/commit/3d9566422b03bd7b303e8e41621326311bbdb344)) +* **docsearch:** use Preact alias in Babel config ([4acf723](https://github.com/algolia/docsearch/commit/4acf723becb8be4708cd5edd2b9ee675ff6d19c0)) +* **DocSearch:** extend dropdown to fill the viewport height ([#72](https://github.com/algolia/docsearch/issues/72)) ([a8fb063](https://github.com/algolia/docsearch/commit/a8fb06305a4a015f102b876a34ae09f93f3718e4)) +* **dosearch:** don't add `distinct` search parameter ([9893bb3](https://github.com/algolia/docsearch/commit/9893bb3c49ce06f2125c29e20f1d1c1b6088c3ea)) +* **react:** make `navigator` optional ([f228c11](https://github.com/algolia/docsearch/commit/f228c1178fbb55867014d43db8f9049cff4a4047)) +* **react:** update `enterkeyhint` to `enterKeyHint` ([0fadc8b](https://github.com/algolia/docsearch/commit/0fadc8b7dd9d133de7e182f3df796744915ebe3b)) +* **search:** hide content when `disableUserPersonalization ([1cec65b](https://github.com/algolia/docsearch/commit/1cec65bbe0cb60cb6ad5d78d2039acbe88dbd5fd)) +* re-export styles from style entry ([5db7483](https://github.com/algolia/docsearch/commit/5db74830e362d81b2f439288f47417c3acf1164a)) +* use correct url ([#936](https://github.com/algolia/docsearch/issues/936)) ([978df0e](https://github.com/algolia/docsearch/commit/978df0eb8e80f301cc64f6d3ad3443b1219c2e41)) +* **homepage:** correct small issues ([#921](https://github.com/algolia/docsearch/issues/921)) ([7fb619b](https://github.com/algolia/docsearch/commit/7fb619b7916b9b0d0999c8896f028d7f04f900b1)) +* **homepage:** make logo inline block ([#887](https://github.com/algolia/docsearch/issues/887)) ([7a6f0a1](https://github.com/algolia/docsearch/commit/7a6f0a17278116a54d9411c06c6e8039b68d74ed)) +* **website:** memoize `onInput` callback ([5411367](https://github.com/algolia/docsearch/commit/5411367582502611ae0191fbf2464d23fafa789f)) + +### Features + +* push keyboard buttons till client ([#983](https://github.com/algolia/docsearch/issues/983)) ([9e4c69d](https://github.com/algolia/docsearch/commit/9e4c69dec762ab1cf0ea469a705c9156f206b1b8)), closes [facebook/docusaurus#3682](https://github.com/facebook/docusaurus/issues/3682) +* select query when coming from a selection search ([b3ee1cc](https://github.com/algolia/docsearch/commit/b3ee1cc2023097b77ebf254fbbc7cd569922c85f)) +* update Autocomplete version ([9a38842](https://github.com/algolia/docsearch/commit/9a388427cada71cae9c92ee72242fc9c051e0436)) +* **design:** icon actions ([6eca639](https://github.com/algolia/docsearch/commit/6eca63971eb5157271cf172b027a9187c5c3ad8f)) +* **design:** new error icons + update icons + update light shadows / searchbox ([2520895](https://github.com/algolia/docsearch/commit/25208954c37da26e4bae69bae688e078b5d31e48)) +* **design:** new icons ([3cc88fa](https://github.com/algolia/docsearch/commit/3cc88fa471fca79495566d41d70b19a69429742b)) +* **docsearch:** add `/` keyboard shortcut ([5725023](https://github.com/algolia/docsearch/commit/5725023ae7afe9e9ebd1dab4641a1bdb3c986659)) +* **docsearch:** add `aria-label` to search button ([f75e1cc](https://github.com/algolia/docsearch/commit/f75e1cc7a1a339a66322edf6ee3409d596f61663)) +* **docsearch:** add `DocSearch` component ([65a98b0](https://github.com/algolia/docsearch/commit/65a98b07ed791f935caeb4115f4d341bafcf7281)) +* **docsearch:** add `enterkeyhint` to `go` ([1a0cbbf](https://github.com/algolia/docsearch/commit/1a0cbbfdbf857ab5949fc71f8b7c7f2bd8dd8a92)) +* **docsearch:** add `hitComponent` and `transformItems` options ([6ad1848](https://github.com/algolia/docsearch/commit/6ad18485fddff0dd9b8e4bd6b4d70a059930a8c5)) +* **docsearch:** add `useDocSearchKeyboardEvents` API ([adadacd](https://github.com/algolia/docsearch/commit/adadacd53132a981d74c0764ea50b383383f42e4)) +* **docsearch:** add DocSearch for Docusaurus ([#39](https://github.com/algolia/docsearch/issues/39)) ([fa42ab2](https://github.com/algolia/docsearch/commit/fa42ab2bd9d8ae405c724b25b7c97050ddf7106b)) +* **docsearch:** add recent searches ([#40](https://github.com/algolia/docsearch/issues/40)) ([4c0aa3b](https://github.com/algolia/docsearch/commit/4c0aa3b71ee451eeeb4df8b87ee3f8f30d47d397)) +* **docsearch:** add search suggestions ([a448e9e](https://github.com/algolia/docsearch/commit/a448e9ee728a157af6e672e8123d051c754375a3)) +* **docsearch:** allow placeholder customization ([7d62c6f](https://github.com/algolia/docsearch/commit/7d62c6fd0fe2c1ae4b67edb3982ad334dd053c79)) +* **docsearch:** animate cards on action ([a917e60](https://github.com/algolia/docsearch/commit/a917e607abf6dce0c4870d43a8c6eda51a368cdc)) +* **docsearch:** append modal to body ([935aa81](https://github.com/algolia/docsearch/commit/935aa81ae7807f559a4edc3778feb79c7451283d)) +* **docsearch:** attach `docsearch.js` user agent to vanilla renderer ([f9398c4](https://github.com/algolia/docsearch/commit/f9398c45a50501b0cf7f16c0f3b7fb92f7d55b15)) +* **docsearch:** attach `docsearch.js` user agent to vanilla renderer ([267c22f](https://github.com/algolia/docsearch/commit/267c22f27f5b1a7512492ee45b302abd8c05a6a8)) +* **docsearch:** catch retry errors in the search client ([ca8e410](https://github.com/algolia/docsearch/commit/ca8e410e8405f1084d7562cdc5c9906fc0c7a801)) +* **docsearch:** display 5 hits per category maximum ([e589657](https://github.com/algolia/docsearch/commit/e5896579be18e65b2f44918f125800e40d4c66bb)) +* **docsearch:** display more recent searches when no favorites ([164a0ce](https://github.com/algolia/docsearch/commit/164a0ce8f053f90ec82897caac208847bede9be7)) +* **docsearch:** export DocSearch types ([159ef8b](https://github.com/algolia/docsearch/commit/159ef8ba89a873aca2f6a9d9afeb784883cb8fc5)) +* **docsearch:** forward props to autocomplete-core ([26a4cc0](https://github.com/algolia/docsearch/commit/26a4cc073c199aec3a9408840853df5cb14196fd)) +* **docsearch:** introduce `disableUserPersonalization` API ([a40cc03](https://github.com/algolia/docsearch/commit/a40cc0351e2605e4afd7ee72bdcc8bff8f72243b)) +* **docsearch:** introduce `initialScrollY` option ([bf4dc9a](https://github.com/algolia/docsearch/commit/bf4dc9a17075420e99dc20c8c6a892aede0cadc7)) +* **docsearch:** introduce `resultsFooterComponent` option ([9eaf18a](https://github.com/algolia/docsearch/commit/9eaf18a81ffff04b7fb8d8389463b56d823f7e9d)) +* **docsearch:** introduce `transformSearchClient` API ([70b4de6](https://github.com/algolia/docsearch/commit/70b4de690d2dcecb15f7c0f3664d56b87352a335)) +* **docsearch:** introduce DocSearch.js v3 ([#56](https://github.com/algolia/docsearch/issues/56)) ([86a6a5d](https://github.com/algolia/docsearch/commit/86a6a5df6c231d013e4ac00ace32467f6a1de2d8)) +* **docsearch:** introduce DocSearch.js v3 ([#56](https://github.com/algolia/docsearch/issues/56)) ([c7f2645](https://github.com/algolia/docsearch/commit/c7f2645d3fbaf7fc3c33d88ccb1b462c00652e0a)) +* **docsearch:** introduce DocSearch.js v3 ([#56](https://github.com/algolia/docsearch/issues/56)) ([a358695](https://github.com/algolia/docsearch/commit/a358695c256595bd256a029f3e3cd1a3e9f84026)) +* **docsearch:** introduce favorite searches ([708b470](https://github.com/algolia/docsearch/commit/708b470cc11dc9f266a4a7f635aa93fa18a370ed)) +* **docsearch:** introduce Selection Search ([f944f30](https://github.com/algolia/docsearch/commit/f944f30af7e20dd5ac28ebc6f42c4d8b3cb9fa02)) +* **docsearch:** save content record hit parent in recent searches ([3c5b4ff](https://github.com/algolia/docsearch/commit/3c5b4fff8f0fe3d5202825f607dd87abc875be6d)) +* **docsearch:** save content record hit parent in recent searches ([4d743bc](https://github.com/algolia/docsearch/commit/4d743bc48c169dff177ee55e14fca0b17d8cb6ec)) +* **docsearch:** support `initialQuery` ([8109206](https://github.com/algolia/docsearch/commit/81092060d5c3416deb3bbf7414cace09313f3189)), closes [#51](https://github.com/algolia/docsearch/issues/51) +* **docsearch:** support keyboard on focus on default integration ([92e2565](https://github.com/algolia/docsearch/commit/92e2565d459e3aeed40767a79d79502273c8e807)) +* **docsearch:** support typing query when search button is focused ([#54](https://github.com/algolia/docsearch/issues/54)) ([929b1c6](https://github.com/algolia/docsearch/commit/929b1c6a72019e78e7e0f0de507d33a7c217e111)) +* **docsearch:** track `docsearch-react` UA ([715bc4f](https://github.com/algolia/docsearch/commit/715bc4f92592521b0096308dfa54d56b34186c37)) +* **docsearch:** track `docsearch-react` UA ([79a972b](https://github.com/algolia/docsearch/commit/79a972b520ad6c45d1c4070035c3588c839ca966)) +* **docsearch:** trap focus in modal ([ec7df43](https://github.com/algolia/docsearch/commit/ec7df43e80d2fa35aa7adef8de2c4ce481df3caf)) +* **docsearch:** update missing results issue link ([b904ff6](https://github.com/algolia/docsearch/commit/b904ff6fc7250a75979a037deb401865d2cc7720)) +* **docsearch:** use relative URLs ([352592e](https://github.com/algolia/docsearch/commit/352592ecc9458a51cf764a2268e393755738ff0a)) +* **DocSearch:** add `DocSearch` CSS class to DocSearch elements ([26bc858](https://github.com/algolia/docsearch/commit/26bc858de664e871235c739d58d647bb9b2947f7)) +* **docusaurus:** import DocSearch modal on hover ([4b007b6](https://github.com/algolia/docsearch/commit/4b007b67c8c0a2883265d8293b407bea71fbd1b6)) +* **website:** lazy load DocSearch styles ([8733274](https://github.com/algolia/docsearch/commit/873327413cf6eb713b6212604bf5f75cef306f35)) +* **website:** lazy load DocSearch styles ([9a21885](https://github.com/algolia/docsearch/commit/9a2188548c446fc195bd009f220a1958a4a3e934)) +* create clean exports ([dbcebed](https://github.com/algolia/docsearch/commit/dbcebed048bcee6de146d3273eb42333fa0e52fa)) +* redirect to ([#926](https://github.com/algolia/docsearch/issues/926)) ([4c32b6f](https://github.com/algolia/docsearch/commit/4c32b6f80b753f592de83351116664bf74b10297)) ## [2.6.3](https://github.com/algolia/docsearch/compare/v2.6.2...v2.6.3) (2019-05-13) ### Bug Fixes -- docsearch input should have aria-label (a11y) ([#687](https://github.com/algolia/docsearch/issues/687)) ([59d21f5](https://github.com/algolia/docsearch/commit/59d21f5e19aec3d8f85106532075ad0ff806e382)) -- search input a11y issue ([#679](https://github.com/algolia/docsearch/issues/679)) ([a68d2a4](https://github.com/algolia/docsearch/commit/a68d2a4a03c1493ba951b9698f8dc6b02d624428)) -- **deps:** update dependency autocomplete.js to v0.35.0 ([2d67a21](https://github.com/algolia/docsearch/commit/2d67a21ae3f4e750b0f27b989e703afe8419e454)) -- **deps:** update dependency autocomplete.js to v0.36.0 ([bbd4ef5](https://github.com/algolia/docsearch/commit/bbd4ef509893045907490cef3cbf37cf267bc10f)) -- **security:** Fix dependencies to avoid infected event-stream ([9f93ffb](https://github.com/algolia/docsearch/commit/9f93ffb6ff93e51d26de2c3d29e25c0ea9d21203)) +* docsearch input should have aria-label (a11y) ([#687](https://github.com/algolia/docsearch/issues/687)) ([59d21f5](https://github.com/algolia/docsearch/commit/59d21f5e19aec3d8f85106532075ad0ff806e382)) +* search input a11y issue ([#679](https://github.com/algolia/docsearch/issues/679)) ([a68d2a4](https://github.com/algolia/docsearch/commit/a68d2a4a03c1493ba951b9698f8dc6b02d624428)) +* **deps:** update dependency autocomplete.js to v0.35.0 ([2d67a21](https://github.com/algolia/docsearch/commit/2d67a21ae3f4e750b0f27b989e703afe8419e454)) +* **deps:** update dependency autocomplete.js to v0.36.0 ([bbd4ef5](https://github.com/algolia/docsearch/commit/bbd4ef509893045907490cef3cbf37cf267bc10f)) +* **security:** Fix dependencies to avoid infected event-stream ([9f93ffb](https://github.com/algolia/docsearch/commit/9f93ffb6ff93e51d26de2c3d29e25c0ea9d21203)) ### Reverts -- Revert "fix: search input a11y issue (#679)" (#686) ([c0e9e19](https://github.com/algolia/docsearch/commit/c0e9e19b9f6d62f1afca8e2333e9493e419d0348)), closes [#679](https://github.com/algolia/docsearch/issues/679) [#686](https://github.com/algolia/docsearch/issues/686) +* Revert "fix: search input a11y issue (#679)" (#686) ([c0e9e19](https://github.com/algolia/docsearch/commit/c0e9e19b9f6d62f1afca8e2333e9493e419d0348)), closes [#679](https://github.com/algolia/docsearch/issues/679) [#686](https://github.com/algolia/docsearch/issues/686) ## [2.6.2](https://github.com/algolia/docsearch/compare/v2.6.1...v2.6.2) (2018-11-22) ### Features -- **ctrl-click:** Enable Ctrl-Click on suggestions ([#542](https://github.com/algolia/docsearch/issues/542)) ([a470c48](https://github.com/algolia/docsearch/commit/a470c480dd6199bf50f2b78de6d26f8e02539615)) +* **ctrl-click:** Enable Ctrl-Click on suggestions ([#542](https://github.com/algolia/docsearch/issues/542)) ([a470c48](https://github.com/algolia/docsearch/commit/a470c480dd6199bf50f2b78de6d26f8e02539615)) ## [2.6.1](https://github.com/algolia/docsearch/compare/v2.6.0...v2.6.1) (2018-11-07) @@ -506,25 +814,25 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **deps:** update dependency algolia-aerial to v1.3.4 ([7e72fe0](https://github.com/algolia/docsearch/commit/7e72fe0fb875375e6935ad45f371731c18ae0216)) -- **deps:** update dependency algolia-aerial to v1.5.3 ([665438e](https://github.com/algolia/docsearch/commit/665438e191de999840580e1cf9cd57bef6d23c8b)) -- **deps:** update dependency algolia-frontend-components to v0.0.35 ([bd5c03f](https://github.com/algolia/docsearch/commit/bd5c03f40e964f7b4215498b5caea67f3ef59cd2)) -- **deps:** update dependency autocomplete.js to ^0.30.0 ([21eb76a](https://github.com/algolia/docsearch/commit/21eb76adda9a8126113b30784ea3faf0c5bc64de)) -- **deps:** update dependency autocomplete.js to ^0.31.0 ([6c65eb2](https://github.com/algolia/docsearch/commit/6c65eb2e0462617d343e14aaafeb29ca7c16eca9)) -- **deps:** update dependency chalk to v2 ([#323](https://github.com/algolia/docsearch/issues/323)) ([f8962bd](https://github.com/algolia/docsearch/commit/f8962bdce51f24d7a5b9dbe2d774fb0fd4dc9761)) -- **deps:** update dependency deepmerge to v2 ([#324](https://github.com/algolia/docsearch/issues/324)) ([db212d0](https://github.com/algolia/docsearch/commit/db212d0074aae923dfd03132d67a3468925c9769)) -- **deps:** update dependency progress to v2 ([#325](https://github.com/algolia/docsearch/issues/325)) ([a99347a](https://github.com/algolia/docsearch/commit/a99347a3f8dea833234d98f5e2c1c2c8ecaf63c2)) -- **wording:** fixed case of DocSearch ([#262](https://github.com/algolia/docsearch/issues/262)) ([d451545](https://github.com/algolia/docsearch/commit/d4515458978a9061ea5cbbb74c1ec8250b92e958)) +* **deps:** update dependency algolia-aerial to v1.3.4 ([7e72fe0](https://github.com/algolia/docsearch/commit/7e72fe0fb875375e6935ad45f371731c18ae0216)) +* **deps:** update dependency algolia-aerial to v1.5.3 ([665438e](https://github.com/algolia/docsearch/commit/665438e191de999840580e1cf9cd57bef6d23c8b)) +* **deps:** update dependency algolia-frontend-components to v0.0.35 ([bd5c03f](https://github.com/algolia/docsearch/commit/bd5c03f40e964f7b4215498b5caea67f3ef59cd2)) +* **deps:** update dependency autocomplete.js to ^0.30.0 ([21eb76a](https://github.com/algolia/docsearch/commit/21eb76adda9a8126113b30784ea3faf0c5bc64de)) +* **deps:** update dependency autocomplete.js to ^0.31.0 ([6c65eb2](https://github.com/algolia/docsearch/commit/6c65eb2e0462617d343e14aaafeb29ca7c16eca9)) +* **deps:** update dependency chalk to v2 ([#323](https://github.com/algolia/docsearch/issues/323)) ([f8962bd](https://github.com/algolia/docsearch/commit/f8962bdce51f24d7a5b9dbe2d774fb0fd4dc9761)) +* **deps:** update dependency deepmerge to v2 ([#324](https://github.com/algolia/docsearch/issues/324)) ([db212d0](https://github.com/algolia/docsearch/commit/db212d0074aae923dfd03132d67a3468925c9769)) +* **deps:** update dependency progress to v2 ([#325](https://github.com/algolia/docsearch/issues/325)) ([a99347a](https://github.com/algolia/docsearch/commit/a99347a3f8dea833234d98f5e2c1c2c8ecaf63c2)) +* **wording:** fixed case of DocSearch ([#262](https://github.com/algolia/docsearch/issues/262)) ([d451545](https://github.com/algolia/docsearch/commit/d4515458978a9061ea5cbbb74c1ec8250b92e958)) ### Features -- **css:** Update menu on small screen ([b5733d2](https://github.com/algolia/docsearch/commit/b5733d2bc98c9747d72a56847e64cdcc5356b32e)) -- **docsearch:** Add custom classes on titles for better crawling ([fc79098](https://github.com/algolia/docsearch/commit/fc790986385db6e5d091bef27985ad33e2fbbfe3)) +* **css:** Update menu on small screen ([b5733d2](https://github.com/algolia/docsearch/commit/b5733d2bc98c9747d72a56847e64cdcc5356b32e)) +* **docsearch:** Add custom classes on titles for better crawling ([fc79098](https://github.com/algolia/docsearch/commit/fc790986385db6e5d091bef27985ad33e2fbbfe3)) ### Reverts -- Revert "Revert "doc(site): Fixed typos (#277)" (#278)" (#279) ([498675f](https://github.com/algolia/docsearch/commit/498675f9a3d9683d004df3bf22a83fa491dd793b)), closes [#277](https://github.com/algolia/docsearch/issues/277) [#278](https://github.com/algolia/docsearch/issues/278) [#279](https://github.com/algolia/docsearch/issues/279) -- Revert "doc(site): Fixed typos (#277)" (#278) ([883894f](https://github.com/algolia/docsearch/commit/883894f9632063753428876cfe1a5ffe5418171e)), closes [#277](https://github.com/algolia/docsearch/issues/277) [#278](https://github.com/algolia/docsearch/issues/278) +* Revert "Revert "doc(site): Fixed typos (#277)" (#278)" (#279) ([498675f](https://github.com/algolia/docsearch/commit/498675f9a3d9683d004df3bf22a83fa491dd793b)), closes [#277](https://github.com/algolia/docsearch/issues/277) [#278](https://github.com/algolia/docsearch/issues/278) [#279](https://github.com/algolia/docsearch/issues/279) +* Revert "doc(site): Fixed typos (#277)" (#278) ([883894f](https://github.com/algolia/docsearch/commit/883894f9632063753428876cfe1a5ffe5418171e)), closes [#277](https://github.com/algolia/docsearch/issues/277) [#278](https://github.com/algolia/docsearch/issues/278) ## [2.5.2](https://github.com/algolia/docsearch/compare/v2.5.1...v2.5.2) (2017-12-07) @@ -534,11 +842,11 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **autocompleteOptions:** allow cssClasses options ([#246](https://github.com/algolia/docsearch/issues/246)) ([6de70c3](https://github.com/algolia/docsearch/commit/6de70c3e7fe8a0c3f632769c48f59c5d2f8f160b)) +* **autocompleteOptions:** allow cssClasses options ([#246](https://github.com/algolia/docsearch/issues/246)) ([6de70c3](https://github.com/algolia/docsearch/commit/6de70c3e7fe8a0c3f632769c48f59c5d2f8f160b)) ### Features -- **global:** Add responsive feature to the dropdown ([777d2d4](https://github.com/algolia/docsearch/commit/777d2d4511efc58b318a4a501e5318c75fcd2699)) +* **global:** Add responsive feature to the dropdown ([777d2d4](https://github.com/algolia/docsearch/commit/777d2d4511efc58b318a4a501e5318c75fcd2699)) ## [2.4.1](https://github.com/algolia/docsearch/compare/v2.4.0...v2.4.1) (2017-08-08) @@ -548,7 +856,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **build:** fix badly handled webpack upgrade ([3cbfbb1](https://github.com/algolia/docsearch/commit/3cbfbb15bbe5247fedc17abfb571c9e94a995dd1)) +* **build:** fix badly handled webpack upgrade ([3cbfbb1](https://github.com/algolia/docsearch/commit/3cbfbb15bbe5247fedc17abfb571c9e94a995dd1)) ## [2.3.2](https://github.com/algolia/docsearch/compare/v2.3.1...v2.3.2) (2017-03-27) @@ -566,7 +874,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **zepto:** upgrade autocomplete.js ([#150](https://github.com/algolia/docsearch/issues/150)) ([e024917](https://github.com/algolia/docsearch/commit/e024917360b59abd86bc620e772bb9289221cea2)) +* **zepto:** upgrade autocomplete.js ([#150](https://github.com/algolia/docsearch/issues/150)) ([e024917](https://github.com/algolia/docsearch/commit/e024917360b59abd86bc620e772bb9289221cea2)) ## [2.1.6](https://github.com/algolia/docsearch/compare/v2.1.5...v2.1.6) (2016-10-02) @@ -574,7 +882,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **empty:** query should be escaped (self-XSS) ([#147](https://github.com/algolia/docsearch/issues/147)) ([3aed41b](https://github.com/algolia/docsearch/commit/3aed41b3e5283b8cfea8773d1f6e856777caaaf3)) +* **empty:** query should be escaped (self-XSS) ([#147](https://github.com/algolia/docsearch/issues/147)) ([3aed41b](https://github.com/algolia/docsearch/commit/3aed41b3e5283b8cfea8773d1f6e856777caaaf3)) ## [2.1.4](https://github.com/algolia/docsearch/compare/v2.1.3...v2.1.4) (2016-08-03) @@ -588,7 +896,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **doc:** docsearch header link ([#117](https://github.com/algolia/docsearch/issues/117)) ([66e68a9](https://github.com/algolia/docsearch/commit/66e68a9aec25fba4f43ffb62e0652aa8bd607c31)) +* **doc:** docsearch header link ([#117](https://github.com/algolia/docsearch/issues/117)) ([66e68a9](https://github.com/algolia/docsearch/commit/66e68a9aec25fba4f43ffb62e0652aa8bd607c31)) ## [2.0.7](https://github.com/algolia/docsearch/compare/v2.0.6...v2.0.7) (2016-06-28) @@ -596,23 +904,23 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **typos:** no result page ([0f40e1d](https://github.com/algolia/docsearch/commit/0f40e1dc684092ec504011226a3493cb5ab85efd)) +* **typos:** no result page ([0f40e1d](https://github.com/algolia/docsearch/commit/0f40e1dc684092ec504011226a3493cb5ab85efd)) ### Features -- **docs:** new community header / footer ([#114](https://github.com/algolia/docsearch/issues/114)) ([af76d31](https://github.com/algolia/docsearch/commit/af76d310fb4c55fe0e3ed6e4c96636c4692af6d8)) +* **docs:** new community header / footer ([#114](https://github.com/algolia/docsearch/issues/114)) ([af76d31](https://github.com/algolia/docsearch/commit/af76d310fb4c55fe0e3ed6e4c96636c4692af6d8)) ## [2.0.5](https://github.com/algolia/docsearch/compare/v2.0.4...v2.0.5) (2016-06-21) ### Bug Fixes -- **DOM:** potential issue with simple layout if there is lvl0, lvl2 but no lvl1 ([57c0101](https://github.com/algolia/docsearch/commit/57c01014590bbfe99ebf8c06737b9905c9e8beb2)) +* **DOM:** potential issue with simple layout if there is lvl0, lvl2 but no lvl1 ([57c0101](https://github.com/algolia/docsearch/commit/57c01014590bbfe99ebf8c06737b9905c9e8beb2)) ## [2.0.4](https://github.com/algolia/docsearch/compare/v2.0.3...v2.0.4) (2016-06-21) ### Features -- **DOM:** split simple and collumns template ([2a3b7eb](https://github.com/algolia/docsearch/commit/2a3b7ebd5947b72a429921babb5ef0b7e71f374c)) +* **DOM:** split simple and collumns template ([2a3b7eb](https://github.com/algolia/docsearch/commit/2a3b7ebd5947b72a429921babb5ef0b7e71f374c)) ## [2.0.3](https://github.com/algolia/docsearch/compare/v2.0.2...v2.0.3) (2016-06-20) @@ -620,7 +928,7 @@ This version follows `1.0.0-alpha.28`. ### Features -- **dom:** better handling of simple design ([6bba640](https://github.com/algolia/docsearch/commit/6bba640e570e2d565b1cce061991809cd40565fe)) +* **dom:** better handling of simple design ([6bba640](https://github.com/algolia/docsearch/commit/6bba640e570e2d565b1cce061991809cd40565fe)) ## [2.0.1](https://github.com/algolia/docsearch/compare/v2.0.0...v2.0.1) (2016-06-14) @@ -628,7 +936,7 @@ This version follows `1.0.0-alpha.28`. ### Features -- **v2:** modification for v2 ([#112](https://github.com/algolia/docsearch/issues/112)) ([f0a7e6b](https://github.com/algolia/docsearch/commit/f0a7e6bc923321187b4fdc5a2b7c88a9df2a4463)), closes [#92](https://github.com/algolia/docsearch/issues/92) [#102](https://github.com/algolia/docsearch/issues/102) +* **v2:** modification for v2 ([#112](https://github.com/algolia/docsearch/issues/112)) ([f0a7e6b](https://github.com/algolia/docsearch/commit/f0a7e6bc923321187b4fdc5a2b7c88a9df2a4463)), closes [#92](https://github.com/algolia/docsearch/issues/92) [#102](https://github.com/algolia/docsearch/issues/102) # [1.3.0](https://github.com/algolia/docsearch/compare/1.2.0...v1.3.0) (2016-06-12) @@ -636,76 +944,76 @@ This version follows `1.0.0-alpha.28`. ### Features -- **DOM:** add an align class ([#108](https://github.com/algolia/docsearch/issues/108)) ([9421897](https://github.com/algolia/docsearch/commit/94218977bc985f73fbad40d6409908b70d48fea5)) -- **serve:** Watch and serve all needed files ([#91](https://github.com/algolia/docsearch/issues/91)) ([ef83775](https://github.com/algolia/docsearch/commit/ef83775f7e58838fe89ae7211981bb9d47d3a7ff)) -- **website:** add echo ([#94](https://github.com/algolia/docsearch/issues/94)) ([8a34ecb](https://github.com/algolia/docsearch/commit/8a34ecbd57f3726045446757ff4df1ae9f9cc8f5)) +* **DOM:** add an align class ([#108](https://github.com/algolia/docsearch/issues/108)) ([9421897](https://github.com/algolia/docsearch/commit/94218977bc985f73fbad40d6409908b70d48fea5)) +* **serve:** Watch and serve all needed files ([#91](https://github.com/algolia/docsearch/issues/91)) ([ef83775](https://github.com/algolia/docsearch/commit/ef83775f7e58838fe89ae7211981bb9d47d3a7ff)) +* **website:** add echo ([#94](https://github.com/algolia/docsearch/issues/94)) ([8a34ecb](https://github.com/algolia/docsearch/commit/8a34ecbd57f3726045446757ff4df1ae9f9cc8f5)) ## [1.1.3](https://github.com/algolia/docsearch/compare/v1.1.2...v1.1.3) (2016-04-04) ### Bug Fixes -- **autocomplete.js:** upgrade the underlying autocomplete.js to ensur… ([4d7022e](https://github.com/algolia/docsearch/commit/4d7022e8cc06829f4b930a57f4c413003823d640)), closes [#89](https://github.com/algolia/docsearch/issues/89) +* **autocomplete.js:** upgrade the underlying autocomplete.js to ensur… ([4d7022e](https://github.com/algolia/docsearch/commit/4d7022e8cc06829f4b930a57f4c413003823d640)), closes [#89](https://github.com/algolia/docsearch/issues/89) ## [1.1.2](https://github.com/algolia/docsearch/compare/v1.1.1...v1.1.2) (2016-03-24) ### Features -- **website:** React :raised_hands:, popped out on the top with React ([db9c100](https://github.com/algolia/docsearch/commit/db9c10081c8ac0a110be53814d2a45b0d293c986)) +* **website:** React :raised_hands:, popped out on the top with React ([db9c100](https://github.com/algolia/docsearch/commit/db9c10081c8ac0a110be53814d2a45b0d293c986)) ## [1.1.1](https://github.com/algolia/docsearch/compare/v1.1.0...v1.1.1) (2016-03-16) ### Bug Fixes -- **algoliaOptions:** ensure we keep default options ([b284dda](https://github.com/algolia/docsearch/commit/b284dda7a9b5f4d272b8b934afbac1d4fcff379f)), closes [#78](https://github.com/algolia/docsearch/issues/78) -- **autocomplete:** auto select top result ([473824a](https://github.com/algolia/docsearch/commit/473824a57480d7c2eedc9be55623f321ee1f31b0)), closes [#81](https://github.com/algolia/docsearch/issues/81) -- **dev:** Fixed the dev example, targeting an existing index ([df13e34](https://github.com/algolia/docsearch/commit/df13e343fd251afc215cfef8b7d883dae28e34bc)) -- **website:** responsive logo ([b856d67](https://github.com/algolia/docsearch/commit/b856d6717f71b31c9fc1fc1f13eafb4ce528abbb)) +* **algoliaOptions:** ensure we keep default options ([b284dda](https://github.com/algolia/docsearch/commit/b284dda7a9b5f4d272b8b934afbac1d4fcff379f)), closes [#78](https://github.com/algolia/docsearch/issues/78) +* **autocomplete:** auto select top result ([473824a](https://github.com/algolia/docsearch/commit/473824a57480d7c2eedc9be55623f321ee1f31b0)), closes [#81](https://github.com/algolia/docsearch/issues/81) +* **dev:** Fixed the dev example, targeting an existing index ([df13e34](https://github.com/algolia/docsearch/commit/df13e343fd251afc215cfef8b7d883dae28e34bc)) +* **website:** responsive logo ([b856d67](https://github.com/algolia/docsearch/commit/b856d6717f71b31c9fc1fc1f13eafb4ce528abbb)) ### Features -- **serve:** Add `npm run serve` to expose js and css locally ([31e5d13](https://github.com/algolia/docsearch/commit/31e5d139d5db42a654941a21369b696170388b6d)) -- **website:** community header ([366cc0d](https://github.com/algolia/docsearch/commit/366cc0d8559877219a8718dff478b9515e13038a)) -- **website:** Logo Update - Og screen + favicon ([c7eb54a](https://github.com/algolia/docsearch/commit/c7eb54afaf1e2908aaeb336dc09e98fe50e4900c)) -- **website:** removed demos ([6d8eb34](https://github.com/algolia/docsearch/commit/6d8eb34e5cfd6903b231971c495727654eff6e83)) -- **website:** update ([9498e3b](https://github.com/algolia/docsearch/commit/9498e3b4a3520e9fa4ad912a1d69e02eaaac2e2b)) -- **website:** updated logo ([fa413d9](https://github.com/algolia/docsearch/commit/fa413d977e4a1500ea0d6e9777030a6fab15bc94)) +* **serve:** Add `npm run serve` to expose js and css locally ([31e5d13](https://github.com/algolia/docsearch/commit/31e5d139d5db42a654941a21369b696170388b6d)) +* **website:** community header ([366cc0d](https://github.com/algolia/docsearch/commit/366cc0d8559877219a8718dff478b9515e13038a)) +* **website:** Logo Update - Og screen + favicon ([c7eb54a](https://github.com/algolia/docsearch/commit/c7eb54afaf1e2908aaeb336dc09e98fe50e4900c)) +* **website:** removed demos ([6d8eb34](https://github.com/algolia/docsearch/commit/6d8eb34e5cfd6903b231971c495727654eff6e83)) +* **website:** update ([9498e3b](https://github.com/algolia/docsearch/commit/9498e3b4a3520e9fa4ad912a1d69e02eaaac2e2b)) +* **website:** updated logo ([fa413d9](https://github.com/algolia/docsearch/commit/fa413d977e4a1500ea0d6e9777030a6fab15bc94)) # [1.1.0](https://github.com/algolia/docsearch/compare/v1.0.2...v1.1.0) (2016-02-01) ### Bug Fixes -- **dropdown:** Visually group suggestions together ([592c8f2](https://github.com/algolia/docsearch/commit/592c8f27cd548226be255bd475c693910a103cdc)) +* **dropdown:** Visually group suggestions together ([592c8f2](https://github.com/algolia/docsearch/commit/592c8f27cd548226be255bd475c693910a103cdc)) ### Features -- **website:** ensure the inputs are mandatory ([e59d132](https://github.com/algolia/docsearch/commit/e59d132c2da5eda8971bade607460aa71dbd1d34)) +* **website:** ensure the inputs are mandatory ([e59d132](https://github.com/algolia/docsearch/commit/e59d132c2da5eda8971bade607460aa71dbd1d34)) ## [1.0.2](https://github.com/algolia/docsearch/compare/v1.0.1...v1.0.2) (2015-12-28) ### Bug Fixes -- **debut:** do not enable debug by default ([188f49e](https://github.com/algolia/docsearch/commit/188f49e863dac21d5e7733b2b55c7058f999cb5c)) -- **url:** Removes concatenation of URL with hash if it contains a hash ([348df1c](https://github.com/algolia/docsearch/commit/348df1cb1bf9264d8e4d20a02b91bb8f2e3551bb)), closes [#53](https://github.com/algolia/docsearch/issues/53) -- **usage:** fixed usage to match the latest prototype version ([f6edc9e](https://github.com/algolia/docsearch/commit/f6edc9e6584c5cb48e11a469bed0b5222a3d324b)) +* **debut:** do not enable debug by default ([188f49e](https://github.com/algolia/docsearch/commit/188f49e863dac21d5e7733b2b55c7058f999cb5c)) +* **url:** Removes concatenation of URL with hash if it contains a hash ([348df1c](https://github.com/algolia/docsearch/commit/348df1cb1bf9264d8e4d20a02b91bb8f2e3551bb)), closes [#53](https://github.com/algolia/docsearch/issues/53) +* **usage:** fixed usage to match the latest prototype version ([f6edc9e](https://github.com/algolia/docsearch/commit/f6edc9e6584c5cb48e11a469bed0b5222a3d324b)) ## [1.0.1](https://github.com/algolia/docsearch/compare/v1.0.0...v1.0.1) (2015-12-24) ### Bug Fixes -- **handleSelected:** ensure we're calling setVal on the right object ([31abbd7](https://github.com/algolia/docsearch/commit/31abbd7400bbf89026d7db0848f2be448090a530)), closes [#51](https://github.com/algolia/docsearch/issues/51) +* **handleSelected:** ensure we're calling setVal on the right object ([31abbd7](https://github.com/algolia/docsearch/commit/31abbd7400bbf89026d7db0848f2be448090a530)), closes [#51](https://github.com/algolia/docsearch/issues/51) # [1.0.0](https://github.com/algolia/docsearch/compare/v0.0.7...v1.0.0) (2015-12-24) ### Bug Fixes -- **isSubCategoryHeader:** ensure we hide the content of the left column if this flag is not set ([0e04ce4](https://github.com/algolia/docsearch/commit/0e04ce488097c4cb168acaba46c58fe17b31b9af)), closes [#47](https://github.com/algolia/docsearch/issues/47) +* **isSubCategoryHeader:** ensure we hide the content of the left column if this flag is not set ([0e04ce4](https://github.com/algolia/docsearch/commit/0e04ce488097c4cb168acaba46c58fe17b31b9af)), closes [#47](https://github.com/algolia/docsearch/issues/47) ## [0.0.7](https://github.com/algolia/docsearch/compare/v0.0.6...v0.0.7) (2015-12-22) ### Bug Fixes -- **appId:** Allow passing a custom appId ([e1777d3](https://github.com/algolia/docsearch/commit/e1777d31a7e3e3574ed2091c124681bb1cd2e855)), closes [#21](https://github.com/algolia/docsearch/issues/21) -- **css:** Renaming all `ads-` prefix to `algolia-docsearch-` ([c6fc53f](https://github.com/algolia/docsearch/commit/c6fc53f6d520665a3a57d3cd5bb420af9556873b)) +* **appId:** Allow passing a custom appId ([e1777d3](https://github.com/algolia/docsearch/commit/e1777d31a7e3e3574ed2091c124681bb1cd2e855)), closes [#21](https://github.com/algolia/docsearch/issues/21) +* **css:** Renaming all `ads-` prefix to `algolia-docsearch-` ([c6fc53f](https://github.com/algolia/docsearch/commit/c6fc53f6d520665a3a57d3cd5bb420af9556873b)) ## [0.0.6](https://github.com/algolia/docsearch/compare/v0.0.5...v0.0.6) (2015-12-16) @@ -719,30 +1027,30 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **colors:** Reduce number of colors ([125d1c7](https://github.com/algolia/docsearch/commit/125d1c7b4e795fdc16b8d844f1db65a7c87d382d)) -- **snippet:** Fix typo in method name ([751dfdb](https://github.com/algolia/docsearch/commit/751dfdb107e7b2cebdbea87906deedba4bf55884)) +* **colors:** Reduce number of colors ([125d1c7](https://github.com/algolia/docsearch/commit/125d1c7b4e795fdc16b8d844f1db65a7c87d382d)) +* **snippet:** Fix typo in method name ([751dfdb](https://github.com/algolia/docsearch/commit/751dfdb107e7b2cebdbea87906deedba4bf55884)) ### Features -- **gh-pages:** Add gh-pages deploy script ([ab1a299](https://github.com/algolia/docsearch/commit/ab1a299c54a21dfee5630b7878d51856f9c6bf47)) -- **main:** Add main wrapper ([bdac9dd](https://github.com/algolia/docsearch/commit/bdac9dd817279e9033a7acefd9231bed9823303b)) -- **styling:** Set grayscale as default, add styling as external file ([5d8cca8](https://github.com/algolia/docsearch/commit/5d8cca8d70508ac8218cf5947f4f00812a8c1d98)) +* **gh-pages:** Add gh-pages deploy script ([ab1a299](https://github.com/algolia/docsearch/commit/ab1a299c54a21dfee5630b7878d51856f9c6bf47)) +* **main:** Add main wrapper ([bdac9dd](https://github.com/algolia/docsearch/commit/bdac9dd817279e9033a7acefd9231bed9823303b)) +* **styling:** Set grayscale as default, add styling as external file ([5d8cca8](https://github.com/algolia/docsearch/commit/5d8cca8d70508ac8218cf5947f4f00812a8c1d98)) ## [2.6.3](https://github.com/algolia/docsearch/compare/v2.6.2...v2.6.3) (2019-05-13) ### Bug Fixes -- **deps:** update dependency autocomplete.js to v0.35.0 ([2d67a21](https://github.com/algolia/docsearch/commit/2d67a21)) -- **deps:** update dependency autocomplete.js to v0.36.0 ([bbd4ef5](https://github.com/algolia/docsearch/commit/bbd4ef5)) -- **security:** Fix dependencies to avoid infected event-stream ([9f93ffb](https://github.com/algolia/docsearch/commit/9f93ffb)) -- docsearch input should have aria-label (a11y) ([#687](https://github.com/algolia/docsearch/issues/687)) ([59d21f5](https://github.com/algolia/docsearch/commit/59d21f5)) -- search input a11y issue ([#679](https://github.com/algolia/docsearch/issues/679)) ([a68d2a4](https://github.com/algolia/docsearch/commit/a68d2a4)) +* **deps:** update dependency autocomplete.js to v0.35.0 ([2d67a21](https://github.com/algolia/docsearch/commit/2d67a21)) +* **deps:** update dependency autocomplete.js to v0.36.0 ([bbd4ef5](https://github.com/algolia/docsearch/commit/bbd4ef5)) +* **security:** Fix dependencies to avoid infected event-stream ([9f93ffb](https://github.com/algolia/docsearch/commit/9f93ffb)) +* docsearch input should have aria-label (a11y) ([#687](https://github.com/algolia/docsearch/issues/687)) ([59d21f5](https://github.com/algolia/docsearch/commit/59d21f5)) +* search input a11y issue ([#679](https://github.com/algolia/docsearch/issues/679)) ([a68d2a4](https://github.com/algolia/docsearch/commit/a68d2a4)) ## [2.6.2](https://github.com/algolia/docsearch/compare/v2.6.1...v2.6.2) (2018-11-22) ### Features -- **ctrl-click:** Enable Ctrl-Click on suggestions ([#542](https://github.com/algolia/docsearch/issues/542)) ([a470c48](https://github.com/algolia/docsearch/commit/a470c48)) +* **ctrl-click:** Enable Ctrl-Click on suggestions ([#542](https://github.com/algolia/docsearch/issues/542)) ([a470c48](https://github.com/algolia/docsearch/commit/a470c48)) ## [2.6.1](https://github.com/algolia/docsearch/compare/v2.6.0...v2.6.1) (2018-11-07) @@ -750,20 +1058,20 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **deps:** update dependency algolia-aerial to v1.3.4 ([7e72fe0](https://github.com/algolia/docsearch/commit/7e72fe0)) -- **deps:** update dependency algolia-aerial to v1.5.3 ([665438e](https://github.com/algolia/docsearch/commit/665438e)) -- **deps:** update dependency algolia-frontend-components to v0.0.35 ([bd5c03f](https://github.com/algolia/docsearch/commit/bd5c03f)) -- **deps:** update dependency autocomplete.js to ^0.30.0 ([21eb76a](https://github.com/algolia/docsearch/commit/21eb76a)) -- **deps:** update dependency autocomplete.js to ^0.31.0 ([6c65eb2](https://github.com/algolia/docsearch/commit/6c65eb2)) -- **deps:** update dependency chalk to v2 ([#323](https://github.com/algolia/docsearch/issues/323)) ([f8962bd](https://github.com/algolia/docsearch/commit/f8962bd)) -- **deps:** update dependency deepmerge to v2 ([#324](https://github.com/algolia/docsearch/issues/324)) ([db212d0](https://github.com/algolia/docsearch/commit/db212d0)) -- **deps:** update dependency progress to v2 ([#325](https://github.com/algolia/docsearch/issues/325)) ([a99347a](https://github.com/algolia/docsearch/commit/a99347a)) -- **wording:** fixed case of DocSearch ([#262](https://github.com/algolia/docsearch/issues/262)) ([d451545](https://github.com/algolia/docsearch/commit/d451545)) +* **deps:** update dependency algolia-aerial to v1.3.4 ([7e72fe0](https://github.com/algolia/docsearch/commit/7e72fe0)) +* **deps:** update dependency algolia-aerial to v1.5.3 ([665438e](https://github.com/algolia/docsearch/commit/665438e)) +* **deps:** update dependency algolia-frontend-components to v0.0.35 ([bd5c03f](https://github.com/algolia/docsearch/commit/bd5c03f)) +* **deps:** update dependency autocomplete.js to ^0.30.0 ([21eb76a](https://github.com/algolia/docsearch/commit/21eb76a)) +* **deps:** update dependency autocomplete.js to ^0.31.0 ([6c65eb2](https://github.com/algolia/docsearch/commit/6c65eb2)) +* **deps:** update dependency chalk to v2 ([#323](https://github.com/algolia/docsearch/issues/323)) ([f8962bd](https://github.com/algolia/docsearch/commit/f8962bd)) +* **deps:** update dependency deepmerge to v2 ([#324](https://github.com/algolia/docsearch/issues/324)) ([db212d0](https://github.com/algolia/docsearch/commit/db212d0)) +* **deps:** update dependency progress to v2 ([#325](https://github.com/algolia/docsearch/issues/325)) ([a99347a](https://github.com/algolia/docsearch/commit/a99347a)) +* **wording:** fixed case of DocSearch ([#262](https://github.com/algolia/docsearch/issues/262)) ([d451545](https://github.com/algolia/docsearch/commit/d451545)) ### Features -- **css:** Update menu on small screen ([b5733d2](https://github.com/algolia/docsearch/commit/b5733d2)) -- **docsearch:** Add custom classes on titles for better crawling ([fc79098](https://github.com/algolia/docsearch/commit/fc79098)) +* **css:** Update menu on small screen ([b5733d2](https://github.com/algolia/docsearch/commit/b5733d2)) +* **docsearch:** Add custom classes on titles for better crawling ([fc79098](https://github.com/algolia/docsearch/commit/fc79098)) ## [2.5.2](https://github.com/algolia/docsearch/compare/v2.5.1...v2.5.2) (2017-12-07) @@ -773,11 +1081,11 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **autocompleteOptions:** allow cssClasses options ([#246](https://github.com/algolia/docsearch/issues/246)) ([6de70c3](https://github.com/algolia/docsearch/commit/6de70c3)) +* **autocompleteOptions:** allow cssClasses options ([#246](https://github.com/algolia/docsearch/issues/246)) ([6de70c3](https://github.com/algolia/docsearch/commit/6de70c3)) ### Features -- **global:** Add responsive feature to the dropdown ([777d2d4](https://github.com/algolia/docsearch/commit/777d2d4)) +* **global:** Add responsive feature to the dropdown ([777d2d4](https://github.com/algolia/docsearch/commit/777d2d4)) ## [2.4.1](https://github.com/algolia/docsearch/compare/v2.4.0...v2.4.1) (2017-08-08) @@ -787,7 +1095,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **build:** fix badly handled webpack upgrade ([3cbfbb1](https://github.com/algolia/docsearch/commit/3cbfbb1)) +* **build:** fix badly handled webpack upgrade ([3cbfbb1](https://github.com/algolia/docsearch/commit/3cbfbb1)) ## [2.3.2](https://github.com/algolia/docsearch/compare/v2.3.1...v2.3.2) (2017-03-27) @@ -805,7 +1113,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **zepto:** upgrade autocomplete.js ([#150](https://github.com/algolia/docsearch/issues/150)) ([e024917](https://github.com/algolia/docsearch/commit/e024917)) +* **zepto:** upgrade autocomplete.js ([#150](https://github.com/algolia/docsearch/issues/150)) ([e024917](https://github.com/algolia/docsearch/commit/e024917)) ## [2.1.6](https://github.com/algolia/docsearch/compare/v2.1.5...v2.1.6) (2016-10-02) @@ -813,7 +1121,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **empty:** query should be escaped (self-XSS) ([#147](https://github.com/algolia/docsearch/issues/147)) ([3aed41b](https://github.com/algolia/docsearch/commit/3aed41b)) +* **empty:** query should be escaped (self-XSS) ([#147](https://github.com/algolia/docsearch/issues/147)) ([3aed41b](https://github.com/algolia/docsearch/commit/3aed41b)) ## [2.1.4](https://github.com/algolia/docsearch/compare/v2.1.3...v2.1.4) (2016-08-03) @@ -827,7 +1135,7 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **doc:** docsearch header link ([#117](https://github.com/algolia/docsearch/issues/117)) ([66e68a9](https://github.com/algolia/docsearch/commit/66e68a9)) +* **doc:** docsearch header link ([#117](https://github.com/algolia/docsearch/issues/117)) ([66e68a9](https://github.com/algolia/docsearch/commit/66e68a9)) ## [2.0.7](https://github.com/algolia/docsearch/compare/v2.0.6...v2.0.7) (2016-06-28) @@ -835,23 +1143,23 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **typos:** no result page ([0f40e1d](https://github.com/algolia/docsearch/commit/0f40e1d)) +* **typos:** no result page ([0f40e1d](https://github.com/algolia/docsearch/commit/0f40e1d)) ### Features -- **docs:** new community header / footer ([#114](https://github.com/algolia/docsearch/issues/114)) ([af76d31](https://github.com/algolia/docsearch/commit/af76d31)) +* **docs:** new community header / footer ([#114](https://github.com/algolia/docsearch/issues/114)) ([af76d31](https://github.com/algolia/docsearch/commit/af76d31)) ## [2.0.5](https://github.com/algolia/docsearch/compare/v2.0.4...v2.0.5) (2016-06-21) ### Bug Fixes -- **DOM:** potential issue with simple layout if there is lvl0, lvl2 but no lvl1 ([57c0101](https://github.com/algolia/docsearch/commit/57c0101)) +* **DOM:** potential issue with simple layout if there is lvl0, lvl2 but no lvl1 ([57c0101](https://github.com/algolia/docsearch/commit/57c0101)) ## [2.0.4](https://github.com/algolia/docsearch/compare/v2.0.3...v2.0.4) (2016-06-21) ### Features -- **DOM:** split simple and collumns template ([2a3b7eb](https://github.com/algolia/docsearch/commit/2a3b7eb)) +* **DOM:** split simple and collumns template ([2a3b7eb](https://github.com/algolia/docsearch/commit/2a3b7eb)) ## [2.0.3](https://github.com/algolia/docsearch/compare/v2.0.2...v2.0.3) (2016-06-20) @@ -859,7 +1167,7 @@ This version follows `1.0.0-alpha.28`. ### Features -- **dom:** better handling of simple design ([6bba640](https://github.com/algolia/docsearch/commit/6bba640)) +* **dom:** better handling of simple design ([6bba640](https://github.com/algolia/docsearch/commit/6bba640)) ## [2.0.1](https://github.com/algolia/docsearch/compare/v2.0.0...v2.0.1) (2016-06-14) @@ -867,7 +1175,7 @@ This version follows `1.0.0-alpha.28`. ### Features -- **v2:** modification for v2 ([#112](https://github.com/algolia/docsearch/issues/112)) ([f0a7e6b](https://github.com/algolia/docsearch/commit/f0a7e6b)), closes [#92](https://github.com/algolia/docsearch/issues/92) [#102](https://github.com/algolia/docsearch/issues/102) +* **v2:** modification for v2 ([#112](https://github.com/algolia/docsearch/issues/112)) ([f0a7e6b](https://github.com/algolia/docsearch/commit/f0a7e6b)), closes [#92](https://github.com/algolia/docsearch/issues/92) [#102](https://github.com/algolia/docsearch/issues/102) # [1.3.0](https://github.com/algolia/docsearch/compare/1.2.0...v1.3.0) (2016-06-12) @@ -875,76 +1183,76 @@ This version follows `1.0.0-alpha.28`. ### Features -- **DOM:** add an align class ([#108](https://github.com/algolia/docsearch/issues/108)) ([9421897](https://github.com/algolia/docsearch/commit/9421897)) -- **serve:** Watch and serve all needed files ([#91](https://github.com/algolia/docsearch/issues/91)) ([ef83775](https://github.com/algolia/docsearch/commit/ef83775)) -- **website:** add echo ([#94](https://github.com/algolia/docsearch/issues/94)) ([8a34ecb](https://github.com/algolia/docsearch/commit/8a34ecb)) +* **DOM:** add an align class ([#108](https://github.com/algolia/docsearch/issues/108)) ([9421897](https://github.com/algolia/docsearch/commit/9421897)) +* **serve:** Watch and serve all needed files ([#91](https://github.com/algolia/docsearch/issues/91)) ([ef83775](https://github.com/algolia/docsearch/commit/ef83775)) +* **website:** add echo ([#94](https://github.com/algolia/docsearch/issues/94)) ([8a34ecb](https://github.com/algolia/docsearch/commit/8a34ecb)) ## [1.1.3](https://github.com/algolia/docsearch/compare/v1.1.2...v1.1.3) (2016-04-04) ### Bug Fixes -- **autocomplete.js:** upgrade the underlying autocomplete.js to ensur… ([4d7022e](https://github.com/algolia/docsearch/commit/4d7022e)), closes [#89](https://github.com/algolia/docsearch/issues/89) +* **autocomplete.js:** upgrade the underlying autocomplete.js to ensur… ([4d7022e](https://github.com/algolia/docsearch/commit/4d7022e)), closes [#89](https://github.com/algolia/docsearch/issues/89) ## [1.1.2](https://github.com/algolia/docsearch/compare/v1.1.1...v1.1.2) (2016-03-24) ### Features -- **website:** React :raised_hands:, popped out on the top with React ([db9c100](https://github.com/algolia/docsearch/commit/db9c100)) +* **website:** React :raised_hands:, popped out on the top with React ([db9c100](https://github.com/algolia/docsearch/commit/db9c100)) ## [1.1.1](https://github.com/algolia/docsearch/compare/v1.1.0...v1.1.1) (2016-03-16) ### Bug Fixes -- **algoliaOptions:** ensure we keep default options ([b284dda](https://github.com/algolia/docsearch/commit/b284dda)), closes [#78](https://github.com/algolia/docsearch/issues/78) -- **autocomplete:** auto select top result ([473824a](https://github.com/algolia/docsearch/commit/473824a)), closes [#81](https://github.com/algolia/docsearch/issues/81) -- **dev:** Fixed the dev example, targeting an existing index ([df13e34](https://github.com/algolia/docsearch/commit/df13e34)) -- **website:** responsive logo ([b856d67](https://github.com/algolia/docsearch/commit/b856d67)) +* **algoliaOptions:** ensure we keep default options ([b284dda](https://github.com/algolia/docsearch/commit/b284dda)), closes [#78](https://github.com/algolia/docsearch/issues/78) +* **autocomplete:** auto select top result ([473824a](https://github.com/algolia/docsearch/commit/473824a)), closes [#81](https://github.com/algolia/docsearch/issues/81) +* **dev:** Fixed the dev example, targeting an existing index ([df13e34](https://github.com/algolia/docsearch/commit/df13e34)) +* **website:** responsive logo ([b856d67](https://github.com/algolia/docsearch/commit/b856d67)) ### Features -- **serve:** Add `npm run serve` to expose js and css locally ([31e5d13](https://github.com/algolia/docsearch/commit/31e5d13)) -- **website:** community header ([366cc0d](https://github.com/algolia/docsearch/commit/366cc0d)) -- **website:** Logo Update - Og screen + favicon ([c7eb54a](https://github.com/algolia/docsearch/commit/c7eb54a)) -- **website:** removed demos ([6d8eb34](https://github.com/algolia/docsearch/commit/6d8eb34)) -- **website:** update ([9498e3b](https://github.com/algolia/docsearch/commit/9498e3b)) -- **website:** updated logo ([fa413d9](https://github.com/algolia/docsearch/commit/fa413d9)) +* **serve:** Add `npm run serve` to expose js and css locally ([31e5d13](https://github.com/algolia/docsearch/commit/31e5d13)) +* **website:** community header ([366cc0d](https://github.com/algolia/docsearch/commit/366cc0d)) +* **website:** Logo Update - Og screen + favicon ([c7eb54a](https://github.com/algolia/docsearch/commit/c7eb54a)) +* **website:** removed demos ([6d8eb34](https://github.com/algolia/docsearch/commit/6d8eb34)) +* **website:** update ([9498e3b](https://github.com/algolia/docsearch/commit/9498e3b)) +* **website:** updated logo ([fa413d9](https://github.com/algolia/docsearch/commit/fa413d9)) # [1.1.0](https://github.com/algolia/docsearch/compare/v1.0.2...v1.1.0) (2016-02-01) ### Bug Fixes -- **dropdown:** Visually group suggestions together ([592c8f2](https://github.com/algolia/docsearch/commit/592c8f2)) +* **dropdown:** Visually group suggestions together ([592c8f2](https://github.com/algolia/docsearch/commit/592c8f2)) ### Features -- **website:** ensure the inputs are mandatory ([e59d132](https://github.com/algolia/docsearch/commit/e59d132)) +* **website:** ensure the inputs are mandatory ([e59d132](https://github.com/algolia/docsearch/commit/e59d132)) ## [1.0.2](https://github.com/algolia/docsearch/compare/v1.0.1...v1.0.2) (2015-12-28) ### Bug Fixes -- **debut:** do not enable debug by default ([188f49e](https://github.com/algolia/docsearch/commit/188f49e)) -- **url:** Removes concatenation of URL with hash if it contains a hash ([348df1c](https://github.com/algolia/docsearch/commit/348df1c)), closes [#53](https://github.com/algolia/docsearch/issues/53) -- **usage:** fixed usage to match the latest prototype version ([f6edc9e](https://github.com/algolia/docsearch/commit/f6edc9e)) +* **debut:** do not enable debug by default ([188f49e](https://github.com/algolia/docsearch/commit/188f49e)) +* **url:** Removes concatenation of URL with hash if it contains a hash ([348df1c](https://github.com/algolia/docsearch/commit/348df1c)), closes [#53](https://github.com/algolia/docsearch/issues/53) +* **usage:** fixed usage to match the latest prototype version ([f6edc9e](https://github.com/algolia/docsearch/commit/f6edc9e)) ## [1.0.1](https://github.com/algolia/docsearch/compare/v1.0.0...v1.0.1) (2015-12-24) ### Bug Fixes -- **handleSelected:** ensure we're calling setVal on the right object ([31abbd7](https://github.com/algolia/docsearch/commit/31abbd7)), closes [#51](https://github.com/algolia/docsearch/issues/51) +* **handleSelected:** ensure we're calling setVal on the right object ([31abbd7](https://github.com/algolia/docsearch/commit/31abbd7)), closes [#51](https://github.com/algolia/docsearch/issues/51) # [1.0.0](https://github.com/algolia/docsearch/compare/v0.0.7...v1.0.0) (2015-12-24) ### Bug Fixes -- **isSubCategoryHeader:** ensure we hide the content of the left column if this flag is not set ([0e04ce4](https://github.com/algolia/docsearch/commit/0e04ce4)), closes [#47](https://github.com/algolia/docsearch/issues/47) +* **isSubCategoryHeader:** ensure we hide the content of the left column if this flag is not set ([0e04ce4](https://github.com/algolia/docsearch/commit/0e04ce4)), closes [#47](https://github.com/algolia/docsearch/issues/47) ## [0.0.7](https://github.com/algolia/docsearch/compare/v0.0.6...v0.0.7) (2015-12-22) ### Bug Fixes -- **appId:** Allow passing a custom appId ([e1777d3](https://github.com/algolia/docsearch/commit/e1777d3)), closes [#21](https://github.com/algolia/docsearch/issues/21) -- **css:** Renaming all `ads-` prefix to `algolia-docsearch-` ([c6fc53f](https://github.com/algolia/docsearch/commit/c6fc53f)) +* **appId:** Allow passing a custom appId ([e1777d3](https://github.com/algolia/docsearch/commit/e1777d3)), closes [#21](https://github.com/algolia/docsearch/issues/21) +* **css:** Renaming all `ads-` prefix to `algolia-docsearch-` ([c6fc53f](https://github.com/algolia/docsearch/commit/c6fc53f)) ## [0.0.6](https://github.com/algolia/docsearch/compare/v0.0.5...v0.0.6) (2015-12-16) @@ -958,11 +1266,11 @@ This version follows `1.0.0-alpha.28`. ### Bug Fixes -- **colors:** Reduce number of colors ([125d1c7](https://github.com/algolia/docsearch/commit/125d1c7)) -- **snippet:** Fix typo in method name ([751dfdb](https://github.com/algolia/docsearch/commit/751dfdb)) +* **colors:** Reduce number of colors ([125d1c7](https://github.com/algolia/docsearch/commit/125d1c7)) +* **snippet:** Fix typo in method name ([751dfdb](https://github.com/algolia/docsearch/commit/751dfdb)) ### Features -- **gh-pages:** Add gh-pages deploy script ([ab1a299](https://github.com/algolia/docsearch/commit/ab1a299)) -- **main:** Add main wrapper ([bdac9dd](https://github.com/algolia/docsearch/commit/bdac9dd)) -- **styling:** Set grayscale as default, add styling as external file ([5d8cca8](https://github.com/algolia/docsearch/commit/5d8cca8)) +* **gh-pages:** Add gh-pages deploy script ([ab1a299](https://github.com/algolia/docsearch/commit/ab1a299)) +* **main:** Add main wrapper ([bdac9dd](https://github.com/algolia/docsearch/commit/bdac9dd)) +* **styling:** Set grayscale as default, add styling as external file ([5d8cca8](https://github.com/algolia/docsearch/commit/5d8cca8)) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/CODEOWNERS b/CODEOWNERS index 04d99ecb..a388fe7f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,4 @@ # This file list code owners for the repository, or part of it. # See https://help.github.com/articles/about-code-owners/ -* @shortcuts @francoischalifour +* @shortcuts @8bittitan @dylantientcheu @NatanTechofNY @LorrisSaintGenez @pipeline1987 diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index bd912bb6..00000000 --- a/babel.config.js +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = (api) => { - const isTest = api.env('test'); - const modules = isTest ? 'commonjs' : false; - const targets = {}; - - if (isTest) { - targets.node = true; - } else { - targets.browsers = ['last 2 versions', 'ie >= 9']; - } - - return { - presets: [ - '@babel/preset-typescript', - [ - '@babel/preset-env', - { - modules, - targets, - }, - ], - ], - plugins: clean([ - '@babel/plugin-transform-react-jsx', - !isTest && [ - 'inline-replace-variables', - { - __DEV__: { - type: 'node', - replacement: "process.env.NODE_ENV === 'development'", - }, - }, - ], - ]), - }; -}; - -function clean(config) { - return config.filter(Boolean); -} diff --git a/bun.lock b/bun.lock new file mode 100644 index 00000000..8e2adb64 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1978 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "@docsearch/monorepo", + "devDependencies": { + "@axe-core/playwright": "^4.12.1", + "@changesets/cli": "^2.29.7", + "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", + "@playwright/test": "1.49.1", + "@stylistic/eslint-plugin": "2.13.0", + "@testing-library/dom": "10.4.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "bundlesize2": "0.0.32", + "jsdom": "26.0.0", + "oxfmt": "^0.59.0", + "oxlint": "^1.74.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "stylelint": "17.14.0", + "stylelint-config-sass-guidelines": "13.0.0", + "stylelint-config-standard": "40.0.0", + "stylelint-no-unsupported-browser-features": "8.1.1", + "stylelint-order": "8.1.1", + "typescript": "5.7.3", + "vitest": "3.0.2", + "watch": "1.0.2", + }, + }, + "examples/demo-js": { + "name": "@docsearch/js-example", + "version": "4.6.0", + "dependencies": { + "typesense-docsearch-css": "workspace:*", + "typesense-docsearch-sidepanel-js": "workspace:*", + "typesense-docsearch.js": "workspace:*", + }, + "devDependencies": { + "vite": "^6.0.7", + }, + }, + "examples/demo-react": { + "name": "@docsearch/react-example", + "version": "4.6.0", + "dependencies": { + "@algolia/autocomplete-core": "1.19.2", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "typesense-docsearch-core": "workspace:*", + "typesense-docsearch-css": "workspace:*", + "typesense-docsearch-modal": "workspace:*", + "typesense-docsearch-react": "workspace:*", + "typesense-docsearch-sidepanel": "workspace:*", + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "typescript": "5.7.3", + "vite": "^6.0.7", + }, + }, + "packages/docsearch-cli": { + "name": "typesense-docsearch-cli", + "version": "0.0.2", + "bin": { + "docsearch": "./dist/index.js", + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "jsonc-parser": "^3.3.1", + "picocolors": "^1.1.1", + }, + "devDependencies": { + "@types/node": "^26.1.1", + "typescript": "5.7.3", + "vitest": "3.0.2", + }, + }, + "packages/docsearch-core": { + "name": "typesense-docsearch-core", + "version": "5.0.2", + "devDependencies": { + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "preact": "11.0.0-beta.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "vitest": "3.0.2", + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0", + }, + "optionalPeers": [ + "@types/react", + "react", + "react-dom", + ], + }, + "packages/docsearch-css": { + "name": "typesense-docsearch-css", + "version": "5.0.2", + "devDependencies": { + "browserslist": "4.28.2", + "lightningcss": "1.32.0", + "nodemon": "^3.1.9", + }, + }, + "packages/docsearch-js": { + "name": "typesense-docsearch.js", + "version": "5.0.2", + "devDependencies": { + "htm": "3.1.1", + "preact": "11.0.0-beta.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-react": "5.0.2", + }, + }, + "packages/docsearch-modal": { + "name": "typesense-docsearch-modal", + "version": "5.0.2", + "dependencies": { + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-react": "5.0.2", + }, + "devDependencies": { + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "vitest": "3.0.2", + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0", + }, + "optionalPeers": [ + "@types/react", + "react", + "react-dom", + ], + }, + "packages/docsearch-react": { + "name": "typesense-docsearch-react", + "version": "5.0.2", + "dependencies": { + "@ai-sdk/react": "^2.0.30", + "@algolia/autocomplete-core": "1.19.2", + "@base-ui/react": "^1.5.0", + "ai": "^5.0.30", + "algoliasearch": "^5.28.0", + "marked": "^16.3.0", + "typesense": "^3.0.1", + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-css": "5.0.2", + "typesense-instantsearch-adapter": "3.0.0", + "zod": "^4.1.8", + }, + "devDependencies": { + "@algolia/autocomplete-core": "1.19.2", + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "preact": "11.0.0-beta.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "typesense-docsearch-core": "5.0.2", + "vitest": "3.0.2", + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0", + "search-insights": ">= 1 < 3", + }, + "optionalPeers": [ + "@types/react", + "react", + "react-dom", + "search-insights", + ], + }, + "packages/docsearch-sidepanel": { + "name": "typesense-docsearch-sidepanel", + "version": "5.0.2", + "dependencies": { + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-css": "5.0.2", + "typesense-docsearch-react": "5.0.2", + }, + "devDependencies": { + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "vitest": "3.0.2", + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0", + }, + "optionalPeers": [ + "@types/react", + "react", + "react-dom", + ], + }, + "packages/docsearch-sidepanel-js": { + "name": "typesense-docsearch-sidepanel-js", + "version": "5.0.2", + "devDependencies": { + "preact": "11.0.0-beta.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-react": "5.0.2", + }, + }, + }, + "packages": { + "@adobe/css-tools": ["@adobe/css-tools@4.5.0", "", {}, "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q=="], + + "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.112", "", { "dependencies": { "@ai-sdk/provider": "2.0.3", "@ai-sdk/provider-utils": "3.0.29", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-M58LfWXTTmwDqYJNDxGfzlAyXDAg8iV2gPq2ZvIGlIZzJ9oTA7yX85lVFEZ0Owc/Gm+j/sVUczu0tnvc575jpg=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.3", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4oNFrqBcy24KNclF1tWp/7ks+kSkDF6VZ1ccIfQoFVIgAAaoNH8bOTbOadVa4Dk70ghsh32caSHYWlzBI8F10g=="], + + "@ai-sdk/react": ["@ai-sdk/react@2.0.215", "", { "dependencies": { "@ai-sdk/provider-utils": "3.0.29", "ai": "5.0.213", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1", "zod": "^3.25.76 || ^4.1.8" }, "optionalPeers": ["zod"] }, "sha512-tZlREeRyWHMO970fnmmLkaDdK3CkNXAvcMOKt4vevAqe3y7ewRw3/e92MziUFlOltjTMutiMlxi1lnJsX55Vcw=="], + + "@algolia/abtesting": ["@algolia/abtesting@1.21.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-uXj0rgk30EpsKvOpuS+R+1XFDrnm56hED1Lz56e8uBkZdKCxw99LS2U8eXBqAHYU8kpkbsnV1GC8velBG070Hg=="], + + "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.19.2", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.19.2", "@algolia/autocomplete-shared": "1.19.2" } }, "sha512-mKv7RyuAzXvwmq+0XRK8HqZXt9iZ5Kkm2huLjgn5JoCPtDy+oh9yxUMfDDaVCw0oyzZ1isdJBc7l9nuCyyR7Nw=="], + + "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.19.2", "", { "dependencies": { "@algolia/autocomplete-shared": "1.19.2" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-TjxbcC/r4vwmnZaPwrHtkXNeqvlpdyR+oR9Wi2XyfORkiGkLTVhX2j+O9SaCCINbKoDfc+c2PB8NjfOnz7+oKg=="], + + "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.19.2", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-jEazxZTVD2nLrC+wYlVHQgpBoBB5KPStrJxLzsIFl6Kqd1AlG9sIAGl39V5tECLpIQzB3Qa2T6ZPJ1ChkwMK/w=="], + + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-y7Epol8HcjlBxKXHhyhfFPFhm78B3P6x9cCbCyGTdxjsdVCptXCy5hpkZWxjGpnaLHvWsHS4QRF0TiBOLst2xg=="], + + "@algolia/client-analytics": ["@algolia/client-analytics@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-8Pxj2VVmpM2d+UZufnlTq7T1QIcYPVugLV5XC50PnHsV5uRM9CSoYkg2Y+CwqwRk2La0xK5QsfZ0obIU+9XftQ=="], + + "@algolia/client-common": ["@algolia/client-common@5.55.2", "", {}, "sha512-9L4IpIYUqA63a7sw1trnHQGUvwiAjKz67nsgDnal98JGAc7wyposRb0Iag+eiMuyzFFaSHLe2/rGyIo+PafRBA=="], + + "@algolia/client-insights": ["@algolia/client-insights@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-ZBm2ytY5EHFcj+kjNsXxMNO/TGlOHe2fBFXGKHJOM1bk1rAy4o2YI+d9oV/w/jrqx44pvJMJlc8X6vKnCuDgUQ=="], + + "@algolia/client-personalization": ["@algolia/client-personalization@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-3FGVW/jDk7sdYwqa2NKnF/qXWcttc4bvGrwNbvqz3VoWSRv42CNvRk+3Y9QJFIUf1vY50hAuVWUoFKdyc8vaXA=="], + + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-JsG8LovDAYul5t8e533tZ3O1uZILxso5zsTtB7ONc5RJ8ACdTxAAC/jaOnsBNYb+x+STP7fzx/Iro55v5DNgoQ=="], + + "@algolia/client-search": ["@algolia/client-search@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-5wDnoIfC75zJ2MSHv5SSzTlRL2z7jQMbqQ5jrzottuq2p3oBObv8pD/JpXWu8pRaimaxNr3/Bs/KZIGVXxJ7hg=="], + + "@algolia/ingestion": ["@algolia/ingestion@1.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-da+SC6ikpza98W7C5ChsKEQDvZc8PQLQ0sxmQ5yMRsHpdD3iPKnclJA6ViB5Nr5T9qOX+IDswC6AyqY4V3rtug=="], + + "@algolia/monitoring": ["@algolia/monitoring@1.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-Y8kEcPqCiIEeaGv83l9RRA09mfYECqAJHNnOyEtZc9UirI6XBMUyFVss/sSeYUiV/Lf30hkbWcl00V1uXsf86Q=="], + + "@algolia/recommend": ["@algolia/recommend@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-5zmobuCQqFZkx+84Nt+suL7vo6jTh2CfAs2ndDSeTS2QHvnzP8YEEGWtWftjyACI0cK/FuH8urWwCHP+d2j8TA=="], + + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2" } }, "sha512-qnGUUuWG66dRMnr33owLsrYIh9fHVxtU4R2rd3SpneAHuoAUcGbDOWNrj05glVU6M8yOqo9gQ22K8zpz0I8Xpg=="], + + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2" } }, "sha512-lKZ5uhafMvR7dWCJEyuaeyZitid1I3ICx+k0vGf5x/ktdIQvc7bndCiOPpmIDqUmN26FE3jTehkAzSqee95G2Q=="], + + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.55.2", "", { "dependencies": { "@algolia/client-common": "5.55.2" } }, "sha512-Zc90xvKWUvxcNicvvTO9Pr/hT2TAnkixOIzJm/KMj5Ptm2pKjk71ngTsdkbRtJQvhZ2Kr9N1YdIjLrNHB5P2xw=="], + + "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], + + "@axe-core/playwright": ["@axe-core/playwright@4.12.1", "", { "dependencies": { "axe-core": "~4.12.1" }, "peerDependencies": { "playwright-core": ">= 1.0.0" } }, "sha512-rMd7xriptqKpP+w5265i4Hdkv2X5kbu6uiBi/B2I7uf3hieRBM3qDCfaKPtxfiYb2mKXfF+yLODJwIx+Jv1GDw=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw=="], + + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q=="], + + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + + "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@base-ui/react": ["@base-ui/react@1.6.0", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@base-ui/utils": "0.3.1", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@date-fns/tz": "^1.2.0", "@types/react": "^17 || ^18 || ^19", "date-fns": "^4.0.0", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw=="], + + "@base-ui/utils": ["@base-ui/utils@0.3.1", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.2.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg=="], + + "@cacheable/memory": ["@cacheable/memory@2.2.0", "", { "dependencies": { "@cacheable/utils": "^2.5.0", "@keyv/bigmap": "^1.3.1", "hookified": "^1.15.1", "keyv": "^5.6.0" } }, "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ=="], + + "@cacheable/utils": ["@cacheable/utils@2.5.0", "", { "dependencies": { "hashery": "^1.5.1", "keyv": "^5.6.0" } }, "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA=="], + + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.1.1", "", { "dependencies": { "@changesets/config": "^3.1.4", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA=="], + + "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.10", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A=="], + + "@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "^6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="], + + "@changesets/cli": ["@changesets/cli@2.31.0", "", { "dependencies": { "@changesets/apply-release-plan": "^7.1.1", "@changesets/assemble-release-plan": "^6.0.10", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.4", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/get-release-plan": "^4.0.16", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.7", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.2", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg=="], + + "@changesets/config": ["@changesets/config@3.1.4", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/logger": "^0.1.1", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q=="], + + "@changesets/errors": ["@changesets/errors@0.2.0", "", { "dependencies": { "extendable-error": "^0.1.5" } }, "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow=="], + + "@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@2.1.4", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg=="], + + "@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.16", "", { "dependencies": { "@changesets/assemble-release-plan": "^6.0.10", "@changesets/config": "^3.1.4", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.7", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g=="], + + "@changesets/get-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="], + + "@changesets/git": ["@changesets/git@3.0.4", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", "micromatch": "^4.0.8", "spawndamnit": "^3.0.1" } }, "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw=="], + + "@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "^1.1.0" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="], + + "@changesets/parse": ["@changesets/parse@0.4.3", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^4.1.1" } }, "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A=="], + + "@changesets/pre": ["@changesets/pre@2.0.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug=="], + + "@changesets/read": ["@changesets/read@0.6.7", "", { "dependencies": { "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.3", "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0", "picocolors": "^1.1.0" } }, "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA=="], + + "@changesets/should-skip-package": ["@changesets/should-skip-package@0.1.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw=="], + + "@changesets/types": ["@changesets/types@6.1.0", "", {}, "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA=="], + + "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], + + "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], + + "@csstools/css-calc": ["@csstools/css-calc@3.2.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg=="], + + "@csstools/css-color-parser": ["@csstools/css-color-parser@3.1.0", "", { "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA=="], + + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="], + + "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.6", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ=="], + + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="], + + "@csstools/media-query-list-parser": ["@csstools/media-query-list-parser@5.0.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg=="], + + "@csstools/selector-resolve-nested": ["@csstools/selector-resolve-nested@4.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA=="], + + "@csstools/selector-specificity": ["@csstools/selector-specificity@6.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA=="], + + "@docsearch/js-example": ["@docsearch/js-example@workspace:examples/demo-js"], + + "@docsearch/react-example": ["@docsearch/react-example@workspace:examples/demo-react"], + + "@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@eslint-community/eslint-plugin-eslint-comments": ["@eslint-community/eslint-plugin-eslint-comments@4.4.1", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "ignore": "^5.2.4" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + + "@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], + + "@floating-ui/core": ["@floating-ui/core@1.8.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.12" } }, "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="], + + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], + + "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.13.0", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], + + "@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@keyv/bigmap": ["@keyv/bigmap@1.3.1", "", { "dependencies": { "hashery": "^1.4.0", "hookified": "^1.15.0" }, "peerDependencies": { "keyv": "^5.6.0" } }, "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ=="], + + "@keyv/serialize": ["@keyv/serialize@1.1.1", "", {}, "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA=="], + + "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], + + "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="], + + "@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA=="], + + "@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-R/Sn7z52QtdAKNqQLLY0EK7hVMjXiz3XUlvoCFCm/60jgIzAnQtiqLKBCFaBkimCQL5rs2ezPMcicpjCsrl54Q=="], + + "@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vm/ynUqE4HjC0ZIEjmXv1UJu1/GngccQ+T+TJudTMxUxm6r+GQTg1TO3E5jJfI71pBaXxSzs1+vWHIwuilGHhw=="], + + "@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-uTtYDpLN/obfKVWGpgEc8BqYlLZBQTPz2uYEvLRy3HPZxjZ34wiFzukUBU2bf64JuCYZI//GTV1EOMmWlPjf/w=="], + + "@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-e2UnxL/ifStSPy8ffBCDbdy595SYsGy+U1pur4G65TuMmWxAMBzYGG7atZo/3mp515p8rZdsflxVD/E1FAdPLQ=="], + + "@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-LtdeZ1l0urxte3VNi3g8cocZwv1xGM1NKHSgF/fJEEVhyQmlgGh7WFWKFd/pNuO7djfvPNtNO1+MS+FEWkgVSA=="], + + "@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-dBTciSsj9GTMl7p+h2gMSI0hoPn2ijfc/dUsbnWsP0RbwgPl2r0C/5zkMb3Pb+gGj17LH7f1o4qLo9aes/pAvA=="], + + "@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-tXVdJ/JINsNWdponPHN0OuKHtC+HdpyoS9sd6IDPNiiEYsRki8b7tefRZ1iMnRkdbyT4SEbguWsr6o+5awvbPQ=="], + + "@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RRTq38i2zT5fnw6XGHjvT6w2mh6x/G3m6AZcAZ56OTDTT/lsOeYnG3SVjwmH40z5kPqF+lf+o35e6m6PpKy9Dw=="], + + "@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-lD3k7glAJSaXW0D6xzu8VOZbYbosvy+0ktOVkfLEoQF5HJlMSxTQ2KNW0JO+08ccP/1ElOKktVEMI0fqRbVB4w=="], + + "@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-WH5ZP1RbuHKBO/yfPRQKpNO/ijHcEDNbnmC4VPf/Bcd3+mbMAZpRiJWRa1PL5bREdIZZHo343mk3sqlc9x7Usw=="], + + "@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-743wOiaI9RZY4QVGkWkfGRavD5ZJUJ6gscFjVrVu1dP8AZh9jM+a6v3NhlR+OIzHdS6DhLM96w+gcVskskz7rw=="], + + "@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-xjRXQsRnrRZCcCkIEnbd2lmsQNobtwwkJxdy2bWXhZ1lIN0ouZwsBXRsoovW3yATuziAYwr9HMiQuR/Cc75NIw=="], + + "@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-4hNjqq/Rbr9B+StY9zMMAfm72+mtM4v80xYL5Qkb59Qd72g2vJMI0iFlPj3kf6miMsie/yJ7rt4urJT292HBgA=="], + + "@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-NH579iN8EVQYsWowUB8B5vFchcylJtwPVJ7NmUAqEQHNLfhPbDT3K56KrECNAkUN4QpF4qiMgN2vsfZwVvjm7g=="], + + "@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-mzZy3Z5Aj1D75Aq9FVlmoRQH5ei8Ga4o/NZmlXkKyeZ5EmPrUXRR7c6BMBteV1ZuZ/356UYDuLRLjAMxTDTiBA=="], + + "@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-0CpDJ1gE3jN1Gk6xms1Ie6LPfPcOtY4FAtoOmVLHQoAf8DvO2wd0DW2dIX2f7YTp5dxrr0ND8JeUEjm3DP3k5g=="], + + "@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zwdKBu3pt87uW0bRcywZb0oGMS7C6n87qogwRYFUgmk44T90ZzYlPjtlFYXs/DnBFrgNCvlHwCuWKfVWLeE7kw=="], + + "@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dUUbZkKgWrmAeI/puzv4bxN8lzcYaFnQVwFTFtwO2Gp8M7lZGSE2qJjC58g518+1bltJ8mizjYwD0BGHym0l/w=="], + + "@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.74.0", "", { "os": "android", "cpu": "arm" }, "sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw=="], + + "@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.74.0", "", { "os": "android", "cpu": "arm64" }, "sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw=="], + + "@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.74.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ=="], + + "@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.74.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q=="], + + "@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.74.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g=="], + + "@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g=="], + + "@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.74.0", "", { "os": "linux", "cpu": "arm" }, "sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ=="], + + "@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w=="], + + "@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.74.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg=="], + + "@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.74.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw=="], + + "@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg=="], + + "@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.74.0", "", { "os": "linux", "cpu": "none" }, "sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw=="], + + "@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.74.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA=="], + + "@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw=="], + + "@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.74.0", "", { "os": "linux", "cpu": "x64" }, "sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg=="], + + "@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.74.0", "", { "os": "none", "cpu": "arm64" }, "sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg=="], + + "@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.74.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA=="], + + "@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.74.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ=="], + + "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.74.0", "", { "os": "win32", "cpu": "x64" }, "sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA=="], + + "@playwright/test": ["@playwright/test@1.49.1", "", { "dependencies": { "playwright": "1.49.1" }, "bin": { "playwright": "cli.js" } }, "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g=="], + + "@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="], + + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], + + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], + + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g=="], + + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA=="], + + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.5", "", { "os": "linux", "cpu": "arm" }, "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw=="], + + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q=="], + + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA=="], + + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg=="], + + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA=="], + + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ=="], + + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg=="], + + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.5", "", { "os": "none", "cpu": "arm64" }, "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw=="], + + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.5", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA=="], + + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw=="], + + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA=="], + + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.2", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="], + + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@2.13.0", "", { "dependencies": { "@typescript-eslint/utils": "^8.13.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "estraverse": "^5.3.0", "picomatch": "^4.0.2" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ=="], + + "@stylistic/stylelint-plugin": ["@stylistic/stylelint-plugin@5.2.1", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0", "postcss": "^8.5.8", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", "style-search": "^0.1.0" }, "peerDependencies": { "stylelint": "^17.6.0" } }, "sha512-BMRWJVtO16uADJmKcGi6mreei/QYdOviABXuaCeHEc3T3pxo4wFYYSvnxIoyiPVfDJqX+DxQdnGkuc6KxsRaSw=="], + + "@testing-library/dom": ["@testing-library/dom@10.4.0", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" } }, "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ=="], + + "@testing-library/jest-dom": ["@testing-library/jest-dom@6.6.3", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "lodash": "^4.17.21", "redent": "^3.0.0" } }, "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA=="], + + "@testing-library/react": ["@testing-library/react@16.2.0", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], + + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="], + + "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="], + + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.64.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.64.0", "@typescript-eslint/types": "^8.64.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.64.0", "", { "dependencies": { "@typescript-eslint/types": "8.64.0", "@typescript-eslint/visitor-keys": "8.64.0" } }, "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.64.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.64.0", "", {}, "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.64.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.64.0", "@typescript-eslint/tsconfig-utils": "8.64.0", "@typescript-eslint/types": "8.64.0", "@typescript-eslint/visitor-keys": "8.64.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.64.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.64.0", "@typescript-eslint/types": "8.64.0", "@typescript-eslint/typescript-estree": "8.64.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.64.0", "", { "dependencies": { "@typescript-eslint/types": "8.64.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.3", "", {}, "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg=="], + + "@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + + "@vitest/expect": ["@vitest/expect@3.0.2", "", { "dependencies": { "@vitest/spy": "3.0.2", "@vitest/utils": "3.0.2", "chai": "^5.1.2", "tinyrainbow": "^2.0.0" } }, "sha512-dKSHLBcoZI+3pmP5hiZ7I5grNru2HRtEW8Z5Zp4IXog8QYcxhlox7JUPyIIFWfN53+3HW3KPLIl6nSzUGgKSuQ=="], + + "@vitest/mocker": ["@vitest/mocker@3.0.2", "", { "dependencies": { "@vitest/spy": "3.0.2", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-Hr09FoBf0jlwwSyzIF4Xw31OntpO3XtZjkccpcBf8FeVW3tpiyKlkeUzxS/txzHqpUCNIX157NaTySxedyZLvA=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.7", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA=="], + + "@vitest/runner": ["@vitest/runner@3.0.2", "", { "dependencies": { "@vitest/utils": "3.0.2", "pathe": "^2.0.1" } }, "sha512-GHEsWoncrGxWuW8s405fVoDfSLk6RF2LCXp6XhevbtDjdDme1WV/eNmUueDfpY1IX3MJaCRelVCEXsT9cArfEg=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.0.2", "", { "dependencies": { "@vitest/pretty-format": "3.0.2", "magic-string": "^0.30.17", "pathe": "^2.0.1" } }, "sha512-h9s67yD4+g+JoYG0zPCo/cLTabpDqzqNdzMawmNPzDStTiwxwkyYM1v5lWE8gmGv3SVJ2DcxA2NpQJZJv9ym3g=="], + + "@vitest/spy": ["@vitest/spy@3.0.2", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-8mI2iUn+PJFMT44e3ISA1R+K6ALVs47W6eriDTfXe6lFqlflID05MB4+rIFhmDSLBj8iBsZkzBYlgSkinxLzSQ=="], + + "@vitest/utils": ["@vitest/utils@3.0.2", "", { "dependencies": { "@vitest/pretty-format": "3.0.2", "loupe": "^3.1.2", "tinyrainbow": "^2.0.0" } }, "sha512-Qu01ZYZlgHvDP02JnMBRpX43nRaZtNpIzw3C1clDXmn8eakgX6iQVGzTQ/NjkIr64WD8ioqOjkaYRVvHQI5qiw=="], + + "@yuku-codegen/binding-darwin-arm64": ["@yuku-codegen/binding-darwin-arm64@0.6.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pbDcFygFmbvo0jGFq5U0m5Sa9U8aVttVJWbBHZDZ68w/X48HdDWS1V4XvBacs8XkmWbTr/ef5fMGG7HsngqTmg=="], + + "@yuku-codegen/binding-darwin-x64": ["@yuku-codegen/binding-darwin-x64@0.6.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-qZMrnA4i8OfqL3NMGoOvLdh1vby8cCGsmNo+tJQIIezXO557m3fdQLenz/57GqtBnnGWzWqd/aDp+faNxWlLwQ=="], + + "@yuku-codegen/binding-freebsd-x64": ["@yuku-codegen/binding-freebsd-x64@0.6.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2+SFpfem2GBH6BlCTAq6R44bZwuieduwRWHkCQnSgbK8tdEjMwB0Ix0IchryVBn6hdiWCZSTkSE3UILliRXsRQ=="], + + "@yuku-codegen/binding-linux-arm-gnu": ["@yuku-codegen/binding-linux-arm-gnu@0.6.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fbxg3cBPdJ++36DXtdzcoKw2xzFov91Wxvmn1khX9MXQbDqJQLJmITZhtokcZsj4uGJe32sUmxAZnKbUtZLjmA=="], + + "@yuku-codegen/binding-linux-arm-musl": ["@yuku-codegen/binding-linux-arm-musl@0.6.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Jk4P7kocGEisSvUFIm1VuHO3hC01LvS3sYAAmVVu1/ve5TuZ0iXyl9kIGtd1ZrgUvchgvZWNOaB+/Kq/RO63FA=="], + + "@yuku-codegen/binding-linux-arm64-gnu": ["@yuku-codegen/binding-linux-arm64-gnu@0.6.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-i1xE8Bx1YZLheWtBZHD0Mq3nAIDrhgiH7o8VB4GiCbHufKb4XKj4CqSDMWSC0RYPmn//E+UEd1NsE2NbOux1tQ=="], + + "@yuku-codegen/binding-linux-arm64-musl": ["@yuku-codegen/binding-linux-arm64-musl@0.6.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZeLkC6xZrlDoIJTadHfqTABTmsj2f6wCCtYBYx/RPGgdmQcGLA0NALRl7m0tnK2CT45eNRuOYzsfEZyF0XWM/A=="], + + "@yuku-codegen/binding-linux-x64-gnu": ["@yuku-codegen/binding-linux-x64-gnu@0.6.3", "", { "os": "linux", "cpu": "x64" }, "sha512-HNYt7zjIChPcnjZRG42CZq3Zn5mqaRo4UcFLr4mIbmGqdhX5hDDE8O8US8YgYyOUosfbBTBFdsbvFwVAx1TOkQ=="], + + "@yuku-codegen/binding-linux-x64-musl": ["@yuku-codegen/binding-linux-x64-musl@0.6.3", "", { "os": "linux", "cpu": "x64" }, "sha512-/1ttT31dAQc7hGtXWSEYEgzGtakAyO2C+/GqAzIuKXlGLpNPZgXdR8LZ0iDHDalbEr3AuHIPRV43sWLUrHWdsA=="], + + "@yuku-codegen/binding-win32-arm64": ["@yuku-codegen/binding-win32-arm64@0.6.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-oAArRDU1lkKg+xFEtiQ7C+/wghpqrkBrFWM05W73S+3sZz8JIHH79Q+Qh5gWls3i8vccitUgCnvln5V7xKn3XQ=="], + + "@yuku-codegen/binding-win32-x64": ["@yuku-codegen/binding-win32-x64@0.6.3", "", { "os": "win32", "cpu": "x64" }, "sha512-bhFDcDsvmp5KuBfWTt4carNo1E4LXH7++S8qqZgDtXVqJxs0xMm7gbuqPihA8kBbphM+NzAUm5qmq6ocfqM6kg=="], + + "@yuku-parser/binding-darwin-arm64": ["@yuku-parser/binding-darwin-arm64@0.6.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Xate6yyZgvi7da/gdnZy+Vu5jlFB0LRlb5m4MY6Y98KSQeJPZIhoVXMK2Vsl48XOmPlDIS8lge414HUVUEo+hg=="], + + "@yuku-parser/binding-darwin-x64": ["@yuku-parser/binding-darwin-x64@0.6.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-WKMZ5UU2HBdGZDEpQoLq+21f1FlS+BjroH1FaVE6zCSeqKmZ7xRP5jIRGtQ4vCYj/k2KHgyABZ16lgK9mTe2Sg=="], + + "@yuku-parser/binding-freebsd-x64": ["@yuku-parser/binding-freebsd-x64@0.6.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-OWX8V2k4bmtl/DNwU3yz3PZa2QXiSqWN3Xk7pNB5IPt7FcJBDlnpkOcX6h3tjMS7CyKE0lMvPUwwcmWSdbjkyA=="], + + "@yuku-parser/binding-linux-arm-gnu": ["@yuku-parser/binding-linux-arm-gnu@0.6.3", "", { "os": "linux", "cpu": "arm" }, "sha512-/4LzmPXPaCWqIpY1j3+XVb8rbXIratqCte3A4sGEjua6aVhvVxEVAqeKlBsoGkORWLeqbpcxhgxKwOGM17eexA=="], + + "@yuku-parser/binding-linux-arm-musl": ["@yuku-parser/binding-linux-arm-musl@0.6.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Df4jk0M/eNKKQfYzXBBKhKkmJBpB+XoX2LkMxmlK3GN+fxUdeb8EM78wX+1+eLVl5dZNo6f7gOd6oDV0gChevw=="], + + "@yuku-parser/binding-linux-arm64-gnu": ["@yuku-parser/binding-linux-arm64-gnu@0.6.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sRCtDktUgIbbV78SYX3wdGVVm1Hz/nSUS24JgXB4MzUeGNwBNB+eQAWMtBxCGriyJNXK3zwfj+SSgvTmUdPf/A=="], + + "@yuku-parser/binding-linux-arm64-musl": ["@yuku-parser/binding-linux-arm64-musl@0.6.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-3J/jV3ROSqlhLyB/6i5EUHxjkom5i59iPvrtiAsnAjHzMsZJJPEke9LSaOsB0rf4MJFH9AmjvsK8gDahTjZy+A=="], + + "@yuku-parser/binding-linux-x64-gnu": ["@yuku-parser/binding-linux-x64-gnu@0.6.3", "", { "os": "linux", "cpu": "x64" }, "sha512-a5mPn/OMSq2Aa2i7eJXcc37Jtw0b89gDO2mDpXN769b06IirEiqOzLNNJd6R7R8DxoadHzY0nLFhYNChE+jyAg=="], + + "@yuku-parser/binding-linux-x64-musl": ["@yuku-parser/binding-linux-x64-musl@0.6.3", "", { "os": "linux", "cpu": "x64" }, "sha512-kQSjfa6zdvotuXEGNKQ9vZZxE+lcEEbTUMSuvY/5+0crlwBCjEqrf9/W9ViFDoQAEt8WJiu/mtVNYkKAOkjLmA=="], + + "@yuku-parser/binding-win32-arm64": ["@yuku-parser/binding-win32-arm64@0.6.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZawdN3R0YKr48BeXCpbax+WDWbgEG6nWyDosVeZasrT5TjgfF4XMP5SfuyMNvRJ4gbTitrcYHZkENSXZ9ncqcg=="], + + "@yuku-parser/binding-win32-x64": ["@yuku-parser/binding-win32-x64@0.6.3", "", { "os": "win32", "cpu": "x64" }, "sha512-NcqZwBXQhKyfC0Eb+yBxXQcPjZoUstD1Dbr3X4UlOD1QiYfnvAYRKCZJ6nQ6KQ5p30dGaKkQeBL4t3qQtDQNWA=="], + + "@yuku-toolchain/types": ["@yuku-toolchain/types@0.5.43", "", {}, "sha512-kSpvPntnXw5+lYjO71ffBEnQ5ycQ74KGIYknh0TS4xeyCuBkOqxyJumxZkMhLBBUCLjDAbx2+Icnr3Zh4ftjpQ=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "ai": ["ai@5.0.213", "", { "dependencies": { "@ai-sdk/gateway": "2.0.112", "@ai-sdk/provider": "2.0.3", "@ai-sdk/provider-utils": "3.0.29", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-QCPMxXxV/I+nXVYyQru62Izx8cU5UmPDkooJmYy1+kBvhT/hDH/n33th/SAE73A2kRXIUO1cFdvhZ0XCRPdWaw=="], + + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "algoliasearch": ["algoliasearch@5.55.2", "", { "dependencies": { "@algolia/abtesting": "1.21.2", "@algolia/client-abtesting": "5.55.2", "@algolia/client-analytics": "5.55.2", "@algolia/client-common": "5.55.2", "@algolia/client-insights": "5.55.2", "@algolia/client-personalization": "5.55.2", "@algolia/client-query-suggestions": "5.55.2", "@algolia/client-search": "5.55.2", "@algolia/ingestion": "1.55.2", "@algolia/monitoring": "1.55.2", "@algolia/recommend": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", "@algolia/requester-fetch": "5.55.2", "@algolia/requester-node-http": "5.55.2" } }, "sha512-OyacJsaeuLUvGWOynNqYc6sx88XvyoG39wMT8SYqL3l9wwaorDW/LPRbUPfhzw0bWsUWzNCZTnFYOrWFBKsUaw=="], + + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "ansis": ["ansis@4.3.1", "", {}, "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + + "array-differ": ["array-differ@3.0.0", "", {}, "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg=="], + + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + + "arrify": ["arrify@2.0.1", "", {}, "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "axe-core": ["axe-core@4.12.1", "", {}, "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA=="], + + "axios": ["axios@1.19.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw=="], + + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.43", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ=="], + + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], + + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + + "body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], + + "brace-expansion": ["brace-expansion@5.0.7", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + + "bundlesize2": ["bundlesize2@0.0.32", "", { "dependencies": { "bytes": "^3.1.0", "chalk": "^4.0.0", "ci-env": "^1.15.0", "commander": "^5.1.0", "cosmiconfig": "5.2.1", "figures": "^3.2.0", "glob": "^7.1.6", "gzip-size": "^5.1.1", "node-fetch": "^2.6.7", "plur": "^4.0.0" }, "bin": { "bundlesize": "index.js" } }, "sha512-1yAxSlRYM//v74FZvcnydV/LrKCaKB3k8Xstz8gN7WgLuvwpLq3NFXz815s3TAPQRmEA1tN+g6ar2Z+AIP63eg=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "cac": ["cac@7.0.0", "", {}, "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ=="], + + "cacheable": ["cacheable@2.5.0", "", { "dependencies": { "@cacheable/memory": "^2.2.0", "@cacheable/utils": "^2.5.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.10.1" } }, "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "caller-callsite": ["caller-callsite@2.0.0", "", { "dependencies": { "callsites": "^2.0.0" } }, "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ=="], + + "caller-path": ["caller-path@2.0.0", "", { "dependencies": { "caller-callsite": "^2.0.0" } }, "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001805", "", {}, "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA=="], + + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "chardet": ["chardet@2.2.0", "", {}, "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA=="], + + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], + + "ci-env": ["ci-env@1.17.0", "", {}, "sha512-NtTjhgSEqv4Aj90TUYHQLxHdnCPXnjdtuGG1X8lTfp/JqeXTdw0FTWl/vUAPuvbWZTF8QVpv6ASe/XacE+7R2A=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "colord": ["colord@2.9.3", "", {}, "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cosmiconfig": ["cosmiconfig@5.2.1", "", { "dependencies": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", "js-yaml": "^3.13.1", "parse-json": "^4.0.0" } }, "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "css-functions-list": ["css-functions-list@3.3.3", "", {}, "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg=="], + + "css-tokenize": ["css-tokenize@1.0.1", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^1.0.33" } }, "sha512-gLmmbJdwH9HLY4bcA17lnZ8GgPwEXRbvxBJGHnkiB6gLhRpTzjkjtMIvz7YORGW/Ptv2oMk8b5g+u7mRD6Dd7A=="], + + "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], + + "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + + "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], + + "doiuse": ["doiuse@6.0.6", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001760", "css-tokenize": "^1.0.1", "duplexify": "^4.1.3", "multimatch": "^5.0.0", "postcss": "^8.5.6", "source-map": "^0.7.6", "yargs": "^17.7.2" }, "bin": { "doiuse": "bin/cli.js" } }, "sha512-XuPRslcWHhQJ+WjCjimRUcNfhZvOiC0610FsY6WeSlzXvoZYtm6iOpR9K0N4wRoM/lP4i7LatT+IhltAzouSOw=="], + + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + + "dts-resolver": ["dts-resolver@3.0.0", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], + + "duplexify": ["duplexify@4.1.3", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.392", "", {}, "sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "empathic": ["empathic@2.0.1", "", {}, "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], + + "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], + + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA=="], + + "eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="], + + "exec-sh": ["exec-sh@0.2.2", "", { "dependencies": { "merge": "^1.2.0" } }, "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw=="], + + "expect-type": ["expect-type@1.4.0", "", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="], + + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="], + + "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-uri": ["fast-uri@3.1.3", "", {}, "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg=="], + + "fastest-levenshtein": ["fastest-levenshtein@1.0.16", "", {}, "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="], + + "file-entry-cache": ["file-entry-cache@11.1.5", "", { "dependencies": { "flat-cache": "^6.1.23" } }, "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@6.1.23", "", { "dependencies": { "cacheable": "^2.5.0", "flatted": "^3.4.2", "hookified": "^1.15.0" } }, "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA=="], + + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + + "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], + + "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-tsconfig": ["get-tsconfig@5.0.0-beta.5", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ=="], + + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "global-modules": ["global-modules@2.0.0", "", { "dependencies": { "global-prefix": "^3.0.0" } }, "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A=="], + + "global-prefix": ["global-prefix@3.0.0", "", { "dependencies": { "ini": "^1.3.5", "kind-of": "^6.0.2", "which": "^1.3.1" } }, "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg=="], + + "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + + "globby": ["globby@16.2.2", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "fast-glob": "^3.3.3", "ignore": "^7.0.5", "is-path-inside": "^4.0.0", "slash": "^5.1.0", "unicorn-magic": "^0.4.0" } }, "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA=="], + + "globjoin": ["globjoin@0.1.4", "", {}, "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "gzip-size": ["gzip-size@5.1.1", "", { "dependencies": { "duplexer": "^0.1.1", "pify": "^4.0.1" } }, "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA=="], + + "has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hashery": ["hashery@1.5.1", "", { "dependencies": { "hookified": "^1.15.0" } }, "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "hono": ["hono@4.12.30", "", {}, "sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog=="], + + "hookable": ["hookable@6.1.1", "", {}, "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ=="], + + "hookified": ["hookified@1.15.1", "", {}, "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg=="], + + "htm": ["htm@3.1.1", "", {}, "sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ=="], + + "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], + + "html-tags": ["html-tags@5.1.0", "", {}, "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "human-id": ["human-id@4.2.0", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA=="], + + "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "ignore-by-default": ["ignore-by-default@1.0.1", "", {}, "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="], + + "import-fresh": ["import-fresh@2.0.0", "", { "dependencies": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" } }, "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg=="], + + "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], + + "import-without-cache": ["import-without-cache@0.4.0", "", {}, "sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "irregular-plurals": ["irregular-plurals@3.5.0", "", {}, "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + + "is-directory": ["is-directory@0.3.1", "", {}, "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + + "is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="], + + "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "is-subdir": ["is-subdir@1.2.0", "", { "dependencies": { "better-path-resolve": "1.0.0" } }, "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw=="], + + "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], + + "isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], + + "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.3.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q=="], + + "jsdom": ["jsdom@26.0.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-better-errors": ["json-parse-better-errors@1.0.2", "", {}, "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], + + "lodash.truncate": ["lodash.truncate@4.4.2", "", {}, "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw=="], + + "loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="], + + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mathml-tag-names": ["mathml-tag-names@4.0.0", "", {}, "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ=="], + + "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "meow": ["meow@14.1.0", "", {}, "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw=="], + + "merge": ["merge@1.2.1", "", {}, "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], + + "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "multimatch": ["multimatch@5.0.0", "", { "dependencies": { "@types/minimatch": "^3.0.3", "array-differ": "^3.0.0", "array-union": "^2.1.0", "arrify": "^2.0.1", "minimatch": "^3.0.4" } }, "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA=="], + + "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "node-releases": ["node-releases@2.0.51", "", {}, "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ=="], + + "nodemon": ["nodemon@3.1.14", "", { "dependencies": { "chokidar": "^3.5.2", "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^10.2.1", "pstree.remy": "^1.1.8", "semver": "^7.5.3", "simple-update-notifier": "^2.0.0", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" } }, "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "nwsapi": ["nwsapi@2.2.24", "", {}, "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "obug": ["obug@2.1.3", "", {}, "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], + + "oxfmt": ["oxfmt@0.59.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.59.0", "@oxfmt/binding-android-arm64": "0.59.0", "@oxfmt/binding-darwin-arm64": "0.59.0", "@oxfmt/binding-darwin-x64": "0.59.0", "@oxfmt/binding-freebsd-x64": "0.59.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.59.0", "@oxfmt/binding-linux-arm-musleabihf": "0.59.0", "@oxfmt/binding-linux-arm64-gnu": "0.59.0", "@oxfmt/binding-linux-arm64-musl": "0.59.0", "@oxfmt/binding-linux-ppc64-gnu": "0.59.0", "@oxfmt/binding-linux-riscv64-gnu": "0.59.0", "@oxfmt/binding-linux-riscv64-musl": "0.59.0", "@oxfmt/binding-linux-s390x-gnu": "0.59.0", "@oxfmt/binding-linux-x64-gnu": "0.59.0", "@oxfmt/binding-linux-x64-musl": "0.59.0", "@oxfmt/binding-openharmony-arm64": "0.59.0", "@oxfmt/binding-win32-arm64-msvc": "0.59.0", "@oxfmt/binding-win32-ia32-msvc": "0.59.0", "@oxfmt/binding-win32-x64-msvc": "0.59.0" }, "peerDependencies": { "svelte": "^5.0.0", "vite-plus": "*" }, "optionalPeers": ["svelte", "vite-plus"], "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-Xqk6cPZS1yMvVa7OAuenaDZUsgMDutvvbZ9/L5gSvAfW64+WN4HVhgipLj5rVERbYQt8fLs9TopyZ1rU1XEG/w=="], + + "oxlint": ["oxlint@1.74.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.74.0", "@oxlint/binding-android-arm64": "1.74.0", "@oxlint/binding-darwin-arm64": "1.74.0", "@oxlint/binding-darwin-x64": "1.74.0", "@oxlint/binding-freebsd-x64": "1.74.0", "@oxlint/binding-linux-arm-gnueabihf": "1.74.0", "@oxlint/binding-linux-arm-musleabihf": "1.74.0", "@oxlint/binding-linux-arm64-gnu": "1.74.0", "@oxlint/binding-linux-arm64-musl": "1.74.0", "@oxlint/binding-linux-ppc64-gnu": "1.74.0", "@oxlint/binding-linux-riscv64-gnu": "1.74.0", "@oxlint/binding-linux-riscv64-musl": "1.74.0", "@oxlint/binding-linux-s390x-gnu": "1.74.0", "@oxlint/binding-linux-x64-gnu": "1.74.0", "@oxlint/binding-linux-x64-musl": "1.74.0", "@oxlint/binding-openharmony-arm64": "1.74.0", "@oxlint/binding-win32-arm64-msvc": "1.74.0", "@oxlint/binding-win32-ia32-msvc": "1.74.0", "@oxlint/binding-win32-x64-msvc": "1.74.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.24.0", "vite-plus": "*" }, "optionalPeers": ["oxlint-tsgolint", "vite-plus"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA=="], + + "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], + + "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], + + "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "^0.2.7" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], + + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + + "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "playwright": ["playwright@1.49.1", "", { "dependencies": { "playwright-core": "1.49.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA=="], + + "playwright-core": ["playwright-core@1.49.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg=="], + + "plur": ["plur@4.0.0", "", { "dependencies": { "irregular-plurals": "^3.2.0" } }, "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg=="], + + "postcss": ["postcss@8.5.19", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ=="], + + "postcss-media-query-parser": ["postcss-media-query-parser@0.2.3", "", {}, "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig=="], + + "postcss-resolve-nested-selector": ["postcss-resolve-nested-selector@0.1.6", "", {}, "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw=="], + + "postcss-safe-parser": ["postcss-safe-parser@7.0.1", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A=="], + + "postcss-scss": ["postcss-scss@4.0.9", "", { "peerDependencies": { "postcss": "^8.4.29" } }, "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg=="], + + "postcss-sorting": ["postcss-sorting@10.0.0", "", { "peerDependencies": { "postcss": "^8.4.20" } }, "sha512-TXbU+h6vVRW+86c/+ewhWq9k7pr7ijASTnepVhCQiC87zAOTkvB1v2dHyWP+ggstSTX/PNvjzS+IOqzejndz9w=="], + + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + + "preact": ["preact@11.0.0-beta.0", "", {}, "sha512-IcODoASASYwJ9kxz7+MJeiJhvLriwSb4y4mHIyxdgaRZp6kPUud7xytrk/6GZw8U3y6EFJaRb5wi9SrEK+8+lg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], + + "pstree.remy": ["pstree.remy@1.1.8", "", {}, "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "qified": ["qified@0.10.1", "", { "dependencies": { "hookified": "^2.1.1" } }, "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA=="], + + "qs": ["qs@6.15.3", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="], + + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + + "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], + + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], + + "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], + + "readable-stream": ["readable-stream@1.1.14", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", "isarray": "0.0.1", "string_decoder": "~0.10.x" } }, "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ=="], + + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + + "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], + + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="], + + "rolldown-plugin-dts": ["rolldown-plugin-dts@0.27.9", "", { "dependencies": { "dts-resolver": "^3.0.0", "get-tsconfig": "5.0.0-beta.5", "obug": "^2.1.3", "yuku-ast": "^0.1.7", "yuku-codegen": "^0.6.1", "yuku-parser": "^0.6.1" }, "peerDependencies": { "@ts-macro/tsc": "^0.3.6", "@typescript/native-preview": "*", "rolldown": "^1.0.0", "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", "vue-tsc": "~3.2.0 || ~3.3.0" }, "optionalPeers": ["@ts-macro/tsc", "@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-d54yt65+ZF/Mk8H6P36As02PAMdaiWRSzVNtJRc1h7nCgUFjuRI4cN2DyTfJyfVpPH6pgy7/2D7YQH1/Rh75Yg=="], + + "rollup": ["rollup@4.62.2", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="], + + "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "simple-update-notifier": ["simple-update-notifier@2.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w=="], + + "slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], + + "slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + + "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "stream-shift": ["stream-shift@1.0.3", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="], + + "string-width": ["string-width@8.2.2", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg=="], + + "string_decoder": ["string_decoder@0.10.31", "", {}, "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "style-search": ["style-search@0.1.0", "", {}, "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg=="], + + "stylelint": ["stylelint@17.14.0", "", { "dependencies": { "@csstools/css-calc": "^3.2.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-syntax-patches-for-csstree": "^1.1.5", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0", "@csstools/selector-resolve-nested": "^4.0.0", "@csstools/selector-specificity": "^6.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.2", "css-functions-list": "^3.3.3", "css-tree": "^3.2.1", "debug": "^4.4.3", "fast-glob": "^3.3.3", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^11.1.3", "global-modules": "^2.0.0", "globby": "^16.2.0", "globjoin": "^0.1.4", "html-tags": "^5.1.0", "ignore": "^7.0.5", "import-meta-resolve": "^4.2.0", "mathml-tag-names": "^4.0.0", "meow": "^14.1.0", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.5.15", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.4", "postcss-value-parser": "^4.2.0", "string-width": "^8.2.1", "supports-hyperlinks": "^4.4.0", "svg-tags": "^1.0.0", "table": "^6.9.0", "write-file-atomic": "^7.0.1" }, "bin": { "stylelint": "bin/stylelint.mjs" } }, "sha512-8xkHPpdqYryeIsOgfsYTmr6cIeC4nLYWk5S8BPxpodq8mIuepggkMljsHewWfuAjj/+qpRKou2QerhjMH3iasg=="], + + "stylelint-config-recommended": ["stylelint-config-recommended@18.0.0", "", { "peerDependencies": { "stylelint": "^17.0.0" } }, "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg=="], + + "stylelint-config-sass-guidelines": ["stylelint-config-sass-guidelines@13.0.0", "", { "dependencies": { "@stylistic/stylelint-plugin": "^5.0.1", "postcss-scss": "^4.0.9", "stylelint-scss": "^7.0.0" }, "peerDependencies": { "postcss": "^8.4.21", "stylelint": "^17.1.0" } }, "sha512-YJT0X8h0OqyEo7ys3EycV5CGWt2rrkEYE8sHSN6sFnrxbXvHiy4KJFHDOWfyb3eWR6wtYpM+yIyvR2Plc8+pCg=="], + + "stylelint-config-standard": ["stylelint-config-standard@40.0.0", "", { "dependencies": { "stylelint-config-recommended": "^18.0.0" }, "peerDependencies": { "stylelint": "^17.0.0" } }, "sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw=="], + + "stylelint-no-unsupported-browser-features": ["stylelint-no-unsupported-browser-features@8.1.1", "", { "dependencies": { "browserslist": "^4.26.3", "doiuse": "^6.0.6", "postcss": "^8.4.32" }, "peerDependencies": { "stylelint": ">=16.0.2" } }, "sha512-sLEe6NUFoWL2vGt6IKKllQEKfKgVxmvTBFs1JVMHKKLWzxtWAXaqSxJvH+j0URM4vLQQqzC/wXc9Kp3XBNKdBw=="], + + "stylelint-order": ["stylelint-order@8.1.1", "", { "dependencies": { "postcss": "^8.5.8", "postcss-sorting": "^10.0.0" }, "peerDependencies": { "stylelint": "^16.18.0 || ^17.0.0" } }, "sha512-LqsEB6VggJuu5v10RtkrQsBObcdwBE7GuAOlwfc/LR3VL/w8UqKX2BOLIjhyGt0Gne/njo7gRNGiJAKhfmPMNw=="], + + "stylelint-scss": ["stylelint-scss@7.2.0", "", { "dependencies": { "@csstools/css-calc": "^3.2.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-syntax-patches-for-csstree": "^1.1.4", "@csstools/css-tokenizer": "^4.0.0", "css-tree": "^3.2.1", "is-plain-object": "^5.0.0", "known-css-properties": "^0.37.0", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.6", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "stylelint": "^16.8.2 || ^17.0.0" } }, "sha512-6E79Bachv0Iz0gqRUZgdqdXCsiq26DWBWIBNHYtjTmAp3wJu6cp/I37VfW7BPntmh2puF3bY09XWl4HZGrLhzw=="], + + "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + + "supports-hyperlinks": ["supports-hyperlinks@4.5.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w=="], + + "svg-tags": ["svg-tags@1.0.0", "", {}, "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA=="], + + "swr": ["swr@2.4.2", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-ej644Y2bvkIajfR32KGeSSdBXQW+ScjGjkybZgSE7kFpk9eGnV44XY9FJylXi+W75pavSX1PVNB57W5EbhGIYw=="], + + "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], + + "table": ["table@6.9.0", "", { "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A=="], + + "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], + + "terser": ["terser@5.49.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA=="], + + "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], + + "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="], + + "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], + + "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "touch": ["touch@3.1.1", "", { "bin": { "nodetouch": "bin/nodetouch.js" } }, "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA=="], + + "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], + + "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], + + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + + "tsdown": ["tsdown@0.22.7", "", { "dependencies": { "ansis": "^4.3.1", "cac": "^7.0.0", "defu": "^6.1.7", "empathic": "^2.0.1", "hookable": "^6.1.1", "import-without-cache": "^0.4.0", "obug": "^2.1.3", "picomatch": "^4.0.5", "rolldown": "~1.1.5", "rolldown-plugin-dts": "^0.27.8", "semver": "^7.8.5", "tinyexec": "^1.2.4", "tinyglobby": "^0.2.17", "tree-kill": "^1.2.2", "unconfig-core": "^7.5.0" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "@tsdown/css": "0.22.7", "@tsdown/exe": "0.22.7", "@vitejs/devtools": "*", "publint": "^0.3.8", "tsx": "*", "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", "unplugin-unused": "^0.5.0", "unrun": "*" }, "optionalPeers": ["@arethetypeswrong/core", "@tsdown/css", "@tsdown/exe", "@vitejs/devtools", "publint", "tsx", "typescript", "unplugin-unused", "unrun"], "bin": { "tsdown": "./dist/run.mjs" } }, "sha512-4egbOzc9dxVv/QS+gDV75FIxDIjQQeOnXBlUuikyjmn0ozuc6FW11djJjEEo3vqkuJRygpnKHurnj+Iwftk4VA=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], + + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + + "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], + + "typesense": ["typesense@3.0.6", "", { "dependencies": { "axios": "^1.15.0", "loglevel": "^1.9.2", "tslib": "^2.8.1" }, "peerDependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-d3LL1qOLS8FCRxgAOqH+uDuK+VVA+/HYI1frU9fjYVwOg68mg/dX6XTtZ4yKKAkDCasB/se5uh/ZpMdOz/uJNg=="], + + "typesense-docsearch-cli": ["typesense-docsearch-cli@workspace:packages/docsearch-cli"], + + "typesense-docsearch-core": ["typesense-docsearch-core@workspace:packages/docsearch-core"], + + "typesense-docsearch-css": ["typesense-docsearch-css@workspace:packages/docsearch-css"], + + "typesense-docsearch-modal": ["typesense-docsearch-modal@workspace:packages/docsearch-modal"], + + "typesense-docsearch-react": ["typesense-docsearch-react@workspace:packages/docsearch-react"], + + "typesense-docsearch-sidepanel": ["typesense-docsearch-sidepanel@workspace:packages/docsearch-sidepanel"], + + "typesense-docsearch-sidepanel-js": ["typesense-docsearch-sidepanel-js@workspace:packages/docsearch-sidepanel-js"], + + "typesense-docsearch.js": ["typesense-docsearch.js@workspace:packages/docsearch-js"], + + "typesense-instantsearch-adapter": ["typesense-instantsearch-adapter@3.0.0", "", { "dependencies": { "typesense": "^3.0.0-2" }, "peerDependencies": { "@babel/runtime": "^7.24.1" } }, "sha512-OSvw0SgSMYMWsDKX1bZKKsCS6jyuopbRqoNJy2nr+J4NkwnZPuioyFiiwvTBPwimuFc8+fSNwqvSAyMRius9Lw=="], + + "unconfig-core": ["unconfig-core@7.5.0", "", { "dependencies": { "@quansync/fs": "^1.0.0", "quansync": "^1.0.0" } }, "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w=="], + + "undefsafe": ["undefsafe@2.0.5", "", {}, "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="], + + "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], + + "unicorn-magic": ["unicorn-magic@0.4.0", "", {}, "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw=="], + + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "vite": ["vite@6.4.3", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="], + + "vite-node": ["vite-node@3.0.2", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.0", "es-module-lexer": "^1.6.0", "pathe": "^2.0.1", "vite": "^5.0.0 || ^6.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-hsEQerBAHvVAbv40m3TFQe/lTEbOp7yDpyqMJqr2Tnd+W58+DEYOt+fluQgekOePcsNBmR77lpVAnIU2Xu4SvQ=="], + + "vitest": ["vitest@3.0.2", "", { "dependencies": { "@vitest/expect": "3.0.2", "@vitest/mocker": "3.0.2", "@vitest/pretty-format": "^3.0.2", "@vitest/runner": "3.0.2", "@vitest/snapshot": "3.0.2", "@vitest/spy": "3.0.2", "@vitest/utils": "3.0.2", "chai": "^5.1.2", "debug": "^4.4.0", "expect-type": "^1.1.0", "magic-string": "^0.30.17", "pathe": "^2.0.1", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", "vite-node": "3.0.2", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.0.2", "@vitest/ui": "3.0.2", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-5bzaHakQ0hmVVKLhfh/jXf6oETDBtgPo8tQCHYB+wftNgFJ+Hah67IsWc8ivx4vFL025Ow8UiuTf4W57z4izvQ=="], + + "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], + + "watch": ["watch@1.0.2", "", { "dependencies": { "exec-sh": "^0.2.0", "minimist": "^1.2.0" }, "bin": { "watch": "./cli.js" } }, "sha512-1u+Z5n9Jc1E2c7qDO8SinPoZuHj7FgbgU1olSFoyaklduDvvtX7GMMtlE6OC9FTXq4KvNAOfj6Zu4vI1e9bAKA=="], + + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], + + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], + + "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], + + "ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + + "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], + + "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yargs": ["yargs@17.7.3", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yuku-ast": ["yuku-ast@0.1.7", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" } }, "sha512-2RiMEWv500TixY5rJy6OZd4fSy9WYZKWh6gGbIJ7y7vAGcuCugWOWwOLGaQcRZrXcPUfqtLtvpaJ3SdXtWlhKA=="], + + "yuku-codegen": ["yuku-codegen@0.6.3", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" }, "optionalDependencies": { "@yuku-codegen/binding-darwin-arm64": "0.6.3", "@yuku-codegen/binding-darwin-x64": "0.6.3", "@yuku-codegen/binding-freebsd-x64": "0.6.3", "@yuku-codegen/binding-linux-arm-gnu": "0.6.3", "@yuku-codegen/binding-linux-arm-musl": "0.6.3", "@yuku-codegen/binding-linux-arm64-gnu": "0.6.3", "@yuku-codegen/binding-linux-arm64-musl": "0.6.3", "@yuku-codegen/binding-linux-x64-gnu": "0.6.3", "@yuku-codegen/binding-linux-x64-musl": "0.6.3", "@yuku-codegen/binding-win32-arm64": "0.6.3", "@yuku-codegen/binding-win32-x64": "0.6.3" } }, "sha512-3c9H521tf1RRDu4cNUySfH01sKlALve4HKu2sITk33gLl5HhsvI6ngSuarpWxMPAiJEgqJc/HTvojWQRnYm9/g=="], + + "yuku-parser": ["yuku-parser@0.6.3", "", { "dependencies": { "@yuku-toolchain/types": "0.5.43" }, "optionalDependencies": { "@yuku-parser/binding-darwin-arm64": "0.6.3", "@yuku-parser/binding-darwin-x64": "0.6.3", "@yuku-parser/binding-freebsd-x64": "0.6.3", "@yuku-parser/binding-linux-arm-gnu": "0.6.3", "@yuku-parser/binding-linux-arm-musl": "0.6.3", "@yuku-parser/binding-linux-arm64-gnu": "0.6.3", "@yuku-parser/binding-linux-arm64-musl": "0.6.3", "@yuku-parser/binding-linux-x64-gnu": "0.6.3", "@yuku-parser/binding-linux-x64-musl": "0.6.3", "@yuku-parser/binding-win32-arm64": "0.6.3", "@yuku-parser/binding-win32-x64": "0.6.3" } }, "sha512-iI6uABvvup9mvv8Mcpz7Tp//gehQlvcSnX4A4/0bf9i6X3RVQDuVUZel8jdpljwlF7WrbKsvD19y55Mc6+sKZw=="], + + "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + + "@asamuzakjp/css-color/@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@asamuzakjp/css-color/@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], + + "@asamuzakjp/css-color/@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@babel/helper-compilation-targets/browserslist": ["browserslist@4.28.6", "", { "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001803", "electron-to-chromium": "^1.5.389", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw=="], + + "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@cacheable/memory/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], + + "@cacheable/utils/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], + + "@csstools/css-color-parser/@csstools/css-calc": ["@csstools/css-calc@2.1.4", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ=="], + + "@csstools/css-color-parser/@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.5", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.4" } }, "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ=="], + + "@csstools/css-color-parser/@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.4", "", {}, "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + + "@eslint/eslintrc/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + + "@eslint/eslintrc/import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "@eslint/eslintrc/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "@humanwhocodes/config-array/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "@keyv/bigmap/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], + + "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@manypkg/find-root/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], + + "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "@manypkg/get-packages/@changesets/types": ["@changesets/types@4.1.0", "", {}, "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw=="], + + "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "@manypkg/get-packages/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + + "@quansync/fs/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], + + "@testing-library/jest-dom/aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], + + "@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], + + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "@vitest/snapshot/@vitest/pretty-format": ["@vitest/pretty-format@3.0.2", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-yBohcBw/T/p0/JRgYD+IYcjCmuHzjC3WLAKsVE4/LwiubzZkE8N49/xIQ/KGQwDRA8PaviF8IRO8JMWMngdVVQ=="], + + "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.0.2", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-yBohcBw/T/p0/JRgYD+IYcjCmuHzjC3WLAKsVE4/LwiubzZkE8N49/xIQ/KGQwDRA8PaviF8IRO8JMWMngdVVQ=="], + + "accepts/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "axios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + + "cacheable/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="], + + "caller-callsite/callsites": ["callsites@2.0.0", "", {}, "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="], + + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "cosmiconfig/js-yaml": ["js-yaml@3.15.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], + + "doiuse/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "duplexify/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "eslint/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + + "eslint/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "eslint/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + + "eslint/file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], + + "eslint/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "express/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "global-prefix/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + + "globby/ignore": ["ignore@7.0.6", "", {}, "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw=="], + + "globby/is-path-inside": ["is-path-inside@4.0.0", "", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="], + + "import-fresh/resolve-from": ["resolve-from@3.0.0", "", {}, "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "multimatch/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "qified/hookified": ["hookified@2.2.0", "", {}, "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA=="], + + "read-yaml-file/js-yaml": ["js-yaml@3.15.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], + + "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], + + "send/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "stylelint/cosmiconfig": ["cosmiconfig@9.0.2", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="], + + "stylelint/ignore": ["ignore@7.0.6", "", {}, "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw=="], + + "stylelint-no-unsupported-browser-features/browserslist": ["browserslist@4.28.6", "", { "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001803", "electron-to-chromium": "^1.5.389", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw=="], + + "supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + + "supports-hyperlinks/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + + "table/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "tsdown/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], + + "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + + "type-is/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "unconfig-core/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], + + "vite/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "vite-node/cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "vitest/tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "@eslint/eslintrc/espree/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], + + "@humanwhocodes/config-array/minimatch/brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], + + "@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + + "@manypkg/get-packages/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "@testing-library/jest-dom/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "accepts/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "axios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "cosmiconfig/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "duplexify/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "eslint/file-entry-cache/flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], + + "eslint/minimatch/brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], + + "express/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], + + "multimatch/minimatch/brace-expansion": ["brace-expansion@1.1.16", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw=="], + + "node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "stylelint/cosmiconfig/import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "stylelint/cosmiconfig/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "@eslint/eslintrc/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "@humanwhocodes/config-array/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + + "@testing-library/jest-dom/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "eslint/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "multimatch/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "stylelint/cosmiconfig/import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "@manypkg/find-root/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + } +} diff --git a/bundlesize.config.json b/bundlesize.config.json index b8e8f9d7..6e456483 100644 --- a/bundlesize.config.json +++ b/bundlesize.config.json @@ -2,15 +2,35 @@ "files": [ { "path": "packages/docsearch-css/dist/style.css", - "maxSize": "3 kB" + "maxSize": "8.2 kB" }, { "path": "packages/docsearch-react/dist/umd/index.js", - "maxSize": "20.30 kB" + "maxSize": "160 kB" }, { "path": "packages/docsearch-js/dist/umd/index.js", - "maxSize": "28.20 kB" + "maxSize": "180 kB" + }, + { + "path": "packages/docsearch-js/dist/umd/docsearch.js", + "maxSize": "100 kB" + }, + { + "path": "packages/docsearch-core/dist/umd/index.js", + "maxSize": "3 kB" + }, + { + "path": "packages/docsearch-modal/dist/umd/index.js", + "maxSize": "113.5 kB" + }, + { + "path": "packages/docsearch-sidepanel/dist/umd/index.js", + "maxSize": "2 kB" + }, + { + "path": "packages/docsearch-sidepanel-js/dist/umd/index.js", + "maxSize": "140 kB" } ] } diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 0dac3fe1..00000000 --- a/cypress.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "projectId": "nf9rdc", - "baseUrl": "http://localhost:3000" -} diff --git a/cypress/integration/search/actions.spec.ts b/cypress/integration/search/actions.spec.ts deleted file mode 100644 index d8c109ef..00000000 --- a/cypress/integration/search/actions.spec.ts +++ /dev/null @@ -1,150 +0,0 @@ -/// - -describe('Start', () => { - beforeEach(() => { - cy.visit(Cypress.config().baseUrl!); - }); - - it('Open modal on search button click', () => { - cy.openModal(); - cy.modalIsVisibleAndFocused(); - }); - - it('Open modal with key shortcut on Windows/Linux', () => { - cy.get('body').type('{ctrl}k'); - cy.modalIsVisibleAndFocused(); - }); - - it('Open modal with key shortcut on Windows/Linux when caps lock is on', () => { - cy.get('body').type('{ctrl}K'); - cy.modalIsVisibleAndFocused(); - }); - - it('Open modal with key shortcut on macOS', () => { - cy.get('body').type('{meta}k'); - cy.modalIsVisibleAndFocused(); - }); - - it('Open modal with key shortcut on macOS when caps lock is on', () => { - cy.get('body').type('{meta}K'); - cy.modalIsVisibleAndFocused(); - }); - - it('Open modal with forward slash key shortcut', () => { - cy.get('body').type('/'); - cy.modalIsVisibleAndFocused(); - }); -}); - -describe('End', () => { - beforeEach(() => { - cy.visit(Cypress.config().baseUrl!); - cy.openModal(); - }); - - it('Close modal with Esc key', () => { - cy.closeModal(); - cy.modalIsNotVisible(); - }); - - it('Close modal by clicking outside its container', () => { - cy.get('.DocSearch-Container').click(); - cy.modalIsNotVisible(); - }); - - it('Close modal with key shortcut on Windows/Linux', () => { - cy.get('body').type('{ctrl}k'); - cy.modalIsNotVisible(); - }); - - it('Close modal with key shortcut on macOS', () => { - cy.get('body').type('{meta}k'); - cy.modalIsNotVisible(); - }); -}); - -describe('Search', () => { - beforeEach(() => { - cy.visit(Cypress.config().baseUrl!); - cy.openModal(); - }); - - it('Results are displayed after a query', () => { - cy.typeQueryMatching(); - cy.get('.DocSearch-Hits').should('be.visible'); - }); - - it('Query can be cleared', () => { - cy.typeQueryMatching(); - cy.get('.DocSearch-Reset').click(); - cy.get('.DocSearch-Hits').should('not.exist'); - cy.contains('No recent searches').should('be.visible'); - }); - - it('Keyboard navigation leads to result', () => { - const currentURL = cy.url(); - - cy.typeQueryMatching(); - cy.get('.DocSearch-Input').type('{downArrow}{downArrow}{upArrow}'); - cy.get('.DocSearch-Input').type('{enter}'); - cy.on('url:changed', (newUrl) => { - expect(newUrl).not.equal(currentURL); - }); - }); - - it('Pointer navigation leads to result', () => { - const currentURL = cy.url(); - - cy.typeQueryMatching(); - cy.get('.DocSearch-Hits #docsearch-item-1 > a').click({ force: true }); - cy.on('url:changed', (newUrl) => { - expect(newUrl).not.equal(currentURL); - }); - }); - - it("No results are displayed if query doesn't match", () => { - cy.typeQueryNotMatching(); - cy.contains('No results for').should('be.visible'); - }); -}); - -describe('Recent and Favorites', () => { - beforeEach(() => { - cy.visit(Cypress.config().baseUrl!); - cy.openModal(); - cy.typeQueryMatching(); - cy.get('#docsearch-item-0 > a').click({ force: true }).wait(3000); - cy.openModal(); - cy.contains('Recent').should('be.visible'); - }); - - it('Recent search is displayed after visiting a result', () => { - cy.get('#docsearch-item-0').should('be.visible'); - }); - - it('Recent search can be deleted', () => { - cy.get('#docsearch-item-0') - .find('[title="Remove this search from history"]') - .trigger('click'); - cy.contains('No recent searches').should('be.visible'); - }); - - it('Recent search can be favorited', () => { - cy.get('#docsearch-item-0') - .find('[title="Save this search"]') - .trigger('click'); - cy.contains('Favorite').should('be.visible'); - cy.get('#docsearch-item-0').should('be.visible'); - }); - - it('Favorite can be deleted', () => { - cy.get('#docsearch-item-0') - .find('[title="Save this search"]') - .trigger('click'); - cy.contains('Favorite').should('be.visible'); - cy.get('#docsearch-item-0') - .find('[title="Remove this search from favorites"]') - .trigger('click'); - cy.contains('No recent searches').should('be.visible'); - }); -}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index 03dd3d08..00000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/// -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) - -/** - * @type {Cypress.PluginConfig} - */ - -// eslint-disable-next-line import/no-commonjs -module.exports = (_on, _config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -}; diff --git a/cypress/support/commands.d.ts b/cypress/support/commands.d.ts deleted file mode 100644 index 5972d22b..00000000 --- a/cypress/support/commands.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/// - -declare namespace Cypress { - interface Chainable { - /** - * Ensures the modal is visible and focused. - */ - modalIsVisibleAndFocused: () => void; - /** - * Ensures the modal not visible. - */ - modalIsNotVisible: () => void; - /** - * Toggles the dark mode on the preview website. - */ - darkmode: () => void; - /** - * Opens the DocSearch modal. - */ - openModal: () => void; - /** - * Closes the DocSearch modal. - */ - closeModal: () => void; - /** - * Search for a given query. - */ - search: (query: string) => void; - /** - * Types a query that returns results. - */ - typeQueryMatching: () => void; - /** - * Types a query that returns no results. - */ - typeQueryNotMatching: () => void; - } -} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts deleted file mode 100644 index 1241d7c4..00000000 --- a/cypress/support/commands.ts +++ /dev/null @@ -1,37 +0,0 @@ -Cypress.Commands.add('modalIsVisibleAndFocused', () => { - cy.get('.DocSearch-Modal').should('be.visible'); - cy.get('.DocSearch-Input').should('be.focus'); -}); - -Cypress.Commands.add('modalIsNotVisible', () => { - cy.get('body').should('not.have.class', 'DocSearch--active'); - cy.get('.DocSearch-Modal').should('not.exist'); -}); - -Cypress.Commands.add('darkmode', () => { - cy.get('.react-toggle').click({ force: true }); - cy.get('.react-toggle-screenreader-only').blur(); - cy.get('html.dark').should('be.visible'); -}); - -Cypress.Commands.add('openModal', () => { - cy.get('.DocSearch-Button').should('be.visible').click(); - cy.modalIsVisibleAndFocused(); -}); - -Cypress.Commands.add('closeModal', () => { - cy.get('body').type('{esc}'); - cy.modalIsNotVisible(); -}); - -Cypress.Commands.add('search', (query: string) => { - cy.get('.DocSearch-Input').should('be.visible').type(query); -}); - -Cypress.Commands.add('typeQueryMatching', () => { - cy.search('g'); -}); - -Cypress.Commands.add('typeQueryNotMatching', () => { - cy.search('zzz'); -}); diff --git a/cypress/support/index.ts b/cypress/support/index.ts deleted file mode 100644 index 37a498fb..00000000 --- a/cypress/support/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands'; - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json deleted file mode 100644 index c63986ce..00000000 --- a/cypress/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "types": ["cypress"] - }, - "exclude": [] -} diff --git a/e2e/a11y.test.ts b/e2e/a11y.test.ts new file mode 100644 index 00000000..a237468b --- /dev/null +++ b/e2e/a11y.test.ts @@ -0,0 +1,71 @@ +import { test, expect, gatherA11yViolations } from './fixtures'; + +test.describe('a11y > Modal', () => { + test.beforeEach(async ({ docSearch }) => { + await docSearch.goto(); + await docSearch.waitForLoad(); + }); + + test('Smoke test', async ({ docSearch, axe }, testInfo) => { + await docSearch.openModal(); + + const scanResults = await axe().include('.DocSearch-Container').analyze(); + + await testInfo.attach('a11y-scan-results-modal', { + body: JSON.stringify(scanResults.violations, null, 2), + contentType: 'application/json', + }); + + // 6 is the current number of reported violations + expect( + gatherA11yViolations(scanResults.violations).length + ).toBeLessThanOrEqual(6); + }); + + test('Search results', async ({ docSearch, axe }, testInfo) => { + await docSearch.openModal(); + + await docSearch.typeQueryMatching(); + + await expect(docSearch.hits).toBeVisible(); + + const scanResults = await axe() + .include('#docsearch-hits_docsearch_0-list') + .analyze(); + + await testInfo.attach('a11y-scan-results-modal-search-results', { + body: JSON.stringify(scanResults.violations, null, 2), + contentType: 'application/json', + }); + + // 24 is the current number of reported violations + expect( + gatherA11yViolations(scanResults.violations).length + ).toBeLessThanOrEqual(24); + }); +}); + +test.describe('a11y > Sidepanel', () => { + test.beforeEach(async ({ docSearch, sidepanel }) => { + await docSearch.goto(); + await sidepanel.waitForLoad(); + }); + + test('Smoke test', async ({ sidepanel, axe }, testInfo) => { + await sidepanel.openSidepanel(); + + const scanResults = await axe() + .include('.DocSearch-Sidepanel-Container') + .analyze(); + + await testInfo.attach('a11y-scan-results-sidepanel', { + body: JSON.stringify(scanResults.violations, null, 2), + contentType: 'application/json', + }); + + // 4 is the current number of reported violations + expect( + gatherA11yViolations(scanResults.violations).length + ).toBeLessThanOrEqual(4); + }); +}); diff --git a/e2e/askai.spec.ts b/e2e/askai.spec.ts new file mode 100644 index 00000000..3919555b --- /dev/null +++ b/e2e/askai.spec.ts @@ -0,0 +1,53 @@ +import { test, expect } from './fixtures'; + +test.describe.skip('Ask AI', () => { + test.beforeEach(async ({ docSearch }) => { + await docSearch.goto(); + await docSearch.openModal(); + }); + + test('Results are displayed after a query', async ({ docSearch }) => { + await docSearch.typeQueryMatching(); + await expect(docSearch.hits).toBeVisible(); + }); + + test('Shows Ask AI as a hit on search', async ({ docSearch, page }) => { + await docSearch.typeQueryMatching(); + await expect(page.locator('.DocSearch-AskAi-Section')).toBeVisible(); + }); + + test('Opens Ask AI on enter key', async ({ docSearch, page }) => { + await docSearch.typeQueryMatching(); + await page.keyboard.press('Enter'); + await expect(page.locator('.DocSearch-AskAiScreen')).toBeVisible(); + }); + + test('Opens Ask AI on click', async ({ docSearch, page }) => { + await docSearch.typeQueryMatching(); + await page.locator('#docsearch-askAI-item-0').click(); + await expect(page.locator('.DocSearch-AskAiScreen')).toBeVisible(); + }); + + test('Streams response after query', async ({ docSearch, page }) => { + await docSearch.goToAskAi(); + await expect(page.locator('.DocSearch-AskAiScreen-Query')).toBeVisible(); + await expect(page.locator('.DocSearch-AskAiScreen-Message')).toBeVisible(); + }); + + test('Copy button copies the response to the clipboard', async ({ + docSearch, + page, + context, + }) => { + // Grant clipboard permissions + await context.grantPermissions(['clipboard-read', 'clipboard-write']); + + await docSearch.goToAskAi(); + await expect(page.locator('.DocSearch-AskAiScreen-Response')).toBeVisible(); + + const clipboardText = await page.evaluate(() => + navigator.clipboard.readText() + ); + expect(clipboardText.length).toBeGreaterThan(0); + }); +}); diff --git a/e2e/fixtures.ts b/e2e/fixtures.ts new file mode 100644 index 00000000..d458cb86 --- /dev/null +++ b/e2e/fixtures.ts @@ -0,0 +1,150 @@ +// oxlint-disable max-classes-per-file react-hooks/rules-of-hooks +import AxeBuilder from '@axe-core/playwright'; +import { + test as base, + expect, + type Page, + type Locator, +} from '@playwright/test'; + +export class DocSearchPage { + readonly page: Page; + readonly searchButton: Locator; + readonly modal: Locator; + readonly input: Locator; + readonly hits: Locator; + readonly clearButton: Locator; + readonly firstHit: Locator; + + constructor(page: Page) { + this.page = page; + this.searchButton = page.locator('.DocSearch-Button'); + this.modal = page.locator('.DocSearch-Modal'); + this.input = page.locator('.DocSearch-Input'); + this.hits = page.locator('.DocSearch-Hits').first(); + this.clearButton = page.locator('.DocSearch-Clear'); + this.firstHit = page + .locator('#docsearch-hits_docsearch_0-list .DocSearch-Hit a') + .first(); + } + + async goto(): Promise { + await this.page.goto('/docs/what-is-docsearch'); + } + + async waitForLoad(): Promise { + await expect(this.searchButton).toBeVisible({ timeout: 10000 }); + } + + async openModal(): Promise { + await this.searchButton.click(); + await this.expectModalVisibleAndFocused(); + } + + async closeModal(): Promise { + await this.page.keyboard.press('Escape'); + await this.expectModalNotVisible(); + } + + async expectModalVisibleAndFocused(): Promise { + await expect(this.modal).toBeVisible({ timeout: 10000 }); + await expect(this.input).toBeFocused(); + } + + async expectModalNotVisible(): Promise { + await expect(this.page.locator('body')).not.toHaveClass( + /DocSearch--active/ + ); + await expect(this.modal).not.toBeVisible(); + } + + async search(query: string): Promise { + await this.input.fill(query); + } + + async typeQueryMatching(): Promise { + await this.search('g'); + } + + async typeQueryNotMatching(): Promise { + await this.search('zzz'); + } + + async clearSearch(): Promise { + await this.input.clear(); + } + + async clickFirstHit(): Promise { + await expect(this.hits).toBeVisible(); + await this.firstHit.click({ force: true }); + } + + async enableDarkMode(): Promise { + await this.page.locator('.react-toggle').click({ force: true }); + await this.page.locator('.react-toggle-screenreader-only').blur(); + await expect(this.page.locator('html.dark')).toBeVisible(); + } + + async goToAskAi(): Promise { + await this.typeQueryMatching(); + await this.page.locator('#docsearch-AskAi-Section').click(); + } +} + +export class SidepanelPage { + readonly page: Page; + readonly sidepanelButton: Locator; + readonly sidepanel: Locator; + + constructor(page: Page) { + this.page = page; + this.sidepanelButton = page.locator('.DocSearch-SidepanelButton'); + this.sidepanel = page.locator('.DocSearch-Sidepanel-Container'); + } + + async waitForLoad(): Promise { + await expect(this.sidepanelButton).toBeVisible({ timeout: 10000 }); + } + + async openSidepanel(): Promise { + await this.sidepanelButton.click(); + await expect(this.sidepanel).toHaveClass(/is-open/, { + timeout: 10000, + }); + } +} + +export const test = base.extend<{ + docSearch: DocSearchPage; + sidepanel: SidepanelPage; + axe: () => AxeBuilder; +}>({ + docSearch: async ({ page }, use) => { + const docSearch = new DocSearchPage(page); + await use(docSearch); + }, + sidepanel: async ({ page }, use) => { + const sidepanel = new SidepanelPage(page); + await use(sidepanel); + }, + axe: async ({ page }, use) => { + const makeBuilder = () => + new AxeBuilder({ page }).withTags([ + 'wcag2a', + 'wcag2aa', + 'wcag21a', + 'wcag21aa', + 'wcag22a', + 'wcag22aa', + ]); + await use(makeBuilder); + }, +}); + +type AxeScanResults = Awaited>; + +function gatherA11yViolations(violations: AxeScanResults['violations']) { + return violations.flatMap((v) => v.nodes); +} + +export { expect, gatherA11yViolations }; diff --git a/e2e/search.spec.ts b/e2e/search.spec.ts new file mode 100644 index 00000000..25dfe71b --- /dev/null +++ b/e2e/search.spec.ts @@ -0,0 +1,221 @@ +import { test, expect } from './fixtures'; + +const MAX_KEYBOARD_NAVIGATION_STEPS = 10; + +test.describe('Start', () => { + test.beforeEach(async ({ docSearch }) => { + await docSearch.goto(); + await docSearch.waitForLoad(); + }); + + test('Open modal on search button click', async ({ docSearch, page }) => { + await docSearch.openModal(); + + // check that the scrollbar offset is compensated + await expect(page.locator('body')).toHaveCSS('overflow', 'hidden'); + await expect(docSearch.modal).toBeVisible(); + }); + + test('Open modal with key shortcut on Windows/Linux', async ({ + docSearch, + page, + }) => { + await page.keyboard.press('Control+k'); + await docSearch.expectModalVisibleAndFocused(); + }); + + test('Open modal with key shortcut on Windows/Linux when caps lock is on', async ({ + docSearch, + page, + }) => { + await page.keyboard.press('Control+K'); + await docSearch.expectModalVisibleAndFocused(); + }); + + test('Open modal with key shortcut on macOS', async ({ docSearch, page }) => { + await page.keyboard.press('Meta+k'); + await docSearch.expectModalVisibleAndFocused(); + }); + + test('Open modal with key shortcut on macOS when caps lock is on', async ({ + docSearch, + page, + }) => { + await page.keyboard.press('Meta+K'); + await docSearch.expectModalVisibleAndFocused(); + }); + + test('Open modal with forward slash key shortcut', async ({ + docSearch, + page, + }) => { + await page.waitForTimeout(1000); + await page.keyboard.press('/'); + await docSearch.expectModalVisibleAndFocused(); + }); +}); + +test.describe('End', () => { + test.beforeEach(async ({ docSearch }) => { + await docSearch.goto(); + await docSearch.openModal(); + }); + + test('Close modal with Esc key', async ({ docSearch }) => { + await docSearch.closeModal(); + }); + + test('Close modal by clicking outside its container', async ({ + docSearch, + page, + }) => { + await page.mouse.click(0, 0); + await docSearch.expectModalNotVisible(); + }); + + test('Close modal with key shortcut on Windows/Linux', async ({ + docSearch, + page, + }) => { + await page.keyboard.press('Control+k'); + await docSearch.expectModalNotVisible(); + }); + + test('Close modal with key shortcut on macOS', async ({ + docSearch, + page, + }) => { + await page.keyboard.press('Meta+k'); + await docSearch.expectModalNotVisible(); + }); +}); + +test.describe('Search', () => { + test.beforeEach(async ({ docSearch }) => { + await docSearch.goto(); + await docSearch.openModal(); + }); + + test('Results are displayed after a query', async ({ docSearch }) => { + await docSearch.typeQueryMatching(); + await expect(docSearch.hits).toBeVisible(); + }); + + test('Query can be cleared', async ({ docSearch }) => { + await docSearch.typeQueryMatching(); + await docSearch.clearButton.click(); + await expect(docSearch.hits).not.toBeVisible(); + }); + + test('Keyboard navigation leads to result', async ({ docSearch, page }) => { + const initialURL = page.url(); + + await docSearch.typeQueryMatching(); + await expect(docSearch.hits).toBeVisible(); + const firstHitOption = page + .locator('#docsearch-hits_docsearch_0-list') + .getByRole('option') + .first(); + const firstHitOptionId = await firstHitOption.getAttribute('id'); + + expect(firstHitOptionId).not.toBeNull(); + + // Prompt suggestions precede document hits, but the number is not stable. + for (let index = 0; index < MAX_KEYBOARD_NAVIGATION_STEPS; index++) { + if ( + (await docSearch.input.getAttribute('aria-activedescendant')) === + firstHitOptionId + ) { + break; + } + + await page.keyboard.press('ArrowDown'); + } + + await expect(docSearch.input).toHaveAttribute( + 'aria-activedescendant', + firstHitOptionId! + ); + await expect(firstHitOption).toHaveAttribute('aria-selected', 'true'); + await page.keyboard.press('Enter'); + + await expect(page).not.toHaveURL(initialURL, { timeout: 10000 }); + }); + + test('Pointer navigation leads to result', async ({ docSearch, page }) => { + const initialURL = page.url(); + + await docSearch.typeQueryMatching(); + await docSearch.clickFirstHit(); + + await expect(page).not.toHaveURL(initialURL); + }); + + test("No results are displayed if query doesn't match", async ({ + docSearch, + page, + }) => { + await docSearch.typeQueryNotMatching(); + await expect(page.getByText('No results found for')).toBeVisible(); + }); + + test('Should not refer to Recent/Favorite in aria-controls', async ({ + docSearch, + }) => { + await expect(docSearch.input).not.toHaveAttribute('aria-controls'); + }); +}); + +test.describe('Recent and Favorites', () => { + test.beforeEach(async ({ docSearch, page }) => { + await docSearch.goto(); + await docSearch.openModal(); + await docSearch.typeQueryMatching(); + await docSearch.clickFirstHit(); + await page.waitForTimeout(1000); + await docSearch.openModal(); + await expect(page.getByText('Recent')).toBeVisible(); + }); + + test('Recent search is displayed after visiting a result', async ({ + docSearch, + page, + }) => { + await docSearch.clearSearch(); + await expect( + page.locator('#docsearch-recentSearches-item-0') + ).toBeVisible(); + }); + + test('Recent search can be deleted', async ({ docSearch, page }) => { + await page + .locator('#docsearch-recentSearches-item-0') + .locator('[title="Remove this search from history"]') + .click(); + await expect(docSearch.hits).not.toBeVisible(); + }); + + test('Recent search can be pinned', async ({ page }) => { + await page + .locator('#docsearch-recentSearches-item-0') + .locator('[title="Pin this search"]') + .click(); + await expect(page.getByText('Pinned')).toBeVisible(); + await expect( + page.locator('#docsearch-favoriteSearches-item-0') + ).toBeVisible(); + }); + + test('Pinned can be deleted', async ({ docSearch, page }) => { + await page + .locator('#docsearch-recentSearches-item-0') + .locator('[title="Pin this search"]') + .click(); + await expect(page.getByText('Pinned')).toBeVisible(); + await page + .locator('#docsearch-favoriteSearches-item-0') + .locator('[title="Remove this saved search"]') + .click(); + await expect(docSearch.hits).not.toBeVisible(); + }); +}); diff --git a/examples/js-demo/index.html b/examples/demo-js/index.html similarity index 64% rename from examples/js-demo/index.html rename to examples/demo-js/index.html index 673b9d91..16272347 100644 --- a/examples/js-demo/index.html +++ b/examples/demo-js/index.html @@ -1,10 +1,9 @@ + + - - - DocSearch v3 - Vanilla JavaScript @@ -13,8 +12,10 @@

DocSearch v3 - Vanilla JavaScript

+ +
- + diff --git a/examples/demo-js/package.json b/examples/demo-js/package.json new file mode 100644 index 00000000..d3f0079c --- /dev/null +++ b/examples/demo-js/package.json @@ -0,0 +1,20 @@ +{ + "name": "@docsearch/js-example", + "description": "DocSearch v4 Vanilla JavaScript example", + "version": "4.6.0", + "private": true, + "license": "MIT", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "typesense-docsearch-css": "workspace:*", + "typesense-docsearch-sidepanel-js": "workspace:*", + "typesense-docsearch.js": "workspace:*" + }, + "devDependencies": { + "vite": "^6.0.7" + } +} diff --git a/packages/website/static/img/favicon.ico b/examples/demo-js/public/favicon.ico similarity index 100% rename from packages/website/static/img/favicon.ico rename to examples/demo-js/public/favicon.ico diff --git a/examples/demo-js/src/app.css b/examples/demo-js/src/app.css new file mode 100644 index 00000000..9afa0a53 --- /dev/null +++ b/examples/demo-js/src/app.css @@ -0,0 +1,5 @@ +body { + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, + sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; +} diff --git a/examples/demo-js/src/main.ts b/examples/demo-js/src/main.ts new file mode 100644 index 00000000..6ee93d27 --- /dev/null +++ b/examples/demo-js/src/main.ts @@ -0,0 +1,128 @@ +import sidepanel, { + type SidepanelInstance, +} from 'typesense-docsearch-sidepanel-js'; +import docsearch, { type DocSearchInstance } from 'typesense-docsearch.js'; + +import './app.css'; +import 'typesense-docsearch-css/dist/style.css'; +import 'typesense-docsearch-css/dist/sidepanel.css'; + +declare global { + interface Window { + docsearch?: DocSearchInstance; + sidepanel?: SidepanelInstance; + } +} + +function logDocSearchState(instance: DocSearchInstance, label: string): void { + // eslint-disable-next-line no-console + console.log(`[demo-js] ${label}`, { + isReady: instance.isReady, + isOpen: instance.isOpen, + }); +} + +function logSidepanelState(instance: SidepanelInstance, label: string): void { + // eslint-disable-next-line no-console + console.log(`[demo-js] ${label}`, { + isReady: instance.isReady, + isOpen: instance.isOpen, + }); +} + +const typesenseServerConfig = { + apiKey: 'xyz', + nodes: [{ host: 'localhost', port: 8108, protocol: 'http' as const }], +}; + +let docsearchInstance: DocSearchInstance | undefined = undefined; +let sidepanelInstance: SidepanelInstance | undefined = undefined; + +sidepanelInstance = sidepanel({ + container: '#docsearch-sidepanel', + typesenseCollectionName: 'docsearch', + typesenseServerConfig, + askAi: { + conversationModelId: 'askAIDemo', + }, + onReady: () => { + // eslint-disable-next-line no-console + console.log('[demo-js] sidepanel onReady()'); + }, + onOpen: () => { + // eslint-disable-next-line no-console + console.log('[demo-js] sidepanel onOpen()'); + docsearchInstance?.close(); + }, + onClose: () => { + // eslint-disable-next-line no-console + console.log('[demo-js] sidepanel onClose()'); + }, +}); + +window.sidepanel = sidepanelInstance; + +// eslint-disable-next-line no-console +console.log('[demo-js] sidepanel instance exposed on window.sidepanel'); +// eslint-disable-next-line no-console +console.log('[demo-js] sidepanel try:', { + open: 'window.sidepanel?.open()', + openWithMessage: "window.sidepanel?.open({ query: 'Hello from demo-js' })", + close: 'window.sidepanel?.close()', + destroy: 'window.sidepanel?.destroy()', +}); +logSidepanelState(sidepanelInstance, 'sidepanel initial state'); + +docsearchInstance = docsearch({ + container: '#docsearch', + typesenseCollectionName: 'docsearch', + typesenseServerConfig, + typesenseSearchParameters: {}, + askAi: { + conversationModelId: 'askAIDemo', + }, + interceptAskAiEvent: (initialMessage) => { + docsearchInstance?.close(); + sidepanelInstance.open(initialMessage); + return true; + }, + onReady: () => { + // eslint-disable-next-line no-console + console.log('[demo-js] docsearch onReady()'); + }, + onOpen: () => { + // eslint-disable-next-line no-console + console.log('[demo-js] docsearch onOpen()'); + sidepanelInstance.close(); + }, + onClose: () => { + // eslint-disable-next-line no-console + console.log('[demo-js] docsearch onClose()'); + }, + resultsFooterComponent: ({ state }, { html }) => { + return html` + + `; + }, +}); + +// Expose instance +window.docsearch = docsearchInstance; + +// eslint-disable-next-line no-console +console.log('[demo-js] docsearch instance exposed on window.docsearch'); +// eslint-disable-next-line no-console +console.log('[demo-js] docsearch try:', { + open: 'window.docsearch?.open()', + close: 'window.docsearch?.close()', + openAskAi: "window.docsearch?.openAskAi({ query: 'Hello from demo-js' })", + destroy: 'window.docsearch?.destroy()', +}); +logDocSearchState(docsearchInstance, 'docsearch initial state'); diff --git a/examples/demo-js/src/vite-env.d.ts b/examples/demo-js/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/demo-js/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/demo-js/tsconfig.json b/examples/demo-js/tsconfig.json new file mode 100644 index 00000000..0cebfe0a --- /dev/null +++ b/examples/demo-js/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/examples/demo-react/index.html b/examples/demo-react/index.html new file mode 100644 index 00000000..c4934212 --- /dev/null +++ b/examples/demo-react/index.html @@ -0,0 +1,14 @@ + + + + + + + DocSearch v4 - Ask AI + + + +
+ + + diff --git a/examples/demo-react/package.json b/examples/demo-react/package.json new file mode 100644 index 00000000..0585990c --- /dev/null +++ b/examples/demo-react/package.json @@ -0,0 +1,28 @@ +{ + "name": "@docsearch/react-example", + "description": "DocSearch v4 React example", + "version": "4.6.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@algolia/autocomplete-core": "1.19.2", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "typesense-docsearch-core": "workspace:*", + "typesense-docsearch-css": "workspace:*", + "typesense-docsearch-modal": "workspace:*", + "typesense-docsearch-react": "workspace:*", + "typesense-docsearch-sidepanel": "workspace:*" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "typescript": "5.7.3", + "vite": "^6.0.7" + } +} diff --git a/examples/demo/favicon.png b/examples/demo-react/public/favicon.ico similarity index 100% rename from examples/demo/favicon.png rename to examples/demo-react/public/favicon.ico diff --git a/examples/demo-react/src/App.css b/examples/demo-react/src/App.css new file mode 100644 index 00000000..915ed2a0 --- /dev/null +++ b/examples/demo-react/src/App.css @@ -0,0 +1,264 @@ +@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&family=TikTok+Sans:opsz,wght@12..36,300..900&display=swap'); + +/* reset and base styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.body-container { + --demo-background: radial-gradient( + 125% 125% at 50% 90%, + #fff 40%, + #7c3aed 100% + ); + --demo-border: rgba(255, 255, 255, 0.2); + --demo-card-background: #fff; + --demo-card-shadow: rgba(0, 0, 0, 0.1); + --demo-docsearch-button-background: #f7fafc; + --demo-docsearch-button-border: #e2e8f0; + --demo-docsearch-button-hover-border: #667eea; + --demo-docsearch-button-hover-shadow: rgba(102, 126, 234, 0.15); + --demo-docsearch-button-placeholder: #718096; + --demo-divider: #e2e8f0; + --demo-text-primary: #111; + --demo-text-secondary: #666; + --demo-theme-button-background: #fff; + --demo-theme-button-border: #d6d6e7; + --demo-theme-button-hover-border: #7c3aed; + min-height: 100vh; + background: var(--demo-background); + color: var(--demo-text-primary); +} + +.body-container[data-theme='dark'] { + --demo-background: radial-gradient( + 125% 125% at 50% 90%, + #161025 40%, + #4c1d95 100% + ); + --demo-border: rgba(255, 255, 255, 0.1); + --demo-card-background: #1f1b2e; + --demo-card-shadow: rgba(0, 0, 0, 0.35); + --demo-docsearch-button-background: #15111f; + --demo-docsearch-button-border: #3b3550; + --demo-docsearch-button-hover-border: #a78bfa; + --demo-docsearch-button-hover-shadow: rgba(167, 139, 250, 0.2); + --demo-docsearch-button-placeholder: #c4b5fd; + --demo-divider: #3b3550; + --demo-text-primary: #f8f7ff; + --demo-text-secondary: #c9c3dc; + --demo-theme-button-background: #2d2540; + --demo-theme-button-border: #5b4a78; + --demo-theme-button-hover-border: #c4b5fd; +} + +body { + font-family: 'TikTok Sans', 'sans-serif'; + min-height: 100vh; + line-height: 1.6; +} + +/* container styles */ +.app-container { + max-width: 1024px; + margin: 0 auto; + padding: 2rem 1rem; + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; +} + +/* header styles */ +.app-header { + text-align: center; + margin-block-end: 3rem; +} + +.app-title { + font-size: 2rem; + font-weight: 700; + color: var(--demo-text-primary); + margin-block-end: 0; +} + +.app-subtitle { + font-size: 1rem; + color: var(--demo-text-secondary); + font-weight: 400; +} + +.theme-switcher { + margin-block-start: 1rem; + border: 1px solid var(--demo-theme-button-border); + border-radius: 100vw; + background: var(--demo-theme-button-background); + color: var(--demo-text-primary); + cursor: pointer; + font: inherit; + display: inline-flex; + align-items: center; + justify-content: center; + inline-size: 2.5rem; + block-size: 2.5rem; + position: absolute; + top: 2rem; + right: 4rem; +} + +.theme-switcher svg { + inline-size: 1.25rem; + block-size: 1.25rem; +} + +.theme-switcher:hover { + border-color: var(--demo-theme-button-hover-border); +} + +main { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(3, 1fr); + gap: 8px; +} + +/* demo section styles */ +.demo-section { + background: var(--demo-card-background); + border-radius: 16px; + padding: 2rem; + box-shadow: 0 10px 30px var(--demo-card-shadow); + backdrop-filter: blur(10px); + border: 1px solid var(--demo-border); +} + +.section-description { + text-align: center; + color: var(--demo-text-secondary); + font-family: 'Source Code Pro', monospace; + font-size: 0.9rem; + margin-block-end: 1rem; + margin-block-start: -1rem; + opacity: 0.8; +} + +.ai-powered { + text-decoration: underline; + text-decoration-color: #f2e8ff; + text-underline-offset: 4px; + text-decoration-thickness: 2px; +} + +/* search component wrapper */ +.search-wrapper, +.default-wrapper { + display: flex; + justify-content: center; + align-items: center; + min-height: 60px; +} + +.search-wrapper.column { + flex-direction: column; + gap: 1rem; +} + +/* divider styles */ +.divider { + border: none; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + var(--demo-divider), + transparent + ); + margin: 0; +} + +/* responsive design */ +@media (max-width: 768px) { + .app-container { + padding: 1rem; + } + + .app-title { + font-size: 2.5rem; + } + + .demo-section { + padding: 1.5rem; + } + .section-description { + font-size: 0.8rem; + } +} + +/* docsearch button customization */ +.search-wrapper .DocSearch-Button { + margin: 0 !important; + background: var(--demo-docsearch-button-background) !important; + border: 2px solid var(--demo-docsearch-button-border) !important; + border-radius: 12px !important; + padding: 12px 16px !important; + font-size: 16px !important; + transition: all 0.2s ease !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important; +} + +@media (prefers-reduced-motion: reduce) { + .search-wrapper .DocSearch-Button { + transition-duration: 0s; + } +} + +.search-wrapper .DocSearch-Button:hover, +.search-wrapper .DocSearch-Button:focus-visible { + border-color: var(--demo-docsearch-button-hover-border) !important; + box-shadow: 0 4px 12px var(--demo-docsearch-button-hover-shadow) !important; + transform: translateY(-1px) !important; +} + +.search-wrapper .DocSearch-Button-Placeholder { + color: var(--demo-docsearch-button-placeholder) !important; +} + +/* add subtle animations */ +.demo-section { + animation: fadeInUp 0.6s ease forwards; +} + +.demo-section:nth-child(2) { + animation-delay: 0.1s; +} + +.demo-section:nth-child(3) { + animation-delay: 0.2s; +} + +@media (prefers-reduced-motion: reduce) { + .demo-section { + animation-duration: 0s; + } + + .demo-section:nth-child(2) { + animation-delay: 0; + } + + .demo-section:nth-child(3) { + animation-delay: 0; + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/examples/demo-react/src/App.tsx b/examples/demo-react/src/App.tsx new file mode 100644 index 00000000..3a510f5f --- /dev/null +++ b/examples/demo-react/src/App.tsx @@ -0,0 +1,127 @@ +import { useState, type JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { version } from 'typesense-docsearch-react'; + +import './App.css'; +import 'typesense-docsearch-css/dist/style.css'; +import 'typesense-docsearch-css/dist/sidepanel.css'; + +import Basic from './examples/basic'; +import BasicAskAI from './examples/basic-askai'; +import Composable from './examples/composable'; +import Default from './examples/default'; +import DynamicImportModal from './examples/dynamic-import-modal'; +import BasicHybrid from './examples/hybrid'; +import MultiIndex from './examples/multi-index'; +import WHitComponent from './examples/w-hit-component'; +import WTransformItems from './examples/w-hit-transformItems'; +import WResultsFooter from './examples/w-results-footer'; +import { MoonIcon, SunIcon } from './icons'; + +export type DemoTheme = 'dark' | 'light'; + +function App(): JSX.Element { + const [theme, setTheme] = useState('light'); + + const toggleTheme = (): void => { + setTheme((currentTheme) => (currentTheme === 'light' ? 'dark' : 'light')); + }; + + return ( +
+
+
+

DocSearch v{version}

+

+ Experience the power of intelligent documentation search +

+ +
+ +
+
+

default

+
+ +
+
+ +
+

basic search functionality

+
+ +
+
+ +
+

+ search with ask ai integration +

+
+ +
+
+ +
+

custom hit rendering

+
+ +
+
+ +
+

+ transform items before rendering +

+
+ +
+
+ +
+

multi index search

+
+ +
+
+ +
+

composable

+
+ +
+
+ +
+

dynamically imported modal

+
+ +
+
+ +
+

results footer component

+
+ +
+
+ +
+

sidepanel hybrid

+
+ +
+
+
+
+
+ ); +} + +export default App; diff --git a/examples/demo-react/src/config.ts b/examples/demo-react/src/config.ts new file mode 100644 index 00000000..ef5992f7 --- /dev/null +++ b/examples/demo-react/src/config.ts @@ -0,0 +1,12 @@ +export const typesenseServerConfig = { + apiKey: 'xyz', + nodes: [{ host: 'localhost', port: 8108, protocol: 'http' as const }], +}; + +export const defaultCollection = 'docsearch'; + +export const defaultSearchParameters = {}; + +export const defaultAskAi = { + conversationModelId: 'askAIDemo', +}; diff --git a/examples/demo-react/src/examples/basic-askai.tsx b/examples/demo-react/src/examples/basic-askai.tsx new file mode 100644 index 00000000..be1d2a71 --- /dev/null +++ b/examples/demo-react/src/examples/basic-askai.tsx @@ -0,0 +1,44 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearchAI } from 'typesense-docsearch-react'; + +import type { DemoTheme } from '../App'; +import { + defaultAskAi, + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +export default function BasicAskAI({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + ); +} diff --git a/examples/demo-react/src/examples/basic.tsx b/examples/demo-react/src/examples/basic.tsx new file mode 100644 index 00000000..80123bd0 --- /dev/null +++ b/examples/demo-react/src/examples/basic.tsx @@ -0,0 +1,28 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearch } from 'typesense-docsearch-react'; + +import type { DemoTheme } from '../App'; +import { + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +export default function Basic({ theme }: { theme: DemoTheme }): JSX.Element { + return ( + + ); +} diff --git a/examples/demo-react/src/examples/composable.tsx b/examples/demo-react/src/examples/composable.tsx new file mode 100644 index 00000000..607f805b --- /dev/null +++ b/examples/demo-react/src/examples/composable.tsx @@ -0,0 +1,33 @@ +import { type JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearch } from 'typesense-docsearch-core'; +import { + DocSearchButton, + DocSearchAskAiModal, +} from 'typesense-docsearch-modal'; + +import type { DemoTheme } from '../App'; +import { + defaultAskAi, + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +export default function Composable({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + + + + ); +} diff --git a/examples/demo-react/src/examples/default.tsx b/examples/demo-react/src/examples/default.tsx new file mode 100644 index 00000000..78e67c19 --- /dev/null +++ b/examples/demo-react/src/examples/default.tsx @@ -0,0 +1,25 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearch } from 'typesense-docsearch-react'; + +import type { DemoTheme } from '../App'; +import { + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +export default function DefaultExperience({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + ); +} diff --git a/examples/demo-react/src/examples/dynamic-import-modal.tsx b/examples/demo-react/src/examples/dynamic-import-modal.tsx new file mode 100644 index 00000000..1e0456e3 --- /dev/null +++ b/examples/demo-react/src/examples/dynamic-import-modal.tsx @@ -0,0 +1,126 @@ +import { useCallback, useRef, useState, type JSX } from 'react'; +import { createPortal } from 'react-dom'; +/* eslint-disable react/react-in-jsx-scope */ +import { useDocSearchKeyboardEvents } from 'typesense-docsearch-core/useDocSearchKeyboardEvents'; +import type { DocSearchAskAiModal as DocSearchAskAiModalType } from 'typesense-docsearch-react/askaiModal'; +import { DocSearchButton } from 'typesense-docsearch-react/button'; + +import type { DemoTheme } from '../App'; +import { + defaultAskAi, + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +let DocSearchModal: typeof DocSearchAskAiModalType | null = null; + +function importDocSearchModalIfNeeded(): Promise { + if (DocSearchModal) { + return Promise.resolve(); + } + // eslint-disable-next-line import/dynamic-import-chunkname + return Promise.all([import('typesense-docsearch-react/askaiModal')]).then( + ([{ DocSearchAskAiModal: Modal }]) => { + DocSearchModal = Modal; + } + ); +} + +function DocSearch({ theme }: { theme: DemoTheme }): JSX.Element { + const [isOpen, setIsOpen] = useState(false); + const [initialQuery, setInitialQuery] = useState( + undefined + ); + const [isAskAiActive, setIsAskAiActive] = useState(false); + const searchContainer = useRef(null); + const searchButtonRef = useRef(null); + + const prepareSearchContainer = useCallback(() => { + if (!searchContainer.current) { + const divElement = document.createElement('div'); + searchContainer.current = divElement; + document.body.insertBefore(divElement, document.body.firstChild); + } + }, []); + + const openModal = useCallback(() => { + prepareSearchContainer(); + importDocSearchModalIfNeeded().then(() => setIsOpen(true)); + }, [prepareSearchContainer]); + + const closeModal = useCallback(() => { + setIsOpen(false); + searchButtonRef.current?.focus(); + setInitialQuery(undefined); + }, []); + + const handleInput = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'f' && (event.metaKey || event.ctrlKey)) { + // ignore browser's ctrl+f + return; + } + // prevents duplicate key insertion in the modal input + event.preventDefault(); + setInitialQuery(event.key); + openModal(); + }, + [openModal] + ); + + const toggleAskAi = (active: boolean): void => { + setIsAskAiActive(active); + }; + + useDocSearchKeyboardEvents({ + isOpen, + onOpen: openModal, + onClose: closeModal, + onInput: handleInput, + searchButtonRef, + isAskAiActive, + onAskAiToggle: toggleAskAi, + }); + + return ( + <> + + + {isOpen && + DocSearchModal && + searchContainer.current && + createPortal( + , + searchContainer.current + )} + + ); +} + +export default function DynamicImportModal({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ; +} diff --git a/examples/demo-react/src/examples/hybrid.tsx b/examples/demo-react/src/examples/hybrid.tsx new file mode 100644 index 00000000..99e3ca20 --- /dev/null +++ b/examples/demo-react/src/examples/hybrid.tsx @@ -0,0 +1,41 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearch } from 'typesense-docsearch-core'; +import { + DocSearchButton, + DocSearchAskAiModal, +} from 'typesense-docsearch-modal'; +import { Sidepanel, SidepanelButton } from 'typesense-docsearch-sidepanel'; + +import type { DemoTheme } from '../App'; +import { + defaultAskAi, + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +export default function BasicHybrid({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + + + + + + + ); +} diff --git a/examples/demo-react/src/examples/sidepanel.tsx b/examples/demo-react/src/examples/sidepanel.tsx new file mode 100644 index 00000000..e165d6c7 --- /dev/null +++ b/examples/demo-react/src/examples/sidepanel.tsx @@ -0,0 +1,29 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearch } from 'typesense-docsearch-core'; +import { SidepanelButton, Sidepanel } from 'typesense-docsearch-sidepanel'; + +import type { DemoTheme } from '../App'; +import { + defaultAskAi, + defaultCollection, + typesenseServerConfig, +} from '../config'; + +export default function SidepanelExample({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + + + + ); +} diff --git a/examples/demo-react/src/examples/w-hit-component.tsx b/examples/demo-react/src/examples/w-hit-component.tsx new file mode 100644 index 00000000..6f27c846 --- /dev/null +++ b/examples/demo-react/src/examples/w-hit-component.tsx @@ -0,0 +1,151 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearch } from 'typesense-docsearch-react'; + +import type { DemoTheme } from '../App'; +import { + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +function CustomHit({ hit }: { hit: any }): JSX.Element { + // Typesense returns flat `hierarchy.lvlN` keys, Algolia a nested object. + const lvl1 = hit['hierarchy.lvl1'] ?? hit.hierarchy?.lvl1; + const lvl2 = hit['hierarchy.lvl2'] ?? hit.hierarchy?.lvl2; + + return ( + { + e.currentTarget.style.backgroundColor = '#f8f9fa'; + }} + onMouseLeave={(e) => { + e.currentTarget.style.backgroundColor = 'transparent'; + }} + > +
+
+ {/* oxlint-disable-next-line no-nested-ternary */} + {hit.type === 'lvl1' + ? 'H1' + : // oxlint-disable-next-line no-nested-ternary + hit.type === 'lvl2' + ? 'H2' + : hit.type === 'lvl3' + ? 'H3' + : 'DOC'} +
+ +
+
+ {lvl1 || 'Untitled'} +
+ + {lvl2 && ( +
+ {lvl2} +
+ )} + + {hit.content && ( +
+ {hit.content} +
+ )} + +
+ {hit.url} + {hit.type && ( + {hit.type} + )} +
+
+
+
+ ); +} + +export default function WHitComponent({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + ); +} diff --git a/examples/demo-react/src/examples/w-hit-transformItems.tsx b/examples/demo-react/src/examples/w-hit-transformItems.tsx new file mode 100644 index 00000000..912785fd --- /dev/null +++ b/examples/demo-react/src/examples/w-hit-transformItems.tsx @@ -0,0 +1,66 @@ +import type { JSX } from 'react'; +/* eslint-disable react/react-in-jsx-scope */ +import { DocSearchAI } from 'typesense-docsearch-react'; + +import type { DemoTheme } from '../App'; +import { defaultAskAi, typesenseServerConfig } from '../config'; + +// this type matches the structure of the provided example hit +/* type _DocSearchCustomHit = { + path: string; + metaDescription: string; + title: string; + h1: string; + h2: string; + content: string; + breadcrumb: string[]; + variation: Record; + pageDepth: number; + domain: string; + objectID: string; + _snippetResult: any; + _highlightResult: any; +}; */ + +export default function WTransformItems({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + { + return items.map((item: any) => ({ + objectID: item.objectID, + content: item.content ?? '', + url: item.domain + item.path, + hierarchy: { + lvl0: item.breadcrumb.join(' > ') ?? '', + lvl1: item.h1 ?? '', + lvl2: item.h2 ?? '', + lvl3: null, + lvl4: null, + lvl5: null, + lvl6: null, + }, + url_without_anchor: item.domain + item.path, + type: 'content' as const, + anchor: null, + _highlightResult: item._highlightResult, + _snippetResult: item._snippetResult, + })); + }} + translations={{ button: { buttonText: 'Search with transformItems' } }} + theme={theme} + /> + ); +} diff --git a/examples/demo-react/src/examples/w-results-footer.tsx b/examples/demo-react/src/examples/w-results-footer.tsx new file mode 100644 index 00000000..aace00da --- /dev/null +++ b/examples/demo-react/src/examples/w-results-footer.tsx @@ -0,0 +1,49 @@ +/* eslint-disable react/react-in-jsx-scope */ +import type { AutocompleteState } from '@algolia/autocomplete-core'; +import type { JSX } from 'react'; +import { DocSearch } from 'typesense-docsearch-react'; +import type { InternalDocSearchHit } from 'typesense-docsearch-react'; + +import type { DemoTheme } from '../App'; +import { + defaultCollection, + defaultSearchParameters, + typesenseServerConfig, +} from '../config'; + +function ResultsFooterComponent({ + state, +}: { + state: AutocompleteState; +}): JSX.Element { + // Using JSX templates + return ( + + ); +} + +export default function WResultsFooter({ + theme, +}: { + theme: DemoTheme; +}): JSX.Element { + return ( + + ); +} diff --git a/examples/demo-react/src/icons.tsx b/examples/demo-react/src/icons.tsx new file mode 100644 index 00000000..ec48d9ae --- /dev/null +++ b/examples/demo-react/src/icons.tsx @@ -0,0 +1,123 @@ +/* eslint-disable react/react-in-jsx-scope */ + +export function MoonIcon() { + return ( + + + + + ); +} + +export function SunIcon() { + return ( + + + + + + + + + + + + + ); +} diff --git a/examples/demo-react/src/main.tsx b/examples/demo-react/src/main.tsx new file mode 100644 index 00000000..c06e9008 --- /dev/null +++ b/examples/demo-react/src/main.tsx @@ -0,0 +1,11 @@ +/* eslint-disable react/react-in-jsx-scope */ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + +import App from './App.tsx'; + +createRoot(document.getElementById('root')!).render( + + + +); diff --git a/examples/demo-react/src/vite-env.d.ts b/examples/demo-react/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/examples/demo-react/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/examples/demo-react/tsconfig.json b/examples/demo-react/tsconfig.json new file mode 100644 index 00000000..183deedd --- /dev/null +++ b/examples/demo-react/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src", "vite.config.ts"] +} diff --git a/examples/demo-react/vite.config.ts b/examples/demo-react/vite.config.ts new file mode 100644 index 00000000..a74ec848 --- /dev/null +++ b/examples/demo-react/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}); diff --git a/examples/demo/index.html b/examples/demo/index.html deleted file mode 100644 index 9013d2e8..00000000 --- a/examples/demo/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - DocSearch v3 - React - - - - -
- - - - diff --git a/examples/demo/package.json b/examples/demo/package.json deleted file mode 100644 index 358cf31b..00000000 --- a/examples/demo/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "@docsearch/react-example", - "description": "DocSearch v3 React example", - "version": "3.3.3", - "private": true, - "license": "MIT", - "scripts": { - "build": "parcel build index.html", - "start": "parcel index.html" - }, - "dependencies": { - "typesense-docsearch-css": "^0.4.0", - "typesense-docsearch-react": "^3.4.0-1", - "react": "^18.1.0", - "react-dom": "^18.1.0" - }, - "devDependencies": { - "parcel": "2.7.0" - } -} diff --git a/examples/demo/src/App.js b/examples/demo/src/App.js deleted file mode 100644 index d3ae3604..00000000 --- a/examples/demo/src/App.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { DocSearch } from 'typesense-docsearch-react'; - -import './app.css'; -import '@docsearch/css'; - -function App() { - const params = { - typesenseCollectionName: 'docusaurus-2', // Replace with your own doc site's name. Should match the collection name in the scraper settings. - - typesenseServerConfig: { - nodes: [ - { - host: 'x3s805zrawjuod9fp-1.a1.typesense.net', - port: 443, - protocol: 'https', - }, - { - host: 'x3s805zrawjuod9fp-2.a1.typesense.net', - port: 443, - protocol: 'https', - }, - ], - apiKey: 'c1DmVFTQGnnP5XtW8FV7btCDeTYhBLz6', //insert search key here - }, - typesenseSearchParameters: {} - } - return ( -
-

DocSearch v3 - React

- -
- ); -} - -export default App; diff --git a/examples/demo/src/app.css b/examples/demo/src/app.css deleted file mode 100644 index d1a94a91..00000000 --- a/examples/demo/src/app.css +++ /dev/null @@ -1,4 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -} diff --git a/examples/demo/src/index.js b/examples/demo/src/index.js deleted file mode 100644 index 64342fa6..00000000 --- a/examples/demo/src/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; - -import App from './App'; - -const root = createRoot(document.getElementById('root')); -root.render(); diff --git a/examples/js-demo-no-pkg-mgr/index.html b/examples/js-demo-no-pkg-mgr/index.html deleted file mode 100644 index f78ad179..00000000 --- a/examples/js-demo-no-pkg-mgr/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - Title - - - - -
- - - - - - diff --git a/examples/js-demo/README.md b/examples/js-demo/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/js-demo/app.css b/examples/js-demo/app.css deleted file mode 100644 index d1a94a91..00000000 --- a/examples/js-demo/app.css +++ /dev/null @@ -1,4 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; -} diff --git a/examples/js-demo/app.js b/examples/js-demo/app.js deleted file mode 100644 index 8623b439..00000000 --- a/examples/js-demo/app.js +++ /dev/null @@ -1,29 +0,0 @@ -// import docsearch from '@docsearch/js/dist/umd'; -import docsearch from 'typesense-docsearch.js/dist/umd'; - -import './app.css'; - -// import '@docsearch/css'; -import 'typesense-docsearch-css'; - -docsearch({ - container: '#docsearch', - typesenseCollectionName: 'docusaurus-2', // Replace with your own doc site's name. Should match the collection name in the scraper settings. - - typesenseServerConfig: { - nodes: [ - { - host: 'x3s805zrawjuod9fp-1.a1.typesense.net', - port: 443, - protocol: 'https', - }, - { - host: 'x3s805zrawjuod9fp-2.a1.typesense.net', - port: 443, - protocol: 'https', - }, - ], - apiKey: 'c1DmVFTQGnnP5XtW8FV7btCDeTYhBLz6', //insert search key here - }, - typesenseSearchParameters: {} -}); diff --git a/examples/js-demo/favicon.png b/examples/js-demo/favicon.png deleted file mode 100644 index c481de75..00000000 Binary files a/examples/js-demo/favicon.png and /dev/null differ diff --git a/examples/js-demo/package.json b/examples/js-demo/package.json deleted file mode 100644 index aa2a2c94..00000000 --- a/examples/js-demo/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@docsearch/js-example", - "description": "DocSearch v3 Vanilla JavaScript example", - "version": "3.3.3", - "private": true, - "license": "MIT", - "scripts": { - "build": "parcel build index.html", - "start": "parcel index.html" - }, - "dependencies": { - "typesense-docsearch.js": "^3.4.0-1", - "typesense-docsearch-css": "^0.4.0" - }, - "devDependencies": { - "parcel": "2.7.0" - } -} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..23e65ce7 --- /dev/null +++ b/flake.nix @@ -0,0 +1,28 @@ +{ + description = "DocSearch dev environment"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + in + { + devShells = forAllSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + default = pkgs.mkShell { + packages = [ + pkgs.nodejs_22 + pkgs.yarn-berry + pkgs.nodePackages.typescript + ]; + }; + }); + }; +} diff --git a/global.d.ts b/global.d.ts index d9ceb13b..b867229b 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,2 +1 @@ -// eslint-disable-next-line @typescript-eslint/naming-convention declare const __DEV__: boolean; diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index e8b52415..00000000 --- a/jest.config.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testMatch: ['/packages/docsearch-*/src/__tests__/*.test.(ts|tsx)'], - transformIgnorePatterns: [ - '/node_modules/', - '/packages/docsearch-*/node_modules/', - ], - transform: { - '^.+\\.(tsx|ts|js)$': 'babel-jest', - }, - setupFilesAfterEnv: ['/scripts/jest/setupTests.ts'], - testEnvironment: 'jsdom', - globals: { - __DEV__: true, - }, -}; diff --git a/lerna.json b/lerna.json deleted file mode 100644 index d50fab4f..00000000 --- a/lerna.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "packages": ["packages/*", "examples/*"], - "version": "3.4.0-7", - "npmClient": "yarn", - "useWorkspaces": true -} diff --git a/mcp/README.md b/mcp/README.md new file mode 100644 index 00000000..b2614568 --- /dev/null +++ b/mcp/README.md @@ -0,0 +1,9 @@ +# DocSearch MCP + +Client plugin packages for the public Algolia DocSearch MCP endpoint live in [`plugins/docsearch`](plugins/docsearch). + +```text +https://mcp.algolia.com/1/docsearch/mcp +``` + +The plugins connect AI coding clients to the public DocSearch documentation corpus. They do not expose crawler operations, DocSearch administration, private documentation, or Algolia index credentials. diff --git a/mcp/plugins/docsearch/README.md b/mcp/plugins/docsearch/README.md new file mode 100644 index 00000000..c93a0f50 --- /dev/null +++ b/mcp/plugins/docsearch/README.md @@ -0,0 +1,72 @@ +# Algolia DocSearch MCP Plugins + +This directory contains client plugin packages for the public Algolia DocSearch MCP endpoint: + +```text +https://mcp.algolia.com/1/docsearch/mcp +``` + +The plugins connect AI coding clients to the public DocSearch documentation corpus. They do not expose crawler operations, DocSearch administration, private documentation, or Algolia index credentials. + +## Packages + +- `mcp/plugins/docsearch/chatgpt/algolia-docsearch` - Universal ChatGPT and Codex plugin package with manifest, MCP config, skill, assets, and submission materials. +- `mcp/plugins/docsearch/cursor/algolia-docsearch` - Cursor plugin package with MCP config, rule, skill, and README. +- `mcp/plugins/docsearch/claude/algolia-docsearch` - Claude Code plugin package with MCP config, skill, command, and README. + +## Public MCP Tools + +You can query in natural language — full sentences and questions work well. + +### algolia_docsearch_search_docs + +One-shot lookup, and the right default for most questions. Pass a `library` and a `query`; it resolves the best matching documentation set and returns ranked content in a single call: + +```json +{ + "library": "Next.js", + "query": "how do middleware matchers work" +} +``` + +If the library is ambiguous, it returns candidate documentation sets instead — pick a `docset_id` and pass it to `algolia_docsearch_query_docs`. + +### algolia_docsearch_resolve_docset + +Step 1 of the manual flow. Finds matching documentation sets and returns candidates with: + +- `docset_id` +- title and description +- optional quality signals such as `trustScore`, `benchmarkScore`, and `popularityScore` + +```json +{ + "query": "Next.js app router" +} +``` + +### algolia_docsearch_query_docs + +Step 2 of the manual flow. Queries documentation content for the selected `docset_id` values. Pass several when a question spans multiple products: + +```json +{ + "query": "middleware matcher config", + "docsetIds": ["nextjs"] +} +``` + +## Claude Desktop And claude.ai + +Claude Desktop and claude.ai can connect directly to the same remote MCP endpoint when remote connectors are available in the client: + +```json +{ + "algolia-docsearch": { + "type": "http", + "url": "https://mcp.algolia.com/1/docsearch/mcp" + } +} +``` + +No user authentication is required for the public DocSearch MCP endpoint. diff --git a/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.codex-plugin/plugin.json b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.codex-plugin/plugin.json new file mode 100644 index 00000000..370aef78 --- /dev/null +++ b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.codex-plugin/plugin.json @@ -0,0 +1,41 @@ +{ + "name": "algolia-docsearch", + "version": "1.0.0", + "description": "Search current public developer documentation from the Algolia DocSearch corpus.", + "author": { + "name": "Algolia", + "url": "https://www.algolia.com" + }, + "homepage": "https://docsearch.algolia.com/docs/mcp/overview", + "repository": "https://github.com/algolia/docsearch", + "license": "MIT", + "keywords": [ + "algolia", + "developer-docs", + "documentation", + "docsearch", + "mcp" + ], + "skills": "./skills/", + "mcpServers": "./.mcp.json", + "interface": { + "displayName": "Algolia DocSearch", + "shortDescription": "Current public developer documentation from the DocSearch corpus", + "longDescription": "Search current public documentation for libraries, frameworks, SDKs, APIs, CLIs, and cloud services. DocSearch resolves the relevant documentation set, returns ranked excerpts, and provides source URLs for grounded answers.", + "developerName": "Algolia", + "category": "Developer Tools", + "capabilities": ["Interactive", "Read"], + "websiteURL": "https://docsearch.algolia.com/docs/mcp/overview", + "privacyPolicyURL": "https://www.algolia.com/policies/privacy", + "termsOfServiceURL": "https://www.algolia.com/policies/terms", + "defaultPrompt": [ + "Explain middleware matchers from the current Next.js docs.", + "Show how React's useTransition hook works.", + "Find Algolia guidance for searchable attributes." + ], + "brandColor": "#5468FF", + "composerIcon": "./assets/docsearch-monogram.svg", + "logo": "./assets/docsearch-monogram.svg", + "screenshots": [] + } +} diff --git a/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.gitignore b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.gitignore new file mode 100644 index 00000000..e397ecd4 --- /dev/null +++ b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.gitignore @@ -0,0 +1 @@ +SUBMISSION.md \ No newline at end of file diff --git a/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.mcp.json b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.mcp.json new file mode 100644 index 00000000..c8797033 --- /dev/null +++ b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "algolia-docsearch": { + "type": "http", + "url": "https://mcp.algolia.com/1/docsearch/mcp" + } + } +} diff --git a/mcp/plugins/docsearch/chatgpt/algolia-docsearch/README.md b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/README.md new file mode 100644 index 00000000..0a6b9f5e --- /dev/null +++ b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/README.md @@ -0,0 +1,57 @@ +# Algolia DocSearch Plugin for ChatGPT and Codex + +Algolia DocSearch connects ChatGPT and Codex to the public DocSearch MCP endpoint for current public developer documentation. + +## What's included + +- **Plugin manifest** - Identifies the universal ChatGPT and Codex plugin and its listing metadata. +- **MCP server** - Connects to `https://mcp.algolia.com/1/docsearch/mcp` without user authentication. +- **Skill** - Routes public developer documentation questions through DocSearch and requires source-grounded answers. + +The plugin teaches the one-shot `search_docs` lookup and the `resolve_docset` plus `query_docs` flow. + +## Public submission + +Submit this as **With MCP** using the universal MCP URL: + +```text +https://mcp.algolia.com/1/docsearch/mcp +``` + +Do not submit an existing ChatGPT integration ID. The OpenAI submission portal scans the production MCP URL directly. See [SUBMISSION.md](SUBMISSION.md) for listing copy, tool annotation requirements, starter prompts, test cases, and the pre-submission checklist. + +## Local ChatGPT testing + +1. Enable Developer mode in ChatGPT under **Settings → Security and login**. +2. Open ChatGPT Plugins, add a connection, and enter the MCP URL above. +3. Test each tool and the bundled skill in a new chat. + +If a packaged local plugin must reference the registered ChatGPT connection, copy its technical ID from the browser URL and add a `.app.json` file: + +```json +{ + "apps": { + "algolia-docsearch": { + "id": "plugin_asdk_app_" + } + } +} +``` + +Then add `"apps": "./.app.json"` to `.codex-plugin/plugin.json`. The committed package intentionally omits this machine-specific development ID. + +## Available tools + +### `algolia_docsearch_search_docs` + +The default one-shot lookup. It resolves a documentation set from `library`, searches it with `query`, and returns ranked documentation chunks with source URLs. + +### `algolia_docsearch_resolve_docset` + +Finds candidate documentation sets for an official product, library, SDK, or platform. + +### `algolia_docsearch_query_docs` + +Queries one or more selected `docset_id` values. Use it after resolving an ambiguous library or when a question spans products. + +This plugin is limited to public developer documentation. It does not expose crawler operations, DocSearch administration, private documentation, or Algolia index credentials. diff --git a/mcp/plugins/docsearch/chatgpt/algolia-docsearch/assets/docsearch-monogram.svg b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/assets/docsearch-monogram.svg new file mode 100644 index 00000000..03da84ef --- /dev/null +++ b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/assets/docsearch-monogram.svg @@ -0,0 +1,4 @@ + + + + diff --git a/mcp/plugins/docsearch/chatgpt/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md new file mode 100644 index 00000000..7a0c6e9b --- /dev/null +++ b/mcp/plugins/docsearch/chatgpt/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md @@ -0,0 +1,51 @@ +--- +name: algolia-docsearch-mcp +description: Use when the user asks about public developer documentation for a library, framework, SDK, API, CLI tool, or cloud service. Fetch current docs from Algolia DocSearch MCP and cite source URLs. +--- + +Use Algolia DocSearch MCP when the answer depends on current public developer documentation. + +## Scope + +Use this skill for: + +- Setup and configuration questions about public libraries, frameworks, SDKs, APIs, CLI tools, and cloud services. +- API reference details, migrations, and code examples that should match current documentation. +- Questions that ask for documentation sources or citations. + +Do not use this skill for: + +- Private or internal documentation. +- Local code review or business logic. +- General programming concepts that do not require external documentation. +- Questions already answered by documentation the user supplied. +- DocSearch crawler operations, administration, or private Algolia indices. + +## Default flow + +For most questions, call `algolia_docsearch_search_docs`. + +1. Set `library` to the official product, library, SDK, or platform name. +2. Set `query` to the actual documentation question in natural language. +3. Answer only from the returned documentation chunks. +4. Include returned source URLs when available. + +If the selected documentation set is ambiguous or irrelevant, do not answer from it. Retry with the exact official product name or use the manual flow. + +## Manual flow + +Use `algolia_docsearch_resolve_docset` followed by `algolia_docsearch_query_docs` when: + +- The question spans multiple products. +- The right documentation set is ambiguous. +- The user asks to compare guidance across products. + +Resolve the documentation set once, select the official vendor's best match, and pass its `docset_id` to `algolia_docsearch_query_docs`. Pass multiple IDs when the question genuinely spans products. + +## Answering + +- Ground the answer only in returned documentation content. +- Prefer official vendor documentation sets over third-party mentions. +- Include source links next to the claims they support. +- If no relevant documentation is returned, say so and ask for a more specific product name or documentation source. +- Never invent citations, credentials, private data, or unsupported capabilities. diff --git a/mcp/plugins/docsearch/claude/algolia-docsearch/.claude-plugin/marketplace.json b/mcp/plugins/docsearch/claude/algolia-docsearch/.claude-plugin/marketplace.json new file mode 100644 index 00000000..a3a25024 --- /dev/null +++ b/mcp/plugins/docsearch/claude/algolia-docsearch/.claude-plugin/marketplace.json @@ -0,0 +1,15 @@ +{ + "name": "algolia-docsearch-marketplace", + "description": "Algolia DocSearch plugins for Claude Code — search up-to-date public developer documentation from the DocSearch corpus.", + "owner": { + "name": "Algolia" + }, + "plugins": [ + { + "name": "algolia-docsearch", + "source": ".", + "description": "Fresh, chunked and ranked excerpts from up-to-date public developer documentation from the DocSearch corpus.", + "version": "1.0.0" + } + ] +} diff --git a/mcp/plugins/docsearch/claude/algolia-docsearch/.claude-plugin/plugin.json b/mcp/plugins/docsearch/claude/algolia-docsearch/.claude-plugin/plugin.json new file mode 100644 index 00000000..81679d9b --- /dev/null +++ b/mcp/plugins/docsearch/claude/algolia-docsearch/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "algolia-docsearch", + "description": "Algolia DocSearch MCP plugin for searching up-to-date public developer documentation from the DocSearch corpus.", + "version": "1.0.0", + "author": { + "name": "Algolia" + } +} diff --git a/mcp/plugins/docsearch/claude/algolia-docsearch/.mcp.json b/mcp/plugins/docsearch/claude/algolia-docsearch/.mcp.json new file mode 100644 index 00000000..6a7d1b76 --- /dev/null +++ b/mcp/plugins/docsearch/claude/algolia-docsearch/.mcp.json @@ -0,0 +1,6 @@ +{ + "algolia-docsearch": { + "type": "http", + "url": "https://mcp.algolia.com/1/docsearch/mcp" + } +} diff --git a/mcp/plugins/docsearch/claude/algolia-docsearch/README.md b/mcp/plugins/docsearch/claude/algolia-docsearch/README.md new file mode 100644 index 00000000..af5f38eb --- /dev/null +++ b/mcp/plugins/docsearch/claude/algolia-docsearch/README.md @@ -0,0 +1,63 @@ +# Algolia DocSearch Plugin for Claude Code + +Algolia DocSearch connects Claude Code to the public DocSearch MCP endpoint for current public developer documentation. + +## What's Included + +- **MCP Server** - Connects Claude Code to `https://mcp.algolia.com/1/docsearch/mcp`. +- **Skill** - Auto-triggers documentation lookups for public developer docs questions. +- **Command** - `/algolia-docsearch:docs` for manual documentation queries. + +The plugin teaches the one-shot `search_docs` lookup and the `resolve_docset` + `query_docs` flow. + +## Installation + +For local beta testing, install this plugin package from: + +```text +mcp/plugins/docsearch/claude/algolia-docsearch +``` + +The MCP server config is also available at: + +```text +mcp/plugins/docsearch/claude/algolia-docsearch/.mcp.json +``` + +For Claude Code marketplace install from this repository: + +```text +/plugin marketplace add algolia/docsearch +/plugin install algolia-docsearch@algolia-docsearch-marketplace +``` + +## Available Tools + +### algolia_docsearch_search_docs + +One-shot lookup, and the right default for most questions. Pass a `library` (product or platform) and a `query` (the question); it resolves the best documentation set and returns ranked content in a single call. + +### algolia_docsearch_resolve_docset + +Step 1 of the manual flow. Finds matching documentation sets and returns candidates with a `docset_id`, title, description, and ranking signals. + +### algolia_docsearch_query_docs + +Step 2 of the manual flow. Queries documentation content for the `docset_id` values returned by `algolia_docsearch_resolve_docset`. Pass several `docsetIds` when a question spans multiple products. + +## Usage Examples + +Ask Claude questions like: + +- "How do I configure middleware in Next.js?" +- "Show me Stripe webhook signature verification from the docs." +- "What is the current Algolia InstantSearch React setup?" + +Or use the manual command: + +```text +/algolia-docsearch:docs Next.js middleware matcher +/algolia-docsearch:docs Stripe webhook signature verification +``` + +The plugin is for public developer documentation only. It does not expose crawler operations, DocSearch administration, private docs, or Algolia index credentials. diff --git a/mcp/plugins/docsearch/claude/algolia-docsearch/commands/docs.md b/mcp/plugins/docsearch/claude/algolia-docsearch/commands/docs.md new file mode 100644 index 00000000..ac45f723 --- /dev/null +++ b/mcp/plugins/docsearch/claude/algolia-docsearch/commands/docs.md @@ -0,0 +1,31 @@ +--- +description: Look up public developer documentation with Algolia DocSearch +argument-hint: [query] +--- + +# /algolia-docsearch:docs + +Fetch current public developer documentation from the Algolia DocSearch MCP corpus. + +## Usage + +```text +/algolia-docsearch:docs [topic] +``` + +For most lookups, call `algolia_docsearch_search_docs` in a single step: + +1. Set `library` to the product, library, or platform (use the official name). +2. Set `query` to the topic or question, in natural language. +3. Answer using the returned docs and include source URLs when available. + +If the library is ambiguous, `algolia_docsearch_search_docs` returns candidate documentation sets — pick the right `docset_id` and pass it to `algolia_docsearch_query_docs`. Use the `algolia_docsearch_resolve_docset` + `algolia_docsearch_query_docs` flow when a question spans several products. + +## Examples + +```text +/algolia-docsearch:docs Next.js middleware matcher +/algolia-docsearch:docs Stripe webhook signature verification +/algolia-docsearch:docs Algolia InstantSearch React configure search client +/algolia-docsearch:docs React Server Components data fetching +``` diff --git a/mcp/plugins/docsearch/claude/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md b/mcp/plugins/docsearch/claude/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md new file mode 100644 index 00000000..0261def9 --- /dev/null +++ b/mcp/plugins/docsearch/claude/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md @@ -0,0 +1,72 @@ +--- +name: algolia-docsearch-mcp +description: Use this skill when the user asks about public developer documentation, SDKs, APIs, libraries, frameworks, setup, configuration, or code examples. Fetch current docs from Algolia DocSearch MCP and cite source URLs. +--- + +When the user asks about public developer documentation, use Algolia DocSearch MCP instead of relying only on training data. + +## When To Use + +Use this skill when the user: + +- Asks setup or configuration questions for a public library, framework, SDK, API, or developer tool. +- Requests code examples that should match current docs. +- Needs API reference details, migration guidance, or command syntax. +- Mentions a library or framework whose docs may have changed recently. +- Asks for sources or citations from public documentation. + +Do not use this skill for private company docs, internal repositories, unpublished APIs, or questions where the user already supplied the relevant docs. + +## Tools + +DocSearch MCP exposes three tools. You can query in natural language — full sentences and questions work well. + +- `algolia_docsearch_search_docs` — one-shot lookup. Use this by default. +- `algolia_docsearch_resolve_docset` — step 1 of the manual flow: find documentation sets. +- `algolia_docsearch_query_docs` — step 2 of the manual flow: fetch content for chosen docsets. + +## Default Flow: One-Shot Search + +Call `algolia_docsearch_search_docs` for most lookups. + +Input: + +- `library`: the product, library, SDK, or platform. Use the official name (for example: `Next.js`, `Stripe`, `Algolia InstantSearch`). +- `query`: the question, in natural language (for example: `how do middleware matchers work`). + +It resolves the best matching documentation set and returns ranked content in a single call. If `library` is ambiguous, it returns candidate documentation sets instead — pick the right one and pass its `docset_id` to `algolia_docsearch_query_docs`, or retry with the official product name. + +## Manual Flow: Resolve, Then Query + +Use the two-step flow when a question spans several products, or when you want to inspect and hand-pick documentation sets. + +### Step 1: Resolve The Docset + +Call `algolia_docsearch_resolve_docset`. + +Input: + +- `query`: describe the product, library, SDK, or platform. Including the vendor name improves matching. +- `topN`: optional. Use the default unless the initial result is ambiguous. + +Each candidate returns a `title`, `docset_id`, `description`, and ranking signals (`trustScore`, `benchmarkScore`, `popularityScore`). Prefer the official vendor's docset over third-party mentions, breaking ties by the higher scores. + +### Step 2: Query The Docs + +Call `algolia_docsearch_query_docs`. + +Input: + +- `query`: what to find in the docs, in natural language. +- `docsetIds`: an array of the `docset_id` values you selected. Pass several when a question spans multiple products. + +## Answer With Sources + +Use the returned documentation content to answer the user directly. + +Guidelines: + +- Include source URLs from the tool result when available. +- Say when DocSearch does not return a relevant result. +- Do not invent source citations. +- Keep the answer focused on the user's question. diff --git a/mcp/plugins/docsearch/cursor/algolia-docsearch/.cursor-plugin/plugin.json b/mcp/plugins/docsearch/cursor/algolia-docsearch/.cursor-plugin/plugin.json new file mode 100644 index 00000000..d808ac28 --- /dev/null +++ b/mcp/plugins/docsearch/cursor/algolia-docsearch/.cursor-plugin/plugin.json @@ -0,0 +1,17 @@ +{ + "name": "algolia-docsearch", + "version": "1.0.0", + "description": "Algolia DocSearch MCP plugin for searching up-to-date public developer documentation from the DocSearch corpus.", + "author": { + "name": "Algolia" + }, + "homepage": "https://docsearch.algolia.com/docs/mcp/overview", + "keywords": [ + "documentation", + "mcp", + "algolia", + "docsearch", + "developer-docs" + ], + "logo": "assets/docsearch-monogram.svg" +} diff --git a/mcp/plugins/docsearch/cursor/algolia-docsearch/README.md b/mcp/plugins/docsearch/cursor/algolia-docsearch/README.md new file mode 100644 index 00000000..a918fb14 --- /dev/null +++ b/mcp/plugins/docsearch/cursor/algolia-docsearch/README.md @@ -0,0 +1,47 @@ +# Algolia DocSearch Plugin for Cursor + +Algolia DocSearch connects Cursor to the public DocSearch MCP endpoint for current public developer documentation. + +## What's Included + +- **MCP Server** - Connects Cursor to `https://mcp.algolia.com/1/docsearch/mcp`. +- **Rule** - Nudges the agent to use DocSearch for public developer docs instead of guessing from training data. +- **Skill** - Documents the one-shot `search_docs` lookup and the `resolve_docset` + `query_docs` flow. + +## Installation + +For local beta testing, install this plugin package from: + +```text +mcp/plugins/docsearch/cursor/algolia-docsearch +``` + +The MCP server config is also available at: + +```text +mcp/plugins/docsearch/cursor/algolia-docsearch/mcp.json +``` + +## Available Tools + +### algolia_docsearch_search_docs + +One-shot lookup, and the right default for most questions. Pass a `library` (product or platform) and a `query` (the question); it resolves the best documentation set and returns ranked content in a single call. + +### algolia_docsearch_resolve_docset + +Step 1 of the manual flow. Finds matching documentation sets and returns candidates with a `docset_id`, title, description, and ranking signals. + +### algolia_docsearch_query_docs + +Step 2 of the manual flow. Queries documentation content for the `docset_id` values returned by `algolia_docsearch_resolve_docset`. Pass several `docsetIds` when a question spans multiple products. + +## Usage Examples + +Ask Cursor questions like: + +- "How do I configure middleware in Next.js?" +- "Show me Stripe webhook signature verification from the docs." +- "What is the current Algolia InstantSearch React setup?" + +The plugin is for public developer documentation only. It does not expose crawler operations, DocSearch administration, private docs, or Algolia index credentials. diff --git a/mcp/plugins/docsearch/cursor/algolia-docsearch/assets/docsearch-monogram.svg b/mcp/plugins/docsearch/cursor/algolia-docsearch/assets/docsearch-monogram.svg new file mode 100644 index 00000000..3ce446d2 --- /dev/null +++ b/mcp/plugins/docsearch/cursor/algolia-docsearch/assets/docsearch-monogram.svg @@ -0,0 +1 @@ + diff --git a/mcp/plugins/docsearch/cursor/algolia-docsearch/mcp.json b/mcp/plugins/docsearch/cursor/algolia-docsearch/mcp.json new file mode 100644 index 00000000..c8797033 --- /dev/null +++ b/mcp/plugins/docsearch/cursor/algolia-docsearch/mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "algolia-docsearch": { + "type": "http", + "url": "https://mcp.algolia.com/1/docsearch/mcp" + } + } +} diff --git a/mcp/plugins/docsearch/cursor/algolia-docsearch/rules/use-algolia-docsearch.mdc b/mcp/plugins/docsearch/cursor/algolia-docsearch/rules/use-algolia-docsearch.mdc new file mode 100644 index 00000000..45d76f9d --- /dev/null +++ b/mcp/plugins/docsearch/cursor/algolia-docsearch/rules/use-algolia-docsearch.mdc @@ -0,0 +1,22 @@ +--- +description: Use Algolia DocSearch MCP for current public developer documentation instead of guessing from training data. +alwaysApply: true +--- + +When the user asks about public developer documentation, APIs, SDKs, libraries, frameworks, configuration, or code examples, use Algolia DocSearch MCP to fetch current documentation before answering. + +Use Algolia DocSearch when: +- The user asks about a public library, framework, SDK, API, or developer tool. +- The user needs syntax, configuration, migration, setup, or code examples from docs. +- The question may depend on recent documentation changes. +- You need source URLs to support an answer. + +Do not use Algolia DocSearch for: +- Private company docs, internal repos, or unpublished APIs. +- Questions already answered by code or docs the user provided. +- Crawler operations, DocSearch administration, or MCP server implementation details. +- General programming concepts that do not require external docs. + +For most lookups call `algolia_docsearch_search_docs` with the product as `library` and the question as `query`. Use the `algolia_docsearch_resolve_docset` + `algolia_docsearch_query_docs` flow when a question spans several products or you need to hand-pick documentation sets. Natural-language queries work well. + +See the `algolia-docsearch-mcp` skill for the full tool flow. diff --git a/mcp/plugins/docsearch/cursor/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md b/mcp/plugins/docsearch/cursor/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md new file mode 100644 index 00000000..0261def9 --- /dev/null +++ b/mcp/plugins/docsearch/cursor/algolia-docsearch/skills/algolia-docsearch-mcp/SKILL.md @@ -0,0 +1,72 @@ +--- +name: algolia-docsearch-mcp +description: Use this skill when the user asks about public developer documentation, SDKs, APIs, libraries, frameworks, setup, configuration, or code examples. Fetch current docs from Algolia DocSearch MCP and cite source URLs. +--- + +When the user asks about public developer documentation, use Algolia DocSearch MCP instead of relying only on training data. + +## When To Use + +Use this skill when the user: + +- Asks setup or configuration questions for a public library, framework, SDK, API, or developer tool. +- Requests code examples that should match current docs. +- Needs API reference details, migration guidance, or command syntax. +- Mentions a library or framework whose docs may have changed recently. +- Asks for sources or citations from public documentation. + +Do not use this skill for private company docs, internal repositories, unpublished APIs, or questions where the user already supplied the relevant docs. + +## Tools + +DocSearch MCP exposes three tools. You can query in natural language — full sentences and questions work well. + +- `algolia_docsearch_search_docs` — one-shot lookup. Use this by default. +- `algolia_docsearch_resolve_docset` — step 1 of the manual flow: find documentation sets. +- `algolia_docsearch_query_docs` — step 2 of the manual flow: fetch content for chosen docsets. + +## Default Flow: One-Shot Search + +Call `algolia_docsearch_search_docs` for most lookups. + +Input: + +- `library`: the product, library, SDK, or platform. Use the official name (for example: `Next.js`, `Stripe`, `Algolia InstantSearch`). +- `query`: the question, in natural language (for example: `how do middleware matchers work`). + +It resolves the best matching documentation set and returns ranked content in a single call. If `library` is ambiguous, it returns candidate documentation sets instead — pick the right one and pass its `docset_id` to `algolia_docsearch_query_docs`, or retry with the official product name. + +## Manual Flow: Resolve, Then Query + +Use the two-step flow when a question spans several products, or when you want to inspect and hand-pick documentation sets. + +### Step 1: Resolve The Docset + +Call `algolia_docsearch_resolve_docset`. + +Input: + +- `query`: describe the product, library, SDK, or platform. Including the vendor name improves matching. +- `topN`: optional. Use the default unless the initial result is ambiguous. + +Each candidate returns a `title`, `docset_id`, `description`, and ranking signals (`trustScore`, `benchmarkScore`, `popularityScore`). Prefer the official vendor's docset over third-party mentions, breaking ties by the higher scores. + +### Step 2: Query The Docs + +Call `algolia_docsearch_query_docs`. + +Input: + +- `query`: what to find in the docs, in natural language. +- `docsetIds`: an array of the `docset_id` values you selected. Pass several when a question spans multiple products. + +## Answer With Sources + +Use the returned documentation content to answer the user directly. + +Guidelines: + +- Include source URLs from the tool result when available. +- Say when DocSearch does not return a relevant result. +- Do not invent source citations. +- Keep the answer focused on the user's question. diff --git a/netlify.toml b/netlify.toml index 237d3b39..0e947ad0 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,8 @@ [build] - command="yarn build" - publish="packages/website/build" - ignore="git diff --quiet $COMMIT_REF $CACHED_COMMIT_REF -- packages/website/ yarn.lock" +command = "bun install --frozen-lockfile && bun run build && bun run playground:build" -[build.environment] - YARN_VERSION = "1.22.5" +[[redirects]] +from = "/apply/" +to = "https://dashboard.algolia.com/users/sign_up?selected_plan=docsearch&utm_source=docsearch.algolia.com&utm_medium=referral&utm_campaign=docsearch&utm_content=apply" +status = 301 +force = true diff --git a/nx.json b/nx.json new file mode 100644 index 00000000..7362de89 --- /dev/null +++ b/nx.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/nrwl/nx/master/packages/nx/schemas/nx-schema.json", + "targetDefaults": { + "build": { + "dependsOn": ["^build"] + } + } +} diff --git a/package.json b/package.json index 5b01e976..16e9d856 100644 --- a/package.json +++ b/package.json @@ -1,113 +1,79 @@ { "name": "@docsearch/monorepo", "private": true, - "workspaces": { - "packages": [ - "packages/*", - "examples/*" - ], - "nohoist": [ - "**/html-minifier-terser" - ] - }, + "workspaces": [ + "packages/*", + "examples/*" + ], + "type": "module", "scripts": { - "build:clean": "lerna run build:clean --scope={@docsearch/css,@docsearch/js,@docsearch/react,typesense-*}", - "build:esm": "lerna run build:esm --scope={@docsearch/css,@docsearch/js,@docsearch/react,typesense-*}", - "build:types": "lerna run build:types --scope={@docsearch/css,@docsearch/js,@docsearch/react,typesense-*}", - "build:umd": "lerna run build:umd --scope={@docsearch/css,@docsearch/js,@docsearch/react,typesense-*}", - "build": "lerna run build --scope={@docsearch/css,@docsearch/js,@docsearch/react,typesense-*}", - "cy:clean": "rm -rf cypress/screenshots", - "cy:info": "cypress info", - "cy:run:chrome": "yarn run cy:run --browser chrome", - "cy:run:edge": "yarn run cy:run --browser edge", - "cy:run:firefox": "yarn run cy:run --browser firefox", - "cy:run": "start-server-and-test 'yarn website:test' http://localhost:3000 'cypress run --spec 'cypress/integration/**/*' --headless'", - "cy:verify": "cypress verify", + "build": "bun run --sequential build:stage:base build:stage:react build:stage:consumers build:pkg:cli", + "build:all": "bun run --sequential build", + "build:pkg:core": "bun --filter typesense-docsearch-core build", + "build:pkg:css": "bun --filter typesense-docsearch-css build", + "build:pkg:react": "bun --filter typesense-docsearch-react build", + "build:pkg:js": "bun --filter typesense-docsearch.js build", + "build:pkg:sidepanel": "bun --filter typesense-docsearch-sidepanel build", + "build:pkg:sidepanel-js": "bun --filter typesense-docsearch-sidepanel-js build", + "build:pkg:modal": "bun --filter typesense-docsearch-modal build", + "build:pkg:cli": "bun --filter typesense-docsearch-cli build", + "build:stage:base": "bun run --parallel build:pkg:core build:pkg:css", + "build:stage:react": "bun run build:pkg:react", + "build:stage:consumers": "bun run --parallel build:pkg:js build:pkg:sidepanel build:pkg:sidepanel-js build:pkg:modal", + "build:examples": "bun --filter './examples/*' build", + "fmt": "oxfmt --config ./.oxfmtrc.json .", + "pw:clean": "rm -rf playwright-report test-results", + "pw:run": "playwright test", + "pw:run:chromium": "bun run pw:run -- --project=chromium", + "pw:run:firefox": "bun run pw:run -- --project=firefox", + "pw:run:webkit": "bun run pw:run -- --project=webkit", + "pw:ui": "playwright test --ui", "lint:css": "stylelint **/src/**/*.css", - "lint": "eslint --ext .js,.ts,.tsx .", - "playground:start": "yarn workspace @docsearch/react-example start", - "playground-js:start": "yarn workspace @docsearch/js-example start", - "release": "shipjs prepare", - "start": "yarn run watch", + "lint": "oxlint .", + "lint:fix": "oxlint --fix .", + "playground:build": "bun --filter @docsearch/react-example build", + "playground:start": "bun --filter @docsearch/react-example dev -- --host", + "playground-js:start": "bun --filter @docsearch/js-example dev -- --host", + "start": "bun run watch", + "dev": "bun run start & bun run playground:start", "test:size": "bundlesize", "test:types": "tsc --noEmit", - "test": "jest", - "watch": "lerna run watch --parallel --scope={@docsearch/css,@docsearch/js,@docsearch/react,typesense-*}", - "website:build": "yarn workspace @docsearch/website build", - "website:start": "yarn workspace @docsearch/website start --host 0.0.0.0", - "website:test": "yarn workspace @docsearch/website start --no-open" + "test": "vitest", + "watch": "bun --parallel --filter './packages/*' watch", + "watch:pkg:core": "bun --filter typesense-docsearch-core watch", + "watch:pkg:css": "bun --filter typesense-docsearch-css watch", + "watch:pkg:react": "bun --filter typesense-docsearch-react watch", + "watch:pkg:js": "bun --filter typesense-docsearch.js watch", + "watch:pkg:sidepanel": "bun --filter typesense-docsearch-sidepanel watch", + "watch:pkg:sidepanel-js": "bun --filter typesense-docsearch-sidepanel-js watch", + "watch:pkg:modal": "bun --filter typesense-docsearch-modal watch", + "changeset": "changeset", + "version": "changeset version && bun run sync:docsearch-version && bun install", + "release": "changeset publish", + "sync:docsearch-version": "bun scripts/updateDocsearchVersion.ts" }, "devDependencies": { - "@babel/cli": "7.19.3", - "@babel/plugin-transform-react-jsx": "7.19.0", - "@babel/plugin-transform-runtime": "^7.19.6", - "@babel/preset-env": "7.19.4", - "@babel/preset-typescript": "7.18.6", - "@rollup/plugin-json": "4.1.0", - "@rollup/plugin-node-resolve": "7.1.1", - "@rollup/plugin-replace": "2.3.3", - "@testing-library/dom": "9.0.0", - "@testing-library/jest-dom": "5.16.5", - "@testing-library/react": "14.0.0", - "@testing-library/user-event": "13.5.0", - "@types/jest": "^29.0.0", - "@types/jest-diff": "^24.3.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@typescript-eslint/eslint-plugin": "5.41.0", - "@typescript-eslint/parser": "5.41.0", - "algoliasearch": "4.14.2", - "babel-eslint": "10.1.0", - "babel-loader": "9.0.0", - "babel-plugin-inline-replace-variables": "1.3.1", - "babel-plugin-module-resolver": "4.0.0", - "bundlesize2": "0.0.31", - "concurrently": "5.3.0", - "cross-env": "7.0.3", - "cssnano": "4.1.10", - "cypress": "9.6.1", - "dotenv": "16.0.2", - "eslint": "8.35.0", - "eslint-config-algolia": "20.0.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-cypress": "2.12.1", - "eslint-plugin-eslint-comments": "3.2.0", - "eslint-plugin-import": "2.26.0", - "eslint-plugin-jest": "27.2.0", - "eslint-plugin-jsdoc": "39.4.0", - "eslint-plugin-jsx-a11y": "6.6.1", - "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-react": "7.31.10", - "eslint-plugin-react-hooks": "4.6.0", - "jest": "29.4.3", - "jest-environment-jsdom": "29.4.3", - "jest-watch-typeahead": "1.1.0", - "lerna": "4.0.0", - "postcss": "8.4.18", - "prettier": "2.7.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "rollup": "1.32.1", - "rollup-plugin-babel": "4.4.0", - "rollup-plugin-commonjs": "10.1.0", - "rollup-plugin-filesize": "9.1.2", - "rollup-plugin-license": "3.0.0", - "rollup-plugin-terser": "7.0.2", - "rollup-plugin-node-builtins": "^2.1.2", - "rollup-plugin-node-globals": "^1.4.0", - "shipjs": "0.24.4", - "start-server-and-test": "1.15.5", - "stylelint": "14.16.1", - "stylelint-a11y": "1.2.3", - "stylelint-config-prettier": "9.0.3", - "stylelint-config-sass-guidelines": "9.0.1", - "stylelint-config-standard": "28.0.0", - "stylelint-no-unsupported-browser-features": "5.0.4", - "stylelint-order": "5.0.0", - "stylelint-prettier": "2.0.0", - "ts-jest": "28.0.8", - "typescript": "4.7.4", - "watch": "1.0.2", - "webpack": "4.44.1" + "@axe-core/playwright": "^4.12.1", + "@changesets/cli": "^2.29.7", + "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", + "@playwright/test": "1.49.1", + "@stylistic/eslint-plugin": "2.13.0", + "@testing-library/dom": "10.4.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "bundlesize2": "0.0.32", + "jsdom": "26.0.0", + "oxfmt": "^0.59.0", + "oxlint": "^1.74.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "stylelint": "17.14.0", + "stylelint-config-sass-guidelines": "13.0.0", + "stylelint-config-standard": "40.0.0", + "stylelint-no-unsupported-browser-features": "8.1.1", + "stylelint-order": "8.1.1", + "typescript": "5.7.3", + "vitest": "3.0.2", + "watch": "1.0.2" } } diff --git a/packages/docsearch-cli/CHANGELOG.md b/packages/docsearch-cli/CHANGELOG.md new file mode 100644 index 00000000..9d258a0d --- /dev/null +++ b/packages/docsearch-cli/CHANGELOG.md @@ -0,0 +1,7 @@ +# typesense-docsearch-cli + +## 0.0.1 + +### Patch Changes + +- 710b410: Add the DocSearch CLI for installing and querying the hosted DocSearch MCP. diff --git a/packages/docsearch-cli/README.md b/packages/docsearch-cli/README.md new file mode 100644 index 00000000..dd5ee6d9 --- /dev/null +++ b/packages/docsearch-cli/README.md @@ -0,0 +1,104 @@ +# DocSearch CLI + +Install the hosted [DocSearch MCP](https://docsearch.algolia.com/mcp) in your AI coding agents and search public developer documentation from the terminal. + +## Run without installing + +```sh +npx typesense-docsearch-cli +``` + +Or install the `docsearch` command globally: + +```sh +npm install --global typesense-docsearch-cli +``` + +Requires Node.js 20 or later. + +## Set up the DocSearch MCP + +Run the interactive installer: + +```sh +docsearch setup +``` + +The installer asks whether to configure the current project or your global agent settings, then lets you select any detected agents. It supports: + +- Cursor +- Claude Code +- Codex +- OpenCode +- Gemini CLI + +Skip the prompts with explicit flags: + +```sh +docsearch setup --project --cursor --claude --yes +docsearch setup --global --all --yes +``` + +Project setup locates the repository root before writing configuration. Re-running setup is idempotent and preserves unrelated configuration. + +## Search documentation + +Use `docs` for most questions: + +```sh +docsearch docs Next.js "how do middleware matchers work" +``` + +For explicit docset selection, resolve a product and then query its docset: + +```sh +docsearch resolve "Algolia InstantSearch React" +docsearch query repo/algolia/instantsearch "configure the React search client" +``` + +Limit the returned results when needed: + +```sh +docsearch docs Next.js "cache revalidation" --max-results 3 --max-docsets 2 +docsearch resolve Next.js --top-n 3 +``` + +Pass `--json` to write the raw MCP result as JSON: + +```sh +docsearch docs Next.js "cache revalidation" --json +``` + +Machine-readable results are written to stdout. Progress and errors are written to stderr. + +## Options + +```text +--project Install into the current repository +--global Install into user-level agent settings +--all Configure every supported agent +--cursor Configure Cursor +--claude Configure Claude Code +--codex Configure Codex +--opencode Configure OpenCode +--gemini Configure Gemini CLI +--yes, -y Run setup non-interactively +--endpoint Override the hosted DocSearch MCP endpoint +--json Print raw MCP results as JSON +``` + +Set [`NO_COLOR`](https://no-color.org/) to disable terminal colors. + +## Development + +From the DocSearch monorepo root: + +```sh +bun install +bun run --filter typesense-docsearch-cli build +bun run --filter typesense-docsearch-cli test +``` + +## License + +[MIT](https://github.com/algolia/docsearch/blob/v5/LICENSE) diff --git a/packages/docsearch-cli/package.json b/packages/docsearch-cli/package.json new file mode 100644 index 00000000..1f31874e --- /dev/null +++ b/packages/docsearch-cli/package.json @@ -0,0 +1,59 @@ +{ + "name": "typesense-docsearch-cli", + "description": "CLI for DocSearch, customized to work with Typesense", + "version": "0.0.2", + "license": "MIT", + "type": "module", + "homepage": "https://typesense.org/docs/latest/guide/docsearch.html", + "repository": "typesense/docsearch.js", + "contributors": [ + { + "name": "Algolia, Inc.", + "url": "https://www.algolia.com" + }, + { + "name": "Typesense, Inc.", + "url": "https://typesense.org" + } + ], + "bugs": { + "url": "https://github.com/algolia/docsearch/issues" + }, + "bin": { + "docsearch": "./dist/index.js" + }, + "files": [ + "dist/" + ], + "keywords": [ + "algolia", + "cli", + "documentation", + "docsearch", + "mcp" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "bun run clean && tsc --project tsconfig.json", + "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"", + "prepack": "bun run build", + "test": "vitest run --config vitest.config.mts", + "test:pack": "npm pack --dry-run", + "watch": "tsc --project tsconfig.json --watch" + }, + "engines": { + "node": ">=20" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "jsonc-parser": "^3.3.1", + "picocolors": "^1.1.1" + }, + "devDependencies": { + "@types/node": "^26.1.1", + "typescript": "5.7.3", + "vitest": "3.0.2" + } +} diff --git a/packages/docsearch-cli/src/__tests__/cli.test.ts b/packages/docsearch-cli/src/__tests__/cli.test.ts new file mode 100644 index 00000000..0bfaf90e --- /dev/null +++ b/packages/docsearch-cli/src/__tests__/cli.test.ts @@ -0,0 +1,75 @@ +// @vitest-environment node + +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { runCli } from '../cli'; +import { callDocSearchTool } from '../mcp/client'; + +vi.mock('../mcp/client.js', () => ({ + callDocSearchTool: vi.fn(), +})); + +interface CapturedOutput { + stderr: string; + stdout: string; +} + +function captureOutput(): CapturedOutput { + const output: CapturedOutput = { stderr: '', stdout: '' }; + vi.spyOn(process.stdout, 'write').mockImplementation((chunk) => { + output.stdout += String(chunk); + return true; + }); + vi.spyOn(process.stderr, 'write').mockImplementation((chunk) => { + output.stderr += String(chunk); + return true; + }); + return output; +} + +afterEach(() => { + vi.clearAllMocks(); + vi.restoreAllMocks(); +}); + +describe('runCli', () => { + it('returns a non-zero exit for MCP tool errors while preserving JSON output', async () => { + const result: CallToolResult = { + content: [{ text: 'Invalid query', type: 'text' }], + isError: true, + }; + vi.mocked(callDocSearchTool).mockResolvedValue(result); + const output = captureOutput(); + + const exitCode = await runCli(['resolve', 'Next.js', '--json']); + + expect(exitCode).toBe(1); + expect(JSON.parse(output.stdout)).toMatchObject({ isError: true }); + expect(output.stderr).toBe(''); + }); + + it('reports MCP tool errors in human-readable mode', async () => { + const result: CallToolResult = { + content: [{ text: 'Invalid query', type: 'text' }], + isError: true, + }; + vi.mocked(callDocSearchTool).mockResolvedValue(result); + const output = captureOutput(); + + const exitCode = await runCli(['resolve', 'Next.js']); + + expect(exitCode).toBe(1); + expect(output.stderr).toContain('Invalid query'); + }); + + it('uses exit code 2 for command usage errors', async () => { + const output = captureOutput(); + + const exitCode = await runCli(['resolve', 'Next.js', '--max-results', '2']); + + expect(exitCode).toBe(2); + expect(output.stderr).toContain('not valid for the resolve command'); + expect(callDocSearchTool).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/docsearch-cli/src/__tests__/commands.test.ts b/packages/docsearch-cli/src/__tests__/commands.test.ts new file mode 100644 index 00000000..d5cf8398 --- /dev/null +++ b/packages/docsearch-cli/src/__tests__/commands.test.ts @@ -0,0 +1,134 @@ +// @vitest-environment node + +import { describe, expect, it } from 'vitest'; + +import { buildSetupRequest, buildToolRequest, parseArgs } from '../commands'; +import { + DEFAULT_MCP_ENDPOINT, + TOOL_QUERY_DOCS, + TOOL_RESOLVE_DOCSET, + TOOL_SEARCH_DOCS, +} from '../constants'; + +describe('buildToolRequest', () => { + it('maps docs to the one-shot DocSearch MCP tool', () => { + const request = buildToolRequest( + 'docs', + parseArgs([ + 'Next.js', + 'middleware matcher config', + '--max-results', + '3', + '--json', + ]) + ); + + expect(request).toEqual({ + endpoint: DEFAULT_MCP_ENDPOINT, + json: true, + toolName: TOOL_SEARCH_DOCS, + toolArguments: { + library: 'Next.js', + query: 'middleware matcher config', + maxResults: 3, + }, + }); + }); + + it('maps resolve to the docset resolver tool', () => { + const request = buildToolRequest( + 'resolve', + parseArgs(['Algolia', 'InstantSearch', '--top-n', '4']) + ); + + expect(request).toEqual({ + endpoint: DEFAULT_MCP_ENDPOINT, + json: false, + toolName: TOOL_RESOLVE_DOCSET, + toolArguments: { + query: 'Algolia InstantSearch', + topN: 4, + }, + }); + }); + + it('maps query to explicit docset lookups', () => { + const request = buildToolRequest( + 'query', + parseArgs([ + 'nextjs,react', + 'server components', + '--endpoint', + 'https://example.com/mcp', + ]) + ); + + expect(request).toEqual({ + endpoint: 'https://example.com/mcp', + json: false, + toolName: TOOL_QUERY_DOCS, + toolArguments: { + docsetIds: ['nextjs', 'react'], + query: 'server components', + }, + }); + }); + + it('supports equals syntax for value options', () => { + const request = buildToolRequest( + 'resolve', + parseArgs(['Next.js', '--top-n=2']) + ); + + expect(request.toolArguments).toEqual({ + query: 'Next.js', + topN: 2, + }); + }); + + it('rejects empty docset IDs', () => { + expect(() => + buildToolRequest('query', parseArgs([',', 'search query'])) + ).toThrow(/docset ID/i); + }); + + it('rejects options that do not belong to the command', () => { + expect(() => + buildToolRequest('resolve', parseArgs(['Next.js', '--max-results', '2'])) + ).toThrow(/not valid for the resolve command/); + }); + + it('rejects non-HTTP endpoints', () => { + expect(() => + buildToolRequest( + 'resolve', + parseArgs(['Next.js', '--endpoint', 'file:///tmp/mcp']) + ) + ).toThrow(/HTTP or HTTPS/); + }); +}); + +describe('buildSetupRequest', () => { + it('maps setup flags to selected agents and project scope', () => { + expect( + buildSetupRequest( + parseArgs(['--cursor', '--claude', '--project', '--yes']) + ) + ).toEqual({ + agents: ['claude', 'cursor'].sort(), + all: false, + endpoint: DEFAULT_MCP_ENDPOINT, + scope: 'project', + yes: true, + }); + }); + + it('rejects positional setup arguments and unrelated flags', () => { + expect(() => buildSetupRequest(parseArgs(['cursor']))).toThrow( + /Unexpected setup argument/ + ); + expect(() => buildSetupRequest(parseArgs(['--json']))).toThrow( + /not valid for the setup command/ + ); + }); +}); diff --git a/packages/docsearch-cli/src/cli.ts b/packages/docsearch-cli/src/cli.ts new file mode 100644 index 00000000..18a38108 --- /dev/null +++ b/packages/docsearch-cli/src/cli.ts @@ -0,0 +1,223 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { + buildSetupRequest, + buildToolRequest, + parseArgs, + type QueryCommandName, + type SetupRequest, +} from './commands.js'; +import { CLI_VERSION } from './constants.js'; +import { UsageError } from './errors.js'; +import { callDocSearchTool } from './mcp/client.js'; +import { formatToolResult, getToolResultText } from './mcp/format.js'; +import type { SetupAgent, SetupScope } from './setup/agents.js'; +import { + PromptCancelledError, + promptAgents, + promptScope, +} from './setup/prompt.js'; +import { + buildAgentChoices, + createPathContext, + detectAgents, + findProjectRoot, + setupDocSearch, +} from './setup/setup.js'; +import { renderMarkdown } from './ui/markdown.js'; +import { + renderError, + renderHelp, + renderLanding, + renderNotice, + renderResultHeader, + renderSetupResults, +} from './ui/render.js'; +import { startSpinner } from './ui/spinner.js'; + +export async function runCli( + argv: string[] = process.argv.slice(2) +): Promise { + const [commandName, ...commandArgs] = argv; + + try { + if (!commandName) { + process.stdout.write(renderLanding()); + return 0; + } + + if ( + commandName === 'help' || + commandName === '--help' || + commandName === '-h' + ) { + process.stdout.write(renderHelp()); + return 0; + } + + if ( + commandName === 'version' || + commandName === '--version' || + commandName === '-v' + ) { + process.stdout.write(`${CLI_VERSION}\n`); + return 0; + } + + const parsedArgs = parseArgs(commandArgs); + if (parsedArgs.options['--help'] || parsedArgs.options['-h']) { + process.stdout.write(renderHelp()); + return 0; + } + + if (commandName === 'setup') { + return await runSetup(buildSetupRequest(parsedArgs)); + } + + if (isQueryCommand(commandName)) { + return await runQuery(commandName, parsedArgs); + } + + throw new UsageError(`Unknown command: ${commandName}.`); + } catch (error) { + if (error instanceof PromptCancelledError) { + process.stderr.write( + renderNotice('Setup cancelled. No changes were made.') + ); + return error.exitCode; + } + + const message = error instanceof Error ? error.message : String(error); + process.stderr.write(renderError(message)); + if (error instanceof UsageError) { + process.stderr.write(renderHelp()); + } + return error instanceof UsageError ? 2 : 1; + } +} + +async function runSetup(request: SetupRequest): Promise { + const baseContext = createPathContext(); + const scope = await resolveScope(request); + const cwd = + scope === 'project' + ? await findProjectRoot(baseContext.cwd, baseContext.homeDir) + : baseContext.cwd; + const context = { ...baseContext, cwd }; + const agents = await resolveSetupAgents(request, context, scope); + + if (agents.length === 0) { + process.stdout.write( + renderNotice( + 'No agents selected. Re-run `docsearch setup` and pick at least one agent.' + ) + ); + return 0; + } + + const results = await setupDocSearch({ + agents, + cwd: context.cwd, + endpoint: request.endpoint, + env: context.env, + homeDir: context.homeDir, + scope, + }); + + process.stdout.write( + renderSetupResults(results, { + endpoint: request.endpoint, + location: scope === 'project' ? context.cwd : undefined, + scope, + }) + ); + return 0; +} + +function resolveScope(request: SetupRequest): Promise { + if (request.scope) { + return Promise.resolve(request.scope); + } + + if (request.yes) { + return Promise.resolve('project'); + } + + return promptScope(); +} + +async function resolveSetupAgents( + request: SetupRequest, + context: ReturnType, + scope: SetupScope +): Promise { + if (request.all) { + return buildAgentChoices(context, []).map((choice) => choice.name); + } + + if (request.agents.length > 0) { + return request.agents; + } + + const detected = await detectAgents(scope, context); + if (request.yes) { + if (detected.length === 0) { + throw new UsageError( + 'No supported agents were detected. Pass an agent flag such as --cursor, or use --all.' + ); + } + return detected; + } + + return promptAgents(buildAgentChoices(context, detected)); +} + +async function runQuery( + commandName: QueryCommandName, + parsedArgs: ReturnType +): Promise { + const request = buildToolRequest(commandName, parsedArgs); + const subject = describeQuery(commandName, parsedArgs.positionals); + + if (request.json) { + const result = await callDocSearchTool(request); + process.stdout.write(formatToolResult(result, true)); + return result.isError === true ? 1 : 0; + } + + const spinner = startSpinner(`Searching ${subject}…`); + try { + const result = await callDocSearchTool(request); + if (result.isError === true) { + throw new Error( + getToolResultText(result) || 'DocSearch MCP returned an error.' + ); + } + spinner.succeed(`Results for ${subject}`); + process.stdout.write(renderResultHeader(commandName, subject)); + process.stdout.write(renderMarkdown(formatToolResult(result, false))); + return 0; + } catch (error) { + spinner.fail(`Search failed for ${subject}`); + throw error; + } +} + +function describeQuery( + commandName: QueryCommandName, + positionals: string[] +): string { + if (commandName === 'docs') { + const [library] = positionals; + return library ?? 'docs'; + } + + return positionals.join(' ').trim() || commandName; +} + +function isQueryCommand(commandName: string): commandName is QueryCommandName { + return ( + commandName === 'docs' || + commandName === 'resolve' || + commandName === 'query' + ); +} diff --git a/packages/docsearch-cli/src/commands.ts b/packages/docsearch-cli/src/commands.ts new file mode 100644 index 00000000..af952e24 --- /dev/null +++ b/packages/docsearch-cli/src/commands.ts @@ -0,0 +1,319 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { + DEFAULT_MCP_ENDPOINT, + TOOL_QUERY_DOCS, + TOOL_RESOLVE_DOCSET, + TOOL_SEARCH_DOCS, +} from './constants.js'; +import { UsageError } from './errors.js'; +import type { SetupAgent, SetupScope } from './setup/agents.js'; + +export type QueryCommandName = 'docs' | 'query' | 'resolve'; +export type CommandName = QueryCommandName | 'help' | 'setup' | 'version'; + +export interface ParsedArgs { + options: Record; + positionals: string[]; +} + +export interface ToolRequest { + endpoint: string; + json: boolean; + toolName: string; + toolArguments: Record; +} + +export interface SetupRequest { + all: boolean; + agents: SetupAgent[]; + endpoint: string; + scope?: SetupScope; + yes: boolean; +} + +const AGENT_FLAGS: Record = { + '--claude': 'claude', + '--codex': 'codex', + '--cursor': 'cursor', + '--gemini': 'gemini', + '--opencode': 'opencode', +}; + +const BOOLEAN_OPTIONS = new Set([ + '--all', + '--claude', + '--codex', + '--cursor', + '--gemini', + '--global', + '--help', + '--json', + '--opencode', + '--project', + '--yes', + '-h', + '-y', +]); + +const VALUE_OPTIONS = new Set([ + '--endpoint', + '--max-docsets', + '--max-results', + '--top-n', +]); +const HELP_OPTIONS = new Set(['--help', '-h']); +const QUERY_COMMON_OPTIONS = new Set(['--endpoint', '--help', '--json', '-h']); +const SETUP_OPTIONS = new Set([ + '--all', + '--claude', + '--codex', + '--cursor', + '--endpoint', + '--gemini', + '--global', + '--help', + '--opencode', + '--project', + '--yes', + '-h', + '-y', +]); + +export function parseArgs(argv: string[]): ParsedArgs { + const options: Record = {}; + const positionals: string[] = []; + + for (let idx = 0; idx < argv.length; idx++) { + const token = argv[idx]; + const equalsIdx = token.startsWith('--') ? token.indexOf('=') : -1; + const option = equalsIdx > 0 ? token.slice(0, equalsIdx) : token; + const inlineValue = equalsIdx > 0 ? token.slice(equalsIdx + 1) : undefined; + + if (VALUE_OPTIONS.has(option)) { + const value = inlineValue ?? argv[idx + 1]; + if (!value || value.startsWith('-')) { + throw new UsageError(`Missing value for ${option}.`); + } + options[option] = value; + if (inlineValue === undefined) { + idx++; + } + } else if (BOOLEAN_OPTIONS.has(option)) { + if (inlineValue !== undefined) { + throw new UsageError(`${option} does not accept a value.`); + } + options[option] = true; + } else if (option.startsWith('-')) { + throw new UsageError(`Unknown option: ${option}.`); + } else { + positionals.push(token); + } + } + + return { options, positionals }; +} + +export function buildToolRequest( + commandName: QueryCommandName, + args: ParsedArgs +): ToolRequest { + validateToolOptions(commandName, args.options); + const endpoint = readEndpoint(args.options); + const json = Boolean(args.options['--json']); + const maxResults = readNumberOption(args.options, '--max-results'); + + switch (commandName) { + case 'docs': { + const [library, ...queryParts] = args.positionals; + const query = queryParts.join(' ').trim(); + if (!library || !query) { + throw new UsageError( + 'Usage: docsearch docs [--json]' + ); + } + + return { + endpoint, + json, + toolName: TOOL_SEARCH_DOCS, + toolArguments: removeUndefined({ + library, + query, + maxResults, + maxDocsets: readNumberOption(args.options, '--max-docsets'), + }), + }; + } + + case 'resolve': { + const query = args.positionals.join(' ').trim(); + if (!query) { + throw new UsageError( + 'Usage: docsearch resolve [--json]' + ); + } + + return { + endpoint, + json, + toolName: TOOL_RESOLVE_DOCSET, + toolArguments: removeUndefined({ + query, + topN: readNumberOption(args.options, '--top-n'), + }), + }; + } + + case 'query': { + const [docsetId, ...queryParts] = args.positionals; + const query = queryParts.join(' ').trim(); + if (!docsetId || !query) { + throw new UsageError( + 'Usage: docsearch query [--json]' + ); + } + + const docsetIds = docsetId + .split(',') + .map((value) => value.trim()) + .filter(Boolean); + if (docsetIds.length === 0) { + throw new UsageError('At least one non-empty docset ID is required.'); + } + + return { + endpoint, + json, + toolName: TOOL_QUERY_DOCS, + toolArguments: removeUndefined({ + docsetIds, + query, + maxResults, + }), + }; + } + + default: { + const exhaustive: never = commandName; + return exhaustive; + } + } +} + +export function buildSetupRequest(args: ParsedArgs): SetupRequest { + validateOptions(args.options, SETUP_OPTIONS, 'setup'); + if (args.positionals.length > 0) { + throw new UsageError(`Unexpected setup argument: ${args.positionals[0]}.`); + } + + const agents = Object.entries(AGENT_FLAGS) + .filter(([flag]) => Boolean(args.options[flag])) + .map(([, agent]) => agent); + + if (args.options['--project'] && args.options['--global']) { + throw new UsageError('Use either --project or --global, not both.'); + } + + return { + agents: uniqueAgents(agents), + all: Boolean(args.options['--all']), + endpoint: readEndpoint(args.options), + scope: resolveScopeFlag(args.options), + yes: Boolean(args.options['--yes'] || args.options['-y']), + }; +} + +function resolveScopeFlag( + options: Record +): SetupScope | undefined { + if (options['--project']) { + return 'project'; + } + + if (options['--global']) { + return 'global'; + } + + return undefined; +} + +function readStringOption( + options: Record, + key: string +): string | undefined { + const value = options[key]; + return typeof value === 'string' ? value : undefined; +} + +function readEndpoint(options: Record): string { + const value = readStringOption(options, '--endpoint') ?? DEFAULT_MCP_ENDPOINT; + let url: URL; + try { + url = new URL(value); + } catch { + throw new UsageError(`--endpoint must be a valid HTTP(S) URL.`); + } + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new UsageError(`--endpoint must use HTTP or HTTPS.`); + } + return url.toString(); +} + +function readNumberOption( + options: Record, + key: string +): number | undefined { + const value = readStringOption(options, key); + if (value === undefined) { + return undefined; + } + + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 1) { + throw new UsageError(`${key} must be a positive integer.`); + } + + return parsed; +} + +function removeUndefined( + values: Record +): Record { + return Object.fromEntries( + Object.entries(values).filter(([, value]) => value !== undefined) + ); +} + +function uniqueAgents(agents: SetupAgent[]): SetupAgent[] { + return [...new Set(agents)]; +} + +function validateToolOptions( + commandName: QueryCommandName, + options: Record +): void { + const allowed = new Set(QUERY_COMMON_OPTIONS); + if (commandName === 'docs') { + allowed.add('--max-docsets'); + allowed.add('--max-results'); + } else if (commandName === 'resolve') { + allowed.add('--top-n'); + } else { + allowed.add('--max-results'); + } + validateOptions(options, allowed, commandName); +} + +function validateOptions( + options: Record, + allowed: ReadonlySet, + commandName: string +): void { + for (const option of Object.keys(options)) { + if (!allowed.has(option) && !HELP_OPTIONS.has(option)) { + throw new UsageError( + `${option} is not valid for the ${commandName} command.` + ); + } + } +} diff --git a/packages/docsearch-cli/src/constants.ts b/packages/docsearch-cli/src/constants.ts new file mode 100644 index 00000000..16e57fd8 --- /dev/null +++ b/packages/docsearch-cli/src/constants.ts @@ -0,0 +1,23 @@ +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const CLI_NAME = 'docsearch'; +export const CLI_VERSION = readPackageVersion(); +export const DEFAULT_MCP_ENDPOINT = 'https://mcp.algolia.com/1/docsearch/mcp'; +export const DOCSEARCH_MCP_SERVER_NAME = 'algolia-docsearch'; + +export const TOOL_SEARCH_DOCS = 'algolia_docsearch_search_docs'; +export const TOOL_RESOLVE_DOCSET = 'algolia_docsearch_resolve_docset'; +export const TOOL_QUERY_DOCS = 'algolia_docsearch_query_docs'; + +function readPackageVersion(): string { + const currentDirectory = dirname(fileURLToPath(import.meta.url)); + const packageJson = JSON.parse( + readFileSync(join(currentDirectory, '..', 'package.json'), 'utf-8') + ) as { + version?: string; + }; + + return packageJson.version ?? '0.0.0'; +} diff --git a/packages/docsearch-cli/src/errors.ts b/packages/docsearch-cli/src/errors.ts new file mode 100644 index 00000000..7879e50c --- /dev/null +++ b/packages/docsearch-cli/src/errors.ts @@ -0,0 +1,6 @@ +export class UsageError extends Error { + constructor(message: string) { + super(message); + this.name = 'UsageError'; + } +} diff --git a/packages/docsearch-cli/src/index.ts b/packages/docsearch-cli/src/index.ts new file mode 100644 index 00000000..9ce5ef08 --- /dev/null +++ b/packages/docsearch-cli/src/index.ts @@ -0,0 +1,5 @@ +#!/usr/bin/env node +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { runCli } from './cli.js'; + +process.exitCode = await runCli(); diff --git a/packages/docsearch-cli/src/mcp/__tests__/client.test.ts b/packages/docsearch-cli/src/mcp/__tests__/client.test.ts new file mode 100644 index 00000000..404fae77 --- /dev/null +++ b/packages/docsearch-cli/src/mcp/__tests__/client.test.ts @@ -0,0 +1,78 @@ +// @vitest-environment node +/* eslint-disable max-classes-per-file -- SDK constructors are represented by minimal test doubles. */ + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { callDocSearchTool } from '../client'; + +const mocks = vi.hoisted(() => ({ + callTool: vi.fn(), + close: vi.fn(), + connect: vi.fn(), + listTools: vi.fn(), +})); + +vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({ + Client: class { + callTool = mocks.callTool; + close = mocks.close; + connect = mocks.connect; + listTools = mocks.listTools; + }, +})); + +vi.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({ + StreamableHTTPClientTransport: class {}, +})); + +beforeEach(() => { + mocks.connect.mockResolvedValue(undefined); + mocks.close.mockResolvedValue(undefined); + mocks.listTools.mockResolvedValue({ + tools: [{ name: 'algolia_docsearch_resolve_docset' }], + }); + mocks.callTool.mockResolvedValue({ + content: [{ text: 'result', type: 'text' }], + }); +}); + +afterEach(() => { + vi.clearAllMocks(); +}); + +describe('callDocSearchTool', () => { + it('applies request timeouts and closes the client', async () => { + await callDocSearchTool({ + endpoint: 'https://example.com/mcp', + toolArguments: { query: 'Next.js' }, + toolName: 'algolia_docsearch_resolve_docset', + }); + + expect(mocks.listTools).toHaveBeenCalledWith(undefined, { + timeout: 30_000, + }); + expect(mocks.callTool).toHaveBeenCalledWith( + { + arguments: { query: 'Next.js' }, + name: 'algolia_docsearch_resolve_docset', + }, + undefined, + { timeout: 30_000 } + ); + expect(mocks.close).toHaveBeenCalledOnce(); + }); + + it('closes the client when connection fails', async () => { + mocks.connect.mockRejectedValue(new Error('connection failed')); + + await expect( + callDocSearchTool({ + endpoint: 'https://example.com/mcp', + toolArguments: {}, + toolName: 'algolia_docsearch_resolve_docset', + }) + ).rejects.toThrow('connection failed'); + + expect(mocks.close).toHaveBeenCalledOnce(); + }); +}); diff --git a/packages/docsearch-cli/src/mcp/__tests__/format.test.ts b/packages/docsearch-cli/src/mcp/__tests__/format.test.ts new file mode 100644 index 00000000..9cca4804 --- /dev/null +++ b/packages/docsearch-cli/src/mcp/__tests__/format.test.ts @@ -0,0 +1,31 @@ +// @vitest-environment node + +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { describe, expect, it } from 'vitest'; + +import { formatToolResult } from '../format'; + +describe('formatToolResult', () => { + it('prints text content for normal output', () => { + const result: CallToolResult = { + content: [ + { type: 'text', text: 'First result' }, + { type: 'text', text: 'Second result' }, + ], + }; + + expect(formatToolResult(result, false)).toBe( + 'First result\n\nSecond result\n' + ); + }); + + it('prints the raw result for JSON output', () => { + const result: CallToolResult = { + content: [{ type: 'text', text: 'Result' }], + }; + + expect(formatToolResult(result, true)).toBe( + `${JSON.stringify(result, null, 2)}\n` + ); + }); +}); diff --git a/packages/docsearch-cli/src/mcp/client.ts b/packages/docsearch-cli/src/mcp/client.ts new file mode 100644 index 00000000..bdc39d4b --- /dev/null +++ b/packages/docsearch-cli/src/mcp/client.ts @@ -0,0 +1,59 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; + +import { CLI_NAME, CLI_VERSION } from '../constants.js'; + +const MCP_REQUEST_TIMEOUT_MS = 30_000; + +export interface CallDocSearchToolOptions { + endpoint: string; + toolArguments: Record; + toolName: string; +} + +export async function callDocSearchTool({ + endpoint, + toolArguments, + toolName, +}: CallDocSearchToolOptions): Promise { + const client = new Client({ + name: CLI_NAME, + version: CLI_VERSION, + }); + const transport = new StreamableHTTPClientTransport(new URL(endpoint)); + + try { + await client.connect(transport); + + const { tools } = await client.listTools(undefined, { + timeout: MCP_REQUEST_TIMEOUT_MS, + }); + if (!tools.some((tool) => tool.name === toolName)) { + throw new Error( + `DocSearch MCP tool "${toolName}" is not available at ${endpoint}.` + ); + } + + const result = await client.callTool( + { + name: toolName, + arguments: toolArguments, + }, + undefined, + { + timeout: MCP_REQUEST_TIMEOUT_MS, + } + ); + if (!('content' in result)) { + throw new Error( + `DocSearch MCP tool "${toolName}" returned an unsupported result shape.` + ); + } + + return result as CallToolResult; + } finally { + await client.close(); + } +} diff --git a/packages/docsearch-cli/src/mcp/format.ts b/packages/docsearch-cli/src/mcp/format.ts new file mode 100644 index 00000000..9e5e0ab5 --- /dev/null +++ b/packages/docsearch-cli/src/mcp/format.ts @@ -0,0 +1,32 @@ +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; + +type TextContent = { + text: string; + type: 'text'; +}; + +export function formatToolResult( + result: CallToolResult, + json: boolean +): string { + if (json) { + return `${JSON.stringify(result, null, 2)}\n`; + } + + const text = getToolResultText(result); + if (text) { + return `${text}\n`; + } + + return `${JSON.stringify(result, null, 2)}\n`; +} + +export function getToolResultText(result: CallToolResult): string { + const text = result.content + .filter((part): part is TextContent => part.type === 'text') + .map((part) => part.text.trim()) + .filter(Boolean) + .join('\n\n'); + + return text; +} diff --git a/packages/docsearch-cli/src/setup/__tests__/prompt.test.ts b/packages/docsearch-cli/src/setup/__tests__/prompt.test.ts new file mode 100644 index 00000000..dee2aa69 --- /dev/null +++ b/packages/docsearch-cli/src/setup/__tests__/prompt.test.ts @@ -0,0 +1,72 @@ +// @vitest-environment node + +import { describe, expect, it } from 'vitest'; + +import type { AgentChoice } from '../prompt'; +import { + parseAgentSelection, + parseScopeSelection, + PromptCancelledError, +} from '../prompt'; + +const CHOICES: AgentChoice[] = [ + { detected: true, displayName: 'Cursor', name: 'cursor' }, + { detected: false, displayName: 'Claude Code', name: 'claude' }, + { detected: false, displayName: 'Codex', name: 'codex' }, +]; + +describe('parseAgentSelection', () => { + it('defaults blank input to detected agents only', () => { + expect(parseAgentSelection('', CHOICES)).toEqual(['cursor']); + }); + + it('selects every agent for "all"', () => { + expect(parseAgentSelection('all', CHOICES)).toEqual([ + 'cursor', + 'claude', + 'codex', + ]); + }); + + it('selects nothing for "none"', () => { + expect(parseAgentSelection('none', CHOICES)).toEqual([]); + }); + + it('parses a mix of numbers and names without duplicates', () => { + expect(parseAgentSelection('1, claude, 2', CHOICES)).toEqual([ + 'cursor', + 'claude', + ]); + }); + + it('rejects unknown tokens', () => { + expect(() => parseAgentSelection('9', CHOICES)).toThrow(/Unknown agent/); + expect(() => parseAgentSelection('vim', CHOICES)).toThrow(/Unknown agent/); + }); +}); + +describe('parseScopeSelection', () => { + it('defaults blank input to project', () => { + expect(parseScopeSelection('')).toBe('project'); + }); + + it('accepts numbers, letters, and names', () => { + expect(parseScopeSelection('1')).toBe('project'); + expect(parseScopeSelection('p')).toBe('project'); + expect(parseScopeSelection('Project')).toBe('project'); + expect(parseScopeSelection('2')).toBe('global'); + expect(parseScopeSelection('g')).toBe('global'); + expect(parseScopeSelection('GLOBAL')).toBe('global'); + }); + + it('rejects unknown scope input', () => { + expect(() => parseScopeSelection('workspace')).toThrow(/Unknown scope/); + }); +}); + +describe('PromptCancelledError', () => { + it('preserves the intended process exit code', () => { + expect(new PromptCancelledError().exitCode).toBe(0); + expect(new PromptCancelledError(130).exitCode).toBe(130); + }); +}); diff --git a/packages/docsearch-cli/src/setup/__tests__/setup.test.ts b/packages/docsearch-cli/src/setup/__tests__/setup.test.ts new file mode 100644 index 00000000..e583f83e --- /dev/null +++ b/packages/docsearch-cli/src/setup/__tests__/setup.test.ts @@ -0,0 +1,349 @@ +// @vitest-environment node + +import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import { afterEach, describe, expect, it } from 'vitest'; + +import { DOCSEARCH_MCP_SERVER_NAME } from '../../constants'; +import type { SetupAgent, SetupScope } from '../agents'; +import { findProjectRoot, setupDocSearch, type SetupOptions } from '../setup'; + +const tempDirs: string[] = []; + +async function createTempDir(): Promise { + const path = await mkdtemp(join(tmpdir(), 'docsearch-cli-')); + tempDirs.push(path); + return path; +} + +afterEach(async () => { + await Promise.all( + tempDirs.splice(0).map((path) => rm(path, { force: true, recursive: true })) + ); +}); + +describe('setupDocSearch', () => { + it('installs Cursor MCP config, rule, and skill in project scope', async () => { + const cwd = await createTempDir(); + const homeDir = await createTempDir(); + + const results = await setupDocSearch({ + agents: ['cursor'], + cwd, + endpoint: 'https://example.com/mcp', + env: {}, + homeDir, + scope: 'project', + }); + + expect(results[0]).toMatchObject({ + agent: 'Cursor', + mcpStatus: 'configured', + ruleStatus: 'installed', + skillStatus: 'installed', + }); + + const mcp = JSON.parse( + await readFile(join(cwd, '.cursor', 'mcp.json'), 'utf-8') + ) as { + mcpServers: Record; + }; + expect(mcp.mcpServers[DOCSEARCH_MCP_SERVER_NAME]).toEqual({ + url: 'https://example.com/mcp', + }); + + const rule = await readFile( + join(cwd, '.cursor', 'rules', `${DOCSEARCH_MCP_SERVER_NAME}.mdc`), + 'utf-8' + ); + expect(rule).toContain('alwaysApply: true'); + expect(rule).toContain('algolia_docsearch_search_docs'); + + const skill = await readFile( + join(cwd, '.cursor', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + 'utf-8' + ); + expect(skill).toMatch(/^---\nname: algolia-docsearch\ndescription:/); + expect(skill).toContain('DocSearch MCP'); + }); + + it('updates an existing Codex TOML config and shared skill idempotently', async () => { + const cwd = await createTempDir(); + const homeDir = await createTempDir(); + const options: SetupOptions = { + agents: ['codex'], + cwd, + endpoint: 'https://example.com/mcp', + env: {}, + homeDir, + scope: 'project' as const, + }; + + await setupDocSearch(options); + const results = await setupDocSearch(options); + + expect(results[0]).toMatchObject({ + agent: 'Codex', + mcpStatus: 'updated', + ruleStatus: 'updated', + skillStatus: 'preserved', + }); + + const config = await readFile(join(cwd, '.codex', 'config.toml'), 'utf-8'); + expect(config.match(/\[mcp_servers\.algolia-docsearch\]/g)).toHaveLength(1); + expect(config).toContain('url = "https://example.com/mcp"'); + + const agentsFile = await readFile(join(cwd, 'AGENTS.md'), 'utf-8'); + expect(agentsFile.match(/algolia-docsearch:start/g)).toHaveLength(1); + + const skill = await readFile( + join(cwd, '.agents', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + 'utf-8' + ); + expect(skill).toContain('Use this skill'); + }); + + it('preserves user edits in dedicated rule and skill files', async () => { + const cwd = await createTempDir(); + const homeDir = await createTempDir(); + const options: SetupOptions = { + agents: ['cursor'], + cwd, + endpoint: 'https://example.com/mcp', + env: {}, + homeDir, + scope: 'project', + }; + + await setupDocSearch(options); + const rulePath = join( + cwd, + '.cursor', + 'rules', + `${DOCSEARCH_MCP_SERVER_NAME}.mdc` + ); + const skillPath = join( + cwd, + '.cursor', + 'skills', + DOCSEARCH_MCP_SERVER_NAME, + 'SKILL.md' + ); + await writeFile( + rulePath, + `${await readFile(rulePath, 'utf-8')}\nUser rule customization.\n` + ); + await writeFile( + skillPath, + `${await readFile(skillPath, 'utf-8')}\nUser skill customization.\n` + ); + + const [result] = await setupDocSearch(options); + + expect(result).toMatchObject({ + ruleStatus: 'preserved', + skillStatus: 'preserved', + }); + expect(await readFile(rulePath, 'utf-8')).toContain( + 'User rule customization.' + ); + expect(await readFile(skillPath, 'utf-8')).toContain( + 'User skill customization.' + ); + }); + + it('updates an existing OpenCode JSONC config in place', async () => { + const cwd = await createTempDir(); + const homeDir = await createTempDir(); + const configPath = join(cwd, 'opencode.jsonc'); + await writeFile( + configPath, + `{ + // Existing OpenCode preferences. + "theme": "system", +} +` + ); + + const [result] = await setupDocSearch({ + agents: ['opencode'], + cwd, + endpoint: 'https://example.com/mcp', + env: {}, + homeDir, + scope: 'project', + }); + + expect(result.mcpPath).toBe(configPath); + expect(await readFile(configPath, 'utf-8')).toContain( + '// Existing OpenCode preferences.' + ); + expect(await readFile(configPath, 'utf-8')).toContain( + '"algolia-docsearch"' + ); + await expect( + readFile(join(cwd, 'opencode.json'), 'utf-8') + ).rejects.toMatchObject({ code: 'ENOENT' }); + }); +}); + +interface AgentPathCase { + agent: SetupAgent; + mcp: string; + rule: string; + scope: SetupScope; + skill: string; +} + +const AGENT_PATH_CASES: AgentPathCase[] = [ + { + agent: 'claude', + mcp: '.mcp.json', + rule: join('.claude', 'rules', `${DOCSEARCH_MCP_SERVER_NAME}.md`), + scope: 'project', + skill: join('.claude', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'cursor', + mcp: join('.cursor', 'mcp.json'), + rule: join('.cursor', 'rules', `${DOCSEARCH_MCP_SERVER_NAME}.mdc`), + scope: 'project', + skill: join('.cursor', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'codex', + mcp: join('.codex', 'config.toml'), + rule: 'AGENTS.md', + scope: 'project', + skill: join('.agents', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'opencode', + mcp: 'opencode.json', + rule: 'AGENTS.md', + scope: 'project', + skill: join('.agents', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'gemini', + mcp: join('.gemini', 'settings.json'), + rule: 'GEMINI.md', + scope: 'project', + skill: join('.gemini', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'claude', + mcp: '.claude.json', + rule: join('.claude', 'rules', `${DOCSEARCH_MCP_SERVER_NAME}.md`), + scope: 'global', + skill: join('.claude', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'cursor', + mcp: join('.cursor', 'mcp.json'), + rule: join('.cursor', 'rules', `${DOCSEARCH_MCP_SERVER_NAME}.mdc`), + scope: 'global', + skill: join('.cursor', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'codex', + mcp: join('.codex', 'config.toml'), + rule: join('.codex', 'AGENTS.md'), + scope: 'global', + skill: join('.agents', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'opencode', + mcp: join('.config', 'opencode', 'opencode.json'), + rule: join('.config', 'opencode', 'AGENTS.md'), + scope: 'global', + skill: join('.agents', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, + { + agent: 'gemini', + mcp: join('.gemini', 'settings.json'), + rule: join('.gemini', 'GEMINI.md'), + scope: 'global', + skill: join('.gemini', 'skills', DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'), + }, +]; + +describe.each(AGENT_PATH_CASES)( + '$agent $scope paths', + ({ agent, mcp, rule, scope, skill }) => { + it('writes every artifact to the expected location', async () => { + const cwd = await createTempDir(); + const homeDir = await createTempDir(); + const [result] = await setupDocSearch({ + agents: [agent], + cwd, + endpoint: 'https://example.com/mcp', + env: {}, + homeDir, + scope, + }); + const base = scope === 'project' ? cwd : homeDir; + + expect(result.mcpPath).toBe(join(base, mcp)); + expect(result.rulePath).toBe(join(base, rule)); + expect(result.skillPath).toBe(join(base, skill)); + }); + } +); + +describe('findProjectRoot', () => { + it('walks up to the nearest VCS root', async () => { + const home = await createTempDir(); + const root = join(home, 'repo'); + const nested = join(root, 'packages', 'app', 'src'); + await mkdir(join(root, '.git'), { recursive: true }); + await mkdir(nested, { recursive: true }); + + expect(await findProjectRoot(nested, home)).toBe(root); + }); + + it('prefers a repo-root marker over a nested package.json when there is no VCS', async () => { + const home = await createTempDir(); + const root = join(home, 'repo'); + const pkg = join(root, 'packages', 'app'); + await mkdir(pkg, { recursive: true }); + await writeFile( + join(root, 'pnpm-workspace.yaml'), + 'packages:\n - packages/*\n' + ); + await writeFile(join(root, 'package.json'), '{}'); + await writeFile(join(pkg, 'package.json'), '{}'); + + expect(await findProjectRoot(pkg, home)).toBe(root); + }); + + it('falls back to the start directory when nothing is found', async () => { + const home = await createTempDir(); + const start = join(home, 'loose', 'dir'); + await mkdir(start, { recursive: true }); + + expect(await findProjectRoot(start, home)).toBe(start); + }); + + it('does not cross the home directory', async () => { + const home = await createTempDir(); + const start = join(home, 'project'); + await mkdir(start, { recursive: true }); + // A .git in home must not be picked up. + await mkdir(join(home, '.git'), { recursive: true }); + + expect(await findProjectRoot(start, home)).toBe(start); + }); + + it('still finds a VCS root when the project is outside the home directory', async () => { + const home = await createTempDir(); + const root = await createTempDir(); + const nested = join(root, 'packages', 'app'); + await mkdir(join(root, '.git'), { recursive: true }); + await mkdir(nested, { recursive: true }); + + expect(await findProjectRoot(nested, home)).toBe(root); + }); +}); diff --git a/packages/docsearch-cli/src/setup/__tests__/writers.test.ts b/packages/docsearch-cli/src/setup/__tests__/writers.test.ts new file mode 100644 index 00000000..619364d2 --- /dev/null +++ b/packages/docsearch-cli/src/setup/__tests__/writers.test.ts @@ -0,0 +1,164 @@ +// @vitest-environment node + +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import { afterEach, describe, expect, it } from 'vitest'; + +import { appendTomlServer, upsertJsonServerEntry } from '../writers'; + +const tempDirs: string[] = []; + +async function createTempDir(): Promise { + const path = await mkdtemp(join(tmpdir(), 'docsearch-writers-')); + tempDirs.push(path); + return path; +} + +afterEach(async () => { + await Promise.all( + tempDirs.splice(0).map((path) => rm(path, { force: true, recursive: true })) + ); +}); + +describe('upsertJsonServerEntry', () => { + it('preserves JSONC comments, trailing commas, and unrelated configuration', async () => { + const cwd = await createTempDir(); + const path = join(cwd, 'opencode.jsonc'); + await writeFile( + path, + `{ + // Keep this comment. + "theme": "system", + "mcp": { + "existing": { + "type": "remote", + "url": "https://example.com/mcp", + }, + }, +} +` + ); + + await upsertJsonServerEntry(path, 'mcp', { + enabled: true, + type: 'remote', + url: 'https://mcp.algolia.com/1/docsearch/mcp', + }); + + const updated = await readFile(path, 'utf-8'); + expect(updated).toContain('// Keep this comment.'); + expect(updated).toContain('"theme": "system"'); + expect(updated).toContain('"existing"'); + expect(updated).toContain('"algolia-docsearch"'); + expect(updated).toContain( + '"url": "https://mcp.algolia.com/1/docsearch/mcp"' + ); + }); + + it('preserves unknown fields on an existing DocSearch entry', async () => { + const cwd = await createTempDir(); + const path = join(cwd, 'mcp.json'); + await writeFile( + path, + `${JSON.stringify({ + mcpServers: { + 'algolia-docsearch': { + headers: { 'X-Custom': 'value' }, + timeout: 15_000, + url: 'https://old.example.com/mcp', + }, + }, + })}\n` + ); + + const result = await upsertJsonServerEntry(path, 'mcpServers', { + url: 'https://mcp.algolia.com/1/docsearch/mcp', + }); + const updated = JSON.parse(await readFile(path, 'utf-8')) as { + mcpServers: Record>; + }; + + expect(result.alreadyExists).toBe(true); + expect(updated.mcpServers['algolia-docsearch']).toEqual({ + headers: { 'X-Custom': 'value' }, + timeout: 15_000, + url: 'https://mcp.algolia.com/1/docsearch/mcp', + }); + }); + + it('rejects malformed JSONC with the file path in the error', async () => { + const cwd = await createTempDir(); + const path = join(cwd, 'broken.jsonc'); + await writeFile(path, '{ "mcp": {'); + + await expect( + upsertJsonServerEntry(path, 'mcp', { url: 'https://example.com' }) + ).rejects.toThrow(path); + }); + + it('rejects an existing configuration section with the wrong shape', async () => { + const cwd = await createTempDir(); + const path = join(cwd, 'mcp.json'); + await writeFile(path, '{ "mcpServers": [] }\n'); + + await expect( + upsertJsonServerEntry(path, 'mcpServers', { url: 'https://example.com' }) + ).rejects.toThrow('"mcpServers" must be an object'); + }); +}); + +describe('appendTomlServer', () => { + it('updates known values while preserving custom keys and nested tables', async () => { + const cwd = await createTempDir(); + const path = join(cwd, 'config.toml'); + await writeFile( + path, + `# [mcp_servers.algolia-docsearch] +[mcp_servers.algolia-docsearch] +url = "https://old.example.com/mcp" # Keep this comment. +enabled = false + +[mcp_servers.algolia-docsearch.http_headers] +X-Custom = "value" + +[mcp_servers.other] +url = "https://other.example.com/mcp" +` + ); + + const result = await appendTomlServer(path, { + url: 'https://mcp.algolia.com/1/docsearch/mcp', + }); + const updated = await readFile(path, 'utf-8'); + + expect(result.alreadyExists).toBe(true); + expect(updated).toContain('# [mcp_servers.algolia-docsearch]'); + expect(updated).toContain( + 'url = "https://mcp.algolia.com/1/docsearch/mcp" # Keep this comment.' + ); + expect(updated).toContain('enabled = false'); + expect(updated).toContain('[mcp_servers.algolia-docsearch.http_headers]'); + expect(updated).toContain('X-Custom = "value"'); + expect(updated).toContain('[mcp_servers.other]'); + }); + + it('refuses to edit duplicate DocSearch sections', async () => { + const cwd = await createTempDir(); + const path = join(cwd, 'config.toml'); + await writeFile( + path, + `[mcp_servers.algolia-docsearch] +url = "https://one.example.com" + +[mcp_servers.algolia-docsearch] +url = "https://two.example.com" +` + ); + + await expect( + appendTomlServer(path, { url: 'https://example.com' }) + ).rejects.toThrow(/duplicate/i); + }); +}); diff --git a/packages/docsearch-cli/src/setup/agents.ts b/packages/docsearch-cli/src/setup/agents.ts new file mode 100644 index 00000000..64ba6b0a --- /dev/null +++ b/packages/docsearch-cli/src/setup/agents.ts @@ -0,0 +1,215 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { join } from 'node:path'; + +import { DOCSEARCH_MCP_SERVER_NAME } from '../constants.js'; + +export type SetupAgent = 'claude' | 'codex' | 'cursor' | 'gemini' | 'opencode'; +export type SetupScope = 'global' | 'project'; + +export interface PathContext { + cwd: string; + env: NodeJS.ProcessEnv; + homeDir: string; +} + +export interface AgentConfig { + detect: { + globalPaths: string[]; + projectPaths: string[]; + }; + displayName: string; + mcp: { + configKey: string; + globalPaths: string[]; + projectPaths: string[]; + buildEntry: (endpoint: string) => Record; + }; + name: SetupAgent; + rule: + | { + dir: (scope: SetupScope) => string; + filename: string; + kind: 'file'; + } + | { + file: (scope: SetupScope) => string; + kind: 'append'; + }; + skill: { + dir: (scope: SetupScope) => string; + }; +} + +export function getAgent(name: SetupAgent, context: PathContext): AgentConfig { + return agents(context)[name]; +} + +export function getAllAgents( + context: PathContext +): Record { + return agents(context); +} + +function agents(context: PathContext): Record { + const claudeConfigDirectory = + context.env.CLAUDE_CONFIG_DIR ?? join(context.homeDir, '.claude'); + const claudeGlobalMcpPath = context.env.CLAUDE_CONFIG_DIR + ? join(claudeConfigDirectory, '.claude.json') + : join(context.homeDir, '.claude.json'); + + return { + claude: { + name: 'claude', + displayName: 'Claude Code', + mcp: { + projectPaths: [join(context.cwd, '.mcp.json')], + globalPaths: [claudeGlobalMcpPath], + configKey: 'mcpServers', + buildEntry: (endpoint) => ({ type: 'http', url: endpoint }), + }, + rule: { + kind: 'file', + dir: (scope) => + scope === 'global' + ? join(claudeConfigDirectory, 'rules') + : join(context.cwd, '.claude', 'rules'), + filename: `${DOCSEARCH_MCP_SERVER_NAME}.md`, + }, + skill: { + dir: (scope) => + scope === 'global' + ? join(claudeConfigDirectory, 'skills') + : join(context.cwd, '.claude', 'skills'), + }, + detect: { + projectPaths: [ + join(context.cwd, '.mcp.json'), + join(context.cwd, '.claude'), + ], + globalPaths: [claudeConfigDirectory, claudeGlobalMcpPath], + }, + }, + cursor: { + name: 'cursor', + displayName: 'Cursor', + mcp: { + projectPaths: [join(context.cwd, '.cursor', 'mcp.json')], + globalPaths: [join(context.homeDir, '.cursor', 'mcp.json')], + configKey: 'mcpServers', + buildEntry: (endpoint) => ({ url: endpoint }), + }, + rule: { + kind: 'file', + dir: (scope) => + scope === 'global' + ? join(context.homeDir, '.cursor', 'rules') + : join(context.cwd, '.cursor', 'rules'), + filename: `${DOCSEARCH_MCP_SERVER_NAME}.mdc`, + }, + skill: { + dir: (scope) => + scope === 'global' + ? join(context.homeDir, '.cursor', 'skills') + : join(context.cwd, '.cursor', 'skills'), + }, + detect: { + projectPaths: [join(context.cwd, '.cursor')], + globalPaths: [join(context.homeDir, '.cursor')], + }, + }, + codex: { + name: 'codex', + displayName: 'Codex', + mcp: { + projectPaths: [join(context.cwd, '.codex', 'config.toml')], + globalPaths: [join(context.homeDir, '.codex', 'config.toml')], + configKey: 'mcp_servers', + buildEntry: (endpoint) => ({ url: endpoint }), + }, + rule: { + kind: 'append', + file: (scope) => + scope === 'global' + ? join(context.homeDir, '.codex', 'AGENTS.md') + : join(context.cwd, 'AGENTS.md'), + }, + skill: { + dir: (scope) => + scope === 'global' + ? join(context.homeDir, '.agents', 'skills') + : join(context.cwd, '.agents', 'skills'), + }, + detect: { + projectPaths: [join(context.cwd, '.codex')], + globalPaths: [join(context.homeDir, '.codex')], + }, + }, + opencode: { + name: 'opencode', + displayName: 'OpenCode', + mcp: { + projectPaths: [ + join(context.cwd, 'opencode.json'), + join(context.cwd, 'opencode.jsonc'), + ], + globalPaths: [ + join(context.homeDir, '.config', 'opencode', 'opencode.json'), + join(context.homeDir, '.config', 'opencode', 'opencode.jsonc'), + ], + configKey: 'mcp', + buildEntry: (endpoint) => ({ + type: 'remote', + url: endpoint, + enabled: true, + }), + }, + rule: { + kind: 'append', + file: (scope) => + scope === 'global' + ? join(context.homeDir, '.config', 'opencode', 'AGENTS.md') + : join(context.cwd, 'AGENTS.md'), + }, + skill: { + dir: (scope) => + scope === 'global' + ? join(context.homeDir, '.agents', 'skills') + : join(context.cwd, '.agents', 'skills'), + }, + detect: { + projectPaths: [ + join(context.cwd, 'opencode.json'), + join(context.cwd, 'opencode.jsonc'), + ], + globalPaths: [join(context.homeDir, '.config', 'opencode')], + }, + }, + gemini: { + name: 'gemini', + displayName: 'Gemini CLI', + mcp: { + projectPaths: [join(context.cwd, '.gemini', 'settings.json')], + globalPaths: [join(context.homeDir, '.gemini', 'settings.json')], + configKey: 'mcpServers', + buildEntry: (endpoint) => ({ httpUrl: endpoint }), + }, + rule: { + kind: 'append', + file: (scope) => + scope === 'global' + ? join(context.homeDir, '.gemini', 'GEMINI.md') + : join(context.cwd, 'GEMINI.md'), + }, + skill: { + dir: (scope) => + scope === 'global' + ? join(context.homeDir, '.gemini', 'skills') + : join(context.cwd, '.gemini', 'skills'), + }, + detect: { + projectPaths: [join(context.cwd, '.gemini')], + globalPaths: [join(context.homeDir, '.gemini')], + }, + }, + }; +} diff --git a/packages/docsearch-cli/src/setup/prompt.ts b/packages/docsearch-cli/src/setup/prompt.ts new file mode 100644 index 00000000..ae130e43 --- /dev/null +++ b/packages/docsearch-cli/src/setup/prompt.ts @@ -0,0 +1,375 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import readline from 'node:readline'; + +import { UsageError } from '../errors.js'; +import { color, isInteractive, symbols } from '../ui/theme.js'; + +import type { SetupAgent, SetupScope } from './agents.js'; + +export class PromptCancelledError extends Error { + constructor(readonly exitCode: number = 0) { + super('Setup cancelled.'); + this.name = 'PromptCancelledError'; + } +} + +export interface AgentChoice { + detected: boolean; + displayName: string; + name: SetupAgent; +} + +interface ScopeChoice { + hint: string; + label: string; + value: SetupScope; +} + +const SCOPE_CHOICES: ScopeChoice[] = [ + { hint: 'the detected repository root', label: 'Project', value: 'project' }, + { + hint: 'every project (~/.cursor, ~/.codex, …)', + label: 'Global', + value: 'global', + }, +]; + +export function parseScopeSelection(raw: string): SetupScope { + const normalized = raw.trim().toLowerCase(); + + if ( + normalized === '' || + normalized === '1' || + normalized === 'p' || + normalized === 'project' + ) { + return 'project'; + } + + if (normalized === '2' || normalized === 'g' || normalized === 'global') { + return 'global'; + } + + throw new UsageError( + `Unknown scope: "${raw.trim()}". Choose "project" or "global".` + ); +} + +export function promptScope(): Promise { + if (isInteractive()) { + return promptScopeInteractive(); + } + + return promptScopeByLine(); +} + +function promptScopeInteractive(): Promise { + const input = process.stdin; + const output = process.stderr; + const wasRaw = input.isRaw === true; + let cursor = 0; + let renderedLines = 0; + + try { + input.setRawMode(true); + } catch { + return promptScopeByLine(); + } + readline.emitKeypressEvents(input); + + return new Promise((resolve, reject) => { + function render(): void { + if (renderedLines > 0) { + output.write(`\u001B[${renderedLines}A`); + } + + const lines = [ + `${color.magenta(symbols.diamond)} ${color.bold('Where should DocSearch be installed?')} ${color.dim('(↑↓ move · enter confirm)')}`, + ...SCOPE_CHOICES.map((choice, index) => { + const active = index === cursor; + const radio = active + ? color.green(symbols.checkboxOn) + : color.dim(symbols.checkboxOff); + const pointer = active ? color.cyan(symbols.pointer) : ' '; + const label = active ? color.cyan(choice.label) : choice.label; + return `${pointer} ${radio} ${label} ${color.dim(`— ${choice.hint}`)}`; + }), + ]; + + output.write(`${lines.map((line) => `\u001B[K${line}`).join('\n')}\n`); + renderedLines = lines.length; + } + + function cleanup(): void { + input.setRawMode(wasRaw); + input.removeListener('keypress', onKeypress); + output.write('\u001B[?25h'); + input.pause(); + } + + function onKeypress(_input: string, key: readline.Key | undefined): void { + if (!key) { + return; + } + + if (key.name === 'up' || key.name === 'k') { + cursor = (cursor - 1 + SCOPE_CHOICES.length) % SCOPE_CHOICES.length; + render(); + } else if (key.name === 'down' || key.name === 'j') { + cursor = (cursor + 1) % SCOPE_CHOICES.length; + render(); + } else if (key.name === 'return') { + cleanup(); + resolve(SCOPE_CHOICES[cursor].value); + } else if ( + key.name === 'escape' || + (key.ctrl === true && key.name === 'c') + ) { + cleanup(); + output.write('\n'); + reject(new PromptCancelledError(key.ctrl === true ? 130 : 0)); + } + } + + output.write('\u001B[?25l'); + input.on('keypress', onKeypress); + input.resume(); + render(); + }); +} + +async function promptScopeByLine(): Promise { + const output = process.stderr; + + output.write( + `${color.magenta(symbols.diamond)} ${color.bold('Where should DocSearch be installed?')}\n` + ); + for (const [index, choice] of SCOPE_CHOICES.entries()) { + output.write( + ` ${color.cyan(String(index + 1))}. ${choice.label} ${color.dim(`— ${choice.hint}`)}\n` + ); + } + output.write( + `${color.dim('Enter "project" or "global" (blank for project):')} ` + ); + + return parseScopeSelection(await readSingleLine()); +} + +export function parseAgentSelection( + raw: string, + choices: AgentChoice[] +): SetupAgent[] { + const normalized = raw.trim().toLowerCase(); + + if (normalized === '') { + return choices + .filter((choice) => choice.detected) + .map((choice) => choice.name); + } + + if (normalized === 'all' || normalized === '*') { + return choices.map((choice) => choice.name); + } + + if (normalized === 'none') { + return []; + } + + const selected = new Set(); + + for (const token of normalized.split(/[\s,]+/).filter(Boolean)) { + const index = Number(token); + if (Number.isInteger(index) && index >= 1 && index <= choices.length) { + selected.add(choices[index - 1].name); + } else { + const match = choices.find((choice) => choice.name === token); + if (!match) { + throw new UsageError( + `Unknown agent: "${token}". Choose a number or one of: ${choices.map((c) => c.name).join(', ')}.` + ); + } + selected.add(match.name); + } + } + + return [...selected]; +} + +export function promptAgents(choices: AgentChoice[]): Promise { + if (isInteractive()) { + return promptAgentsInteractive(choices); + } + + return promptAgentsByLine(choices); +} + +function promptAgentsInteractive( + choices: AgentChoice[] +): Promise { + const input = process.stdin; + const output = process.stderr; + const wasRaw = input.isRaw === true; + const selected = choices.map((choice) => choice.detected); + let cursor = 0; + let renderedLines = 0; + + try { + input.setRawMode(true); + } catch { + return promptAgentsByLine(choices); + } + readline.emitKeypressEvents(input); + + return new Promise((resolve, reject) => { + function render(): void { + if (renderedLines > 0) { + output.write(`\u001B[${renderedLines}A`); + } + + const lines = [ + `${color.magenta(symbols.diamond)} ${color.bold('Select agents to configure')} ${color.dim('(↑↓ move · space toggle · a all · enter confirm)')}`, + ...choices.map((choice, index) => { + const box = selected[index] + ? color.green(symbols.checkboxOn) + : color.dim(symbols.checkboxOff); + const pointer = index === cursor ? color.cyan(symbols.pointer) : ' '; + const name = + index === cursor + ? color.cyan(choice.displayName) + : choice.displayName; + const detected = choice.detected ? color.dim(' (detected)') : ''; + return `${pointer} ${box} ${name}${detected}`; + }), + ]; + + output.write(`${lines.map((line) => `\u001B[K${line}`).join('\n')}\n`); + renderedLines = lines.length; + } + + function cleanup(): void { + input.setRawMode(wasRaw); + input.removeListener('keypress', onKeypress); + output.write('\u001B[?25h'); + input.pause(); + } + + function onKeypress(_input: string, key: readline.Key | undefined): void { + if (!key) { + return; + } + + if (key.name === 'up' || key.name === 'k') { + cursor = (cursor - 1 + choices.length) % choices.length; + render(); + } else if (key.name === 'down' || key.name === 'j') { + cursor = (cursor + 1) % choices.length; + render(); + } else if (key.name === 'space') { + selected[cursor] = !selected[cursor]; + render(); + } else if (key.name === 'a') { + const allSelected = selected.every(Boolean); + selected.fill(!allSelected); + render(); + } else if (key.name === 'return') { + cleanup(); + resolve( + choices + .filter((_, index) => selected[index]) + .map((choice) => choice.name) + ); + } else if ( + key.name === 'escape' || + (key.ctrl === true && key.name === 'c') + ) { + cleanup(); + output.write('\n'); + reject(new PromptCancelledError(key.ctrl === true ? 130 : 0)); + } + } + + output.write('\u001B[?25l'); + input.on('keypress', onKeypress); + input.resume(); + render(); + }); +} + +async function promptAgentsByLine( + choices: AgentChoice[] +): Promise { + const output = process.stderr; + + output.write( + `${color.magenta(symbols.diamond)} ${color.bold('Select agents to configure')}\n` + ); + for (const [index, choice] of choices.entries()) { + const detected = choice.detected ? color.dim(' (detected)') : ''; + output.write( + ` ${color.cyan(String(index + 1))}. ${choice.displayName}${detected}\n` + ); + } + output.write( + `${color.dim('Enter numbers/names (comma-separated), "all", or blank for detected:')} ` + ); + + const raw = await readSingleLine(); + return parseAgentSelection(raw, choices); +} + +// A single persistent line reader shared across prompts. Reading one line at a +// time with fresh listeners loses buffered input between prompts (and can hang +// at EOF), so we buffer stdin once and hand out lines on demand. +let stdinBuffer = ''; +let stdinInitialized = false; +let stdinEnded = false; +const lineResolvers: Array<(line: string) => void> = []; + +function pumpLines(): void { + while (lineResolvers.length > 0) { + const newline = stdinBuffer.indexOf('\n'); + if (newline >= 0) { + const line = stdinBuffer.slice(0, newline).replace(/\r$/, ''); + stdinBuffer = stdinBuffer.slice(newline + 1); + lineResolvers.shift()?.(line); + } else if (stdinEnded) { + const line = stdinBuffer; + stdinBuffer = ''; + lineResolvers.shift()?.(line); + } else { + break; + } + } + + if (lineResolvers.length === 0 && !stdinEnded) { + process.stdin.pause(); + } +} + +function initStdin(): void { + if (stdinInitialized) { + return; + } + + stdinInitialized = true; + process.stdin.setEncoding('utf8'); + process.stdin.on('data', (chunk: string) => { + stdinBuffer += chunk; + pumpLines(); + }); + process.stdin.on('end', () => { + stdinEnded = true; + pumpLines(); + }); +} + +function readSingleLine(): Promise { + initStdin(); + + return new Promise((resolve) => { + lineResolvers.push(resolve); + process.stdin.resume(); + pumpLines(); + }); +} diff --git a/packages/docsearch-cli/src/setup/setup.ts b/packages/docsearch-cli/src/setup/setup.ts new file mode 100644 index 00000000..4c74063f --- /dev/null +++ b/packages/docsearch-cli/src/setup/setup.ts @@ -0,0 +1,262 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { homedir } from 'node:os'; +import { dirname, isAbsolute, join, relative, resolve } from 'node:path'; + +import { DOCSEARCH_MCP_SERVER_NAME } from '../constants.js'; + +import { + getAgent, + getAllAgents, + type AgentConfig, + type PathContext, + type SetupAgent, + type SetupScope, +} from './agents.js'; +import type { AgentChoice } from './prompt.js'; +import { getRuleContent, getSkillContent } from './templates.js'; +import { + appendRuleSection, + appendTomlServer, + pathExists, + resolveConfigPath, + upsertJsonServerEntry, + writeRuleFile, + writeSkill, +} from './writers.js'; + +export interface SetupOptions { + agents: SetupAgent[]; + cwd?: string; + endpoint: string; + env?: NodeJS.ProcessEnv; + homeDir?: string; + scope: SetupScope; +} + +export interface SetupResult { + agent: string; + mcpPath: string; + mcpStatus: string; + rulePath: string; + ruleStatus: string; + skillPath: string; + skillStatus: string; +} + +export function createPathContext( + overrides: Partial = {} +): PathContext { + return { + cwd: overrides.cwd ?? process.cwd(), + env: overrides.env ?? process.env, + homeDir: overrides.homeDir ?? homedir(), + }; +} + +// Version control roots are the canonical project root: they match how Cursor, +// Claude Code, Codex, etc. resolve project config (from the repo/workspace top). +const VCS_MARKERS = ['.git', '.hg', '.svn']; + +// Markers that only exist at the true repo root, so they beat a nested +// package.json when locating a monorepo root without VCS metadata. +const ROOT_MARKERS = [ + 'pnpm-workspace.yaml', + 'bun.lock', + 'bun.lockb', + 'pnpm-lock.yaml', + 'package-lock.json', + 'yarn.lock', + 'go.work', + 'deno.json', + 'deno.jsonc', + 'turbo.json', + 'nx.json', + 'lerna.json', +]; + +// Walks up from `startDir` (never crossing `homeDir` or the filesystem root) +// and returns the best project root. Falls back to `startDir` when nothing is +// found, so a directory-less invocation still installs into the current folder. +export async function findProjectRoot( + startDir: string, + homeDir: string +): Promise { + const start = resolve(startDir); + const home = resolve(homeDir); + const stopBeforeHome = start !== home && isPathWithin(start, home); + let vcsRoot: string | undefined; + let rootMarkerDir: string | undefined; + let topmostPackage: string | undefined; + + let dir = start; + let reachedFileSystemRoot = false; + while (!reachedFileSystemRoot) { + if (stopBeforeHome && dir === home) { + break; + } + if (vcsRoot === undefined && (await hasAnyMarker(dir, VCS_MARKERS))) { + vcsRoot = dir; + } + if ( + rootMarkerDir === undefined && + (await hasAnyMarker(dir, ROOT_MARKERS)) + ) { + rootMarkerDir = dir; + } + if (await pathExists(join(dir, 'package.json'))) { + topmostPackage = dir; + } + + const parent = dirname(dir); + reachedFileSystemRoot = parent === dir; + dir = parent; + } + + return vcsRoot ?? rootMarkerDir ?? topmostPackage ?? start; +} + +async function hasAnyMarker(dir: string, markers: string[]): Promise { + const found = await Promise.all( + markers.map((marker) => pathExists(join(dir, marker))) + ); + return found.some(Boolean); +} + +function isPathWithin(path: string, parent: string): boolean { + const pathFromParent = relative(parent, path); + return ( + pathFromParent !== '..' && + !pathFromParent.startsWith( + `..${process.platform === 'win32' ? '\\' : '/'}` + ) && + !isAbsolute(pathFromParent) + ); +} + +export async function setupDocSearch( + options: SetupOptions +): Promise { + const context = createPathContext({ + cwd: options.cwd, + env: options.env, + homeDir: options.homeDir, + }); + const results: SetupResult[] = []; + + for (const agentName of options.agents) { + results.push( + await setupAgent(getAgent(agentName, context), { + endpoint: options.endpoint, + scope: options.scope, + }) + ); + } + + return results; +} + +export async function detectAgents( + scope: SetupScope, + context: PathContext +): Promise { + const detected: SetupAgent[] = []; + const allAgents = getAllAgents(context); + + for (const agent of Object.values(allAgents)) { + const paths = + scope === 'global' ? agent.detect.globalPaths : agent.detect.projectPaths; + for (const path of paths) { + if (await pathExists(path)) { + detected.push(agent.name); + break; + } + } + } + + return detected; +} + +export function buildAgentChoices( + context: PathContext, + detected: SetupAgent[] +): AgentChoice[] { + const detectedSet = new Set(detected); + + return Object.values(getAllAgents(context)).map((agent) => ({ + detected: detectedSet.has(agent.name), + displayName: agent.displayName, + name: agent.name, + })); +} + +async function setupAgent( + agent: AgentConfig, + options: { endpoint: string; scope: SetupScope } +): Promise { + const mcpPath = await installMcpConfig( + agent, + options.endpoint, + options.scope + ); + const rulePath = await installRule(agent, options.scope); + const skill = await writeSkill( + agent.skill.dir(options.scope), + getSkillContent() + ); + + return { + agent: agent.displayName, + mcpPath: mcpPath.path, + mcpStatus: mcpPath.alreadyExists ? 'updated' : 'configured', + rulePath: rulePath.path, + ruleStatus: rulePath.status, + skillPath: skill.path, + skillStatus: skill.alreadyExists ? 'preserved' : 'installed', + }; +} + +async function installMcpConfig( + agent: AgentConfig, + endpoint: string, + scope: SetupScope +): Promise<{ alreadyExists: boolean; path: string }> { + const candidates = + scope === 'global' ? agent.mcp.globalPaths : agent.mcp.projectPaths; + const mcpPath = await resolveConfigPath(candidates); + const entry = agent.mcp.buildEntry(endpoint); + + if (mcpPath.endsWith('.toml')) { + const result = await appendTomlServer(mcpPath, entry); + return { alreadyExists: result.alreadyExists, path: mcpPath }; + } + + const result = await upsertJsonServerEntry( + mcpPath, + agent.mcp.configKey, + entry + ); + return { alreadyExists: result.alreadyExists, path: mcpPath }; +} + +async function installRule( + agent: AgentConfig, + scope: SetupScope +): Promise<{ path: string; status: string }> { + const content = getRuleContent(agent.name); + + if (agent.rule.kind === 'file') { + const rulePath = join(agent.rule.dir(scope), agent.rule.filename); + const result = await writeRuleFile(rulePath, content); + return { + path: rulePath, + status: result.alreadyExists ? 'preserved' : 'installed', + }; + } + + const rulePath = agent.rule.file(scope); + const existed = await pathExists(rulePath); + await appendRuleSection(rulePath, content); + return { path: rulePath, status: existed ? 'updated' : 'installed' }; +} + +export { DOCSEARCH_MCP_SERVER_NAME }; diff --git a/packages/docsearch-cli/src/setup/templates.ts b/packages/docsearch-cli/src/setup/templates.ts new file mode 100644 index 00000000..8b0fe287 --- /dev/null +++ b/packages/docsearch-cli/src/setup/templates.ts @@ -0,0 +1,57 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { DOCSEARCH_MCP_SERVER_NAME } from '../constants.js'; + +import type { SetupAgent } from './agents.js'; + +const BASE_GUIDANCE = `Use DocSearch MCP when the user asks about public developer documentation for a library, framework, SDK, API, CLI tool, or cloud service. + +Prefer the one-shot \`algolia_docsearch_search_docs\` tool for normal lookups: + +1. Put the official product, library, SDK, or platform name in \`library\`. +2. Put the actual documentation question in \`query\`. +3. Answer only from returned documentation chunks and include source links when available. + +Use \`algolia_docsearch_resolve_docset\` followed by \`algolia_docsearch_query_docs\` when a question spans multiple products or when the right documentation set is ambiguous. + +Do not use DocSearch MCP for general programming concepts, local code review, or business logic that is not answered by public docs.`; + +const CURSOR_FRONTMATTER = `---\nalwaysApply: true\n---\n\n`; + +export function getRuleContent(agent: SetupAgent): string { + if (agent === 'cursor') { + return `${CURSOR_FRONTMATTER}${BASE_GUIDANCE}\n`; + } + + return `${BASE_GUIDANCE}\n`; +} + +export function getSkillContent(): string { + return `--- +name: ${DOCSEARCH_MCP_SERVER_NAME} +description: Search current public developer documentation through DocSearch MCP. Use for library, framework, SDK, API, CLI, and cloud service documentation questions. +--- + +# DocSearch MCP + +Use this skill to fetch current public developer documentation through DocSearch MCP before answering documentation questions. + +## When to use + +- The user asks about a library, framework, SDK, API, CLI tool, or cloud service. +- The answer depends on current public docs, configuration, API syntax, migration steps, or version-specific behavior. +- The user explicitly says to use DocSearch MCP. + +## Steps + +1. For most questions, call \`algolia_docsearch_search_docs\`. + - \`library\`: official product or vendor/library name, for example \`Next.js\`, \`Stripe\`, or \`Algolia InstantSearch\`. + - \`query\`: the user's actual documentation question. +2. If the library is ambiguous, call \`algolia_docsearch_resolve_docset\`, pick the best \`docset_id\`, then call \`algolia_docsearch_query_docs\`. +3. Use separate focused queries for unrelated topics. Combined broad queries dilute ranking. +4. Answer from retrieved chunks only. Include source URLs when present. + +## Fallback + +If DocSearch MCP returns no useful results, retry with the official product name and a narrower query. If it still returns nothing, say that the answer was not found in the indexed docs. +`; +} diff --git a/packages/docsearch-cli/src/setup/writers.ts b/packages/docsearch-cli/src/setup/writers.ts new file mode 100644 index 00000000..123803db --- /dev/null +++ b/packages/docsearch-cli/src/setup/writers.ts @@ -0,0 +1,386 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { + access, + mkdir, + readFile, + realpath, + rename, + rm, + stat, + writeFile, +} from 'node:fs/promises'; +import { dirname, join } from 'node:path'; + +import { + applyEdits, + modify, + parse, + printParseErrorCode, + type FormattingOptions, + type ParseError, +} from 'jsonc-parser'; + +import { DOCSEARCH_MCP_SERVER_NAME } from '../constants.js'; +import { UsageError } from '../errors.js'; + +const SECTION_START = ''; +const SECTION_END = ''; + +export async function pathExists(path: string): Promise { + try { + await access(path); + return true; + } catch (error) { + if (isMissingFileError(error)) { + return false; + } + throw error; + } +} + +export async function resolveConfigPath(candidates: string[]): Promise { + for (const candidate of candidates) { + if (await pathExists(candidate)) { + return candidate; + } + } + + return candidates[0]; +} + +export async function upsertJsonServerEntry( + filePath: string, + configKey: string, + entry: Record +): Promise<{ alreadyExists: boolean }> { + const existing = await readTextFile(filePath); + const source = existing.trim() === '' ? '{}\n' : existing; + const errors: ParseError[] = []; + const parsed = parse(source, errors, { + allowTrailingComma: true, + disallowComments: false, + }) as unknown; + + if (errors.length > 0) { + const firstError = errors[0]; + throw new UsageError( + `Cannot update ${filePath}: invalid JSON/JSONC (${printParseErrorCode(firstError.error)} at offset ${firstError.offset}).` + ); + } + if (!isPlainObject(parsed)) { + throw new UsageError( + `Cannot update ${filePath}: the configuration root must be an object.` + ); + } + + const section = parsed[configKey]; + if (section !== undefined && !isPlainObject(section)) { + throw new UsageError( + `Cannot update ${filePath}: "${configKey}" must be an object.` + ); + } + + const serverEntry = section?.[DOCSEARCH_MCP_SERVER_NAME]; + if (serverEntry !== undefined && !isPlainObject(serverEntry)) { + throw new UsageError( + `Cannot update ${filePath}: "${configKey}.${DOCSEARCH_MCP_SERVER_NAME}" must be an object.` + ); + } + + const alreadyExists = serverEntry !== undefined; + const mergedEntry = { + ...serverEntry, + ...entry, + }; + const edits = modify( + source, + [configKey, DOCSEARCH_MCP_SERVER_NAME], + mergedEntry, + { + formattingOptions: inferFormatting(source), + } + ); + const updated = ensureFinalNewline(applyEdits(source, edits)); + + await writeFileAtomic(filePath, updated); + return { alreadyExists }; +} + +export function buildTomlServerBlock(entry: Record): string { + return [ + `[mcp_servers.${DOCSEARCH_MCP_SERVER_NAME}]`, + ...Object.entries(entry).map( + ([key, value]) => `${key} = ${JSON.stringify(value)}` + ), + '', + ].join('\n'); +} + +export async function appendTomlServer( + filePath: string, + entry: Record +): Promise<{ alreadyExists: boolean }> { + const existing = await readTextFile(filePath); + const block = buildTomlServerBlock(entry); + const sectionHeader = `[mcp_servers.${DOCSEARCH_MCP_SERVER_NAME}]`; + const headerRegex = new RegExp( + `^${escapeRegExp(sectionHeader)}[ \\t]*$`, + 'gm' + ); + const matches = [...existing.matchAll(headerRegex)]; + if (matches.length > 1) { + throw new UsageError( + `Cannot update ${filePath}: duplicate ${sectionHeader} sections.` + ); + } + + const alreadyExists = matches.length === 1; + const content = alreadyExists + ? mergeTomlSection(existing, matches[0], entry, filePath) + : appendBlock(existing, block); + + await writeFileAtomic(filePath, content); + return { alreadyExists }; +} + +export async function writeRuleFile( + filePath: string, + content: string +): Promise<{ alreadyExists: boolean }> { + const alreadyExists = await pathExists(filePath); + if (!alreadyExists) { + await writeFileAtomic(filePath, content); + } + return { alreadyExists }; +} + +export async function appendRuleSection( + filePath: string, + content: string +): Promise { + const existing = await readTextFile(filePath); + const hasStart = existing.includes(SECTION_START); + const hasEnd = existing.includes(SECTION_END); + if (hasStart !== hasEnd) { + throw new UsageError( + `Cannot update ${filePath}: incomplete DocSearch managed section.` + ); + } + + const section = `${SECTION_START}\n${content.trim()}\n${SECTION_END}`; + const updated = + hasStart && hasEnd + ? existing.replace( + new RegExp( + `${escapeRegExp(SECTION_START)}[\\s\\S]*?${escapeRegExp(SECTION_END)}` + ), + section + ) + : appendBlock(existing, `${section}\n`); + + await writeFileAtomic(filePath, updated); +} + +export async function writeSkill( + skillDir: string, + content: string +): Promise<{ alreadyExists: boolean; path: string }> { + const skillPath = join(skillDir, DOCSEARCH_MCP_SERVER_NAME, 'SKILL.md'); + const alreadyExists = await pathExists(skillPath); + if (!alreadyExists) { + await writeFileAtomic(skillPath, content); + } + return { alreadyExists, path: skillPath }; +} + +function mergeTomlSection( + existing: string, + headerMatch: RegExpMatchArray, + entry: Record, + filePath: string +): string { + const startIdx = headerMatch.index; + if (startIdx === undefined) { + throw new UsageError( + `Cannot update ${filePath}: failed to locate the DocSearch MCP section.` + ); + } + + const lineBreak = existing.includes('\r\n') ? '\r\n' : '\n'; + const headerEnd = startIdx + headerMatch[0].length; + const rest = existing.slice(headerEnd); + const nextHeader = /^[ \t]*\[[^\]\r\n]+\][ \t]*(?:#.*)?$/m.exec(rest); + const endIdx = + nextHeader?.index === undefined + ? existing.length + : headerEnd + nextHeader.index; + const lines = existing + .slice(startIdx, endIdx) + .replace(/\s+$/, '') + .split(/\r?\n/); + + for (const [key, value] of Object.entries(entry)) { + const keyRegex = new RegExp(`^(\\s*)${escapeRegExp(key)}\\s*=`); + const matchingLines = lines + .map((line, index) => ({ index, match: keyRegex.exec(line) })) + .filter((candidate) => candidate.match !== null); + + if (matchingLines.length > 1) { + throw new UsageError( + `Cannot update ${filePath}: duplicate "${key}" values in the DocSearch MCP section.` + ); + } + + const replacement = `${key} = ${toTomlValue(value, filePath)}`; + const matchingLine = matchingLines[0]; + if (matchingLine?.match) { + const comment = readTomlInlineComment(lines[matchingLine.index]); + lines[matchingLine.index] = + `${matchingLine.match[1]}${replacement}${comment}`; + } else { + lines.push(replacement); + } + } + + const mergedSection = `${lines.join(lineBreak)}${lineBreak}`; + return ensureFinalNewline( + `${existing.slice(0, startIdx)}${mergedSection}${existing.slice(endIdx)}` + ); +} + +function toTomlValue(value: unknown, filePath: string): string { + if ( + typeof value === 'string' || + typeof value === 'boolean' || + typeof value === 'number' + ) { + return JSON.stringify(value); + } + + throw new UsageError( + `Cannot update ${filePath}: unsupported TOML value in the DocSearch MCP configuration.` + ); +} + +function readTomlInlineComment(line: string): string { + let quote: '"' | "'" | undefined; + let escaped = false; + + for (let index = 0; index < line.length; index++) { + const character = line[index]; + if (escaped) { + escaped = false; + } else if (quote === '"' && character === '\\') { + escaped = true; + } else if (quote && character === quote) { + quote = undefined; + } else if (!quote && (character === '"' || character === "'")) { + quote = character; + } else if (!quote && character === '#') { + const whitespace = line.slice(0, index).match(/[ \t]*$/)?.[0] ?? ''; + return `${whitespace || ' '}${line.slice(index)}`; + } + } + + return ''; +} + +function appendBlock(existing: string, block: string): string { + const lineBreak = existing.includes('\r\n') ? '\r\n' : '\n'; + const normalizedBlock = block.replace(/\r?\n/g, lineBreak); + if (!existing) { + return normalizedBlock.endsWith(lineBreak) + ? normalizedBlock + : `${normalizedBlock}${lineBreak}`; + } + + const separator = existing.endsWith(lineBreak) + ? lineBreak + : `${lineBreak}${lineBreak}`; + return `${existing}${separator}${ + normalizedBlock.endsWith(lineBreak) + ? normalizedBlock + : `${normalizedBlock}${lineBreak}` + }`; +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function inferFormatting(source: string): FormattingOptions { + const lineBreak = source.includes('\r\n') ? '\r\n' : '\n'; + const indentation = source.match(/^[ \t]+(?=")/m)?.[0]; + const insertSpaces = indentation?.includes('\t') !== true; + + return { + eol: lineBreak, + insertSpaces, + tabSize: insertSpaces ? (indentation?.length ?? 2) : 1, + }; +} + +function isPlainObject(value: unknown): value is Record { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + +function ensureFinalNewline(content: string): string { + if (content.endsWith('\n')) { + return content; + } + return `${content}\n`; +} + +async function readTextFile(filePath: string): Promise { + try { + return await readFile(filePath, 'utf-8'); + } catch (error) { + if (isMissingFileError(error)) { + return ''; + } + throw error; + } +} + +async function writeFileAtomic( + filePath: string, + content: string +): Promise { + const targetPath = await resolveExistingPath(filePath); + await mkdir(dirname(targetPath), { recursive: true }); + + let mode: number | undefined; + try { + mode = (await stat(targetPath)).mode % 0o1000; + } catch (error) { + if (!isMissingFileError(error)) { + throw error; + } + } + + const tempPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`; + try { + await writeFile(tempPath, content, { + encoding: 'utf-8', + mode, + }); + await rename(tempPath, targetPath); + } finally { + await rm(tempPath, { force: true }); + } +} + +async function resolveExistingPath(filePath: string): Promise { + try { + return await realpath(filePath); + } catch (error) { + if (isMissingFileError(error)) { + return filePath; + } + throw error; + } +} + +function isMissingFileError(error: unknown): error is NodeJS.ErrnoException { + return error instanceof Error && 'code' in error && error.code === 'ENOENT'; +} diff --git a/packages/docsearch-cli/src/ui/__tests__/markdown.test.ts b/packages/docsearch-cli/src/ui/__tests__/markdown.test.ts new file mode 100644 index 00000000..aa6e07d1 --- /dev/null +++ b/packages/docsearch-cli/src/ui/__tests__/markdown.test.ts @@ -0,0 +1,56 @@ +// @vitest-environment node + +import { describe, expect, it } from 'vitest'; + +import { renderMarkdown } from '../markdown'; + +describe('renderMarkdown', () => { + it('renders headings, sources, bullets, and dividers as plain text when color is disabled', () => { + const input = [ + 'Searched docsets:', + '', + '### Configure props', + '', + 'Source: https://example.com/docs', + '', + '- `apiKey`: Your key.', + '- Read the [configuration guide](https://example.com/config).', + '--------------------------------', + ].join('\n'); + + const output = renderMarkdown(input); + + expect(output).toContain('Configure props'); + expect(output).toContain('https://example.com/docs'); + expect(output).toContain('apiKey'); + expect(output).toContain('https://example.com/config'); + expect(output).not.toContain('###'); + expect(output).toMatch(/─{10,}/); + }); + + it('renders resolve metadata as a card with a score bar', () => { + const input = [ + '- title: Next.js Docs', + '- docset_id: repo/vercel/next.js', + '- description: Official documentation.', + '- trustScore: 100', + ].join('\n'); + + const output = renderMarkdown(input); + + expect(output).toContain('Next.js Docs'); + expect(output).toContain('repo/vercel/next.js'); + expect(output).toContain('trust'); + expect(output).toContain('100'); + expect(output).toMatch(/[▰▱#-]{10}/); + }); + + it('keeps code fences as a bordered block without backticks', () => { + const input = ['```ts', 'const x = 1;', '```'].join('\n'); + + const output = renderMarkdown(input); + + expect(output).toContain('const x = 1;'); + expect(output).not.toContain('```'); + }); +}); diff --git a/packages/docsearch-cli/src/ui/__tests__/render.test.ts b/packages/docsearch-cli/src/ui/__tests__/render.test.ts new file mode 100644 index 00000000..5e2b4f08 --- /dev/null +++ b/packages/docsearch-cli/src/ui/__tests__/render.test.ts @@ -0,0 +1,34 @@ +// @vitest-environment node + +import { describe, expect, it } from 'vitest'; + +import { renderHelp, renderLanding, renderLogo } from '../render'; + +describe('renderLanding', () => { + it('includes the logo, tagline, commands, and discovery link', () => { + const output = renderLanding(); + + expect(output).toContain('▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓'); + expect(output).toContain('The open documentation search for AI agents'); + expect(output).toContain('setup'); + expect(output).toContain('docs'); + expect(output).toContain('resolve'); + expect(output).toContain('query'); + expect(output).toContain('https://docsearch.algolia.com'); + }); + + it('renders the logo as multi-line art', () => { + expect(renderLogo().slice(1, -1).split('\n')).toHaveLength(21); + }); + + it('documents setup and query flags', () => { + const output = renderHelp(); + + expect(output).toContain('--all'); + expect(output).toContain('--cursor'); + expect(output).toContain('--max-results'); + expect(output).toContain('--max-docsets'); + expect(output).toContain('--top-n'); + expect(output).toContain('project + detected agents'); + }); +}); diff --git a/packages/docsearch-cli/src/ui/markdown.ts b/packages/docsearch-cli/src/ui/markdown.ts new file mode 100644 index 00000000..ecb0a2c2 --- /dev/null +++ b/packages/docsearch-cli/src/ui/markdown.ts @@ -0,0 +1,206 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { color, symbols } from './theme.js'; + +const CODE_FENCE = /^```(.*)$/; +const HORIZONTAL_RULE = /^\s*([-*_]|─){3,}\s*$/; +const HEADING = /^(#{1,6})\s+(.*)$/; +const SOURCE = /^Source:\s+(.+)$/; +const BULLET = /^(\s*)[-*]\s+(.+)$/; +const META_FIELD = /^([A-Za-z][\w]*):\s+([\s\S]*)$/; +const SECTION_LABEL = /^[A-Z][^:`]*:\s*$/; +const TABLE_ROW = /^\s*\|.*\|\s*$/; +const TABLE_SEPARATOR = /^\s*\|[\s:|-]+\|\s*$/; + +const MAX_WIDTH = 88; +const RULE_WIDTH = 56; + +export function renderMarkdown(text: string): string { + const width = Math.max(40, Math.min(process.stdout.columns ?? 80, MAX_WIDTH)); + const lines = text.replace(/\n+$/, '').split('\n'); + const output: string[] = []; + let inCodeBlock = false; + + for (const line of lines) { + const fence = line.match(CODE_FENCE); + if (fence) { + output.push( + inCodeBlock ? renderCodeBottom() : renderCodeTop(fence[1].trim()) + ); + inCodeBlock = !inCodeBlock; + } else if (inCodeBlock) { + output.push(renderCodeLine(line)); + } else { + output.push(...renderProseLine(line, width)); + } + } + + return `${output.join('\n')}\n`; +} + +function renderProseLine(line: string, width: number): string[] { + if (line.trim() === '') { + return ['']; + } + + if (HORIZONTAL_RULE.test(line)) { + return [color.dim(symbols.rule.repeat(RULE_WIDTH))]; + } + + const heading = line.match(HEADING); + if (heading) { + return [ + `${color.magenta(symbols.accent)} ${color.bold(color.cyan(heading[2]))}`, + ]; + } + + const source = line.match(SOURCE); + if (source) { + return [ + ` ${color.dim(symbols.link)} ${color.underline(color.blue(source[1]))}`, + ]; + } + + const bullet = line.match(BULLET); + if (bullet) { + return renderBullet(bullet[2], width); + } + + if (TABLE_ROW.test(line)) { + return [renderTableRow(line)]; + } + + if (SECTION_LABEL.test(line)) { + return [color.bold(line)]; + } + + return wrap(line, width).map((part) => styleInline(part)); +} + +function renderBullet(content: string, width: number): string[] { + const field = content.match(META_FIELD); + if (field && !content.startsWith('`')) { + return renderMetaField(field[1], field[2], width); + } + + const marker = color.cyan(symbols.bullet); + return wrap(content, width - 2).map((part, index) => + index === 0 ? `${marker} ${styleInline(part)}` : ` ${styleInline(part)}` + ); +} + +function renderMetaField(key: string, value: string, width: number): string[] { + if (key === 'title') { + return [`${color.magenta(symbols.diamond)} ${color.bold(value)}`]; + } + + if (key === 'docset_id') { + return [` ${color.dim('id')} ${color.cyan(value)}`]; + } + + if (/score$/i.test(key)) { + return [renderScore(key.replace(/score$/i, ''), value)]; + } + + if (key === 'description') { + return wrap(value, width - 2).map((part) => ` ${color.dim(part)}`); + } + + const indent = ' '.repeat(key.length + 4); + return wrap(value, width - key.length - 4).map((part, index) => + index === 0 + ? ` ${color.dim(key)} ${styleInline(part)}` + : `${indent}${styleInline(part)}` + ); +} + +function renderScore(label: string, value: string): string { + const score = Number(value); + const paddedLabel = color.dim(label.padEnd(11)); + + if (!Number.isFinite(score)) { + return ` ${paddedLabel} ${value}`; + } + + const clamped = Math.max(0, Math.min(100, score)); + const filled = Math.round((clamped / 100) * 10); + const bar = + symbols.scoreOn.repeat(filled) + symbols.scoreOff.repeat(10 - filled); + const tint = scoreTint(score); + + return ` ${paddedLabel} ${tint(bar)} ${tint(String(value))}`; +} + +function scoreTint(score: number): (text: string) => string { + if (score >= 80) { + return color.green; + } + + if (score >= 50) { + return color.yellow; + } + + return color.red; +} + +function renderTableRow(line: string): string { + if (TABLE_SEPARATOR.test(line)) { + return color.dim(symbols.rule.repeat(RULE_WIDTH)); + } + + const cells = line + .trim() + .replace(/^\|/, '') + .replace(/\|$/, '') + .split('|') + .map((cell) => styleInline(cell.trim())); + + return ` ${cells.join(color.dim(` ${symbols.codeGutter} `))}`; +} + +function renderCodeTop(language: string): string { + const chip = language ? ` ${color.dim(language)}` : ''; + return `${color.dim(`${symbols.codeTop}${symbols.rule}`)}${chip}`; +} + +function renderCodeLine(line: string): string { + return `${color.dim(`${symbols.codeGutter} `)}${color.gray(line)}`; +} + +function renderCodeBottom(): string { + return color.dim(`${symbols.codeBottom}${symbols.rule}`); +} + +function styleInline(text: string): string { + return text + .replace( + /\[([^\]]+)\]\(([^)]+)\)/g, + (_match, label: string, url: string) => + `${color.underline(color.blue(label))}${color.dim(` (${url})`)}` + ) + .replace(/`([^`]+)`/g, (_match, code: string) => color.cyan(code)) + .replace(/\*\*([^*]+)\*\*/g, (_match, bold: string) => color.bold(bold)); +} + +function wrap(text: string, width: number): string[] { + const limit = Math.max(20, width); + const words = text.split(/\s+/).filter(Boolean); + const lines: string[] = []; + let current = ''; + + for (const word of words) { + if (current === '') { + current = word; + } else if (current.length + 1 + word.length <= limit) { + current += ` ${word}`; + } else { + lines.push(current); + current = word; + } + } + + if (current !== '') { + lines.push(current); + } + + return lines.length > 0 ? lines : ['']; +} diff --git a/packages/docsearch-cli/src/ui/render.ts b/packages/docsearch-cli/src/ui/render.ts new file mode 100644 index 00000000..f9a98b62 --- /dev/null +++ b/packages/docsearch-cli/src/ui/render.ts @@ -0,0 +1,206 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { CLI_NAME, CLI_VERSION } from '../constants.js'; +import type { SetupResult } from '../setup/setup.js'; + +import { brandShade, color, symbols } from './theme.js'; + +const LOGO: string[] = [ + ' ', + ' ░░░░░░░░░░░░░░░░░░░░░░░░░░░', + ' ░░░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░', + ' ░░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░', + ' ░░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░', + ' ░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░', + ' ░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░', + ' ░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░', + ' ░░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░', + ' ░░░', + ' ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░░░', + ' ░░░░░', + ' ░░░░░░░░░░░░░░░░░░░░░░░░░░', + ' ', +]; + +const LOGO_TOP_LEVEL = 1; +const LOGO_BOTTOM_LEVEL = 0; + +export function renderLogo(): string { + const span = Math.max(1, LOGO.length - 1); + const rows = LOGO.map((line, index) => { + const level = + LOGO_TOP_LEVEL - (index / span) * (LOGO_TOP_LEVEL - LOGO_BOTTOM_LEVEL); + return brandShade(level)(line); + }); + + return `\n${rows.join('\n')}\n`; +} + +export function renderBanner(): string { + const title = color.bold(color.cyan('DocSearch')); + const subtitle = color.dim('search public docs & set up the DocSearch MCP'); + + return `\n${color.magenta(symbols.diamond)} ${title} ${color.dim(`v${CLI_VERSION}`)}\n ${subtitle}\n`; +} + +interface LandingCommand { + args?: string; + description: string; + name: string; +} + +const LANDING_COMMANDS: LandingCommand[] = [ + { + description: 'Configure the DocSearch MCP for your AI agents', + name: 'setup', + }, + { + args: ' ', + description: "One-shot search across a library's docs", + name: 'docs', + }, + { + args: '', + description: 'Find matching docsets for a product', + name: 'resolve', + }, + { + args: ' ', + description: 'Search inside specific docsets', + name: 'query', + }, +]; + +export function renderLanding(): string { + const prefix = `${CLI_NAME} `; + const plainLefts = LANDING_COMMANDS.map( + (command) => + ` $ ${prefix}${command.name}${command.args ? ` ${command.args}` : ''}` + ); + const column = Math.max(...plainLefts.map((left) => left.length)) + 3; + + const commandLines = LANDING_COMMANDS.map((command, index) => { + const styledLeft = ` ${color.dim('$')} ${color.dim(prefix.trim())} ${color.green(command.name)}${ + command.args ? ` ${color.cyan(command.args)}` : '' + }`; + const padding = ' '.repeat(Math.max(2, column - plainLefts[index].length)); + return `${styledLeft}${padding}${command.description}`; + }); + + return [ + renderLogo(), + color.dim(' The open documentation search for AI agents'), + '', + ...commandLines, + '', + ` ${color.dim('try:')} ${color.bold(`${CLI_NAME} docs Next.js`)} ${color.dim('"how do middleware matchers work"')}`, + '', + ` ${color.dim('Docs & MCP setup at')} ${color.underline(color.blue('https://docsearch.algolia.com/mcp'))}`, + '', + ].join('\n'); +} + +export function renderHelp(): string { + const heading = (text: string): string => color.bold(color.yellow(text)); + const cmd = (text: string): string => color.green(text); + const meta = (text: string): string => color.dim(text); + + return [ + renderBanner(), + heading('Usage'), + ` ${color.bold(CLI_NAME)} ${color.cyan('')} ${meta('[options]')}`, + '', + heading('Commands'), + ` ${cmd('setup')} Configure the DocSearch MCP for your AI agents`, + ` ${cmd('docs')} ${color.cyan(' ')} One-shot search across a library's docs`, + ` ${cmd('resolve')} ${color.cyan('')} Find matching docsets for a product`, + ` ${cmd('query')} ${color.cyan(' ')} Search inside specific docsets`, + '', + heading('Setup options'), + ` ${color.green('--project')} Install at the detected repository root`, + ` ${color.green('--global')} Install into user-level agent settings`, + ` ${color.green('--all')} Configure every supported agent`, + ` ${color.green('--cursor')}, ${color.green('--claude')} Configure Cursor or Claude Code`, + ` ${color.green('--codex')}, ${color.green('--opencode')} Configure Codex or OpenCode`, + ` ${color.green('--gemini')} Configure Gemini CLI`, + ` ${color.green('--yes')}, ${color.green('-y')} Run setup non-interactively ${meta('(project + detected agents)')}`, + '', + heading('Query options'), + ` ${color.green('--max-results')} ${color.cyan('')} Limit documentation results`, + ` ${color.green('--max-docsets')} ${color.cyan('')} Limit docsets searched by ${cmd('docs')}`, + ` ${color.green('--top-n')} ${color.cyan('')} Limit docsets returned by ${cmd('resolve')}`, + ` ${color.green('--json')} Print raw MCP results as JSON`, + '', + heading('Global options'), + ` ${color.green('--endpoint')} ${color.cyan('')} Override the hosted DocSearch MCP endpoint`, + ` ${color.green('--help')}, ${color.green('-h')} Show help`, + '', + heading('Examples'), + ` ${meta('$')} ${CLI_NAME} setup`, + ` ${meta('$')} ${CLI_NAME} setup ${color.green('--cursor --claude --project')}`, + ` ${meta('$')} ${CLI_NAME} docs Next.js ${color.dim('"how do middleware matchers work"')}`, + ` ${meta('$')} ${CLI_NAME} resolve ${color.dim('"Algolia InstantSearch React"')}`, + ` ${meta('$')} ${CLI_NAME} query nextjs ${color.dim('"middleware matcher config"')} --json`, + '', + ].join('\n'); +} + +export function renderResultHeader(command: string, subject: string): string { + return `\n${color.magenta(symbols.diamond)} ${color.bold(color.cyan(command))} ${color.dim(symbols.arrow)} ${color.bold(subject)}\n`; +} + +export function renderError(message: string): string { + return `${color.red(symbols.cross)} ${color.red(message)}\n`; +} + +export function renderNotice(message: string): string { + return `${color.yellow(symbols.info)} ${message}\n`; +} + +export interface SetupSummaryMeta { + endpoint: string; + location?: string; + scope: string; +} + +export function renderSetupResults( + results: SetupResult[], + meta: SetupSummaryMeta +): string { + if (results.length === 0) { + return renderNotice('No agents selected. Nothing was configured.'); + } + + const label = results.length === 1 ? 'agent' : 'agents'; + const lines: string[] = [ + `\n${color.green(symbols.tick)} ${color.bold(`Configured DocSearch MCP for ${results.length} ${label}`)}`, + ` ${color.dim('scope')} ${meta.scope}`, + ...(meta.location ? [` ${color.dim('root')} ${meta.location}`] : []), + ` ${color.dim('endpoint')} ${color.underline(meta.endpoint)}`, + '', + ]; + + for (const result of results) { + lines.push(`${color.magenta(symbols.diamond)} ${color.bold(result.agent)}`); + lines.push(renderStep('MCP server', result.mcpStatus, result.mcpPath)); + lines.push(renderStep('Rule', result.ruleStatus, result.rulePath)); + lines.push(renderStep('Skill', result.skillStatus, result.skillPath)); + lines.push(''); + } + + lines.push(color.dim(`Restart your agent to load the new configuration.`)); + + return `${lines.join('\n')}\n`; +} + +function renderStep(name: string, status: string, path: string): string { + const badge = + status === 'updated' ? color.yellow(status) : color.green(status); + return ` ${color.green(symbols.tick)} ${name} ${color.dim(symbols.bullet)} ${badge}\n ${color.dim(path)}`; +} diff --git a/packages/docsearch-cli/src/ui/spinner.ts b/packages/docsearch-cli/src/ui/spinner.ts new file mode 100644 index 00000000..0fe09f08 --- /dev/null +++ b/packages/docsearch-cli/src/ui/spinner.ts @@ -0,0 +1,54 @@ +/* eslint-disable import/no-unresolved -- NodeNext source imports use runtime .js extensions. */ +import { color, symbols } from './theme.js'; + +const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; +const FRAME_INTERVAL_MS = 80; + +export interface Spinner { + fail: (text?: string) => void; + stop: () => void; + succeed: (text?: string) => void; +} + +export function startSpinner( + text: string, + stream: NodeJS.WriteStream = process.stderr +): Spinner { + const animate = stream.isTTY === true && !('NO_COLOR' in process.env); + + if (!animate) { + stream.write(`${color.dim(symbols.bullet)} ${text}\n`); + return { fail: noop, stop: noop, succeed: noop }; + } + + let frame = 0; + stream.write('\u001B[?25l'); + + const timer = setInterval(() => { + frame = (frame + 1) % FRAMES.length; + stream.write(`\r${color.cyan(FRAMES[frame])} ${text}`); + }, FRAME_INTERVAL_MS); + + function clear(): void { + clearInterval(timer); + stream.write('\r\u001B[K\u001B[?25h'); + } + + return { + fail(message) { + clear(); + stream.write(`${color.red(symbols.cross)} ${message ?? text}\n`); + }, + stop() { + clear(); + }, + succeed(message) { + clear(); + stream.write(`${color.green(symbols.tick)} ${message ?? text}\n`); + }, + }; +} + +function noop(): void { + // Intentionally empty: non-animated spinners have nothing to clear. +} diff --git a/packages/docsearch-cli/src/ui/theme.ts b/packages/docsearch-cli/src/ui/theme.ts new file mode 100644 index 00000000..d771b306 --- /dev/null +++ b/packages/docsearch-cli/src/ui/theme.ts @@ -0,0 +1,75 @@ +import pc from 'picocolors'; + +const colorEnabled = + !('NO_COLOR' in process.env) && + (process.stdout.isTTY === true || process.env.FORCE_COLOR !== undefined); + +function paint(fn: (input: string) => string): (text: string) => string { + return (text: string) => (colorEnabled ? fn(text) : text); +} + +export const color = { + blue: paint(pc.blue), + bold: paint(pc.bold), + cyan: paint(pc.cyan), + dim: paint(pc.dim), + gray: paint(pc.gray), + green: paint(pc.green), + magenta: paint(pc.magenta), + red: paint(pc.red), + underline: paint(pc.underline), + yellow: paint(pc.yellow), +}; + +const richUnicode = + process.platform !== 'win32' || + process.env.WT_SESSION !== undefined || + process.env.TERM_PROGRAM !== undefined; + +export const symbols = { + accent: richUnicode ? '▍' : '|', + arrow: richUnicode ? '→' : '->', + bullet: richUnicode ? '•' : '*', + checkboxOff: richUnicode ? '◯' : '[ ]', + checkboxOn: richUnicode ? '◉' : '[x]', + codeBottom: richUnicode ? '└' : '+', + codeGutter: richUnicode ? '│' : '|', + codeTop: richUnicode ? '┌' : '+', + cross: richUnicode ? '✖' : 'x', + diamond: richUnicode ? '◆' : '*', + info: richUnicode ? 'ℹ' : 'i', + link: richUnicode ? '↗' : '->', + pointer: richUnicode ? '❯' : '>', + rule: richUnicode ? '─' : '-', + scoreOff: richUnicode ? '▱' : '-', + scoreOn: richUnicode ? '▰' : '#', + tick: richUnicode ? '✔' : 'v', +}; + +export function isInteractive(): boolean { + return ( + process.stdin.isTTY === true && + process.stderr.isTTY === true && + typeof process.stdin.setRawMode === 'function' + ); +} + +const DOCSEARCH_CYAN = [44, 200, 247] as const; +const DOCSEARCH_NEBULA = [84, 104, 255] as const; +const DOCSEARCH_PINK = [248, 44, 170] as const; + +export function brandShade(level: number): (text: string) => string { + const clamped = Math.max(0, Math.min(1, level)); + const [from, to, progress] = + clamped >= 0.5 + ? [DOCSEARCH_NEBULA, DOCSEARCH_PINK, (clamped - 0.5) * 2] + : [DOCSEARCH_CYAN, DOCSEARCH_NEBULA, clamped * 2]; + const [red, green, blue] = from.map((channel, index) => + Math.round(channel + (to[index] - channel) * progress) + ); + + return (text: string) => + colorEnabled + ? `\u001B[38;2;${red};${green};${blue}m${text}\u001B[39m` + : text; +} diff --git a/packages/docsearch-cli/tsconfig.json b/packages/docsearch-cli/tsconfig.json new file mode 100644 index 00000000..11bd96ae --- /dev/null +++ b/packages/docsearch-cli/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "allowImportingTsExtensions": false, + "declaration": false, + "declarationMap": false, + "lib": ["ES2022"], + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "target": "ES2022", + "allowImportingTsExtensions": false + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts", "src/**/__tests__/**"] +} diff --git a/packages/docsearch-cli/vitest.config.mts b/packages/docsearch-cli/vitest.config.mts new file mode 100644 index 00000000..7eeb3f85 --- /dev/null +++ b/packages/docsearch-cli/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + include: ['src/**/*.test.ts'], + }, +}); diff --git a/packages/docsearch-core/CHANGELOG.md b/packages/docsearch-core/CHANGELOG.md new file mode 100644 index 00000000..0b5b9ad5 --- /dev/null +++ b/packages/docsearch-core/CHANGELOG.md @@ -0,0 +1,117 @@ +# typesense-docsearch-core + +## 5.0.2 + +## 5.0.1 + +## 5.0.0 + +### Major Changes + +- ecd905d: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed + +### Patch Changes + +- ecd905d: feat(v5): UI and DX updates + + - Rename the Ask AI assistantId option to agentId (adapter theme.SearchModal.askAi.assistantId → agentId) + - appId and apiKey moved up into typesense-docsearch-core, so they're configured once and shared + - Removed the indexName prop from the Sidepanel + - Facet defaults can now be read from the index searchParameters + - Restored nested grouping of search results + +- ecd905d: Prevent the slash search shortcut from intercepting key events on focused buttons. + +## 5.0.0-beta.4 + +## 5.0.0-beta.3 + +### Patch Changes + +- 3f74c33: Prevent the slash search shortcut from intercepting key events on focused buttons. + +## 5.0.0-beta.2 + +### Patch Changes + +- 4f6b5b1: feat(v5): UI and DX updates + + - Rename the Ask AI assistantId option to agentId (adapter theme.SearchModal.askAi.assistantId → agentId) + - appId and apiKey moved up into typesense-docsearch-core, so they're configured once and shared + - Removed the indexName prop from the Sidepanel + - Facet defaults can now be read from the index searchParameters + - Restored nested grouping of search results + +## 5.0.0-beta.1 + +## 5.0.0-beta.0 + +### Major Changes + +- a8ed1ea: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed diff --git a/packages/docsearch-core/README.md b/packages/docsearch-core/README.md new file mode 100644 index 00000000..f4f5a286 --- /dev/null +++ b/packages/docsearch-core/README.md @@ -0,0 +1,3 @@ +# typesense-docsearch-core + +Core logic and state package for [DocSearch](http://docsearch.algolia.com/), the best search experience for docs. \ No newline at end of file diff --git a/packages/docsearch-core/package.json b/packages/docsearch-core/package.json new file mode 100644 index 00000000..b1e80c5a --- /dev/null +++ b/packages/docsearch-core/package.json @@ -0,0 +1,64 @@ +{ + "name": "typesense-docsearch-core", + "description": "Core package for DocSearch, customized to work with Typesense", + "version": "5.0.2", + "license": "MIT", + "type": "module", + "homepage": "https://typesense.org/docs/latest/guide/docsearch.html", + "repository": "typesense/docsearch.js", + "contributors": [ + { + "name": "Algolia, Inc.", + "url": "https://www.algolia.com" + }, + { + "name": "Typesense, Inc.", + "url": "https://typesense.org" + } + ], + "sideEffects": false, + "files": [ + "dist/" + ], + "exports": { + ".": "./dist/esm/index.js", + "./useDocSearchKeyboardEvents": "./dist/esm/useDocSearchKeyboardEvents.js", + "./useKeyboardShortcuts": "./dist/esm/useKeyboardShortcuts.js", + "./useTheme": "./dist/esm/useTheme.js" + }, + "source": "src/index.ts", + "types": "./dist/esm/index.d.ts", + "main": "./dist/esm/index.js", + "module": "./dist/esm/index.js", + "umd:main": "dist/umd/index.js", + "unpkg": "dist/umd/index.js", + "jsdelivr": "dist/umd/index.js", + "scripts": { + "build": "tsdown", + "watch": "tsdown --watch" + }, + "devDependencies": { + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "preact": "11.0.0-beta.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "vitest": "3.0.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } +} diff --git a/packages/docsearch-core/src/DocSearch.tsx b/packages/docsearch-core/src/DocSearch.tsx new file mode 100644 index 00000000..e1083bff --- /dev/null +++ b/packages/docsearch-core/src/DocSearch.tsx @@ -0,0 +1,316 @@ +import type { JSX } from 'react'; +import React from 'react'; + +import { useDocSearchKeyboardEvents } from './useDocSearchKeyboardEvents'; +import { useIsMobile } from './useIsMobile'; +import { useKeyboardShortcuts } from './useKeyboardShortcuts'; +import type { KeyboardShortcuts } from './useKeyboardShortcuts.ts'; +import type { DocSearchTheme } from './useTheme'; +import { useTheme } from './useTheme'; + +export type DocSearchState = + | 'modal-askai' + | 'modal-search' + | 'ready' + | 'sidepanel'; + +export type View = 'modal' | 'sidepanel' | (Record & string); + +export type InitialAskAiMessage = { + query: string; + messageId?: string; + suggestedQuestionId?: string; +}; + +export type OnAskAiToggle = ( + active: boolean, + initialMessage?: InitialAskAiMessage +) => void; + +/** Imperative handle exposed by the DocSearch provider for programmatic control. */ +export interface DocSearchRef { + /** Opens the search modal. */ + open: () => void; + /** Closes the search modal. */ + close: () => void; + /** Opens Ask AI mode (sidepanel if available, otherwise modal). */ + openAskAi: (initialMessage?: InitialAskAiMessage) => void; + /** Opens the sidepanel directly (no-op if sidepanel view not registered). */ + openSidepanel: (initialMessage?: InitialAskAiMessage) => void; + /** Returns true once the component is mounted and ready. */ + readonly isReady: boolean; + /** Returns true if the modal is currently open. */ + readonly isOpen: boolean; + /** Returns true if the sidepanel is currently open. */ + readonly isSidepanelOpen: boolean; + /** Returns true if sidepanel view is registered (hybrid mode). */ + readonly isSidepanelSupported: boolean; +} + +export interface DocSearchContext { + docsearchState: DocSearchState; + setDocsearchState: (newState: DocSearchState) => void; + searchButtonRef: React.RefObject; + initialQuery: string; + keyboardShortcuts: Required; + openModal: () => void; + closeModal: () => void; + isAskAiActive: boolean; + isModalActive: boolean; + onAskAiToggle: OnAskAiToggle; + initialAskAiMessage: InitialAskAiMessage | undefined; + registerView: (view: View) => void; + isHybridModeSupported: boolean; +} + +/** Lifecycle callbacks for DocSearch. */ +export interface DocSearchCallbacks { + /** Called once DocSearch is mounted and ready for interaction. */ + onReady?: () => void; + /** Called when the modal opens. */ + onOpen?: () => void; + /** Called when the modal closes. */ + onClose?: () => void; + /** Called when the sidepanel opens. */ + onSidepanelOpen?: () => void; + /** Called when the sidepanel closes. */ + onSidepanelClose?: () => void; +} + +export interface DocSearchProps extends DocSearchCallbacks { + children: Array | JSX.Element | React.ReactNode | null; + theme?: DocSearchTheme; + initialQuery?: string; + keyboardShortcuts?: KeyboardShortcuts; +} + +const Context = React.createContext(undefined); +Context.displayName = 'DocSearchContext'; + +function DocSearchInner( + { + children, + theme, + onReady, + onOpen, + onClose, + onSidepanelOpen, + onSidepanelClose, + ...props + }: DocSearchProps, + ref: React.ForwardedRef +): JSX.Element { + const [docsearchState, setDocsearchState] = + React.useState('ready'); + const [initialQuery, setInitialQuery] = React.useState( + props.initialQuery || '' + ); + const searchButtonRef = React.useRef(null); + const keyboardShortcuts = useKeyboardShortcuts(props.keyboardShortcuts); + const [initialAskAiMessage, setInitialAskAiMessage] = + React.useState(); + const [registeredViews, setRegisteredViews] = React.useState( + () => new Set() + ); + const isMobile = useIsMobile(); + const prevStateRef = React.useRef('ready'); + + const isModalActive = ['modal-search', 'modal-askai'].includes( + docsearchState + ); + const isAskAiActive = docsearchState === 'modal-askai'; + const isHybridModeSupported = !isMobile && registeredViews.has('sidepanel'); + const isSidepanelOpen = docsearchState === 'sidepanel'; + + // Call onReady on mount + React.useEffect(() => { + onReady?.(); + }, [onReady]); + + // Track state changes for lifecycle callbacks + React.useEffect(() => { + const prevState = prevStateRef.current; + const currentState = docsearchState; + + // Modal opened + if ( + (currentState === 'modal-search' || currentState === 'modal-askai') && + prevState !== 'modal-search' && + prevState !== 'modal-askai' + ) { + onOpen?.(); + } + + // Modal closed + if ( + currentState === 'ready' && + (prevState === 'modal-search' || prevState === 'modal-askai') + ) { + onClose?.(); + } + + // Sidepanel opened + if (currentState === 'sidepanel' && prevState !== 'sidepanel') { + onSidepanelOpen?.(); + } + + // Sidepanel closed + if (currentState !== 'sidepanel' && prevState === 'sidepanel') { + onSidepanelClose?.(); + } + + prevStateRef.current = currentState; + }, [docsearchState, onOpen, onClose, onSidepanelOpen, onSidepanelClose]); + + const openModal = React.useCallback((): void => { + setDocsearchState('modal-search'); + }, []); + + const closeModal = React.useCallback((): void => { + setDocsearchState('ready'); + // Refocus the Modal trigger on close + searchButtonRef.current?.focus(); + setInitialQuery(props.initialQuery ?? ''); + }, [setDocsearchState, props.initialQuery]); + + const onAskAiToggle: OnAskAiToggle = React.useCallback( + (active, initialMessage) => { + // Don't use hybrid mode on mobile + if (!isMobile && active && isHybridModeSupported) { + setInitialAskAiMessage(initialMessage); + setDocsearchState('sidepanel'); + return; + } + + setDocsearchState(active ? 'modal-askai' : 'modal-search'); + }, + [setDocsearchState, isMobile, isHybridModeSupported] + ); + + const openSidepanel = React.useCallback( + (initialMessage?: InitialAskAiMessage): void => { + // Guard: no-op if sidepanel view hasn't been registered + if (!registeredViews.has('sidepanel')) return; + + setInitialAskAiMessage(initialMessage); + setDocsearchState('sidepanel'); + }, + [setDocsearchState, registeredViews] + ); + + const onInput = React.useCallback( + (event: KeyboardEvent): void => { + setDocsearchState('modal-search'); + setInitialQuery(event.key); + }, + [setDocsearchState, setInitialQuery] + ); + + const registerView = React.useCallback( + (view: View): void => { + if (registeredViews.has(view)) return; + + setRegisteredViews((prev) => { + const newViews = new Set(prev); + newViews.add(view); + return newViews; + }); + }, + [registeredViews] + ); + + // Expose imperative handle for programmatic control + React.useImperativeHandle( + ref, + () => ({ + open: openModal, + close: closeModal, + openAskAi: (initialMessage?: InitialAskAiMessage): void => + onAskAiToggle(true, initialMessage), + openSidepanel, + get isReady(): boolean { + return true; + }, + get isOpen(): boolean { + return isModalActive; + }, + get isSidepanelOpen(): boolean { + return isSidepanelOpen; + }, + get isSidepanelSupported(): boolean { + return isHybridModeSupported; + }, + }), + [ + openModal, + closeModal, + onAskAiToggle, + openSidepanel, + isModalActive, + isSidepanelOpen, + isHybridModeSupported, + ] + ); + + useTheme({ theme }); + + useDocSearchKeyboardEvents({ + isOpen: isModalActive, + onOpen: openModal, + onClose: closeModal, + onAskAiToggle, + onInput, + isAskAiActive, + searchButtonRef, + keyboardShortcuts, + }); + + const value: DocSearchContext = React.useMemo( + () => ({ + docsearchState, + setDocsearchState, + searchButtonRef, + initialQuery, + keyboardShortcuts, + openModal, + closeModal, + isAskAiActive, + isModalActive, + onAskAiToggle, + initialAskAiMessage, + registerView, + isHybridModeSupported, + }), + [ + docsearchState, + searchButtonRef, + initialQuery, + keyboardShortcuts, + openModal, + closeModal, + isAskAiActive, + isModalActive, + onAskAiToggle, + initialAskAiMessage, + registerView, + isHybridModeSupported, + ] + ); + + return {children}; +} + +export const DocSearch = React.forwardRef(DocSearchInner); +DocSearch.displayName = 'DocSearch'; + +export function useDocSearch(): DocSearchContext { + const ctx = React.useContext(Context); + + if (ctx === undefined) { + throw new Error( + '`useDocSearch` must be used within the `DocSearch` provider' + ); + } + + return ctx; +} diff --git a/packages/docsearch-core/src/__tests__/DocSearch.test.tsx b/packages/docsearch-core/src/__tests__/DocSearch.test.tsx new file mode 100644 index 00000000..0578456d --- /dev/null +++ b/packages/docsearch-core/src/__tests__/DocSearch.test.tsx @@ -0,0 +1,475 @@ +import type { RenderHookResult, RenderResult } from '@testing-library/react'; +import { + render, + screen, + cleanup, + renderHook, + act, + fireEvent, +} from '@testing-library/react'; +import type { JSX } from 'react'; +import React, { useRef } from 'react'; +import { describe, it, expect, afterEach, vi } from 'vitest'; +import '@testing-library/jest-dom/vitest'; + +import { + DocSearch, + useDocSearch, + type DocSearchContext, + type DocSearchProps, +} from '../DocSearch'; +import { useDocSearchKeyboardEvents } from '../useDocSearchKeyboardEvents'; +import { useKeyboardShortcuts } from '../useKeyboardShortcuts'; +import { useTheme } from '../useTheme'; + +type DocSearchPropsNoChildren = Omit; + +const renderWithProvider = ( + comp: React.ReactNode, + props: DocSearchPropsNoChildren = {} +): RenderResult => + render(comp, { + wrapper({ children }) { + return {children}; + }, + }); + +const renderCustomHook = ( + props: DocSearchPropsNoChildren = {} +): RenderHookResult => + renderHook(() => useDocSearch(), { + wrapper({ children }) { + return {children}; + }, + }); + +const StateRender = (): JSX.Element => { + const { docsearchState } = useDocSearch(); + + return

State: {docsearchState}

; +}; + +describe('typesense-docsearch-core', () => { + afterEach(() => { + cleanup(); + }); + + describe('DocSearch', () => { + it('renders without errors', () => { + renderWithProvider(

Hello world!

); + + expect(screen.getByText('Hello world!')).toBeInTheDocument(); + }); + + it('provides state to children', () => { + renderWithProvider(); + + expect(screen.getByText('State: ready')).toBeInTheDocument(); + }); + + it('updates state from children', async () => { + const Comp = (): JSX.Element => { + const { docsearchState, openModal } = useDocSearch(); + + return ( + <> +

State: {docsearchState}

+ + + ); + }; + + renderWithProvider(); + + expect(screen.getByText('State: ready')).toBeInTheDocument(); + + const updateBtn = await screen.findByRole('button', { name: /Update/ }); + + act(() => { + fireEvent.click(updateBtn); + }); + + expect(screen.getByText('State: modal-search')).toBeInTheDocument(); + }); + + it('manages keyboard shortcuts', () => { + renderWithProvider(); + + expect(screen.getByText('State: ready')).toBeInTheDocument(); + + act(() => { + fireEvent.keyDown(document, { + key: 'k', + ctrlKey: true, + }); + }); + + expect(screen.getByText('State: modal-search')).toBeInTheDocument(); + + act(() => { + fireEvent.keyDown(document, { + code: 'Escape', + }); + }); + + expect(screen.getByText('State: ready')).toBeInTheDocument(); + + act(() => { + fireEvent.keyDown(document, { + key: '/', + code: 'Slash', + }); + }); + + expect(screen.getByText('State: modal-search')).toBeInTheDocument(); + }); + + it('does not open search with / from a focused button', () => { + renderWithProvider( + <> + + + + ); + + const button = screen.getByRole('button', { name: 'Action' }); + button.focus(); + fireEvent.keyDown(button, { key: '/', code: 'Slash' }); + + expect(screen.getByText('State: ready')).toBeInTheDocument(); + }); + + it('respects keyboard shortcuts', () => { + renderWithProvider(, { + keyboardShortcuts: { + 'Ctrl/Cmd+K': false, + }, + }); + + act(() => { + fireEvent.keyDown(document, { + key: 'k', + ctrlKey: true, + }); + }); + + expect(screen.getByText('State: ready')).toBeInTheDocument(); + + act(() => { + fireEvent.keyDown(document, { + key: '/', + code: 'Slash', + }); + }); + + expect(screen.getByText('State: modal-search')).toBeInTheDocument(); + }); + + it('sets the theme', () => { + renderWithProvider(, { + theme: 'dark', + }); + + expect(document.documentElement.getAttribute('data-theme')).toBe('dark'); + }); + + it('respects initial query', () => { + const Comp = (): JSX.Element => { + const { initialQuery } = useDocSearch(); + + return

Query: {initialQuery}

; + }; + + renderWithProvider(, { + initialQuery: 'hitComponent', + }); + + expect(screen.getByText('Query: hitComponent')).toBeInTheDocument(); + }); + }); + + describe('useDocSearch', () => { + it('renders hook with default state', () => { + const { result } = renderCustomHook(); + + expect(result.current).toMatchObject({ + docsearchState: 'ready', + isModalActive: false, + isAskAiActive: false, + initialQuery: '', + keyboardShortcuts: { + 'Ctrl/Cmd+K': true, + '/': true, + }, + searchButtonRef: { + current: null, + }, + }); + }); + + it('allows setting current state', () => { + const { result } = renderCustomHook(); + + act(() => { + result.current.setDocsearchState('modal-askai'); + }); + + expect(result.current.docsearchState).toBe('modal-askai'); + + act(() => { + result.current.setDocsearchState('modal-search'); + }); + + expect(result.current.docsearchState).toBe('modal-search'); + + act(() => { + result.current.setDocsearchState('ready'); + }); + + expect(result.current.docsearchState).toBe('ready'); + }); + + it('sets modal state', () => { + const { result } = renderCustomHook(); + + act(() => { + result.current.openModal(); + }); + + expect(result.current.docsearchState).toBe('modal-search'); + + act(() => { + result.current.closeModal(); + }); + + expect(result.current.docsearchState).toBe('ready'); + }); + + it('sets askai state', () => { + const { result } = renderCustomHook(); + + act(() => { + result.current.onAskAiToggle(true); + }); + + expect(result.current.isAskAiActive).toBe(true); + expect(result.current.isModalActive).toBe(true); + + act(() => { + result.current.onAskAiToggle(false); + }); + + expect(result.current.isAskAiActive).toBe(false); + expect(result.current.isModalActive).toBe(true); + }); + + it('returns custom keyboard shortcuts', () => { + const { result } = renderCustomHook({ + keyboardShortcuts: { + '/': false, + 'Ctrl/Cmd+K': false, + }, + }); + + expect(result.current.keyboardShortcuts).toMatchObject({ + 'Ctrl/Cmd+K': false, + '/': false, + }); + }); + + it('handles initial query', () => { + const { result } = renderCustomHook({ + initialQuery: 'hitComponent', + }); + + expect(result.current.initialQuery).toBe('hitComponent'); + }); + }); + + describe('useTheme', () => { + it('sets dark theme', () => { + renderHook(() => useTheme({ theme: 'dark' })); + + expect(document.documentElement.getAttribute('data-theme')).toBe('dark'); + }); + + it('sets light theme', () => { + renderHook(() => useTheme({ theme: 'light' })); + + expect(document.documentElement.getAttribute('data-theme')).toBe('light'); + }); + }); + + describe('useKeyboardShortcuts', () => { + it('uses defaults', () => { + const shortcuts = useKeyboardShortcuts(); + + expect(shortcuts).toMatchObject({ + 'Ctrl/Cmd+K': true, + '/': true, + }); + }); + + it('uses custom shortcuts', () => { + const shortcuts = useKeyboardShortcuts({ + '/': false, + 'Ctrl/Cmd+K': false, + }); + + expect(shortcuts).toMatchObject({ + 'Ctrl/Cmd+K': false, + '/': false, + }); + }); + }); + + describe('useDocSearchKeyboardEvents', () => { + const onOpen = vi.fn(); + const onClose = vi.fn(); + const onAskAiToggle = vi.fn(); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('calls to open on keybind', () => { + renderHook(() => { + const searchRef = useRef(null); + return useDocSearchKeyboardEvents({ + isOpen: false, + onOpen, + isAskAiActive: false, + onClose, + searchButtonRef: searchRef, + onAskAiToggle: () => {}, + }); + }); + + act(() => { + fireEvent.keyDown(document, { + key: 'k', + ctrlKey: true, + }); + }); + + expect(onOpen).toHaveBeenCalled(); + }); + + it('calls to open on slash', () => { + renderHook(() => { + const searchRef = useRef(null); + return useDocSearchKeyboardEvents({ + isOpen: false, + onOpen, + isAskAiActive: false, + onClose, + searchButtonRef: searchRef, + onAskAiToggle: () => {}, + }); + }); + + act(() => { + fireEvent.keyDown(document, { + key: '/', + }); + }); + + expect(onOpen).toHaveBeenCalled(); + }); + + it('calls to close on keybind', () => { + renderHook(() => { + const searchRef = useRef(null); + return useDocSearchKeyboardEvents({ + isOpen: true, + onOpen, + isAskAiActive: false, + onClose, + searchButtonRef: searchRef, + onAskAiToggle: () => {}, + }); + }); + + act(() => { + fireEvent.keyDown(document, { + key: 'k', + ctrlKey: true, + }); + }); + + expect(onClose).toHaveBeenCalled(); + }); + + it('closes on escape key', () => { + renderHook(() => { + const searchRef = useRef(null); + return useDocSearchKeyboardEvents({ + isOpen: true, + onOpen, + isAskAiActive: false, + onClose, + searchButtonRef: searchRef, + onAskAiToggle: () => {}, + }); + }); + + act(() => { + fireEvent.keyDown(document, { + code: 'Escape', + }); + }); + + expect(onClose).toHaveBeenCalled(); + }); + + it('respects keyboard shortcuts', () => { + renderHook(() => { + const searchRef = useRef(null); + return useDocSearchKeyboardEvents({ + isOpen: false, + onOpen, + isAskAiActive: false, + onClose, + searchButtonRef: searchRef, + onAskAiToggle: () => {}, + keyboardShortcuts: { + '/': false, + }, + }); + }); + + act(() => { + fireEvent.keyDown(document, { + key: '/', + }); + }); + + expect(onOpen).not.toHaveBeenCalled(); + }); + + it('calls to toggle AskAI', () => { + renderHook(() => { + const searchRef = useRef(null); + return useDocSearchKeyboardEvents({ + isOpen: true, + onOpen, + isAskAiActive: true, + onClose, + searchButtonRef: searchRef, + onAskAiToggle, + }); + }); + + act(() => { + fireEvent.keyDown(document, { + code: 'Escape', + }); + }); + + expect(onAskAiToggle).toHaveBeenCalled(); + expect(onClose).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/docsearch-core/src/index.ts b/packages/docsearch-core/src/index.ts new file mode 100644 index 00000000..f61dd64c --- /dev/null +++ b/packages/docsearch-core/src/index.ts @@ -0,0 +1,4 @@ +export * from './DocSearch'; +export * from './useTheme'; +export * from './useDocSearchKeyboardEvents'; +export * from './useKeyboardShortcuts'; diff --git a/packages/docsearch-core/src/useDocSearchKeyboardEvents.ts b/packages/docsearch-core/src/useDocSearchKeyboardEvents.ts new file mode 100644 index 00000000..73ebf98c --- /dev/null +++ b/packages/docsearch-core/src/useDocSearchKeyboardEvents.ts @@ -0,0 +1,91 @@ +import React from 'react'; + +import { + DEFAULT_KEYBOARD_SHORTCUTS, + type KeyboardShortcuts, +} from './useKeyboardShortcuts'; + +export interface UseDocSearchKeyboardEventsProps { + isOpen: boolean; + onOpen: () => void; + onClose: () => void; + /** + * Deprecated: Still here for backwards compat. + * + * @deprecated + */ + onInput?: (event: KeyboardEvent) => void; + /** + * Deprecated: Still here for backwards compat. + * + * @deprecated + */ + searchButtonRef?: React.RefObject; + isAskAiActive: boolean; + onAskAiToggle: (toggle: boolean) => void; + keyboardShortcuts?: KeyboardShortcuts; +} + +function isEditingContent(event: KeyboardEvent): boolean { + const element = event.composedPath()[0] as HTMLElement; + const tagName = element.tagName; + + return ( + element.isContentEditable || + tagName === 'INPUT' || + tagName === 'SELECT' || + tagName === 'TEXTAREA' || + tagName === 'BUTTON' + ); +} + +export function useDocSearchKeyboardEvents({ + isOpen, + isAskAiActive, + onAskAiToggle, + onClose, + onOpen, + keyboardShortcuts = DEFAULT_KEYBOARD_SHORTCUTS, +}: UseDocSearchKeyboardEventsProps): void { + React.useEffect(() => { + function onKeyDown(event: KeyboardEvent): void { + if (isOpen && event.code === 'Escape' && isAskAiActive) { + onAskAiToggle(false); + return; + } + + const isCmdK = + keyboardShortcuts['Ctrl/Cmd+K'] && + event.key?.toLowerCase() === 'k' && + (event.metaKey || event.ctrlKey); + const isSlash = keyboardShortcuts['/'] && event.key === '/'; + + if ( + (event.code === 'Escape' && isOpen) || + isCmdK || + (!isEditingContent(event) && isSlash && !isOpen) + ) { + event.preventDefault(); + + if (isOpen) { + onClose(); + } else if (!document.body.classList.contains('DocSearch--active')) { + onOpen(); + } + } + } + + window.addEventListener('keydown', onKeyDown); + + return (): void => { + window.removeEventListener('keydown', onKeyDown); + }; + }, [ + isOpen, + isAskAiActive, + keyboardShortcuts, + onOpen, + onClose, + onAskAiToggle, + ]); +} diff --git a/packages/docsearch-core/src/useIsMobile.ts b/packages/docsearch-core/src/useIsMobile.ts new file mode 100644 index 00000000..c83568ba --- /dev/null +++ b/packages/docsearch-core/src/useIsMobile.ts @@ -0,0 +1,23 @@ +import React from 'react'; + +export const useIsMobile = (): boolean => { + const [isMobile, setIsMobile] = React.useState(false); + + React.useEffect(() => { + const checkForMobile = (): void => { + const isMobileMediaQuery = window.matchMedia('(max-width: 768px)'); + + setIsMobile(isMobileMediaQuery.matches); + }; + + checkForMobile(); + + window.addEventListener('resize', checkForMobile); + + return (): void => { + window.removeEventListener('resize', checkForMobile); + }; + }, []); + + return isMobile; +}; diff --git a/packages/docsearch-core/src/useKeyboardShortcuts.ts b/packages/docsearch-core/src/useKeyboardShortcuts.ts new file mode 100644 index 00000000..ca918264 --- /dev/null +++ b/packages/docsearch-core/src/useKeyboardShortcuts.ts @@ -0,0 +1,51 @@ +export interface DocSearchModalShortcuts { + /** + * Enable/disable the Ctrl/Cmd+K shortcut to toggle the DocSearch modal. + * + * @default true + */ + 'Ctrl/Cmd+K'?: boolean; + /** + * Enable/disable the / shortcut to open the DocSearch modal. + * + * @default true + */ + '/'?: boolean; +} + +export interface SidepanelShortcuts { + /** + * Enable/disable the Ctrl/Cmd+I shortcut to toggle the DocSearch sidepanel. + * + * @default true + */ + 'Ctrl/Cmd+I'?: boolean; +} + +export type KeyboardShortcuts = DocSearchModalShortcuts & SidepanelShortcuts; + +/** + * Default keyboard shortcuts configuration for DocSearch. These values are used + * when no keyboardShortcuts prop is provided or when specific shortcuts are not + * configured. + */ +export const DEFAULT_KEYBOARD_SHORTCUTS: Required = { + 'Ctrl/Cmd+K': true, + '/': true, + 'Ctrl/Cmd+I': true, +} as const; + +/** + * Merges user-provided keyboard shortcuts with defaults. + * + * @param userShortcuts - Optional user configuration. + * @returns Complete keyboard shortcuts configuration with defaults applied. + */ +export function useKeyboardShortcuts( + userShortcuts?: KeyboardShortcuts +): Required { + return { + ...DEFAULT_KEYBOARD_SHORTCUTS, + ...userShortcuts, + }; +} diff --git a/packages/docsearch-core/src/useTheme.ts b/packages/docsearch-core/src/useTheme.ts new file mode 100644 index 00000000..924113b4 --- /dev/null +++ b/packages/docsearch-core/src/useTheme.ts @@ -0,0 +1,31 @@ +import { useEffect } from 'react'; + +export type DocSearchTheme = 'dark' | 'light'; + +export interface UseThemeProps { + theme?: DocSearchTheme; +} + +export const useTheme = ({ theme }: UseThemeProps): void => { + useEffect(() => { + if (!theme) { + return undefined; + } + + const previousTheme = document.documentElement.dataset.theme; + + if (theme === previousTheme) { + return undefined; + } + + document.documentElement.dataset.theme = theme; + + return (): void => { + if (previousTheme === undefined) { + delete document.documentElement.dataset.theme; + } else { + document.documentElement.dataset.theme = previousTheme; + } + }; + }, [theme]); +}; diff --git a/packages/docsearch-core/tsdown.config.mts b/packages/docsearch-core/tsdown.config.mts new file mode 100644 index 00000000..2d745cdd --- /dev/null +++ b/packages/docsearch-core/tsdown.config.mts @@ -0,0 +1,56 @@ +import type { UserConfig } from 'tsdown'; +import { defineConfig } from 'tsdown'; + +import { getBundleBanner } from '../../scripts/getBundleBanner.ts'; +import { defines, pkgExports } from '../../tsdown.base.ts'; + +import pkg from './package.json' with { type: 'json' }; + +const externals = ['react', 'react-dom', /^react\//]; + +const sharedConfig: UserConfig = { + platform: 'browser', + deps: { + neverBundle: externals, + }, + target: 'es2017', + define: defines, + sourcemap: true, + outExtensions: () => ({ + js: '.js', + }), +}; + +export default defineConfig([ + { + entry: { + index: 'src/index.ts', + useTheme: 'src/useTheme.ts', + useDocSearchKeyboardEvents: 'src/useDocSearchKeyboardEvents.ts', + useKeyboardShortcuts: 'src/useKeyboardShortcuts.ts', + }, + ...sharedConfig, + dts: true, + format: 'esm', + minify: false, + outDir: 'dist/esm', + exports: pkgExports, + }, + { + entry: 'src/index.ts', + outDir: 'dist/umd', + ...sharedConfig, + banner: getBundleBanner(pkg), + dts: false, + globalName: 'DocSearchCore', + minify: true, + outputOptions: { + entryFileNames: '[name].js', + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, + }, + format: 'umd', + }, +]); diff --git a/packages/docsearch-css/CHANGELOG.md b/packages/docsearch-css/CHANGELOG.md new file mode 100644 index 00000000..a95ded21 --- /dev/null +++ b/packages/docsearch-css/CHANGELOG.md @@ -0,0 +1,242 @@ +# typesense-docsearch-css + +## 5.0.2 + +## 5.0.1 + +## 5.0.0 + +### Major Changes + +- ecd905d: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed + +### Patch Changes + +- ecd905d: feat(v5): General UI styling updates and fixes + + - New `--docsearch-font-family` variable, used by the search button, keyboard + keys, modal, and sidepanel. It replaces the system font stacks that were + duplicated across `sidepanel.css` and `button.css`, so overriding one + variable now themes every DocSearch surface + - The search input and modal heading are `1rem` at every breakpoint + (previously `0.875rem` with a mobile-only override) + - `.DocSearch-Title` uses `line-height: 1.5em` instead of `0.5em` and adds + `overflow-wrap: anywhere`, so long titles wrap instead of overlapping (#2908) + - Hit icon `svg` sizing moved into `.DocSearch-Hit-icon`. The + `.DocSearch-Hit-icon--small` modifier is replaced by + `.DocSearch-Hit-icon--start`, which top-aligns the icon; recent + conversations use it + - The Ask AI button icon centers with `display: inline-flex` instead of + `margin-block-start`/`align-self` overrides + - Removed the 2px offset on the Ask AI sources action text + - `SourcesPanel` accepts `pluralTitleText`. `titleText` is now the singular + label, and the trigger renders `{count} {label}` + - `AskAiScreenTranslations` and `ConversationScreenTranslations` expose + `relatedSourcesTextPlural` + - Docusaurus adapter: `theme.SearchModal.askAiScreen.relatedSourcesText` is + now the singular "Source", and the new + `theme.SearchModal.askAiScreen.relatedSourcesTextPlural` provides "Sources" + +- ecd905d: Surface Agent Studio cost-control errors and block prompts until the user can recover. [#2878](https://github.com/algolia/docsearch/pull/2878) +- ecd905d: Stop over-truncating mobile snippets and allow long search hits to wrap. +- ecd905d: fix(askai): Ask AI fixes for v5 + + - `getMessageContent` now joins every assistant text part instead of stopping + at the first one, so copying an answer that interleaves text with tool calls + returns the complete response (#2782) + - Key Ask AI conversation storage by `appId` instead of `indexName`, fixes + triggering a new conversation in hybrid mode + - Negative feedback panel is labelled with `aria-labelledby` and reason chips + expose selection via `aria-pressed` (styling moved off the + `--selected` modifier) + - `Popover.Trigger` forwards refs, fixes sources panel not showing in some cases + - Add `--docsearch-error-soft-color`, `--docsearch-error-text-color`, and + `--docsearch-code-block-background` variables; restyle the Ask AI error panel + and markdown code blocks to use them + + ``` + + ``` + +- ecd905d: feat(v5): add customizable footer action + + - New `footerAction` prop renders a custom action in the modal footer, + before the Algolia logo, inside `.DocSearch-Footer-Action` + - `typesense-docsearch.js` supports `footerAction` via template patterns (html helper, + JSX, or function-based) + - Fixes typing differences between `typesense-docsearch-react` and `typesense-docsearch.js` + - Restyle the footer with a `.DocSearch-Footer-Actions` wrapper + +- ecd905d: Align CSS class and animation names with the `DocSearch-` naming convention. + + - The `shimmer` utility class is now `DocSearch-shimmer` + - Fixed a typo in `DocSearck-AskAiScreen-MessageContent-Stopped`, now + `DocSearch-AskAiScreen-MessageContent-Stopped` + - Keyframes renamed to kebab-case: `shimmerText` → `shimmer-text`, + `slideDown` → `slide-down`, `fadeIn` → `fade-in` + - Dark theme selectors use `:root[data-theme='dark']` instead of + `html[data-theme='dark']` + - Dropped redundant type qualifiers from `.DocSearch-Hits-padded` and + `.DocSearch-Hit-Select-Icon` selectors, slightly lowering their specificity + + If you override these classes or keyframes in custom styles, update your + selectors accordingly. + +## 5.0.0-beta.4 + +### Patch Changes + +- ee9fddb: Surface Agent Studio cost-control errors and block prompts until the user can recover. [#2878](https://github.com/algolia/docsearch/pull/2878) + +## 5.0.0-beta.3 + +### Patch Changes + +- 4e44b55: Stop over-truncating mobile snippets and allow long search hits to wrap. +- b28bc84: feat(v5): add customizable footer action + + - New `footerAction` prop renders a custom action in the modal footer, + before the Algolia logo, inside `.DocSearch-Footer-Action` + - `typesense-docsearch.js` supports `footerAction` via template patterns (html helper, + JSX, or function-based) + - Fixes typing differences between `typesense-docsearch-react` and `typesense-docsearch.js` + - Restyle the footer with a `.DocSearch-Footer-Actions` wrapper + +## 5.0.0-beta.2 + +### Patch Changes + +- 5eac1fd: feat(v5): General UI styling updates and fixes + + - New `--docsearch-font-family` variable, used by the search button, keyboard + keys, modal, and sidepanel. It replaces the system font stacks that were + duplicated across `sidepanel.css` and `button.css`, so overriding one + variable now themes every DocSearch surface + - The search input and modal heading are `1rem` at every breakpoint + (previously `0.875rem` with a mobile-only override) + - `.DocSearch-Title` uses `line-height: 1.5em` instead of `0.5em` and adds + `overflow-wrap: anywhere`, so long titles wrap instead of overlapping (#2908) + - Hit icon `svg` sizing moved into `.DocSearch-Hit-icon`. The + `.DocSearch-Hit-icon--small` modifier is replaced by + `.DocSearch-Hit-icon--start`, which top-aligns the icon; recent + conversations use it + - The Ask AI button icon centers with `display: inline-flex` instead of + `margin-block-start`/`align-self` overrides + - Removed the 2px offset on the Ask AI sources action text + - `SourcesPanel` accepts `pluralTitleText`. `titleText` is now the singular + label, and the trigger renders `{count} {label}` + - `AskAiScreenTranslations` and `ConversationScreenTranslations` expose + `relatedSourcesTextPlural` + - Docusaurus adapter: `theme.SearchModal.askAiScreen.relatedSourcesText` is + now the singular "Source", and the new + `theme.SearchModal.askAiScreen.relatedSourcesTextPlural` provides "Sources" + +- 9fe6738: fix(askai): Ask AI fixes for v5 + + - `getMessageContent` now joins every assistant text part instead of stopping + at the first one, so copying an answer that interleaves text with tool calls + returns the complete response (#2782) + - Key Ask AI conversation storage by `appId` instead of `indexName`, fixes + triggering a new conversation in hybrid mode + - Negative feedback panel is labelled with `aria-labelledby` and reason chips + expose selection via `aria-pressed` (styling moved off the + `--selected` modifier) + - `Popover.Trigger` forwards refs, fixes sources panel not showing in some cases + - Add `--docsearch-error-soft-color`, `--docsearch-error-text-color`, and + `--docsearch-code-block-background` variables; restyle the Ask AI error panel + and markdown code blocks to use them + + ``` + + ``` + +## 5.0.0-beta.1 + +### Patch Changes + +- 09861c8: Align CSS class and animation names with the `DocSearch-` naming convention. + + - The `shimmer` utility class is now `DocSearch-shimmer` + - Fixed a typo in `DocSearck-AskAiScreen-MessageContent-Stopped`, now + `DocSearch-AskAiScreen-MessageContent-Stopped` + - Keyframes renamed to kebab-case: `shimmerText` → `shimmer-text`, + `slideDown` → `slide-down`, `fadeIn` → `fade-in` + - Dark theme selectors use `:root[data-theme='dark']` instead of + `html[data-theme='dark']` + - Dropped redundant type qualifiers from `.DocSearch-Hits-padded` and + `.DocSearch-Hit-Select-Icon` selectors, slightly lowering their specificity + + If you override these classes or keyframes in custom styles, update your + selectors accordingly. + +## 5.0.0-beta.0 + +### Major Changes + +- a8ed1ea: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed diff --git a/packages/docsearch-css/build-css.js b/packages/docsearch-css/build-css.js deleted file mode 100644 index 7d2d7004..00000000 --- a/packages/docsearch-css/build-css.js +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable import/no-commonjs, import/no-extraneous-dependencies */ - -const { execSync } = require('child_process'); -const fs = require('fs'); -const util = require('util'); - -const cssnano = require('cssnano'); -const postcss = require('postcss'); - -const pkg = require('./package.json'); - -const readFile = util.promisify(fs.readFile); - -function getBundleBanner(_pkg) { - const lastCommitHash = execSync('git rev-parse --short HEAD') - .toString() - .trim(); - const version = process.env.SHIPJS - ? _pkg.version - : `${_pkg.version} (UNRELEASED ${lastCommitHash})`; - const authors = '© Algolia, Inc. and contributors'; - - return `/*! ${_pkg.name} ${version} | MIT License | ${authors} | ${_pkg.homepage} */`; -} - -function build({ input, output, banner }) { - fs.readFile(input, (error, css) => { - if (error) { - throw error; - } - - postcss([cssnano]) - .process(css, { from: input, to: output }) - .then((result) => { - fs.writeFile(output, [banner, result.css].join('\n'), () => true); - }); - }); -} - -build({ - input: 'src/_variables.css', - output: 'dist/_variables.css', - banner: getBundleBanner({ ...pkg, name: `${pkg.name} Variables` }), -}); -build({ - input: 'src/button.css', - output: 'dist/button.css', - banner: getBundleBanner({ ...pkg, name: `${pkg.name} Button` }), -}); -build({ - input: 'src/modal.css', - output: 'dist/modal.css', - banner: getBundleBanner({ ...pkg, name: `${pkg.name} Modal` }), -}); - -async function buildStyle() { - const variablesCss = await readFile('src/_variables.css'); - const buttonCss = await readFile('src/button.css'); - const modalCss = await readFile('src/modal.css'); - - const variablesOutput = await postcss([cssnano]).process(variablesCss, { - from: undefined, - }); - const buttonOutput = await postcss([cssnano]).process(buttonCss, { - from: undefined, - }); - const modalOutput = await postcss([cssnano]).process(modalCss, { - from: undefined, - }); - - fs.writeFile( - 'dist/style.css', - [ - getBundleBanner(pkg), - [variablesOutput.css, buttonOutput.css, modalOutput.css].join(''), - ].join('\n'), - () => true - ); -} - -buildStyle(); diff --git a/packages/docsearch-css/build-css.ts b/packages/docsearch-css/build-css.ts new file mode 100644 index 00000000..b9925a2b --- /dev/null +++ b/packages/docsearch-css/build-css.ts @@ -0,0 +1,70 @@ +import fs from 'fs'; +import path from 'path'; + +import browserslist from 'browserslist'; +import { browserslistToTargets, bundle } from 'lightningcss'; + +import { getBundleBanner } from '../../scripts/getBundleBanner'; + +import pkg from './package.json' with { type: 'json' }; + +interface Entry { + input: string; + outputs: string[]; + /** Suffix appended to the package name in the bundle banner. */ + bannerName?: string; +} + +const ENTRIES: Entry[] = [ + { + input: 'src/_variables.css', + outputs: ['dist/_variables.css', 'dist/_variables.scss'], + bannerName: 'Variables', + }, + { + input: 'src/button.css', + outputs: ['dist/button.css', 'dist/button.scss'], + bannerName: 'Button', + }, + { + input: 'src/modal.css', + outputs: ['dist/modal.css', 'dist/modal.scss'], + bannerName: 'Modal', + }, + { + input: 'src/_askai.css', + outputs: ['dist/_askai.css', 'dist/_askai.scss'], + bannerName: 'Ask AI', + }, + { input: 'src/style.css', outputs: ['dist/style.css', 'dist/style.scss'] }, + { + input: 'src/sidepanel.css', + outputs: ['dist/sidepanel.css', 'dist/sidepanel.scss'], + bannerName: 'Sidepanel', + }, +]; + +const targets = browserslistToTargets( + browserslist(undefined, { path: import.meta.dirname }) +); + +function build({ input, outputs, bannerName }: Entry): void { + const banner = getBundleBanner({ + ...pkg, + name: bannerName ? `${pkg.name} ${bannerName}` : pkg.name, + }); + + const { code } = bundle({ + filename: path.join(import.meta.dirname, input), + minify: true, + targets, + }); + + const content = [banner, code.toString()].join('\n'); + + for (const output of outputs) { + fs.writeFileSync(path.join(import.meta.dirname, output), content); + } +} + +ENTRIES.forEach(build); diff --git a/packages/docsearch-css/package.json b/packages/docsearch-css/package.json index 6a16bad5..9e575a16 100644 --- a/packages/docsearch-css/package.json +++ b/packages/docsearch-css/package.json @@ -1,7 +1,7 @@ { "name": "typesense-docsearch-css", "description": "Styles for DocSearch, customized for Typesense", - "version": "0.4.1", + "version": "5.0.2", "license": "MIT", "homepage": "https://typesense.org/docs/latest/guide/docsearch.html", "repository": "typesense/docsearch.js", @@ -23,8 +23,19 @@ "jsdelivr": "dist/style.css", "scripts": { "build:clean": "rm -rf ./dist", - "build:css": "node build-css.js", - "build": "yarn build:clean && mkdir dist && yarn build:css", - "watch": "watch \"yarn build:css\" --ignoreDirectoryPattern \"/dist/\"" - } + "build:css": "bun build-css.ts", + "build": "bun run build:clean && mkdir dist && bun run build:css", + "watch": "nodemon --watch 'src/**/*.css' --exec 'bun run build:css' --ext css" + }, + "devDependencies": { + "browserslist": "4.28.2", + "lightningcss": "1.32.0", + "nodemon": "^3.1.9" + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "Firefox ESR", + "not dead" + ] } diff --git a/packages/docsearch-css/src/_askai.css b/packages/docsearch-css/src/_askai.css new file mode 100644 index 00000000..24cbd771 --- /dev/null +++ b/packages/docsearch-css/src/_askai.css @@ -0,0 +1,1202 @@ +/* Ask AI-specific modal rules */ + +.DocSearch-AskAi-Return { + margin: 0; + align-items: center; + display: flex; + justify-content: center; + height: 2rem; + width: 2rem; + margin-left: -0.5rem; + animation: fade-in 0.1s ease-in forwards; + appearance: none; + background: none; + border: 0; + border-radius: var(--docsearch-border-radius); + color: var(--docsearch-icon-color); + cursor: pointer; + padding: 2px; + inset-inline-end: 0; + stroke-width: var(--docsearch-icon-stroke-width); +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-AskAi-Return { + animation: none; + } +} + +.DocSearch-AskAi-Return[hidden] { + display: none; +} + +.DocSearch-StreamingIndicator[hidden] { + display: none; +} + +.DocSearch-AskAi-Return:hover { + color: var(--docsearch-highlight-color); + background: var(--docsearch-soft-primary-color); +} + +.DocSearch-Hit--AskAI { + background: var(--docsearch-soft-primary-color); + border: 1px solid var(--docsearch-askai-hit-border-color); + border-radius: var(--docsearch-hit-radius); + display: block; + padding-inline-start: 0.5rem; + width: 100%; + cursor: pointer; + transition: + background-color 120ms var(--ease-smooth), + border-color 120ms var(--ease-smooth); +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-Hit--AskAI { + transition: none; + } +} + +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit--AskAI { + background-color: var(--docsearch-askai-hit-focus-background); + border-color: var(--docsearch-askai-hit-focus-border-color); +} + +.DocSearch-AskAiScreen { + font-size: 1.25em; + font-weight: 400; + margin: 0 auto; + color: var(--docsearch-secondary-text-color); + text-align: center; + width: 80%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + max-height: 80%; + gap: 24px; +} + +.DocSearch-NoResults--withAskAi { + justify-content: flex-start; + margin-block-start: 0; +} + +/* Docsearch Result Actions */ + +.DocSearch-AskAi-Section { + padding: 12px 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +/* Ask AI Button */ + +.DocSearch-Hit-AskAIButton { + height: 44px; +} + +.DocSearch-Hit-AskAIButton-icon { + display: inline-flex; + align-items: center; + margin-inline-end: 8px; + color: var(--docsearch-highlight-color); +} + +.DocSearch-Hit-AskAIButton-title { + display: flex; + flex: 1 1 auto; + position: relative; + width: 80%; + font-size: 0.9em; +} + +.DocSearch-Hit-AskAIButton-title-query { + white-space: nowrap; + overflow: hidden; + background: none; + text-overflow: ellipsis; +} + +@keyframes fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +/* ask ai screen specific styles */ +.DocSearch-AskAiScreen-Container { + display: flex; + flex-direction: column; + text-align: start; + justify-content: flex-start; + padding: 0; + width: 100%; + height: 100%; + gap: 0; +} + +.DocSearch-AskAiScreen-Disclaimer { + display: flex; + font-size: 0.6em; + font-weight: 300; + padding-block: 0.5em 1em; + padding-inline: 0; + text-align: center; + margin: 0; + align-self: center; + align-items: center; + gap: 1ch; + color: var(--docsearch-secondary-text-color); +} + +.DocSearch-AskAiScreen-Disclaimer svg { + height: 1rem; + width: 1rem; +} + +.DocSearch-AskAiScreen-Body { + display: flex; + flex-direction: column; + gap: 24px; + width: 100%; +} + +.DocSearch-AskAiScreen-Response-Container { + display: flex; + flex-direction: column; +} + +.DocSearch-AskAiScreen-Response { + display: flex; + flex-direction: column; + width: 100%; + gap: 0.8125rem; + font-size: 0.8em; + padding: 16px; + color: var(--docsearch-text-color); + border-radius: 4px; + align-self: flex-start; +} + +.DocSearch-AskAiScreen-Message--user { + align-self: flex-end; + background-color: var(--docsearch-background-color); + padding: 0.5rem 0.75rem; + border-radius: var(--docsearch-modal-radius); + font-size: 14px; + font-weight: 400; + line-break: loose; + margin: 0; +} + +.DocSearch-AskAiScreen-Answer { + line-height: 1.5; + font-weight: 400; + color: var(--docsearch-secondary-text-color); + margin: 0; +} + +.DocSearch-AskAiScreen-ThinkingDots { + font-size: 0.8em; + font-weight: 400; + color: var(--docsearch-secondary-text-color); + margin: 0; +} + +.DocSearch-AskAiScreen-Answer-Footer { + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.DocSearch-AskAiScreen-Actions-Controls { + display: flex; + flex-direction: row; + gap: 12px; + align-items: center; + align-self: flex-end; +} + +/* Negative feedback note panel */ +.DocSearch-Feedback-Panel { + flex-basis: 100%; + width: 100%; + padding-block: var(--docsearch-spacing); + padding-inline: var(--docsearch-spacing); + margin-block-start: var(--docsearch-spacing); + border-top: 1px solid var(--docsearch-subtle-color); + display: flex; + flex-direction: column; + gap: 0.75rem; + text-align: start; + animation: fade-in 0.2s ease-in forwards; + font-size: 0.875rem; +} + +.DocSearch-Feedback-Panel-Header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.DocSearch-Feedback-Panel-Title { + margin: 0; + font-size: 1em; + font-weight: 600; + color: var(--docsearch-text-color); +} + +.DocSearch-Feedback-Panel-Close { + display: flex; + align-items: center; + justify-content: center; + padding-inline: 0.25rem; + padding-block: 0.25rem; + border: 0; + background: none; + cursor: pointer; + border-radius: var(--docsearch-border-radius); + color: var(--docsearch-icon-color); + transition: background-color 0.2s ease; +} + +.DocSearch-Feedback-Panel-Close:hover { + background: var(--docsearch-hit-highlight-color); +} + +.DocSearch-Feedback-Panel-Close svg { + width: 1rem; + height: 1rem; +} + +.DocSearch-Feedback-Panel-Reasons { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.DocSearch-Feedback-Panel-Reason { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding-block: 0.375rem; + padding-inline: 0.75rem; + border-radius: var(--docsearch-border-radius); + border: 1px solid var(--docsearch-subtle-color); + background: var(--docsearch-hit-background); + color: var(--docsearch-text-color); + font-family: inherit; + font-size: 1em; + cursor: pointer; + transition: + border-color 0.2s ease, + background-color 0.2s ease, + color 0.2s ease; + + &:hover, + &[aria-pressed='true'] { + border-color: var(--docsearch-highlight-color); + background: var(--docsearch-hit-highlight-color); + color: var(--docsearch-highlight-color); + } +} + +.DocSearch-Feedback-Panel-Reason svg { + width: 14px; + height: 14px; + flex-shrink: 0; +} + +.DocSearch-Feedback-Panel-Textarea { + width: 100%; + min-height: 64px; + resize: vertical; + box-sizing: border-box; + padding: 10px 12px; + border-radius: var(--docsearch-border-radius); + border: 1px solid var(--docsearch-subtle-color); + background: var(--docsearch-hit-background); + color: var(--docsearch-text-color); + font-family: inherit; + font-size: 1em; + line-height: 1.5; +} + +.DocSearch-Feedback-Panel-Textarea::placeholder { + color: var(--docsearch-muted-color); + opacity: 1; +} + +.DocSearch-Feedback-Panel-Textarea:focus { + outline: none; + border-color: var(--docsearch-highlight-color); +} + +.DocSearch-Feedback-Panel-CharCount { + margin: 0; + align-self: flex-end; + font-size: 0.8em; + color: var(--docsearch-muted-color); + font-variant-numeric: tabular-nums; +} + +.DocSearch-Feedback-Panel-Error { + margin: 0; + font-size: 0.85em; + color: var(--docsearch-error-color); +} + +.DocSearch-Feedback-Panel-Actions { + align-self: flex-end; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 1em; +} + +.DocSearch-Feedback-Panel-Submit, +.DocSearch-Feedback-Panel-Cancel { + display: flex; + align-items: center; + justify-content: center; + padding-block: 0.5em; + padding-inline: 1em; + border: 0; + border-radius: var(--docsearch-border-radius); + font-family: inherit; + font-size: 0.9em; + font-weight: 600; + cursor: pointer; + transition: opacity 0.2s ease; +} + +.DocSearch-Feedback-Panel-Submit { + background: var(--docsearch-highlight-color); + color: #fff; +} + +.DocSearch-Feedback-Panel-Submit:disabled { + opacity: 0.6; + cursor: default; +} + +.DocSearch-Feedback-Panel-Submit:hover:not(:disabled) { + opacity: 0.9; +} + +.DocSearch-Feedback-Panel-Cancel { + background: var(--docsearch-modal-background); + color: var(--docsearch-text-color); +} + +.DocSearch-Feedback-Panel-Cancel:hover { + background: var(--docsearch-subtle-color); +} + +/* Prompt Suggestions */ + +.DocSearch-PromptSuggestions { + display: flex; + flex-direction: column; + gap: 0.625rem; +} + +.DocSearch-PromptSuggestions-Title { + color: var(--docsearch-text-color); + font-size: 0.875em; + line-height: 1.25em; + font-weight: 400; +} + +.DocSearch-PromptSuggestions-Content { + display: flex; + flex-direction: column; + align-items: start; + justify-content: start; + gap: 0.625rem; + + @media screen and (width >= 768px) { + align-items: unset; + justify-content: unset; + flex-flow: row wrap; + } +} + +.DocSearch-PromptSuggestions-Action { + display: inline-flex; + border: 1px solid var(--docsearch-subtle-color); + padding-inline: 0.625rem; + padding-block: 0.4375rem; + border-radius: 100vw; + background-color: var(--docsearch-modal-background); + font-size: 0.875em; + color: var(--docsearch-text-color); + font-weight: 400; + min-height: 1.875rem; + cursor: pointer; + transition-property: background-color, color, border-color; + transition-duration: 0.2s; + transition-timing-function: ease; + + @media screen and (prefers-reduced-motion: reduce) { + transition-property: none; + transition-duration: 0s; + } + + &:focus-visible { + outline: 2px solid var(--docsearch-primary-color); + outline-offset: 2px; + } + + &:hover { + background-color: var(--docsearch-soft-primary-color); + color: var(--docsearch-highlight-color); + border-color: var(--docsearch-soft-primary-color); + } +} + +.DocSearch-AskAiScreen-ActionButton { + display: flex; + align-items: center; + justify-content: center; + border: 0; + width: 24px; + background: none; + padding: 4px; + margin: 0; + cursor: pointer; + transition: background-color 0.2s ease; + border-radius: 4px; +} + +.DocSearch-AskAiScreen-ActionButton:hover { + background: var(--docsearch-hit-highlight-color); +} + +.DocSearch-AskAiScreen-ActionButton svg { + width: 20px; + height: 20px; + stroke-width: 1.5; + color: var(--docsearch-icon-color); +} + +.DocSearch-AskAiScreen-CopyButton--copied { + background-color: var(--docsearch-success-color); + cursor: default; +} + +.DocSearch-AskAiScreen-MessageContent { + display: flex; + flex-direction: column; + row-gap: 0.75rem; +} + +.DocSearch-AskAiScreen-Error { + display: flex; + padding-inline: 1em; + padding-block: 1em; + gap: 8px; + background-color: var(--docsearch-error-soft-color); + border-radius: var(--docsearch-border-radius); + color: var(--docsearch-error-text-color); + font-size: 1em; + font-weight: 400; + flex-direction: row; +} + +.DocSearch-AskAiScreen-Error svg { + margin-top: 0.25rem; + stroke-width: var(--docsearch-icon-stroke-width); + height: 1rem; + width: 1rem; +} + +.DocSearch-AskAiScreen-MessageContent-Tool svg { + width: 0.9rem; + height: 0.9rem; + flex-shrink: 0; +} + +.DocSearch-AskAiScreen-Error p { + margin: 0; + color: var(--docsearch-error-text-color); +} + +.DocSearch-AskAiScreen-Error-Content { + display: flex; + flex-direction: column; + flex: 1 1 0%; +} + +.DocSearch-AskAiScreen-Error-Title { + font-weight: 600; + font-size: 1em; +} + +/* Thread Depth Error - positioned below input */ +.DocSearch-AskAiScreen-Error--ThreadDepth { + margin: 12px 0 8px; + color: var(--docsearch-text-color); + font-size: 12px; + border: 1px solid var(--docsearch-error-soft-color); + width: 100%; +} + +@media (prefers-reduced-motion: no-preference) { + .DocSearch-AskAiScreen-Error--ThreadDepth { + animation: slide-down 0.3s ease-out; + } +} + +@keyframes slide-down { + from { + opacity: 0; + transform: translateY(-10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.DocSearch-ThreadDepthError-Link { + color: var(--docsearch-highlight-color); + text-decoration: underline; + cursor: pointer; + background: none; + border: 0; + padding: 0; + font-size: inherit; + font-family: inherit; +} + +.DocSearch-ThreadDepthError-Link:hover { + opacity: 0.8; +} + +.DocSearch-ThreadDepthError-Link:active { + color: rgb(153 27 27); +} + +.DocSearch-ThreadDepthError-Link:focus-visible { + outline: 2px solid var(--docsearch-focus-color); + outline-offset: 2px; +} + +.DocSearch-AskAiScreen-FeedbackText { + font-size: 0.7em; + font-weight: 400; + color: var(--docsearch-muted-color); + margin: 0; +} + +/* fade in animation when feedback has been given */ +.DocSearch-AskAiScreen-FeedbackText--visible { + animation: fade-in 0.3s ease-in forwards; +} + +.DocSearch-AskAiScreen-RelatedSources { + display: flex; + flex-direction: column; + width: 100%; + gap: 4px; +} + +.DocSearch-AskAiScreen-RelatedSources-List { + display: flex; + flex-flow: row wrap; + width: 100%; + gap: 12px; +} + +.DocSearch-AskAiScreen-RelatedSources-Title { + font-size: 0.7em; + font-weight: 400; + color: var(--docsearch-secondary-text-color); + padding: 6px 0; + margin: 0; +} + +.DocSearch-AskAiScreen-RelatedSources-NoResults { + font-size: 0.8rem; + font-weight: 400; + margin: 0; + color: var(--docsearch-text-color); +} + +.DocSearch-AskAiScreen-RelatedSources-Error { + font-size: 0.8rem; + font-weight: 400; + margin: 0; + color: var(--docsearch-error-color); +} + +.DocSearch-AskAiScreen-RelatedSources-Item-Link { + display: flex; + align-items: center; + gap: 6px; + padding: 12px 8px; + background: var(--docsearch-hit-background); + border-radius: 4px; + color: var(--docsearch-text-color); + font-size: 0.75em; + max-width: 70%; + text-decoration: none; + transition: background-color 0.2s ease; +} + +.DocSearch-AskAiScreen-RelatedSources-Item-Link svg { + flex-shrink: 0; + color: var(--docsearch-icon-color); + stroke-width: 1.2; +} + +.DocSearch-AskAiScreen-RelatedSources-Item-Link span { + flex: 1 1 0; + min-width: 0; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.DocSearch-AskAiScreen-ExchangesList { + gap: 12px; + margin: 8px 0; + display: flex; + flex-direction: column; +} + +.DocSearch-AskAiScreen-RelatedSources-Item-Link:hover { + background: var(--docsearch-hit-highlight-color); +} + +.DocSearch-Markdown-Content { + color: var(--docsearch-text-color); + line-height: 1.6; + font-size: 0.9355em; + overflow-wrap: break-word; +} + +.DocSearch-Markdown-Content--streaming { + animation: fade-in 0.3s ease-in-out both; +} + +.DocSearch-Markdown-Content p { + margin-block: 1em; + margin-inline: 0; +} + +.DocSearch-Markdown-Content p:last-child { + margin-block-end: 0; +} + +.DocSearch-Markdown-Content p:first-child { + margin-block-start: 0; +} + +.DocSearch-Markdown-Content code { + background-color: var(--docsearch-code-block-background); + color: var(--docsearch-text-color); + padding-block: 0.2em; + padding-inline: 0; + margin: 0; + border-radius: var(--docsearch-border-radius); + font-family: + ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', + monospace; + font-size: 0.9rem; + letter-spacing: normal; +} + +.DocSearch-Markdown-Content pre { + background-color: var(--docsearch-code-block-background); + color: var(--docsearch-text-color); + padding-block: 1.2em; + padding-inline: 1.2em; + border-radius: var(--docsearch-border-radius); + overflow-x: auto; + margin-block: 1.5em; + margin-inline: 0; +} + +.DocSearch-Markdown-Content pre code { + background-color: transparent; + color: inherit; + padding: 0; + margin: 0; + border-radius: 0; + white-space: pre-wrap; + overflow-wrap: break-word; + line-height: 1.5; +} + +.DocSearch-Markdown-Content h1, +.DocSearch-Markdown-Content h2, +.DocSearch-Markdown-Content h3, +.DocSearch-Markdown-Content h4, +.DocSearch-Markdown-Content h5, +.DocSearch-Markdown-Content h6 { + color: var(--docsearch-text-color); + margin: 1em 0; + font-weight: 600; + letter-spacing: -0.02em; + line-height: 1.3; +} + +.DocSearch-Markdown-Content h1 { + font-size: 1.5em; +} + +.DocSearch-Markdown-Content h2 { + font-size: 1.2em; +} + +.DocSearch-Markdown-Content h3 { + font-size: 1em; +} + +.DocSearch-Markdown-Content h4 { + font-size: 0.9em; +} + +.DocSearch-Markdown-Content h5, +.DocSearch-Markdown-Content h6 { + font-size: 0.8em; +} + +.DocSearch-Markdown-Content ul, +.DocSearch-Markdown-Content ol { + color: var(--docsearch-text-color); + margin: 1.2em 0; + padding-inline-start: 1.5em; +} + +.DocSearch-Markdown-Content ul { + list-style-type: disc; +} + +.DocSearch-Markdown-Content ol { + list-style-type: decimal; +} + +.DocSearch-Markdown-Content li { + color: var(--docsearch-text-color); + margin: 0.8em 0; + padding-inline-start: 0.3em; + line-height: 1.6; +} + +.DocSearch-Markdown-Content li > ul, +.DocSearch-Markdown-Content li > ol { + margin: 0.5em; +} + +.DocSearch-Markdown-Content li::marker { + color: var(--docsearch-muted-color); +} + +.DocSearch-AskAiScreen-Sources-source a { + display: grid; + grid-template-columns: 1.25rem 1fr; + align-items: center; + gap: 0.375em; + font-size: 0.875rem; + color: var(--docsearch-text-color); + text-decoration: none; +} + +.DocSearch-Markdown-Content a { + color: var(--docsearch-highlight-color); + text-decoration: none; + transition: all 0.2s ease; +} + +.DocSearch-AskAiScreen-Sources-source a:hover, +.DocSearch-AskAiScreen-Sources-source a:focus-visible { + text-decoration: underline; +} + +.DocSearch-Markdown-Content a:hover, +.DocSearch-Markdown-Content a:focus-visible { + text-decoration: underline; + opacity: 0.9; +} + +.DocSearch-Markdown-Content blockquote { + border-inline-start: 4px solid var(--docsearch-hit-highlight-color); + margin: 1.5em 0; + padding-block: 0.5em; + padding-inline: 1em 0; + color: var(--docsearch-secondary-text-color); + font-style: italic; +} + +.DocSearch-Markdown-Content hr { + border: 0; + border-block-start: 1px solid var(--docsearch-subtle-color); + margin: 1em 0; +} + +.DocSearch-Markdown-Content table { + width: 100%; + border-collapse: collapse; + margin: 1.5em 0; +} + +.DocSearch-Markdown-Content th, +.DocSearch-Markdown-Content td { + padding: 0.75em; + border: 1px solid var(--docsearch-subtle-color); + text-align: start; +} + +.DocSearch-Markdown-Content th { + background-color: var(--docsearch-hit-background); + font-weight: 600; +} + +.DocSearch-AskAi-Thinking-Skeleton { + --shimmer-gradient: rgb(170 193 250) 0%, rgb(245 251 255) 50%, rgb(170 193 250) 100%; + --shimmer-bg: linear-gradient(95deg, var(--shimmer-gradient)); + + display: block; + height: 0.875rem; + width: 100%; + border-radius: 100px; + background-color: rgb(187 209 255 / 100%); +} + +.DocSearch-AskAi-Thinking-Skeleton.DocSearch-shimmer { + background-clip: unset; +} + +.DocSearch-AskAiScreen-MessageContent-Thinking { + --shimmer-gradient: rgb(40 55 190) 0%, rgb(150 162 240) 50%, rgb(40 55 190) 100%; + --shimmer-bg: linear-gradient(95deg, var(--shimmer-gradient)); + + display: flex; + flex-direction: column; + gap: 0.875rem; + font-size: 1em; +} + +.DocSearch-AskAiScreen-MessageContent-Thinking +.DocSearch-AskAi-Thinking-Skeleton { + max-width: 40%; +} + +.DocSearch-AskAiScreen-MessageContent-Thinking +.DocSearch-AskAi-Thinking-Skeleton--short { + max-width: 25%; +} + +[data-theme='dark'] .DocSearch-AskAi-Thinking-Skeleton { + --shimmer-gradient: rgb(31 64 195) 0%, rgb(160 185 250) 50%, rgb(31 64 195) 100%; +} + +[data-theme='dark'] .DocSearch-AskAiScreen-MessageContent-Thinking { + --shimmer-gradient: rgb(108 145 255) 0%, rgb(200 218 255) 50%, rgb(108 145 255) 100%; +} + +.DocSearch-AskAiScreen-MessageContent-Reasoning { + display: flex; + align-items: center; + gap: 4px; + font-size: 1em; + color: var(--docsearch-muted-color); +} + +.DocSearch-AskAiScreen-MessageContent-Reasoning svg { + color: var(--docsearch-icon-color); +} + +.DocSearch-AskAiScreen-MessageContent-Tool { + display: flex; + align-items: center; + width: 100%; + color: var(--docsearch-secondary-text-color); + line-height: 1.2; + font-size: 0.9rem; +} + +/* stylelint-disable-next-line selector-class-pattern */ +.DocSearch-AskAiScreen-MessageContent-Tool.Tool--Result { + padding-block-start: 0; +} + +.DocSearch-AskAiScreen-MessageContent-Tool > svg { + color: var(--docsearch-icon-color); + margin-inline-end: 8px; +} + +.DocSearch-AskAiScreen-MessageContent-Tool-Query { + transition: box-shadow 0.2s ease; +} + +.DocSearch-AskAiScreen-MessageContent-Tool-Query svg { + color: var(--docsearch-muted-color); +} + +.DocSearch-AskAiScreen-MessageContent-Tool-Query:hover { + box-shadow: 0 1px 0 0 var(--docsearch-highlight-color); + color: var(--docsearch-highlight-color); + cursor: pointer; +} + +.DocSearch-AskAiScreen-MessageContent-Stopped { + color: var(--docsearch-muted-color); + font-style: italic; + margin-block-start: 1em; +} + +.DocSearch-AskAiScreen-SmallerLoadingIcon { + width: 16px; + height: 16px; +} + +.DocSearch-shimmer { + background: var(--shimmer-bg); + background-size: 200% 100%; + display: flex; + background-clip: text; + color: transparent; + -webkit-text-fill-color: transparent; + animation: shimmer-text 2.5s linear infinite; + pointer-events: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .DocSearch-shimmer { + animation: none; + } +} + +@keyframes shimmer-text { + 0% { + background-position-x: 200%; + } + + 100% { + background-position-x: 0%; + } +} + +@keyframes pulse { + 0%, + 100% { + opacity: 0.3; + } + + 50% { + opacity: 0.6; + } +} + +/* Responsive */ +@media (width <= 768px) { + .DocSearch-Hit-AskAIButton-icon { + margin-inline-end: 8px; + } + + .DocSearch-AskAiScreen-Response-Container { + flex-direction: column; + } + + .DocSearch-AskAiScreen-Response { + width: 100%; + } + + .DocSearch-AskAiScreen-RelatedSources { + width: 100%; + } +} + +.DocSearch-AskAiScreen-Sources-Action { + display: flex; + align-items: center; + gap: 1ch; + font-size: 0.75em; + border-radius: 100vw; + border: 1px solid var(--docsearch-subtle-color); + background-color: transparent; + padding-inline: 0.75em; + padding-block: 0.375em; + cursor: pointer; + user-select: none; + color: var(--docsearch-text-color); + transition: + background-color 0.2s ease, + border-color 0.2s ease, + color 0.2s ease; + + @media (prefers-reduced-motion: reduce) { + transition: none; + } + + &:hover { + background-color: var(--docsearch-soft-primary-color); + color: var(--docsearch-highlight-color); + border-color: var(--docsearch-soft-primary-color); + } +} + +.DocSearch-AskAiScreen-Sources-Action-icon { + display: inline-flex; + align-items: center; + justify-content: center; +} + +.DocSearch-AskAiScreen-Sources-Action-icon svg { + height: 0.875rem; + width: 0.875rem; + stroke-width: var(--docsearch-icon-stroke-width); +} + +.DocSearch-AskAiScreen-Sources-Action-text { + font-size: 1em; + line-height: 1.15em; + white-space: nowrap; + font-weight: 300; +} + +.DocSearch-AskAiScreen-Sources { + padding-inline: 0; + margin-block: 0; + margin-block-start: 0.5rem; + color: var(--docsearch-text-color); + list-style-type: none; + display: flex; + flex-direction: column; + gap: 0.375em; +} + +.DocSearch-AskAiScreen-Sources-source svg { + stroke-width: var(--docsearch-icon-stroke-width); + height: 1.25rem; + width: 1.25rem; + color: var(--docsearch-icon-color); +} + +.DocSearch-Conversation-History .DocSearch-Hit[aria-selected='true'] a { + background-color: var(--docsearch-hit-focus-background); +} + +.DocSearch-AskAiScreen-Sources-source-title { + white-space: nowrap; + max-width: 15rem; + overflow-x: hidden; + text-overflow: ellipsis; +} + +@media screen and (width >= 768px) { + .DocSearch-AskAiScreen-Sources-source-title { + max-width: 20rem; + } +} + +/* code snippet wrapper */ +.DocSearch-CodeSnippet { + position: relative; +} + +.DocSearch-CodeSnippet-CopyButton { + position: absolute; + inset-block-start: 8px; + inset-inline-end: 8px; + background: var(--docsearch-code-block-background); + color: var(--docsearch-text-color); + border: 0; + padding-block: 0.2em; + padding-inline: 0.6em; + font-size: 0.95em; + border-radius: 4px; + cursor: pointer; + transition: opacity 0.2s ease; + display: flex; + align-items: center; +} + +.DocSearch-CodeSnippet-CopyButton:hover { + opacity: 0.8; +} + +.DocSearch-CodeSnippet-CopyButton:active { + opacity: 0.6; +} + +.DocSearch-CodeSnippet-CopyButton svg { + width: 16px; + height: 16px; + margin-inline-end: 4px; +} + +.DocSearch-AskAiScreen-MessageContent-Tool-Query:hover svg { + color: var(--docsearch-highlight-color); +} + +.DocSearch-CodeSnippet-CheckIcon { + display: none; +} + +.DocSearch-CodeSnippet-CopyButton--copied .DocSearch-CodeSnippet-CopyIcon { + display: none; +} + +.DocSearch-CodeSnippet-CopyButton--copied .DocSearch-CodeSnippet-CheckIcon { + display: inline-block; +} + +/* hide copy button while streaming markdown */ +.DocSearch-Markdown-Content--streaming .DocSearch-CodeSnippet-CopyButton { + display: none; +} + +/* NewConversationScreen */ +.DocSearch-NewConversationScreen { + padding: 3em var(--docsearch-spacing); +} + +.DocSearch-NewConversationScreen-Title { + font-weight: 600; + font-size: 26px; + color: var(--docsearch-text-color); + margin-block-end: 0.15em; +} + +.DocSearch-NewConversationScreen-Description { + font-size: 14px; + color: var(--docsearch-muted-color); +} + +.DocSearch-NewConversationScreen-SuggestedQuestions { + display: flex; + flex-direction: column; + gap: var(--docsearch-spacing); + align-items: start; + margin-block-start: 1.5em; +} + +.DocSearch-NewConversationScreen-SuggestedQuestion { + border-radius: var(--docsearch-border-radius); + padding: 12px; + border: 1px solid var(--docsearch-subtle-color); + height: 40px; + display: inline-flex; + align-items: center; + justify-content: center; + background-color: var(--docsearch-searchbox-background); + color: var(--docsearch-text-color); + cursor: pointer; +} diff --git a/packages/docsearch-css/src/_variables.css b/packages/docsearch-css/src/_variables.css index cb4f57ed..50c2faba 100644 --- a/packages/docsearch-css/src/_variables.css +++ b/packages/docsearch-css/src/_variables.css @@ -1,74 +1,246 @@ /* Variables */ :root { - --docsearch-primary-color: rgb(16, 53, 188); - --docsearch-text-color: rgb(28, 30, 33); - --docsearch-spacing: 12px; - --docsearch-icon-stroke-width: 1.4; - --docsearch-highlight-color: var(--docsearch-primary-color); - --docsearch-muted-color: rgb(150, 159, 175); - --docsearch-container-background: rgba(101, 108, 133, 0.8); - --docsearch-logo-color: rgba(16, 53, 188); + --typesense-brand: #c0ff58; + + /* Fallbacks for browsers without relative color / color-mix support */ + --typesense-primary: #4f8000; + --typesense-soft-primary: #f0ffd6; + --typesense-primary-muted: #b7f24d; + --typesense-primary-dark: var(--typesense-brand); + --typesense-ink: #000; + --typesense-muted: #4d4d4d; + --typesense-surface: #fff; + --typesense-surface-dim: #f9f9f9; + --typesense-surface-line: #f1f1f1; + --typesense-border-color: #eaeaea; + --typesense-logo-text: #000; + --typesense-logo-mark: #000; + --docsearch-font-family: + system-ui, -apple-system, blinkmacsystemfont, 'Segoe UI', roboto, oxygen, + ubuntu, cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + --docsearch-primary-color: var(--typesense-primary); + --docsearch-primary-dark-color: var(--typesense-primary); + --docsearch-soft-primary-color: var(--typesense-soft-primary); + --docsearch-subtle-color: var(--typesense-border-color); + --docsearch-text-color: var(--typesense-ink); + --docsearch-error-color: #ef5350; + --docsearch-error-soft-color: rgb(254 189 197); + --docsearch-error-text-color: rgb(122 28 43); + --docsearch-success-color: #c3e38f; + --docsearch-secondary-text-color: var(--typesense-muted); + --docsearch-background-color: var(--typesense-surface-dim); + --docsearch-spacing: 14px; + --docsearch-icon-stroke-width: 1.6; + --docsearch-focus-color: var(--typesense-primary); + --docsearch-highlight-color: var(--typesense-primary); + --docsearch-muted-color: var(--typesense-muted); + --docsearch-muted-color-darker: rgb(77 77 77 / 25%); + --docsearch-icon-color: var(--typesense-muted); + --docsearch-container-background: rgb(0 0 0 / 20%); + --docsearch-logo-color: var(--typesense-primary); + --docsearch-border-radius: 16px; + + /* button */ + --docsearch-search-button-background: var(--typesense-surface); + --docsearch-search-button-text-color: var(--docsearch-secondary-text-color); /* modal */ - --docsearch-modal-width: 560px; - --docsearch-modal-height: 600px; - --docsearch-modal-background: rgb(245, 246, 247); - --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), - 0 3px 8px 0 rgba(85, 90, 100, 1); + --docsearch-modal-width: 800px; + --docsearch-modal-height: 700px; + --docsearch-modal-radius: calc(var(--docsearch-border-radius) + 4px); + --docsearch-modal-variable-height: 600px; + --docsearch-modal-background: var(--typesense-surface); + --docsearch-modal-shadow: + inset 0 1px 0 0 rgb(255 255 255 / 60%), + 0 24px 48px -12px rgb(50 90 12 / 20%), 0 0 0 1px rgb(222 255 151 / 32.5%); /* searchbox */ --docsearch-searchbox-height: 56px; - --docsearch-searchbox-background: rgb(235, 237, 240); - --docsearch-searchbox-focus-background: #fff; - --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color); + --docsearch-searchbox-initial-height: 56px; + --docsearch-searchbox-background: var(--typesense-surface-dim); + --docsearch-searchbox-focus-background: var(--typesense-surface); + --docsearch-actions-width: 22px; + --docsearch-actions-height: 22px; /* hit */ --docsearch-hit-height: 56px; - --docsearch-hit-color: rgb(68, 73, 80); - --docsearch-hit-active-color: #fff; - --docsearch-hit-background: #fff; - --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225); + --docsearch-hit-radius: 10px; + --docsearch-hit-color: var(--typesense-ink); + --docsearch-hit-highlight-color: var(--typesense-soft-primary); + --docsearch-hit-background: var(--typesense-surface-dim); + --docsearch-hit-focus-background: var(--typesense-surface-line); + + /* ask ai hit */ + --docsearch-askai-hit-border-color: #dff2ab; + --docsearch-askai-hit-focus-background: #e6ffb8; + --docsearch-askai-hit-focus-border-color: var(--typesense-primary-muted); /* key */ - --docsearch-key-gradient: linear-gradient( - -225deg, - rgb(213, 219, 228) 0%, - rgb(248, 248, 248) 100% - ); - --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), - inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4); + --docsearch-key-background: var(--typesense-surface-line); + --docsearch-key-color: var(--typesense-muted); + --docsearch-key-pressed-shadow: inset 0 2px 4px rgb(0 0 0 / 10%); /* footer */ - --docsearch-footer-height: 44px; - --docsearch-footer-background: #fff; - --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), - 0 -3px 6px 0 rgba(69, 98, 155, 0.12); + --docsearch-footer-height: 48px; + --docsearch-footer-background: var(--typesense-surface); + --docsearch-footer-shadow: 0 -1px 0 0 var(--typesense-border-color); + + /* animations */ + --fav-out-dur: 160ms; + --del-dur: 150ms; + --ease-smooth: cubic-bezier(0.25, 0.8, 0.4, 1); + --ease-fast: cubic-bezier(0.45, 0.15, 0.6, 0.9); + --shadow-pop: 0 4px 12px rgb(0 0 0 / 6%); + --shimmer-bg: linear-gradient( + 90deg, + #4d4d4d 0%, + rgb(150 230 80 / 70%) 35%, + var(--typesense-brand) 50%, + #4f8000 65%, + rgb(150 230 80 / 70%) 100% + ); + --docsearch-backdrop-enter-dur: 230ms; + --docsearch-modal-enter-dur: 200ms; + --docsearch-dropdown-menu-background: var(--typesense-surface); + --docsearch-dropdown-menu-item-hover-background: var(--typesense-surface-dim); + + /* Chip */ + --docsearch-chip-text-color: var(--typesense-primary); + --docsearch-chip-background: var(--typesense-soft-primary); + + /* Menu */ + --docsearch-menu-trigger-active-background: var( + --docsearch-soft-primary-color + ); + --docsearch-menu-trigger-active-text-color: var(--typesense-primary); + + /* Popover */ + --docsearch-popover-background: var(--typesense-surface); + --docsearch-popover-shadow: 0 0 0 1px rgb(0 0 0 / 5%), 0 4px 8px -2px rgb(0 0 0 / 25%); + --docsearch-popover-arrow-color: var(--typesense-border-color); + + /* markdown */ + --docsearch-code-block-background: var(--typesense-surface-line); } /* Darkmode */ -html[data-theme='dark'] { - --docsearch-text-color: rgb(245, 246, 247); - --docsearch-container-background: rgba(9, 10, 17, 0.8); - --docsearch-modal-background: rgb(21, 23, 42); - --docsearch-modal-shadow: inset 1px 1px 0 0 rgb(44, 46, 64), - 0 3px 8px 0 rgb(0, 3, 9); - --docsearch-searchbox-background: rgb(9, 10, 17); - --docsearch-searchbox-focus-background: #000; - --docsearch-hit-color: rgb(190, 195, 201); +/* stylelint-disable selector-no-qualifying-type, selector-class-pattern -- + `html.dark` and `html[data-theme]` are the dark-mode conventions used by + Docusaurus and VitePress, so consumers get themed without extra config. */ +html.dark, +html[data-theme='dark'], +:root[data-theme='dark'] { + --typesense-primary-muted: #ade64f; + --typesense-ink: #fff; + --typesense-logo-text: #fff; + --typesense-logo-mark: #c0ff58; + --typesense-border-color: #303030; + --typesense-muted: #b3b3b3; + --typesense-surface: rgb(16 16 18); + --typesense-surface-dim: #141414; + --typesense-surface-line: #1f1f1f; + --docsearch-primary-color: var(--typesense-primary-dark); + --docsearch-primary-dark-color: var(--typesense-primary-dark); + --docsearch-highlight-color: var(--typesense-primary-dark); + --docsearch-focus-color: var(--typesense-primary-dark); + --docsearch-logo-color: var(--typesense-primary-dark); + --docsearch-text-color: #f9f9f9; + --docsearch-secondary-text-color: var(--typesense-muted); + --docsearch-subtle-color: var(--typesense-border-color); + --docsearch-success-color: rgb(192 255 88 / 20%); + --docsearch-background-color: var(--typesense-surface-dim); + --docsearch-icon-color: var(--typesense-muted); + --docsearch-muted-color: var(--typesense-muted); + --docsearch-muted-color-darker: rgb(179 179 179 / 25%); + --docsearch-soft-primary-color: rgb(192 255 88 / 8%); + --docsearch-hit-highlight-color: rgb(192 255 88 / 16%); + --docsearch-searchbox-background: var(--typesense-surface); + --docsearch-searchbox-focus-background: var(--typesense-surface-line); + --docsearch-dropdown-menu-background: var(--typesense-surface-dim); + --docsearch-dropdown-menu-item-hover-background: var( + --typesense-surface-line + ); + --docsearch-hit-color: var(--typesense-ink); --docsearch-hit-shadow: none; - --docsearch-hit-background: rgb(9, 10, 17); - --docsearch-key-gradient: linear-gradient( - -26.5deg, - rgb(86, 88, 114) 0%, - rgb(49, 53, 91) 100% + --docsearch-hit-background: #191919; + --docsearch-hit-focus-background: var(--typesense-surface-line); + --docsearch-askai-hit-border-color: rgb(192 255 88 / 20%); + --docsearch-askai-hit-focus-background: rgb(192 255 88 / 15%); + --docsearch-askai-hit-focus-border-color: rgb(192 255 88 / 40%); + --docsearch-key-background: var(--typesense-surface-line); + --docsearch-key-color: var(--typesense-muted); + --docsearch-key-pressed-shadow: inset 0 2px 4px rgb(0 0 0 / 40%); + --docsearch-container-background: rgb(0 0 0 / 45%); + --docsearch-modal-background: var(--typesense-surface); + --docsearch-modal-shadow: + inset 0 1px 0 0 rgb(255 255 255 / 4%), + 0 24px 32px -12px hsl(91deg 76% 20% / 20%), + 0 0 0 0.75px rgb(192 255 88 / 9%); + --docsearch-footer-background: var(--typesense-surface); + --docsearch-footer-shadow: inset 0 1px 0 0 var(--typesense-border-color); + --docsearch-search-button-background: var(--typesense-surface); + --docsearch-search-button-text-color: var(--docsearch-text-color); + --shimmer-bg: linear-gradient( + 90deg, + #4d4d4d 0%, + rgb(150 230 80 / 70%) 35%, + var(--typesense-brand) 50%, + rgb(150 230 80 / 40%) 65%, + var(--typesense-brand) 100% ); - --docsearch-key-shadow: inset 0 -2px 0 0 rgb(40, 45, 85), - inset 0 0 1px 1px rgb(81, 87, 125), 0 2px 2px 0 rgba(3, 4, 9, 0.3); - --docsearch-footer-background: rgb(30, 33, 54); - --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), - 0 -4px 8px 0 rgba(0, 0, 0, 0.2); - --docsearch-logo-color: rgb(255, 255, 255); - --docsearch-muted-color: rgb(127, 132, 151); + + /* Chip */ + --docsearch-chip-text-color: var(--docsearch-text-color); + --docsearch-chip-background: var(--typesense-surface-line); + + /* Menu */ + --docsearch-menu-trigger-active-background: var(--typesense-surface-line); + --docsearch-menu-trigger-active-text-color: var(--docsearch-text-color); + + /* Popover */ + --docsearch-popover-background: var(--typesense-surface-line); + --docsearch-popover-shadow: 0 0 0 1px rgb(255 255 255 / 8%), 0 4px 8px -2px rgb(0 0 0 / 55%); + --docsearch-popover-arrow-color: var(--typesense-surface-line); + + /* markdown */ + --docsearch-code-block-background: var(--typesense-surface-line); +} + +@supports (color: hsl(from red h s l)) { + :root { + --typesense-primary: hsl(from var(--typesense-brand) h 100% 25%); + --typesense-soft-primary: hsl(from var(--typesense-brand) h s 92%); + } +} + +@supports (color: color-mix(in srgb, blue, red)) { + :root { + --typesense-primary-muted: color-mix( + in srgb, + var(--typesense-brand), + #000 5% + ); + --docsearch-askai-hit-border-color: color-mix( + in srgb, + var(--typesense-brand) 40%, + #fff + ); + --docsearch-askai-hit-focus-background: color-mix( + in srgb, + var(--typesense-brand) 35%, + #fff + ); + } + + html.dark, + html[data-theme='dark'], + :root[data-theme='dark'] { + --typesense-primary-muted: color-mix( + in srgb, + var(--typesense-brand), + #000 10% + ); + } } diff --git a/packages/docsearch-css/src/button.css b/packages/docsearch-css/src/button.css index d8577aed..6b0a09db 100644 --- a/packages/docsearch-css/src/button.css +++ b/packages/docsearch-css/src/button.css @@ -1,44 +1,46 @@ .DocSearch-Button { + all: unset; align-items: center; - background: var(--docsearch-searchbox-background); - border: 0; - border-radius: 40px; - color: var(--docsearch-muted-color); + border: 1px solid var(--docsearch-subtle-color); + border-radius: 0.5rem; + background-color: var(--docsearch-search-button-background); + color: var(--docsearch-search-button-text-color); cursor: pointer; display: flex; - font-weight: 500; height: 36px; justify-content: space-between; - margin: 0 0 0 16px; padding: 0 8px; user-select: none; -} - -.DocSearch-Button:hover, -.DocSearch-Button:active, -.DocSearch-Button:focus { - background: var(--docsearch-searchbox-focus-background); - box-shadow: var(--docsearch-searchbox-shadow); - color: var(--docsearch-text-color); - outline: none; + font-family: var(--docsearch-font-family); } .DocSearch-Button-Container { + height: 100%; align-items: center; display: flex; } +.DocSearch-Button-Container svg { + color: currentcolor; +} + .DocSearch-Search-Icon { + color: var(--docsearch-highlight-color); stroke-width: 1.6; } -.DocSearch-Button .DocSearch-Search-Icon { - color: var(--docsearch-text-color); +.DocSearch-Back-Icon { + color: var(--docsearch-highlight-color); + stroke-width: 1.6; } .DocSearch-Button-Placeholder { font-size: 1rem; - padding: 0 12px 0 6px; + padding-block: 0; + padding-inline: 8px 12px; + color: currentcolor; + display: inline-block; + line-height: normal; } .DocSearch-Button-Keys { @@ -48,22 +50,48 @@ .DocSearch-Button-Key { align-items: center; - background: var(--docsearch-key-gradient); - border-radius: 3px; - box-shadow: var(--docsearch-key-shadow); - color: var(--docsearch-muted-color); + background: var(--docsearch-key-background); + color: var(--docsearch-key-color); + box-shadow: none !important; display: flex; - height: 18px; + height: 24px; + width: 24px; justify-content: center; - margin-right: 0.4em; position: relative; - padding: 0px 0px 2px 0px; - border: 0px; - top: -1px; - width: 20px; + font-size: 14px; + font-family: var(--docsearch-font-family); + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 100ms; + border: 1px solid + color-mix(in srgb, var(--docsearch-subtle-color) 20%, transparent); + border-radius: 4px; +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-Button-Key { + transition-duration: 0s; + } } -@media (max-width: 768px) { +.DocSearch-Button-Key--ctrl { + width: 33px; +} + +.DocSearch-Button-Key:first-child { + margin-inline-end: 0.4em; +} + +.DocSearch-Button-Key--pressed { + transform: translateY(1px); + box-shadow: var(--docsearch-key-pressed-shadow) !important; +} + +@media (width <= 768px) { + .DocSearch-Button { + width: auto; + } + .DocSearch-Button-Keys, .DocSearch-Button-Placeholder { display: none; diff --git a/packages/docsearch-css/src/modal.css b/packages/docsearch-css/src/modal.css index 3aa46e19..48568a45 100644 --- a/packages/docsearch-css/src/modal.css +++ b/packages/docsearch-css/src/modal.css @@ -17,12 +17,15 @@ .DocSearch-Container { background-color: var(--docsearch-container-background); + backdrop-filter: blur(6px); height: 100vh; - left: 0; + inset-inline-start: 0; position: fixed; - top: 0; + inset-block-start: 0; width: 100vw; - z-index: 200; + z-index: 400; + animation: docsearch-backdrop-in var(--docsearch-backdrop-enter-dur) + var(--ease-smooth) both; } .DocSearch-Container a { @@ -42,50 +45,459 @@ .DocSearch-Modal { background: var(--docsearch-modal-background); - border-radius: 6px; + border-radius: var(--docsearch-modal-radius); box-shadow: var(--docsearch-modal-shadow); + display: flex; flex-direction: column; margin: 60px auto auto; max-width: var(--docsearch-modal-width); position: relative; + font-family: var(--docsearch-font-family); + padding: 6px; + transform-origin: top center; + animation: docsearch-modal-in var(--docsearch-modal-enter-dur) + var(--ease-smooth) 30ms both; +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-Container, + .DocSearch-Modal { + animation: none; + } +} + +@keyframes docsearch-backdrop-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@keyframes docsearch-modal-in { + from { + opacity: 0; + transform: scale(0.97); + } + + to { + opacity: 1; + transform: scale(1); + } +} + +.DocSearch-Popover-Positioner { + width: var(--positioner-width); + height: var(--positioner-height); + max-width: var(--available-width); + isolation: isolate; +} + +.DocSearch-Popover-Popup { + width: var(--popup-width, auto); + height: var(--popup-height, auto); + max-width: 500px; + position: relative; + background-color: var(--docsearch-popover-background); + z-index: 999; + box-shadow: var(--docsearch-popover-shadow); + border-radius: var(--docsearch-border-radius); + padding-inline: 1rem; + padding-block: 1rem; + transform-origin: var(--transform-origin); + outline: none; +} + +.DocSearch-Popover-Title { + color: var(--docsearch-text-color); + font-size: 0.875em; + font-weight: 600; + line-height: 1.25em; +} + +.DocSearch-Popover-Arrow { + --arrow-height: 8px; + --arrow-width: 18px; + + position: relative; + display: block; + width: var(--arrow-width); + height: var(--arrow-height); + overflow: clip; +} + +.DocSearch-Popover-Arrow[data-side='top'] { + bottom: calc(var(--arrow-height) * -1); + rotate: 180deg; +} + +.DocSearch-Popover-Arrow[data-side='bottom'] { + top: calc(var(--arrow-height) * -1); + rotate: 0deg; +} + +.DocSearch-Popover-Arrow[data-side='left'] { + right: calc(calc(var(--arrow-width) * 0.67) * -1); + rotate: 90deg; +} + +.DocSearch-Popover-Arrow[data-side='right'] { + left: calc(calc(var(--arrow-width) * 0.67) * -1); + rotate: -90deg; +} + +.DocSearch-Popover-Arrow::before { + content: ''; + display: block; + position: absolute; + bottom: 0; + left: 50%; + box-sizing: border-box; + width: calc(var(--arrow-height) * sqrt(2)); + height: calc(var(--arrow-height) * sqrt(2)); + background-color: var(--docsearch-popover-background); + border: 1px solid var(--docsearch-popover-arrow-color); + transform: translate(-50%, 50%) rotate(45deg); +} + +.DocSearch-Popover-Close { + position: absolute; + top: 1em; + right: 1rem; + min-height: 1.5em; + min-width: 1.5em; + background: none; + border: 0; + display: flex; + align-items: center; + justify-content: center; + appearance: none; + stroke-width: var(--docsearch-icon-stroke-width); + border-radius: var(--docsearch-border-radius); + cursor: pointer; + color: var(--docsearch-text-color); +} + +.DocSearch-Popover-Close:hover, +.DocSearch-Popover-Close:focus-visible { + color: var(--docsearch-highlight-color); + background: var(--docsearch-soft-primary-color); +} + +.DocSearch-Popover-Close svg { + height: 1rem; + width: 1rem; +} + +/* DocSearch Menu */ + +.DocSearch-Menu { + position: relative; +} + +.DocSearch-Menu-content { + display: none; + position: absolute; + inset-inline-end: 0; + padding: 8px 0; + flex-direction: column; + border-radius: var(--docsearch-border-radius); + background-color: var(--docsearch-dropdown-menu-background); + box-shadow: + 0 0 0 1px #21243d0d, + 0 8px 16px -4px #21243d40; + min-width: 195px; + inset-block-start: calc(100% + 12px); + z-index: 422; +} + +/* stylelint-disable-next-line selector-class-pattern */ +.DocSearch-Menu-content.open { + display: flex; +} + +.DocSearch-Menu-item { + padding: 10px 16px; + display: flex; + align-items: center; + color: var(--docsearch-text-color); + cursor: pointer; + font-size: 14px; + gap: 8px; + white-space: nowrap; + border: 0; + background-color: transparent; +} + +.DocSearch-Menu-item:hover { + background-color: var(--docsearch-dropdown-menu-item-hover-background); +} + +.DocSearch-Menu-Trigger { + align-items: center; + appearance: none; + background: transparent; + border: 0; + border-radius: 0.25rem; + color: var(--docsearch-text-color); + cursor: pointer; + display: inline-flex; + font: inherit; + font-size: 0.875em; + font-weight: 400; + gap: 0.5em; + line-height: 1.43em; + padding-block: 0.285em; + padding-inline: 0.571em; + + &:hover, + &[data-popup-open], + &[data-has-selection='true'] { + background: var(--docsearch-menu-trigger-active-background); + color: var(--docsearch-menu-trigger-active-text-color); + } + + &:focus-visible { + outline: 2px solid var(--docsearch-primary-color); + } + + svg { + stroke-width: var(--docsearch-icon-stroke-width); + inline-size: 1.5rem; + block-size: 1.5rem; + flex-shrink: 0; + transform: rotate(0deg); + transition: transform 0.15s ease-in-out; + } + + &[data-popup-open] svg { + transform: rotate(-180deg); + } + + @media (prefers-reduced-motion: reduce) { + svg { + transition-duration: 0s; + } + } +} + +.DocSearch-Menu-Positioner { + isolation: isolate; + z-index: 999; +} + +.DocSearch-Menu-Popup { + background-color: var(--docsearch-popover-background); + border-radius: 0.25rem; + box-shadow: var(--docsearch-popover-shadow); + max-height: 20rem; + min-width: 12.5rem; + outline: none; + overflow-y: auto; + padding-block: 0.5rem; + transform-origin: var(--transform-origin); +} + +.DocSearch-Menu-Item { + display: flex; + align-items: center; + color: var(--docsearch-text-color); + cursor: pointer; + font-size: 0.875em; + line-height: 1.43em; + outline: none; + padding-block: 0.714em; + padding-inline: 1.14em; + user-select: none; + gap: 0.25rem; + min-height: 3rem; + + @media screen and (width >= 768px) { + min-height: 0; + } + + &:hover, + &[data-highlighted] { + background: var(--docsearch-dropdown-menu-item-hover-background); + } + + svg { + flex-shrink: 0; + } +} + +.DocSearch-Menu-RadioItem[data-checked] { + color: var(--docsearch-highlight-color); +} + +.DocSearch-Menu-RadioItem-Indicator { + --indicator-size: 1rem; + + pointer-events: none; + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--indicator-size); + + span { + display: inline-flex; + align-items: center; + justify-content: center; + } + + svg { + stroke-width: var(--docsearch-icon-stroke-width); + height: var(--indicator-size); + width: var(--indicator-size); + } +} + +/* Chip */ + +.DocSearch-Chip { + display: inline-flex; + align-items: center; + gap: 0.29em; + font-size: 0.875em; + color: var(--docsearch-chip-text-color); + background: var(--docsearch-chip-background); + padding-inline: 0.57em; + border-radius: 100vw; + height: 2.14em; + + &:focus-within, + &:has(:focus-visible) { + outline: 2px solid var(--docsearch-primary-color); + } +} + +.DocSearch-Chip-Dismiss { + all: unset; + color: inherit; + display: inline-flex; + align-items: center; + justify-content: center; + height: 1em; + width: 1em; + cursor: pointer; + + svg { + stroke-width: var(--docsearch-icon-stroke-width); + } } /* Modal Searchbox */ .DocSearch-SearchBar { display: flex; - padding: var(--docsearch-spacing) var(--docsearch-spacing) 0; } +.DocSearch-SearchBar-Magnifier { + color: var(--docsearch-highlight-color); +} + +/* Facet bar */ + +.DocSearch-FacetBar { + align-items: center; + background: var(--docsearch-modal-background); + display: flex; + gap: 0.57em; + overflow-x: auto; + padding-block: 0.57em; + padding-inline: 0.857em; + scrollbar-width: none; +} + +.DocSearch-FacetBar::-webkit-scrollbar { + display: none; +} + +/* Facet selection bar */ + +.DocSearch-FacetSelectionBar { + display: flex; + align-items: center; + flex-wrap: wrap; + padding-block: 0.57em; + padding-inline: 0.857em; + gap: 0.57em; + border-block: 1px solid var(--docsearch-subtle-color); +} + +.DocSearch-FacetSelectionBar::-webkit-scrollbar { + display: none; +} + +.DocSearch-FacetSelectionBar-Action { + all: unset; + color: var(--docsearch-secondary-text-color); + font-size: 0.875em; + display: inline-flex; + align-items: center; + padding-inline: 0.57em; + border-radius: 100vw; + height: 2.14em; + cursor: pointer; + + &:hover, + &:focus { + background-color: var(--docsearch-background-color); + color: var(--docsearch-text-color); + } + + &:focus-visible { + outline: 2px solid var(--docsearch-primary-color); + } +} + +/* DocSearch Form */ + .DocSearch-Form { align-items: center; background: var(--docsearch-searchbox-focus-background); - border-radius: 4px; - box-shadow: var(--docsearch-searchbox-shadow); + border-block-end: 1px solid var(--docsearch-subtle-color); + border-radius: var(--docsearch-modal-radius) var(--docsearch-modal-radius) 0 0; display: flex; - height: var(--docsearch-searchbox-height); + min-height: var(--docsearch-searchbox-initial-height); + height: var(--docsearch-searchbox-height, + var(--docsearch-searchbox-initial-height)); margin: 0; - padding: 0 var(--docsearch-spacing); + padding-block: var(--docsearch-spacing); + padding-inline: 16px; position: relative; width: 100%; } -.DocSearch-Input { +.DocSearch-Input, +.DocSearch-Modal-heading { appearance: none; background: transparent; border: 0; color: var(--docsearch-text-color); flex: 1; font: inherit; - font-size: 1.2em; + font-size: 1rem; + font-weight: 400; height: 100%; outline: none; - padding: 0 0 0 8px; + padding-block-start: 0; + padding-inline-start: 8px; width: 80%; + line-height: 1.4; + resize: none; + overflow-y: hidden; /* js toggles to auto when exceeding max */ +} + +.DocSearch-Modal-heading { + align-items: center; + display: inline-flex; } .DocSearch-Input::placeholder { - color: var(--docsearch-muted-color); + color: var(--docsearch-secondary-text-color); opacity: 1; /* Firefox */ } @@ -96,95 +508,175 @@ display: none; } +.DocSearch-Actions { + width: var(--docsearch-actions-width); + height: var(--docsearch-actions-height); + display: flex; + align-items: center; + justify-content: flex-end; + padding: 0 2px; + gap: 8px; +} + +.DocSearch-Divider { + height: 16px; + border-inline-start: 1px solid var(--docsearch-subtle-color); +} + +.DocSearch-Action { + min-height: 24px; + min-width: 24px; + animation: fade-in 0.1s ease-in forwards; + appearance: none; + background: none; + align-items: center; + color: var(--docsearch-icon-color); + display: flex; + justify-content: center; + margin: 0; + padding: 2px; + border: 0; + border-radius: var(--docsearch-border-radius); + cursor: pointer; + inset-inline-end: 0; + stroke-width: var(--docsearch-icon-stroke-width); +} + .DocSearch-LoadingIndicator, -.DocSearch-MagnifierLabel, -.DocSearch-Reset { +.DocSearch-MagnifierLabel { margin: 0; padding: 0; } -.DocSearch-MagnifierLabel, -.DocSearch-Reset { +.DocSearch-MagnifierLabel { align-items: center; color: var(--docsearch-highlight-color); display: flex; justify-content: center; -} - -.DocSearch-Container--Stalled .DocSearch-MagnifierLabel { - display: none; + height: 2rem; + width: 2rem; + margin-left: -0.5rem; } .DocSearch-LoadingIndicator { - display: none; -} - -.DocSearch-Container--Stalled .DocSearch-LoadingIndicator { align-items: center; color: var(--docsearch-highlight-color); display: flex; justify-content: center; } -@media screen and (prefers-reduced-motion: reduce) { - .DocSearch-Reset { +@media (prefers-reduced-motion: reduce) { + .DocSearch-Action { animation: none; - appearance: none; - background: none; - border: 0; - border-radius: 50%; - color: var(--docsearch-icon-color); - cursor: pointer; - right: 0; - stroke-width: var(--docsearch-icon-stroke-width); } } -.DocSearch-Reset { - animation: fade-in 0.1s ease-in forwards; - appearance: none; - background: none; - border: 0; - border-radius: 50%; - color: var(--docsearch-icon-color); - cursor: pointer; - padding: 2px; - right: 0; - stroke-width: var(--docsearch-icon-stroke-width); +@keyframes fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } } -.DocSearch-Reset[hidden] { +@media (prefers-reduced-motion: reduce) { + .DocSearch-Action, + .DocSearch-AskAi-Return { + animation-duration: 0s; + } +} + +.DocSearch-Close[hidden], +.DocSearch-Input[hidden] { display: none; } -.DocSearch-Reset:hover { +.DocSearch-Action:hover, +.DocSearch-Action:focus-visible { color: var(--docsearch-highlight-color); + background: var(--docsearch-soft-primary-color); } .DocSearch-LoadingIndicator svg, .DocSearch-MagnifierLabel svg { - height: 24px; - width: 24px; + color: var(--docsearch-icon-color); + height: 1rem; + width: 1rem; } -.DocSearch-Cancel { - display: none; +/* highlight icon color when search input is focused */ +.DocSearch-Form:focus-within .DocSearch-MagnifierLabel svg { + color: var(--docsearch-highlight-color); +} + +.DocSearch-Clear { + appearance: none; + background: none; + border: 0; + color: var(--docsearch-highlight-color); + cursor: pointer; + flex: none; + font: inherit; + font-size: 0.9em; + font-weight: 300; + height: 28px; + margin-inline-start: var(--docsearch-spacing); + outline: none; + overflow: hidden; + padding: 0; + user-select: none; + white-space: nowrap; +} + +.DocSearch-Close:focus-visible, +.DocSearch-Clear:focus-visible { + outline: 2px solid var(--docsearch-focus-color); + outline-offset: 1px; + border-radius: 4px; } /* Modal Dropdown */ .DocSearch-Dropdown { - max-height: calc( - var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - - var(--docsearch-spacing) - var(--docsearch-footer-height) - ); - min-height: var(--docsearch-spacing); - overflow-y: auto; /* firefox */ - overflow-y: overlay; + height: var(--docsearch-modal-variable-height); + max-height: calc(var(--docsearch-modal-height) - var(--docsearch-spacing) - + var(--docsearch-footer-height)); + min-height: var(--docsearch-spacing); /* firefox */ + overflow-y: auto; padding: 0 var(--docsearch-spacing); scrollbar-color: var(--docsearch-muted-color) var(--docsearch-modal-background); scrollbar-width: thin; + scrollbar-gutter: stable; + animation: docsearch-dropdown-in 250ms var(--ease-smooth); + transition: + height 250ms var(--ease-smooth), + visibility 0s; +} + +/* Compound selector so this outranks the mobile height override */ +.DocSearch-Dropdown.DocSearch-Dropdown--collapsed { + height: 0; + min-height: 0; + visibility: hidden; + transition: + height 200ms var(--ease-smooth), + visibility 0s 200ms; +} + +@keyframes docsearch-dropdown-in { + from { + height: 0; + } +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-Dropdown { + animation: none; + transition: none; + } } .DocSearch-Dropdown::-webkit-scrollbar { @@ -201,42 +693,73 @@ border-radius: 20px; } -.DocSearch-Dropdown ul { +.DocSearch-Dropdown-Container ul { list-style: none; margin: 0; padding: 0; } .DocSearch-Label { - color: var(--docsearch-muted-color); - font-size: 0.75em; + color: var(--docsearch-secondary-text-color); + font-size: 0.875em; line-height: 1.6em; + font-weight: 400; +} + +.DocSearch-NoResults-Help { + color: var(--docsearch-secondary-text-color); + font-size: 0.8em; + margin: 0; + user-select: none; + line-height: 1.5em; + font-weight: 300; } .DocSearch-Help { - color: var(--docsearch-muted-color); - font-size: 0.9em; + color: var(--docsearch-secondary-text-color); + font-size: 0.8em; margin: 0; user-select: none; + line-height: 1.5em; + font-weight: 300; } .DocSearch-Title { - font-size: 1.2em; + color: var(--docsearch-text-color); + vertical-align: middle; + font-size: 1.1em; + line-height: 1.5em; + font-weight: 300; + overflow-wrap: anywhere; +} + +.DocSearch-Title strong { + font-weight: 500; } .DocSearch-Logo a { display: flex; + align-items: center; } .DocSearch-Logo svg { + align-self: center; color: var(--docsearch-logo-color); - margin-left: 8px; + margin-inline-start: 8px; } /* Hit */ +.DocSearch-Hits { + padding-block-end: 10px; +} + +.DocSearch-Hits + .DocSearch-Hits { + border-block-start: 1px solid var(--docsearch-subtle-color); +} + .DocSearch-Hits:last-of-type { - margin-bottom: 24px; + margin-block-end: 24px; } .DocSearch-Hits mark { @@ -249,66 +772,67 @@ display: flex; font-size: 0.85em; justify-content: center; - margin-bottom: var(--docsearch-spacing); + margin-block-end: var(--docsearch-spacing); padding: var(--docsearch-spacing); } .DocSearch-HitsFooter a { - border-bottom: 1px solid; + border-block-end: 1px solid; color: inherit; } .DocSearch-Hit { - border-radius: 4px; + border-radius: var(--docsearch-hit-radius); display: flex; - padding-bottom: 4px; + padding-block-end: 4px; position: relative; + scroll-margin-block-start: 40px; } -@media screen and (prefers-reduced-motion: reduce) { - .DocSearch-Hit--deleting { - transition: none; - } +.DocSearch-Hit:first-of-type { + margin-block-start: 4px; } -.DocSearch-Hit--deleting { - opacity: 0; - transition: all 250ms linear; +.DocSearch-Hit a { + background: transparent; + border-radius: var(--docsearch-hit-radius); + display: block; + padding-inline-start: 0.5rem; + width: 100%; + cursor: pointer; + transition: background-color 120ms var(--ease-smooth); } -@media screen and (prefers-reduced-motion: reduce) { - .DocSearch-Hit--favoriting { +@media (prefers-reduced-motion: reduce) { + .DocSearch-Hit a { transition: none; } } -.DocSearch-Hit--favoriting { - transform: scale(0); - transform-origin: top center; - transition: all 250ms linear; - transition-delay: 250ms; -} - -.DocSearch-Hit a { - background: var(--docsearch-hit-background); - border-radius: 4px; - box-shadow: var(--docsearch-hit-shadow); - display: block; - padding-left: var(--docsearch-spacing); - width: 100%; -} - .DocSearch-Hit-source { background: var(--docsearch-modal-background); - color: var(--docsearch-highlight-color); - font-size: 0.85em; - font-weight: 600; + color: var(--docsearch-secondary-text-color); + font-size: 0.9em; + font-weight: 400; line-height: 32px; margin: 0 -4px; - padding: 8px 4px 0; + padding: 8px 4px 4px; position: sticky; - top: 0; + inset-block-start: 0; z-index: 10; + display: flex; + align-items: center; + gap: 10px; +} + +.DocSearch-Hit-source svg { + width: 18px; + height: 18px; + stroke-width: var(--docsearch-icon-stroke-width); +} + +.DocSearch-Dropdown-Container .DocSearch-Hits-padded { + padding-inline-start: 4px; } .DocSearch-Hit-Tree { @@ -320,36 +844,52 @@ } .DocSearch-Hit[aria-selected='true'] a { - background-color: var(--docsearch-highlight-color); + background-color: var(--docsearch-hit-focus-background); } -.DocSearch-Hit[aria-selected='true'] mark { +.DocSearch-Hit mark { + color: var(--docsearch-highlight-color); text-decoration: underline; + text-underline-offset: 0.3em; } .DocSearch-Hit-Container { align-items: center; - color: var(--docsearch-hit-color); + color: var(--docsearch-text-color); display: flex; flex-direction: row; height: var(--docsearch-hit-height); - padding: 0 var(--docsearch-spacing) 0 0; + padding: 0 0.5rem 0 0; } .DocSearch-Hit-icon { - color: var(--docsearch-muted-color); - height: 20px; + color: var(--docsearch-secondary-text-color); + height: 24px; stroke-width: var(--docsearch-icon-stroke-width); - width: 20px; + width: 24px; + + svg { + height: 20px; + width: 20px; + } +} + +.DocSearch-Hit-icon--start { + align-self: flex-start; + margin-block-start: 0.5em; +} + +.DocSearch-Hit-action, +.DocSearch-Hit-action-button { + height: 22px; + width: 22px; } .DocSearch-Hit-action { align-items: center; color: var(--docsearch-muted-color); display: flex; - height: 22px; stroke-width: var(--docsearch-icon-stroke-width); - width: 22px; } .DocSearch-Hit-action svg { @@ -358,8 +898,14 @@ width: 18px; } +.DocSearch-Hit-action-button--pin svg { + margin-inline-start: 2px; + height: 14px; + width: 14px; +} + .DocSearch-Hit-action + .DocSearch-Hit-action { - margin-left: 6px; + margin-inline-start: 6px; } .DocSearch-Hit-action-button { @@ -370,82 +916,105 @@ color: inherit; cursor: pointer; padding: 2px; + display: none; } -svg.DocSearch-Hit-Select-Icon { +.DocSearch-Hit-Select-Icon { display: none; } +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-action-button, .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-Select-Icon { display: block; } -@media screen and (prefers-reduced-motion: reduce) { +@media (prefers-reduced-motion: reduce) { .DocSearch-Hit-action-button:hover, .DocSearch-Hit-action-button:focus { - background: rgba(0, 0, 0, 0.2); - transition: none; + transition-duration: 0s; } } .DocSearch-Hit-action-button:hover, .DocSearch-Hit-action-button:focus { - background: rgba(0, 0, 0, 0.2); + background: rgb(0 0 0 / 20%); transition: background-color 0.1s ease-in; } -@media screen and (prefers-reduced-motion: reduce) { - .DocSearch-Hit-action-button:hover, - .DocSearch-Hit-action-button:focus { - transition: none; - } -} - -.DocSearch-Hit-action-button:hover path, -.DocSearch-Hit-action-button:focus path { - fill: #fff; -} - .DocSearch-Hit-content-wrapper { display: flex; flex: 1 1 auto; flex-direction: column; - font-weight: 500; + font-weight: 400; justify-content: center; line-height: 1.2em; margin: 0 8px; - overflow-x: hidden; position: relative; + width: 80%; + gap: 4px; +} + +.DocSearch-Hit-title, +.DocSearch-Hit-path { + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - width: 80%; + max-width: 200px; +} + +@media screen and (width >= 768px) { + .DocSearch-Hit-title, + .DocSearch-Hit-path { + max-width: 584px; + } } .DocSearch-Hit-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 200px; font-size: 0.9em; } +@media screen and (width >= 768px) { + .DocSearch-Hit-title { + max-width: 584px; + } +} + .DocSearch-Hit-path { - color: var(--docsearch-muted-color); + color: var(--docsearch-secondary-text-color); font-size: 0.75em; } .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-title, -.DocSearch-Hit[aria-selected='true'] mark, .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-text, -.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-path, -.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-icon, -.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-action, .DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-Tree { - color: var(--docsearch-hit-active-color) !important; + color: var(--docsearch-text-color); } -@media screen and (prefers-reduced-motion: reduce) { - .DocSearch-Hit-action-button:hover, - .DocSearch-Hit-action-button:focus { - background: rgba(0, 0, 0, 0.2); - transition: none; - } +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-action { + color: var(--docsearch-secondary-text-color); +} + +.DocSearch-Hit[aria-selected='true'] mark { + color: var(--docsearch-highlight-color); +} + +.DocSearch-Hit-badge { + font-size: 0.75rem; + margin-right: 1em; + display: inline-flex; + padding-inline: 1em; + padding-block: 0.5em; + border-radius: 100vw; + background-color: var(--docsearch-hit-focus-background); +} + +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-badge, +.DocSearch-Hit:hover .DocSearch-Hit-badge { + background-color: var(--docsearch-modal-background); } /* No Results - Start Screen - Error Screen */ @@ -453,32 +1022,65 @@ svg.DocSearch-Hit-Select-Icon { .DocSearch-NoResults, .DocSearch-StartScreen, .DocSearch-ErrorScreen { - font-size: 0.9em; + font-size: 1.25em; + font-weight: 400; margin: 0 auto; - padding: 36px 0; + color: var(--docsearch-secondary-text-color); text-align: center; width: 80%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.DocSearch-NoResults, +.DocSearch-ErrorScreen { + max-height: 80%; +} + +.DocSearch-StartScreen { + height: 100%; +} + +.DocSearch-NoResults { + gap: 0.8em; + margin-block-start: 2em; +} + +.DocSearch-StartScreen, +.DocSearch-ErrorScreen { + gap: 24px; +} + +.DocSearch-StartScreen-Icon { + width: 64px; + height: 64px; + stroke: var(--docsearch-icon-color); } .DocSearch-Screen-Icon { - color: var(--docsearch-muted-color); - padding-bottom: 12px; + color: var(--docsearch-icon-color); } .DocSearch-NoResults-Prefill-List { - display: inline-block; - padding-bottom: 24px; - text-align: left; + display: flex; + flex-direction: column; + gap: 12px; + text-align: center; } -.DocSearch-NoResults-Prefill-List ul { - display: inline-block; - padding: 8px 0 0; +.DocSearch-NoResults-Prefill-List-Items { + display: flex; + flex-direction: column; + gap: 2px; } -.DocSearch-NoResults-Prefill-List li { - list-style-position: inside; - list-style-type: '» '; +.DocSearch-NoResults-Prefill-List-Items p { + text-align: start; + align-items: center; + display: flex; + margin: 0; } .DocSearch-Prefill { @@ -488,10 +1090,13 @@ svg.DocSearch-Hit-Select-Icon { border-radius: 1em; color: var(--docsearch-highlight-color); cursor: pointer; - display: inline-block; - font-size: 1em; - font-weight: 700; - padding: 0; + display: inline-flex; + align-items: center; + gap: 4px; + height: 40px; + font-size: 0.8em; + font-weight: 300; + padding: 0 4px; } .DocSearch-Prefill:hover, @@ -505,10 +1110,9 @@ svg.DocSearch-Hit-Select-Icon { .DocSearch-Footer { align-items: center; background: var(--docsearch-footer-background); - border-radius: 0 0 8px 8px; - box-shadow: var(--docsearch-footer-shadow); + border-block-start: 1px solid var(--docsearch-subtle-color); + border-radius: 0 0 var(--docsearch-modal-radius) var(--docsearch-modal-radius); display: flex; - flex-direction: row-reverse; flex-shrink: 0; height: var(--docsearch-footer-height); justify-content: space-between; @@ -525,6 +1129,7 @@ svg.DocSearch-Hit-Select-Icon { list-style: none; margin: 0; padding: 0; + gap: 16px; } .DocSearch-Commands li { @@ -532,92 +1137,130 @@ svg.DocSearch-Hit-Select-Icon { display: flex; } -.DocSearch-Commands li:not(:last-of-type) { - margin-right: 0.8em; -} - .DocSearch-Commands-Key { align-items: center; - background: var(--docsearch-key-gradient); border-radius: 2px; - box-shadow: var(--docsearch-key-shadow); + background-color: var(--docsearch-background-color); display: flex; - height: 18px; + height: 24px; justify-content: center; - margin-right: 0.4em; - padding: 0px 0px 1px 0px; - color: var(--docsearch-muted-color); - border: 0px; - width: 20px; + margin-inline-end: 4px; + color: var(--docsearch-icon-color); + border: 0; + width: 24px; + box-shadow: none !important; +} + +.DocSearch-Commands-Key svg { + height: 1rem; + width: 1rem; +} + +.DocSearch-Commands-Key:last-of-type { + margin-inline-end: 8px; +} + +.DocSearch-Escape-Key { + font-weight: 300; + font-size: 0.5rem; + line-height: 1.2; + letter-spacing: normal; + text-align: center; + text-transform: uppercase; +} + +.DocSearch-Footer-Actions { + display: flex; + align-items: center; + gap: 1rem; + justify-content: space-between; + width: 100%; + + @media screen and (width > 768px) { + justify-content: unset; + width: auto; + } + + &:not(:has(.DocSearch-Footer-Action)) .DocSearch-Logo { + margin-inline-start: auto; + } +} + +/* Hide element accessibly, so that it is still accessible to +assistive tech users */ +.DocSearch-VisuallyHiddenForAccessibility { + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; } /* Responsive */ -@media (max-width: 768px) { +@media (width <= 768px) { :root { --docsearch-spacing: 10px; - --docsearch-footer-height: 40px; + --docsearch-footer-height: 48px; + } + + /* Prevent body scroll on modal-open for mobile */ + + /* https://stackoverflow.com/a/24727206/6276948 */ + body:has(.DocSearch-Container) { + overflow: hidden; + position: fixed; } .DocSearch-Dropdown { height: 100%; + max-height: none; + animation: none; } .DocSearch-Container { - height: 100vh; - height: -webkit-fill-available; height: calc(var(--docsearch-vh, 1vh) * 100); - position: absolute; + height: 100dvh; } .DocSearch-Footer { border-radius: 0; - bottom: 0; - position: absolute; + inset-block-end: 0; + position: initial; } .DocSearch-Hit-content-wrapper { display: flex; + overflow-wrap: break-word; position: relative; + white-space: normal; width: 80%; } + .DocSearch-Hit-title, + .DocSearch-Hit-path { + max-width: 100%; + overflow: visible; + text-overflow: clip; + white-space: normal; + } + + .DocSearch-Hit-Container { + height: auto; + min-height: var(--docsearch-hit-height); + padding-block: 8px; + } + .DocSearch-Modal { border-radius: 0; box-shadow: none; - height: 100vh; - height: -webkit-fill-available; height: calc(var(--docsearch-vh, 1vh) * 100); + height: 100dvh; margin: 0; max-width: 100%; width: 100%; } - .DocSearch-Dropdown { - max-height: calc( - var(--docsearch-vh, 1vh) * 100 - var(--docsearch-searchbox-height) - - var(--docsearch-spacing) - var(--docsearch-footer-height) - ); - } - - .DocSearch-Cancel { - appearance: none; - background: none; - border: 0; - color: var(--docsearch-highlight-color); - cursor: pointer; - display: inline-block; - flex: none; - font: inherit; - font-size: 1em; - font-weight: 500; - margin-left: var(--docsearch-spacing); - outline: none; - overflow: hidden; - padding: 0; - user-select: none; - white-space: nowrap; - } - .DocSearch-Commands { display: none; } @@ -626,12 +1269,3 @@ svg.DocSearch-Hit-Select-Icon { display: none; } } - -@keyframes fade-in { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} diff --git a/packages/docsearch-css/src/sidepanel.css b/packages/docsearch-css/src/sidepanel.css new file mode 100644 index 00000000..8613504d --- /dev/null +++ b/packages/docsearch-css/src/sidepanel.css @@ -0,0 +1,756 @@ +@import url('./_variables.css'); + +:root { + /* Theme colors */ + --docsearch-sidepanel-background: var(--docsearch-modal-background); + --docsearch-sidepanel-background-dark: var(--docsearch-searchbox-background); + --docsearch-sidepanel-white: #fff; + --docsearch-sidepanel-primary: var(--docsearch-primary-color); + --docsearch-sidepanel-primary-dark: rgb(2 46 185 / 100%); + --docsearch-sidepanel-primary-disabled: rgb(187 209 255 / 100%); + --docsearch-sidepanel-accent: var(--docsearch-highlight-color); + --docsearch-sidepanel-accent-muted: rgb(187 209 255 / 100%); + --docsearch-sidepanel-border: var(--docsearch-subtle-color); + --docsearch-sidepanel-text-base: rgb(35 38 59 / 100%); + --docsearch-sidepanel-text-muted: var(--docsearch-secondary-text-color); + --docsearch-sidepanel-scrollbar-color: var(--docsearch-muted-color); + --docsearch-sidepanel-scrollbar-bg: var(--docsearch-modal-background); + --docsearch-sidepanel-hit-background: var(--docsearch-hit-background); + --docsearch-sidepanel-hit-color: var(--docsearch-hit-color); + --docsearch-sidepanel-hit-highlight-color: var(--docsearch-hit-highlight-color); + --docsearch-sidepanel-button-background: var(--docsearch-sidepanel-background); + --docsearch-sidepanel-button-background-dark: var(--docsearch-sidepanel-background); +} + +:root[data-theme='dark'] { + --docsearch-sidepanel-text-base: var(--docsearch-text-color); + --docsearch-sidepanel-primary-disabled: rgb(1 45 186 / 60%); + --docsearch-sidepanel-button-background-dark: rgb(4 4 8 / 100%); +} + +.DocSearch-SidepanelButton { + align-items: center; + cursor: pointer; + display: inline-flex; + user-select: none; + border: 0; + transition: all 150ms ease-in-out; + font-family: var(--docsearch-font-family); +} + +.DocSearch-SidepanelButton.floating { + border-radius: 0.5rem; + justify-content: center; + position: fixed; + padding: 0.5rem; + width: 3.25rem; + height: 3.25rem; + bottom: 1rem; + right: 1rem; + z-index: 380; + background-color: var(--docsearch-sidepanel-primary); + color: var(--docsearch-sidepanel-white); +} + +.DocSearch-SidepanelButton.floating:hover, +.DocSearch-SidepanelButton.floating:focus { + background-color: var(--docsearch-sidepanel-primary-dark); +} + +.DocSearch-SidepanelButton.floating svg { + width: 1.375rem; + height: 1.375rem; + stroke-width: 1.4; +} + +@media screen and (width >= 769px) { + .DocSearch-SidepanelButton.floating { + border-radius: 1rem; + width: 4rem; + height: 4rem; + } + + .DocSearch-SidepanelButton.floating svg { + width: 2rem; + height: 2rem; + } +} + +.DocSearch-SidepanelButton.inline { + display: inline-flex; + align-items: center; + border-radius: 0.5rem; + padding: 0 0.75rem; + gap: 0.5em; + border: 1px solid var(--docsearch-sidepanel-border); + color: var(--docsearch-sidepanel-text-base); + background-color: var(--docsearch-sidepanel-background); + height: 2.25rem; + user-select: none; + font-size: 1rem; + all: unset; + cursor: pointer; + transition: all 150ms ease-in-out; + font-family: var(--docsearch-font-family); + line-height: normal; +} + +.DocSearch-SidepanelButton.inline:hover { + border-color: var(--docsearch-sidepanel-accent); +} + +:root[data-theme='dark'] .DocSearch-SidepanelButton.inline:hover { + background-color: var(--docsearch-sidepanel-button-background-dark); +} + +.DocSearch-SidepanelButton.inline svg { + width: 1.25rem; + height: 1.25rem; + color: var(--docsearch-sidepanel-accent); +} + +.DocSearch-Sidepanel-Container { + position: fixed; + min-width: 0; + overflow: hidden; + box-shadow: + 0 0 0 1px rgb(35 38 59 / 5%), + 0 4px 8px -2px rgb(35 38 59 / 25%); + opacity: 0; + z-index: 999; + width: 100svw; +} + +.DocSearch-Sidepanel-Container.inline, +.DocSearch-Sidepanel-Container.floating { + top: 0; + right: 0; + left: 0; + border-radius: 0; + transform: translateX(100%); + height: calc(var(--sp-vh) * 100); + height: 100dvh; +} + +.DocSearch-Sidepanel-Container.inline.is-open, +.DocSearch-Sidepanel-Container.floating.is-open { + transform: none; + opacity: 1; +} + +@media screen and (width <= 768px) { + html:has(.DocSearch-Sidepanel-Container.is-open), + body:has(.DocSearch-Sidepanel-Container.is-open) { + overflow: hidden; + position: fixed; + } +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel-Container { + width: var(--sp-width); + transition: + transform 280ms cubic-bezier(0.22, 1, 0.36, 1), + opacity 280ms cubic-bezier(0.22, 1, 0.36, 1), + width 280ms cubic-bezier(0.22, 1, 0.36, 1); + } + + .DocSearch-Sidepanel-Container.floating { + top: 1rem; + bottom: 1rem; + border-radius: 1rem; + height: calc(calc(var(--sp-vh) * 100) - 2rem); + height: calc(100dvh - 2rem); + } + + .DocSearch-Sidepanel-Container.inline { + top: 0; + bottom: 0; + } + + .DocSearch-Sidepanel-Container.floating.side-right { + left: unset; + right: 1rem; + transform: translateX(calc(100% + 2rem)); + } + + .DocSearch-Sidepanel-Container.floating.side-left { + transform: translateX(calc(-100% - 2rem)); + left: 1rem; + } + + .DocSearch-Sidepanel-Container.inline.side-right { + left: unset; + right: 0; + border-top-left-radius: 1rem; + border-bottom-left-radius: 1rem; + transform: translateX(100%); + } + + .DocSearch-Sidepanel-Container.inline.side-left { + transform: translateX(-100%); + left: 0; + border-top-right-radius: 1rem; + border-bottom-right-radius: 1rem; + } +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-Sidepanel-Container { + transition: none; + } +} + +.DocSearch-Sidepanel-Container.is-open { + opacity: 1; +} + +/* Ensure open state always overrides side-specific transforms even if CSS gets reordered during bundling/minification. */ +.DocSearch-Sidepanel-Container.floating.side-right.is-open, +.DocSearch-Sidepanel-Container.floating.side-left.is-open, +.DocSearch-Sidepanel-Container.inline.side-right.is-open, +.DocSearch-Sidepanel-Container.inline.side-left.is-open { + transform: none; +} + +.DocSearch-Sidepanel-Container.side-right.is-open { + transform: none; +} + +.DocSearch-Sidepanel-Container.side-left.is-open { + transform: none; +} + +.DocSearch-Sidepanel { + display: flex; + flex-direction: column; + background-color: var(--docsearch-sidepanel-background); + font-family: var(--docsearch-font-family); + overflow: hidden; + box-sizing: border-box; + height: 100%; + position: relative; + + @media screen and (width >= 768px) { + .DocSearch-PromptSuggestions-Content { + align-items: start; + justify-content: start; + flex-flow: column nowrap; + } + } +} + +.DocSearch-Sidepanel * { + box-sizing: border-box; +} + +.DocSearch-Sidepanel a { + text-decoration: none; +} + +.DocSearch-Sidepanel svg { + stroke-width: 1.4; +} + +.DocSearch-Sidepanel-Header { + background-color: var(--docsearch-sidepanel-background-dark); + display: flex; + align-items: center; + padding: 0.2rem 1rem; + border-bottom: 1px solid var(--docsearch-sidepanel-border); + height: 3.5rem; + color: var(--docsearch-sidepanel-text-base); + position: relative; + justify-content: center; +} + +.DocSearch-Sidepanel-Header--left, +.DocSearch-Sidepanel-Header--right { + display: flex; + align-items: center; + gap: 0.5rem; + position: absolute; +} + +.DocSearch-Sidepanel-Header--left { + left: 1rem; +} + +.DocSearch-Sidepanel-Header--right { + right: 1rem; +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel-Header { + justify-content: flex-start; + } + + .DocSearch-Sidepanel-Header--left { + display: none; + } +} + +.DocSearch-Sidepanel-Header--center { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.DocSearch-Sidepanel-Action-menu, +.DocSearch-Sidepanel-Action-expand { + display: none; +} + +.DocSearch-Sidepanel-Action-menu[aria-disabled='true'], +.DocSearch-Sidepanel-Action-back[aria-disabled='true'] { + user-select: none; + pointer-events: none; + color: var(--docsearch-sidepanel-accent-muted); +} + +.DocSearch-Sidepanel-Action-menu[aria-disabled='true']:hover, +.DocSearch-Sidepanel-Action-back[aria-disabled='true']:hover { + color: var(--docsearch-sidepanel-accent-muted); + background-color: transparent; +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel-Action-menu, + .DocSearch-Sidepanel-Action-expand { + display: flex; + } +} + +.DocSearch-Action.DocSearch-Sidepanel-Action-back { + display: none; +} + +.DocSearch-Sidepanel.conversation-history +/* stylelint-disable-next-line selector-class-pattern */ +.DocSearch-Action.DocSearch-Sidepanel-Action-back.mobile { + display: inline-flex; +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel.conversation-history .DocSearch-Sidepanel-Action-back { + display: inline-flex; + } +} + +.DocSearch-Sidepanel-Header-TitleIcon { + display: block; + height: 1.25rem; + width: 1.25rem; + color: var(--docsearch-sidepanel-primary); +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel.conversation-history + .DocSearch-Sidepanel-Header-TitleIcon { + display: none; + } +} + +.DocSearch-Sidepanel-Title { + font-size: 0.875rem; + font-weight: 600; + line-height: 1.25rem; + color: var(--docsearch-sidepanel-text-base); + margin: 0; +} + +.DocSearch-Sidepanel-Content { + flex: 1; + padding: 0 1rem; + overflow-y: auto; + scrollbar-color: var(--docsearch-sidepanel-scrollbar-color) + var(--docsearch-sidepanel-scrollbar-bg); + scrollbar-width: thin; + scrollbar-gutter: stable; +} + +.DocSearch-Sidepanel-Content::-webkit-scrollbar { + width: 12px; +} + +.DocSearch-Sidepanel-Content::-webkit-scrollbar-track { + background: transparent; +} + +.DocSearch-Sidepanel-Content::-webkit-scrollbar-thumb { + background-color: var(--docsearch-muted-color); + border: 3px solid var(--docsearch-modal-background); + border-radius: 20px; +} + +.DocSearch-Sidepanel-Prompt { + border-top: 1px solid var(--docsearch-sidepanel-border); + padding: 1rem; + padding-bottom: 0; + background-color: var(--docsearch-sidepanel-background-dark); + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.DocSearch-Sidepanel-Prompt--form { + --prompt-form-padding: 0.75rem; + + border: 1px solid #777aaf; + padding: var(--prompt-form-padding); + border-radius: 0.25rem; + display: flex; + flex-direction: column; + position: relative; + gap: 0.5rem; +} + +.DocSearch-Sidepanel-PromptBlockingBanner { + padding: 0.75rem; + color: var(--docsearch-sidepanel-text-base); + font-size: 0.75rem; + border: 1px solid var(--docsearch-error-soft-color); + border-radius: 0.25rem; + background: var(--docsearch-sidepanel-background); +} + +.DocSearch-Sidepanel-PromptBlockingBanner p { + margin: 0; +} + +.DocSearch-Sidepanel-PromptBlockingBanner p + p { + margin-top: 0.5rem; +} + +.DocSearch-Sidepanel-PromptBlockingBanner .DocSearch-ThreadDepthError-Link:focus-visible { + outline: 2px solid var(--docsearch-sidepanel-primary); + outline-offset: 2px; +} + +.DocSearch-Sidepanel-Prompt--form:focus-within { + border-color: var(--docsearch-sidepanel-primary); +} + +.DocSearch-Sidepanel-Prompt--textarea { + border: 0; + width: 100%; + resize: none; + color: var(--docsearch-sidepanel-text-base); + outline: none; + background: none; + appearance: none; + font-size: 1rem; + line-height: 1.5rem; + padding-right: 1.75rem; + min-height: 1.5rem; + height: 1.5rem; + overflow: hidden; + font-family: var(--docsearch-font-family); +} + +.DocSearch-Sidepanel-Prompt--textarea::placeholder { + color: var(--docsearch-muted-color); + opacity: 1; +} + +.DocSearch-Sidepanel-Prompt--actions { + position: absolute; + right: var(--prompt-form-padding); + bottom: var(--prompt-form-padding); + height: 1.5rem; +} + +.DocSearch-Sidepanel-Prompt--stop, +.DocSearch-Sidepanel-Prompt--submit { + display: inline-flex; + width: 1.5rem; + height: 1.5rem; + align-items: center; + justify-content: center; + border-radius: 0.25rem; + cursor: pointer; + transition: all 150ms ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .DocSearch-Sidepanel-Prompt--stop, + .DocSearch-Sidepanel-Prompt--submit { + transition: none; + } +} + +.DocSearch-Sidepanel-Prompt--stop { + background-color: var(--docsearch-sidepanel-white); + border: 1px solid var(--docsearch-sidepanel-border); + color: var(--docsearch-sidepanel-text-muted); +} + +:root[data-theme='dark'] .DocSearch-Sidepanel-Prompt--stop { + background-color: var(--docsearch-sidepanel-background); +} + +.DocSearch-Sidepanel-Prompt--stop:hover { + background-color: var(--docsearch-sidepanel-background); +} + +:root[data-theme='dark'] .DocSearch-Sidepanel-Prompt--stop:hover { + background-color: var(--docsearch-sidepanel-background-dark); +} + +.DocSearch-Sidepanel-Prompt--stop svg { + height: 1rem; + width: 1rem; +} + +.DocSearch-Sidepanel-Prompt--submit { + background-color: var(--docsearch-sidepanel-primary); + border: 0; + color: var(--docsearch-sidepanel-white); +} + +.DocSearch-Sidepanel-Prompt--submit:hover { + background-color: var(--docsearch-sidepanel-primary-dark); +} + +.DocSearch-Sidepanel-Prompt--submit:disabled, +.DocSearch-Sidepanel-Prompt--submit[aria-disabled='true'] { + background-color: var(--docsearch-sidepanel-primary-disabled); + user-select: none; + cursor: not-allowed; +} + +.DocSearch-Sidepanel-Prompt--submit svg { + height: 1.25em; + width: 1.25em; +} + +.DocSearch-Sidepanel-Prompt--disclaimer { + display: none; +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel-Prompt--disclaimer { + display: block; + font-size: 0.75rem; + color: var(--docsearch-sidepanel-text-muted); + text-align: center; + } +} + +.DocSearch-Sidepanel-Footer { + display: flex; + justify-content: flex-end; + background-color: var(--docsearch-sidepanel-background-dark); + padding: 0.75rem 1rem 1rem; +} + +.DocSearch-Sidepanel--powered-by { + align-self: flex-end; +} + +.DocSearch-Sidepanel-Screen--title { + font-size: 1.625rem; + font-weight: 600; + line-height: 2rem; + color: var(--docsearch-sidepanel-text-base); +} + +.DocSearch-Sidepanel-NewConversationScreen { + padding-top: 3.5rem; +} + +.DocSearch-Sidepanel-Screen--introduction { + font-size: 0.875rem; + color: var(--docsearch-sidepanel-text-muted); + line-height: 1.25rem; + margin-top: 0.5rem; + margin-bottom: 1.5rem; +} + +.DocSearch-Sidepanel-List { + list-style-type: none; + display: flex; + flex-direction: column; + gap: 0.75rem; + align-items: flex-start; +} + +.DocSearch-Sidepanel-ConversationHistoryScreen { + padding-top: 1rem; +} + +.DocSearch-Sidepanel-ConversationHistoryScreen ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.DocSearch-Sidepanel-RecentConversation { + border-radius: 0.25rem; + display: flex; + position: relative; + scroll-margin-top: 2.5rem; + background-color: var(--docsearch-hit-background); + color: var(--docsearch-sidepanel-text-base); + padding: 0; + width: 100%; + margin-bottom: 0.25rem; + cursor: pointer; +} + +.DocSearch-Sidepanel-RecentConversation a { + width: 100%; + display: block; + padding: 0 0.75rem; +} + +.DocSearch-Sidepanel-RecentConversation:hover, +.DocSearch-Sidepanel-RecentConversation:focus-within { + background-color: var(--docsearch-hit-highlight-color); +} + +.DocSearch-Sidepanel-RecentConversation:focus-within +.DocSearch-Sidepanel-RecentConversation-icon, +.DocSearch-Sidepanel-RecentConversation:focus-within .DocSearch-Hit-action, +.DocSearch-Sidepanel-RecentConversation:hover +.DocSearch-Sidepanel-RecentConversation-icon, +.DocSearch-Sidepanel-RecentConversation:hover .DocSearch-Hit-action { + color: var(--docsearch-sidepanel-accent); +} + +.DocSearch-Sidepanel-RecentConversation-container { + align-items: center; + display: flex; + flex-direction: row; + height: 3.5rem; + width: 100%; + color: var(--docsearch-text-color); +} + +.DocSearch-Sidepanel-RecentConversation-icon { + height: 1.25rem; + width: 1.25rem; +} + +.DocSearch-Sidepanel-RecentConversation-content { + margin: 0 0.5rem; + overflow-x: hidden; + position: relative; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; +} + +.DocSearch-Sidepanel-RecentConversation-title { + font-size: 0.9em; +} + +.DocSearch-Sidepanel-SuggestedQuestion { + background-color: var(--docsearch-sidepanel-background-dark); + border: 1px solid var(--docsearch-sidepanel-border); + border-radius: 0.25rem; + height: 2.5rem; + padding: 0 0.75rem; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.875rem; + line-height: 1.25rem; + color: var(--docsearch-sidepanel-text-base); + cursor: pointer; +} + +.DocSearch-Sidepanel-ConversationScreen { + padding: 0; + display: flex; + flex-direction: column; +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel-ConversationScreen { + padding: 1rem 0; + } +} + +.DocSearch-Sidepanel-ConversationScreen-disclaimer { + font-size: 0.75rem; + line-height: 1rem; + color: var(--docsearch-sidepanel-text-muted); + margin: 1rem 0; + font-weight: 400; +} + +@media screen and (width >= 769px) { + .DocSearch-Sidepanel-ConversationScreen-disclaimer { + display: none; + } +} + +.DocSearch-Sidepanel .DocSearch-AskAiScreen-Response-Container { + margin-block: 1rem; +} + +.DocSearch-Sidepanel .DocSearch-AskAiScreen-Response { + padding: 0; + font-size: 0.95em; +} + +.DocSearch-Sidepanel .DocSearch-Markdown-Content { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.DocSearch-Sidepanel .DocSearch-Markdown-Content h1, +.DocSearch-Sidepanel .DocSearch-Markdown-Content h2, +.DocSearch-Sidepanel .DocSearch-Markdown-Content h3, +.DocSearch-Sidepanel .DocSearch-Markdown-Content h4, +.DocSearch-Sidepanel .DocSearch-Markdown-Content h5, +.DocSearch-Sidepanel .DocSearch-Markdown-Content h6 { + margin: 0.5em 0; +} + +.DocSearch-Sidepanel .DocSearch-Markdown-Content code { + font-size: 0.8rem; +} + +.DocSearch-Sidepanel .DocSearch-CodeSnippet pre { + padding-block-start: 2.4em; +} + +.DocSearch-Sidepanel .DocSearch-AskAiScreen-MessageContent-Tool-Query:hover { + cursor: unset; + box-shadow: none; + color: var(--docsearch-muted-color); +} + +.DocSearch-Sidepanel .DocSearch-AskAiScreen-Actions-Controls { + align-self: flex-start; +} + +.DocSearch-Sidepanel .DocSearch-AskAiScreen-CopyButton { + order: -1; +} + +.DocSearch-Sidepanel .DocSearch-Feedback-Panel { + padding: 0.75em; +} + +.DocSearch-Sidepanel .DocSearch-AskAiScreen-RelatedSources { + padding-bottom: 1rem; +} + +.DocSearch-Sidepanel .DocSearch-Menu-content { + right: -4rem; +} + +.sr-only { + clip-path: inset(50%); + white-space: nowrap; + border-width: 0; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + position: absolute; + overflow: hidden; +} diff --git a/packages/docsearch-css/src/style.css b/packages/docsearch-css/src/style.css new file mode 100644 index 00000000..29d347d7 --- /dev/null +++ b/packages/docsearch-css/src/style.css @@ -0,0 +1,4 @@ +@import url('./_variables.css'); +@import url('./button.css'); +@import url('./modal.css'); +@import url('./_askai.css'); diff --git a/packages/docsearch-js/CHANGELOG.md b/packages/docsearch-js/CHANGELOG.md new file mode 100644 index 00000000..4e036049 --- /dev/null +++ b/packages/docsearch-js/CHANGELOG.md @@ -0,0 +1,175 @@ +# typesense-docsearch.js + +## 5.0.2 + +### Patch Changes + +- 29c23e2: Move bundled DocSearch packages to development dependencies to reduce consumers' install footprint. + +## 5.0.1 + +### Patch Changes + +- Updated dependencies [4ac53c4] + - typesense-docsearch-react@5.0.1 + - typesense-docsearch-core@5.0.1 + +## 5.0.0 + +### Major Changes + +- ecd905d: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed + +### Patch Changes + +- ecd905d: feat(v5): add customizable footer action + + - New `footerAction` prop renders a custom action in the modal footer, + before the Algolia logo, inside `.DocSearch-Footer-Action` + - `typesense-docsearch.js` supports `footerAction` via template patterns (html helper, + JSX, or function-based) + - Fixes typing differences between `typesense-docsearch-react` and `typesense-docsearch.js` + - Restyle the footer with a `.DocSearch-Footer-Actions` wrapper + +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] + - typesense-docsearch-react@5.0.0 + - typesense-docsearch-core@5.0.0 + +## 5.0.0-beta.4 + +### Patch Changes + +- Updated dependencies [ee9fddb] + - typesense-docsearch-react@5.0.0-beta.4 + - typesense-docsearch-core@5.0.0-beta.4 + +## 5.0.0-beta.3 + +### Patch Changes + +- b28bc84: feat(v5): add customizable footer action + + - New `footerAction` prop renders a custom action in the modal footer, + before the Algolia logo, inside `.DocSearch-Footer-Action` + - `typesense-docsearch.js` supports `footerAction` via template patterns (html helper, + JSX, or function-based) + - Fixes typing differences between `typesense-docsearch-react` and `typesense-docsearch.js` + - Restyle the footer with a `.DocSearch-Footer-Actions` wrapper + +- Updated dependencies [3f74c33] +- Updated dependencies [b33a7f7] +- Updated dependencies [fe4d0c2] +- Updated dependencies [4e44b55] +- Updated dependencies [9a1b3e4] +- Updated dependencies [b28bc84] + - typesense-docsearch-core@5.0.0-beta.3 + - typesense-docsearch-react@5.0.0-beta.3 + +## 5.0.0-beta.2 + +### Patch Changes + +- Updated dependencies [5eac1fd] +- Updated dependencies [4f6b5b1] +- Updated dependencies [9fe6738] + - typesense-docsearch-react@5.0.0-beta.2 + - typesense-docsearch-core@5.0.0-beta.2 + +## 5.0.0-beta.1 + +### Patch Changes + +- Updated dependencies [f8e0678] +- Updated dependencies [09861c8] + - typesense-docsearch-react@5.0.0-beta.1 + - typesense-docsearch-core@5.0.0-beta.1 + +## 5.0.0-beta.0 + +### Major Changes + +- a8ed1ea: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed + +### Patch Changes + +- Updated dependencies [a8ed1ea] + - typesense-docsearch-core@5.0.0-beta.0 + - typesense-docsearch-react@5.0.0-beta.0 diff --git a/packages/docsearch-js/babel.config.js b/packages/docsearch-js/babel.config.js deleted file mode 100644 index 23a25b16..00000000 --- a/packages/docsearch-js/babel.config.js +++ /dev/null @@ -1,37 +0,0 @@ -module.exports = (api) => { - const isTest = api.env('test'); - const modules = isTest ? 'commonjs' : false; - const targets = {}; - - if (isTest) { - targets.node = true; - } else { - targets.browsers = ['last 2 versions', 'ie >= 11']; - } - - return { - presets: [ - '@babel/preset-typescript', - [ - '@babel/preset-env', - { - modules, - targets, - }, - ], - ], - plugins: [ - ['@babel/plugin-transform-react-jsx'], - [ - 'module-resolver', - { - root: ['./src'], - alias: { - react: 'preact/compat', - 'react-dom': 'preact/compat', - }, - }, - ], - ], - }; -}; diff --git a/packages/docsearch-js/package.json b/packages/docsearch-js/package.json index 34828510..dc030e67 100644 --- a/packages/docsearch-js/package.json +++ b/packages/docsearch-js/package.json @@ -1,7 +1,7 @@ { "name": "typesense-docsearch.js", - "description": "JavaScript package for DocSearch, adapted to work with Typesense", - "version": "3.4.1", + "description": "DocSearch, customized to work with Typesense", + "version": "5.0.4", "license": "MIT", "homepage": "https://typesense.org/docs/latest/guide/docsearch.html", "repository": "typesense/docsearch.js", @@ -19,6 +19,10 @@ "files": [ "dist/" ], + "exports": { + ".": "./dist/esm/index.js", + "./docsearch": "./dist/esm/docsearch.js" + }, "source": "src/index.ts", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", @@ -27,16 +31,20 @@ "unpkg": "dist/umd/index.js", "jsdelivr": "dist/umd/index.js", "scripts": { - "build:clean": "rm -rf ./dist", - "build:esm": "cross-env BUILD=esm rollup --config", - "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm", - "build:umd": "cross-env BUILD=umd rollup --config", - "build": "yarn build:clean && yarn build:umd && yarn build:esm && yarn build:types", - "on:change": "concurrently \"yarn build:esm\" \"yarn build:types\"", - "watch": "watch \"yarn on:change\" --ignoreDirectoryPattern \"/dist/\"" + "build": "tsdown", + "watch": "tsdown --watch" + }, + "devDependencies": { + "htm": "3.1.1", + "preact": "11.0.0-beta.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-react": "5.0.2" }, - "dependencies": { - "preact": "^10.0.0", - "typesense-docsearch-react": "3.4.1" + "nx": { + "implicitDependencies": [ + "typesense-docsearch-react" + ] } } diff --git a/packages/docsearch-js/rollup.config.js b/packages/docsearch-js/rollup.config.js deleted file mode 100644 index 295a7f92..00000000 --- a/packages/docsearch-js/rollup.config.js +++ /dev/null @@ -1,44 +0,0 @@ -import replace from '@rollup/plugin-replace'; -import builtins from 'rollup-plugin-node-builtins'; -import globals from 'rollup-plugin-node-globals'; - -import { plugins } from '../../rollup.base.config'; -import { getBundleBanner } from '../../scripts/getBundleBanner'; - -import pkg from './package.json'; - -if (!process.env.BUILD) { - throw new Error('The `BUILD` environment variable is required to build.'); -} - -const output = { - umd: { - file: 'dist/umd/index.js', - format: 'umd', - sourcemap: true, - name: 'docsearch', - banner: getBundleBanner(pkg), - }, - esm: { - file: 'dist/esm/index.js', - format: 'es', - sourcemap: true, - banner: getBundleBanner(pkg), - }, -}; - -export default { - input: 'src/index.ts', - output: output[process.env.BUILD], - plugins: - process.env.BUILD === 'umd' - ? [ - ...plugins, - replace({ - 'process.env.NODE_ENV': JSON.stringify('production'), - }), - globals(), - builtins(), - ] - : plugins, -}; diff --git a/packages/docsearch-js/src/__tests__/createDocSearch.test.tsx b/packages/docsearch-js/src/__tests__/createDocSearch.test.tsx new file mode 100644 index 00000000..fb4c221a --- /dev/null +++ b/packages/docsearch-js/src/__tests__/createDocSearch.test.tsx @@ -0,0 +1,103 @@ +import { createElement, type JSX } from 'preact'; +import { afterEach, describe, expect, it } from 'vitest'; + +import { createDocSearch, type DocSearchInstance } from '../createDocSearch'; +import type { FooterActionFn as KeywordFooterActionFn } from '../docsearch'; +import type { FooterActionFn as AskAiFooterActionFn } from '../index'; + +interface TestComponentProps { + footerAction?: JSX.Element | null; +} + +function TestComponent({ footerAction }: TestComponentProps): JSX.Element { + return createElement('div', null, footerAction); +} + +const askAiFooterAction: AskAiFooterActionFn = (_props, { html }) => + html``; + +const keywordFooterAction: KeywordFooterActionFn = (_props, { html }) => + html``; + +describe('createDocSearch', () => { + let instance: DocSearchInstance | undefined; + + afterEach(() => { + instance?.destroy(); + instance = undefined; + }); + + it('adapts footerAction templates for the rendered component', () => { + const container = document.createElement('div'); + const footerAction = (): JSX.Element => + createElement('button', { type: 'button' }, 'Footer action'); + const docsearch = createDocSearch( + TestComponent, + 'test' + ); + + instance = docsearch({ container, footerAction }); + + expect(container.textContent).toContain('Footer action'); + }); + + it('provides the html helper to footerAction templates', () => { + const container = document.createElement('div'); + const docsearch = createDocSearch( + TestComponent, + 'test' + ); + + instance = docsearch({ container, footerAction: askAiFooterAction }); + + expect(container.textContent).toContain('Ask AI footer action'); + }); + + it('accepts footerAction templates from the keyword-only entry point', () => { + const container = document.createElement('div'); + const docsearch = createDocSearch( + TestComponent, + 'test' + ); + + instance = docsearch({ container, footerAction: keywordFooterAction }); + + expect(container.textContent).toContain('Keyword footer action'); + }); + + it('renders string and component footerAction template returns', () => { + const container = document.createElement('div'); + const docsearch = createDocSearch( + TestComponent, + 'test' + ); + + instance = docsearch({ + container, + footerAction: () => () => + createElement('button', { type: 'button' }, 'Component footer action'), + }); + + expect(container.textContent).toContain('Component footer action'); + + instance.destroy(); + instance = docsearch({ + container, + footerAction: () => 'Text footer action', + }); + + expect(container.textContent).toContain('Text footer action'); + }); + + it('supports footerAction templates that return null', () => { + const container = document.createElement('div'); + const docsearch = createDocSearch( + TestComponent, + 'test' + ); + + instance = docsearch({ container, footerAction: () => null }); + + expect(container.textContent).toBe(''); + }); +}); diff --git a/packages/docsearch-js/src/createDocSearch.tsx b/packages/docsearch-js/src/createDocSearch.tsx new file mode 100644 index 00000000..c3036571 --- /dev/null +++ b/packages/docsearch-js/src/createDocSearch.tsx @@ -0,0 +1,220 @@ +import htm from 'htm'; +import type { ComponentType, JSX, Attributes } from 'preact'; +import { + createElement, + createRef, + isValidElement, + render, + unmountComponentAtNode, +} from 'preact/compat'; +import type { + DocSearchRef, + InitialAskAiMessage, +} from 'typesense-docsearch-core'; +import type { + ResultsFooterComponentProps, + HitComponentProps, +} from 'typesense-docsearch-react'; + +export interface DocSearchInstance { + readonly isReady: boolean; + readonly isOpen: boolean; + open(): void; + close(): void; + openAskAi(initialMessage?: InitialAskAiMessage): void; + destroy(): void; +} + +export interface DocSearchCallbacks { + onReady?: () => void; + onOpen?: () => void; + onClose?: () => void; + interceptAskAiEvent?: (initialMessage: InitialAskAiMessage) => boolean | void; +} + +export type TemplateHelpers = { html: typeof html }; + +// Defines the public facing interface for each "template" function +type TemplateFnReturnType = JSX.Element | string | (() => JSX.Element) | null; + +export type HitComponentFn = ( + props: HitComponentProps, + helpers: TemplateHelpers +) => TemplateFnReturnType; + +export type ResultsFooterComponentFn = ( + props: ResultsFooterComponentProps, + helpers: TemplateHelpers +) => TemplateFnReturnType; + +export type FooterActionFn = ( + props: never, + helpers: TemplateHelpers +) => TemplateFnReturnType; + +export type DocSearchProps = DocSearchCallbacks & + Omit< + TProps, + | 'onSidepanelClose' + | 'onSidepanelOpen' + | 'hitComponent' + | 'resultsFooterComponent' + | 'footerAction' + > & { + container: HTMLElement | string; + environment?: typeof window; + /** + * Custom component to render an individual hit. Supports template patterns: + * + * - HTML strings with html helper: (props, { html }) => html`
...
` + * - JSX templates: (props) =>
...
+ * - Function-based templates: (props) => string | JSX.Element | Function. + */ + hitComponent?: HitComponentFn; + /** + * Custom component rendered at the bottom of the results panel. Supports + * template patterns: + * + * - HTML strings with html helper: (props, { html }) => html`
...
` + * - JSX templates: (props) =>
...
+ * - Function-based templates: (props) => string | JSX.Element | Function. + */ + resultsFooterComponent?: ResultsFooterComponentFn; + /** + * A custom action that can be rendered in the Modal's footer before the + * Algolia logo. The component will be rendered as a child of `
`. Supports template patterns: + * + * - HTML strings with html helper: (props, { html }) => html`
...
` + * - JSX templates: (props) =>
...
+ * - Function-based templates: (props) => string | JSX.Element | Function. + */ + footerAction?: FooterActionFn; + }; + +function getHTMLElement( + value: HTMLElement | string, + env: typeof window | undefined +): HTMLElement { + if (typeof value !== 'string') return value; + if (!env) + throw new Error('Cannot resolve a selector without a browser environment.'); + + const element = env.document.querySelector(value); + if (!element) + throw new Error(`Container selector did not match any element: "${value}"`); + + return element; +} + +const html = htm.bind(createElement) as unknown as ( + strings: TemplateStringsArray, + ...values: unknown[] +) => JSX.Element; + +function createTemplateFunction< + P = Record, + R = TemplateFnReturnType, +>( + original: ((props: P, helpers: TemplateHelpers) => R) | undefined +): ((props: P) => JSX.Element | null) | undefined { + if (!original) return undefined; + + return (props: P) => { + const output = original(props, { html }); + + if (isValidElement(output)) return output; + if (typeof output === 'function') return output(props); + if (typeof output === 'string') return createElement('span', null, output); + + return output as JSX.Element; + }; +} + +interface ComponentProps { + hitComponent?: HitComponentFn; + resultsFooterComponent?: ResultsFooterComponentFn; + transformSearchClient?: (searchClient: unknown) => unknown; + footerAction?: FooterActionFn; +} + +export function createDocSearch( + Component: ComponentType, + version: string +): (allProps: DocSearchProps) => DocSearchInstance { + return (allProps) => { + const input = allProps as unknown as DocSearchProps; + const { + container, + environment, + transformSearchClient, + hitComponent, + resultsFooterComponent, + footerAction, + ...rest + } = input; + const containerElement = getHTMLElement( + container, + environment || (typeof window !== 'undefined' ? window : undefined) + ); + const ref = createRef(); + let isReady = false; + + const FooterAction = createTemplateFunction(footerAction); + + const props: TComponentProps = { + ...rest, + ref, + hitComponent: createTemplateFunction(hitComponent), + resultsFooterComponent: + createTemplateFunction( + resultsFooterComponent + ), + footerAction: FooterAction + ? createElement(FooterAction, null) + : undefined, + transformSearchClient: (searchClient: unknown): unknown => { + if ( + typeof searchClient === 'object' && + searchClient !== null && + 'addAlgoliaAgent' in searchClient && + typeof searchClient.addAlgoliaAgent === 'function' + ) { + searchClient.addAlgoliaAgent('docsearch.js', version); + } + + return typeof transformSearchClient === 'function' + ? transformSearchClient(searchClient) + : searchClient; + }, + } as unknown as TComponentProps; + + render( + createElement(Component, props as Attributes & TComponentProps), + containerElement + ); + isReady = true; + + return { + open(): void { + ref.current?.open(); + }, + close(): void { + ref.current?.close(); + }, + openAskAi(initialMessage?: InitialAskAiMessage): void { + ref.current?.openAskAi(initialMessage); + }, + get isReady(): boolean { + return isReady; + }, + get isOpen(): boolean { + return ref.current?.isOpen ?? false; + }, + destroy(): void { + unmountComponentAtNode(containerElement); + isReady = false; + }, + }; + }; +} diff --git a/packages/docsearch-js/src/docsearch.ts b/packages/docsearch-js/src/docsearch.ts new file mode 100644 index 00000000..2ca97b73 --- /dev/null +++ b/packages/docsearch-js/src/docsearch.ts @@ -0,0 +1,12 @@ +export type { + HitComponentFn, + ResultsFooterComponentFn, + FooterActionFn, + TemplateHelpers, +} from './createDocSearch'; +export { docsearch as default } from './docsearchComponent'; +export type { + DocSearchCallbacks, + DocSearchInstance, + DocSearchProps, +} from './docsearchComponent'; diff --git a/packages/docsearch-js/src/docsearch.tsx b/packages/docsearch-js/src/docsearch.tsx deleted file mode 100644 index 2cd92972..00000000 --- a/packages/docsearch-js/src/docsearch.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { render } from 'preact/compat'; -import type { DocSearchProps as DocSearchComponentProps } from 'typesense-docsearch-react'; -import { DocSearch } from 'typesense-docsearch-react'; - -function getHTMLElement( - value: HTMLElement | string, - environment: DocSearchProps['environment'] = window -): HTMLElement { - if (typeof value === 'string') { - return environment.document.querySelector(value)!; - } - - return value; -} - -interface DocSearchProps extends DocSearchComponentProps { - container: HTMLElement | string; - environment?: typeof window; -} - -export function docsearch(props: DocSearchProps) { - render( - , - getHTMLElement(props.container, props.environment) - ); -} diff --git a/packages/docsearch-js/src/docsearchAi.tsx b/packages/docsearch-js/src/docsearchAi.tsx new file mode 100644 index 00000000..b112e66b --- /dev/null +++ b/packages/docsearch-js/src/docsearchAi.tsx @@ -0,0 +1,15 @@ +import type { DocSearchAIProps as DocSearchComponentProps } from 'typesense-docsearch-react/docsearchAi'; +import { DocSearchAI } from 'typesense-docsearch-react/docsearchAi'; +import { version } from 'typesense-docsearch-react/version'; + +import { + createDocSearch, + type DocSearchInstance, + type DocSearchProps as CreateDocSearchProps, +} from './createDocSearch'; + +export type { DocSearchCallbacks, DocSearchInstance } from './createDocSearch'; +export type DocSearchAIProps = CreateDocSearchProps; + +export const docsearchAi: (allProps: DocSearchAIProps) => DocSearchInstance = + createDocSearch(DocSearchAI, version); diff --git a/packages/docsearch-js/src/docsearchComponent.tsx b/packages/docsearch-js/src/docsearchComponent.tsx new file mode 100644 index 00000000..c476f224 --- /dev/null +++ b/packages/docsearch-js/src/docsearchComponent.tsx @@ -0,0 +1,14 @@ +import type { DocSearchProps as DocSearchComponentProps } from 'typesense-docsearch-react'; +import { DocSearch, version } from 'typesense-docsearch-react'; + +import { + createDocSearch, + type DocSearchInstance, + type DocSearchProps as CreateDocSearchProps, +} from './createDocSearch'; + +export type { DocSearchCallbacks, DocSearchInstance } from './createDocSearch'; +export type DocSearchProps = CreateDocSearchProps; + +export const docsearch: (allProps: DocSearchProps) => DocSearchInstance = + createDocSearch(DocSearch, version); diff --git a/packages/docsearch-js/src/index.ts b/packages/docsearch-js/src/index.ts index 4f419249..de8cdb9e 100644 --- a/packages/docsearch-js/src/index.ts +++ b/packages/docsearch-js/src/index.ts @@ -1 +1,12 @@ -export { docsearch as default } from './docsearch'; +export type { + HitComponentFn, + ResultsFooterComponentFn, + FooterActionFn, + TemplateHelpers, +} from './createDocSearch'; +export { docsearchAi as default } from './docsearchAi'; +export type { + DocSearchAIProps as DocSearchProps, + DocSearchInstance, + DocSearchCallbacks, +} from './docsearchAi'; diff --git a/packages/docsearch-js/tsconfig.declaration.json b/packages/docsearch-js/tsconfig.declaration.json deleted file mode 100644 index 1e0c6449..00000000 --- a/packages/docsearch-js/tsconfig.declaration.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.declaration" -} diff --git a/packages/docsearch-js/tsdown.config.mts b/packages/docsearch-js/tsdown.config.mts new file mode 100644 index 00000000..959157da --- /dev/null +++ b/packages/docsearch-js/tsdown.config.mts @@ -0,0 +1,76 @@ +import type { UserConfig } from 'tsdown'; +import { defineConfig } from 'tsdown'; + +import { getBundleBanner } from '../../scripts/getBundleBanner.ts'; +import { defines } from '../../tsdown.base.ts'; + +import pkg from './package.json' with { type: 'json' }; + +const sharedConfig: UserConfig = { + platform: 'browser', + target: 'es2017', + define: defines, + banner: getBundleBanner(pkg), + alias: { + react: 'preact/compat', + 'react-dom': 'preact/compat', + }, + outExtensions: () => ({ + js: '.js', + }), + sourcemap: true, + deps: { + alwaysBundle: [/^typesense-docsearch-(react|core)(?:\/|$)/], + dts: { + alwaysBundle: [], + // `typesense` pulls in axios and loglevel, whose CommonJS .d.ts files + // rolldown-plugin-dts cannot bundle. + neverBundle: ['axios', 'loglevel', /^typesense(?:\/|$)/], + }, + onlyBundle: false, + }, +}; + +export default defineConfig([ + { + ...sharedConfig, + entry: 'src/index.ts', + dts: true, + format: 'esm', + outDir: 'dist/esm', + minify: false, + }, + { + ...sharedConfig, + clean: false, + entry: 'src/docsearch.ts', + dts: true, + format: 'esm', + outDir: 'dist/esm', + minify: false, + }, + { + ...sharedConfig, + entry: 'src/index.ts', + dts: false, + globalName: 'docsearch', + outDir: 'dist/umd', + outputOptions: { + entryFileNames: '[name].js', + }, + format: 'umd', + minify: true, + }, + { + ...sharedConfig, + entry: 'src/docsearch.ts', + dts: false, + globalName: 'docsearch', + outDir: 'dist/umd', + outputOptions: { + entryFileNames: '[name].js', + }, + format: 'umd', + minify: true, + }, +]); diff --git a/packages/docsearch-modal/CHANGELOG.md b/packages/docsearch-modal/CHANGELOG.md new file mode 100644 index 00000000..3bb8301b --- /dev/null +++ b/packages/docsearch-modal/CHANGELOG.md @@ -0,0 +1,176 @@ +# typesense-docsearch-modal + +## 5.0.2 + +### Patch Changes + +- Updated dependencies [1508196] +- Updated dependencies [59c575f] + - typesense-docsearch-react@5.0.2 + - typesense-docsearch-core@5.0.2 + +## 5.0.1 + +### Patch Changes + +- Updated dependencies [4ac53c4] + - typesense-docsearch-react@5.0.1 + - typesense-docsearch-core@5.0.1 + +## 5.0.0 + +### Major Changes + +- ecd905d: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed + +### Patch Changes + +- ecd905d: feat(v5): UI and DX updates + + - Rename the Ask AI assistantId option to agentId (adapter theme.SearchModal.askAi.assistantId → agentId) + - appId and apiKey moved up into typesense-docsearch-core, so they're configured once and shared + - Removed the indexName prop from the Sidepanel + - Facet defaults can now be read from the index searchParameters + - Restored nested grouping of search results + +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] +- Updated dependencies [ecd905d] + - typesense-docsearch-react@5.0.0 + - typesense-docsearch-core@5.0.0 + +## 5.0.0-beta.4 + +### Patch Changes + +- Updated dependencies [ee9fddb] + - typesense-docsearch-react@5.0.0-beta.4 + - typesense-docsearch-core@5.0.0-beta.4 + +## 5.0.0-beta.3 + +### Patch Changes + +- Updated dependencies [3f74c33] +- Updated dependencies [b33a7f7] +- Updated dependencies [fe4d0c2] +- Updated dependencies [4e44b55] +- Updated dependencies [9a1b3e4] +- Updated dependencies [b28bc84] + - typesense-docsearch-core@5.0.0-beta.3 + - typesense-docsearch-react@5.0.0-beta.3 + +## 5.0.0-beta.2 + +### Patch Changes + +- 4f6b5b1: feat(v5): UI and DX updates + + - Rename the Ask AI assistantId option to agentId (adapter theme.SearchModal.askAi.assistantId → agentId) + - appId and apiKey moved up into typesense-docsearch-core, so they're configured once and shared + - Removed the indexName prop from the Sidepanel + - Facet defaults can now be read from the index searchParameters + - Restored nested grouping of search results + +- Updated dependencies [5eac1fd] +- Updated dependencies [4f6b5b1] +- Updated dependencies [9fe6738] + - typesense-docsearch-react@5.0.0-beta.2 + - typesense-docsearch-core@5.0.0-beta.2 + +## 5.0.0-beta.1 + +### Patch Changes + +- Updated dependencies [f8e0678] +- Updated dependencies [09861c8] + - typesense-docsearch-react@5.0.0-beta.1 + - typesense-docsearch-core@5.0.0-beta.1 + +## 5.0.0-beta.0 + +### Major Changes + +- a8ed1ea: # DocSearch v5-beta + + DocSearch v5 is a major release that introduces AI-powered answers via Agent + Studio, a refreshed search UI, and a modernized package architecture. + + ## Ask AI & Agent Studio + + - Agent Studio integration with core tools and dynamic tool calls + - Conversation memory support + - Compatibility with the Algolia MCP search tool, with aggregated MCP search + tool calls + - Dynamic index selection for Agent Studio + - Feedback integration, including feedback notes and tags + - Prompt suggestions in keyword search and follow-up prompt suggestions + - Ask AI modal split into its own component and Ask AI transport layer removed + + ## Search UI + + - Refreshed v5 UI with improved dark theme, sources panel, and accessibility + - Faceted search with filter chips + - Hit breadcrumbs and result badges + + ## Packaging & architecture + + - New `typesense-docsearch-cli` package for MCP setup and search + - MCP plugin support + - Split JS bundles for search-only usage and JS-based hybrid mode + - Migrated the build system to tsdown + - Migrated CSS building to LightningCSS + + ## Breaking changes + + - Ask AI related props are now nested under a single root `askai` option + - The Ask AI transport layer has been removed + +### Patch Changes + +- Updated dependencies [a8ed1ea] + - typesense-docsearch-core@5.0.0-beta.0 + - typesense-docsearch-react@5.0.0-beta.0 diff --git a/packages/docsearch-modal/README.md b/packages/docsearch-modal/README.md new file mode 100644 index 00000000..368fb94d --- /dev/null +++ b/packages/docsearch-modal/README.md @@ -0,0 +1,4 @@ +# typesense-docsearch-modal + +Keyword Search and AskAI modal package for [DocSearch](http://docsearch.algolia.com/), the best search experience for docs. + diff --git a/packages/docsearch-modal/package.json b/packages/docsearch-modal/package.json new file mode 100644 index 00000000..dd50c218 --- /dev/null +++ b/packages/docsearch-modal/package.json @@ -0,0 +1,66 @@ +{ + "name": "typesense-docsearch-modal", + "description": "Modal package for DocSearch, customized to work with Typesense", + "version": "5.0.2", + "license": "MIT", + "homepage": "https://typesense.org/docs/latest/guide/docsearch.html", + "repository": "typesense/docsearch.js", + "contributors": [ + { + "name": "Algolia, Inc.", + "url": "https://www.algolia.com" + }, + { + "name": "Typesense, Inc.", + "url": "https://typesense.org" + } + ], + "sideEffects": false, + "files": [ + "dist/" + ], + "exports": { + ".": "./dist/esm/index.js", + "./askai": "./dist/esm/askai.js", + "./button": "./dist/esm/button.js", + "./modal": "./dist/esm/modal.js" + }, + "source": "src/index.ts", + "types": "./dist/esm/index.d.ts", + "main": "./dist/esm/index.js", + "module": "./dist/esm/index.js", + "umd:main": "dist/umd/index.js", + "unpkg": "dist/umd/index.js", + "jsdelivr": "dist/umd/index.js", + "scripts": { + "build": "tsdown", + "watch": "tsdown --watch" + }, + "dependencies": { + "typesense-docsearch-core": "5.0.2", + "typesense-docsearch-react": "5.0.2" + }, + "devDependencies": { + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.2.0", + "tsdown": "0.22.7", + "typescript": "5.7.3", + "vitest": "3.0.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 20.0.0", + "react": ">= 16.8.0 < 20.0.0", + "react-dom": ">= 16.8.0 < 20.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } +} diff --git a/packages/docsearch-modal/src/DocSearchAskAiModal.tsx b/packages/docsearch-modal/src/DocSearchAskAiModal.tsx new file mode 100644 index 00000000..f62dd00e --- /dev/null +++ b/packages/docsearch-modal/src/DocSearchAskAiModal.tsx @@ -0,0 +1,67 @@ +import type { JSX } from 'react'; +import React from 'react'; +import { createPortal } from 'react-dom'; +import { useDocSearch } from 'typesense-docsearch-core'; +import type { DocSearchAskAiModalProps as ReactDocSearchAskAiModalProps } from 'typesense-docsearch-react/askaiModal'; +import { DocSearchAskAiModal as Modal } from 'typesense-docsearch-react/askaiModal'; + +export type DocSearchAskAiModalProps = Omit< + ReactDocSearchAskAiModalProps, + | 'initialScrollY' + | 'isAskAiActive' + | 'isHybridModeSupported' + | 'keyboardShortcuts' + | 'onAskAiToggle' + | 'onClose' + | 'theme' +>; + +export function DocSearchAskAiModal( + props: DocSearchAskAiModalProps +): JSX.Element | null { + const { + isModalActive, + onAskAiToggle, + closeModal, + isAskAiActive, + initialQuery, + registerView, + isHybridModeSupported, + } = useDocSearch(); + + const containerElement = React.useMemo( + () => props.portalContainer ?? document.body, + [props.portalContainer] + ); + + const initialScroll = React.useMemo(() => window.scrollY, []); + + React.useEffect(() => { + registerView('modal'); + }, [registerView]); + + const modalProps: ReactDocSearchAskAiModalProps = React.useMemo( + () => ({ + ...props, + isAskAiActive, + initialQuery: props.initialQuery ?? initialQuery, + initialScrollY: initialScroll, + onAskAiToggle, + onClose: closeModal, + isHybridModeSupported, + }), + [ + props, + isAskAiActive, + initialQuery, + initialScroll, + onAskAiToggle, + closeModal, + isHybridModeSupported, + ] + ); + + return isModalActive + ? createPortal(, containerElement) + : null; +} diff --git a/packages/docsearch-modal/src/DocSearchButton.tsx b/packages/docsearch-modal/src/DocSearchButton.tsx new file mode 100644 index 00000000..cc6560cc --- /dev/null +++ b/packages/docsearch-modal/src/DocSearchButton.tsx @@ -0,0 +1,33 @@ +import type { JSX } from 'react'; +import React from 'react'; +import { useDocSearch } from 'typesense-docsearch-core'; +import { + DocSearchButton as Button, + type DocSearchButtonProps as ButtonProps, +} from 'typesense-docsearch-react/button'; + +export type DocSearchButtonProps = Omit< + ButtonProps, + 'keyboardShortcuts' | 'theme' +>; + +export function DocSearchButton({ + onClick, + ...props +}: DocSearchButtonProps): JSX.Element { + const { searchButtonRef, keyboardShortcuts, openModal } = useDocSearch(); + + return ( + {' '} + to continue. +

+ )} +
+ + )} + + + + +
+
+ {exchanges.map((exchange, index) => ( + + ))} +
+
+ + ); +} diff --git a/packages/docsearch-react/src/AskAiScreenState.tsx b/packages/docsearch-react/src/AskAiScreenState.tsx new file mode 100644 index 00000000..72656a07 --- /dev/null +++ b/packages/docsearch-react/src/AskAiScreenState.tsx @@ -0,0 +1,190 @@ +import type { + AutocompleteApi, + AutocompleteState, + BaseItem, +} from '@algolia/autocomplete-core'; +import type { JSX } from 'react'; +import React from 'react'; + +import type { AskAiScreenTranslations } from './AskAiScreen'; +import { AskAiScreen } from './AskAiScreen'; +import type { AskAiStartScreenTranslations } from './components/AskAiStartScreen'; +import { AskAiStartScreen } from './components/AskAiStartScreen'; +import { ConversationHistoryScreen } from './ConversationHistoryScreen'; +import type { DocSearchProps } from './DocSearch'; +import type { ErrorScreenTranslations } from './ErrorScreen'; +import { ErrorScreen } from './ErrorScreen'; +import type { NewConversationTranslations } from './NewConversationScreen'; +import { NewConversationScreen } from './NewConversationScreen'; +import type { NoResultsScreenTranslations } from './NoResultsScreen'; +import { NoResultsScreen } from './NoResultsScreen'; +import type { ResultsScreenTranslations } from './ResultsScreen'; +import { ResultsScreen } from './ResultsScreen'; +import type { StoredSearchPlugin } from './stored-searches'; +import type { + InternalDocSearchHit, + OnAskAiFeedback, + StoredAskAiState, + StoredDocSearchHit, + SuggestedQuestionHit, +} from './types'; +import type { AIMessage, AskAiState, AskAiStatus } from './types/AskiAi'; +import { isQueryEmpty } from './utils'; + +export type AskAiScreenStateTranslations = Partial<{ + errorScreen: ErrorScreenTranslations; + startScreen: AskAiStartScreenTranslations; + noResultsScreen: NoResultsScreenTranslations; + resultsScreen: ResultsScreenTranslations; + askAiScreen: AskAiScreenTranslations; + newConversation: NewConversationTranslations; +}>; + +export interface AskAiScreenStateProps< + TItem extends BaseItem, +> extends AutocompleteApi< + TItem, + React.FormEvent, + React.MouseEvent, + React.KeyboardEvent +> { + state: AutocompleteState; + recentSearches: StoredSearchPlugin; + favoriteSearches: StoredSearchPlugin; + conversations: StoredSearchPlugin; + onItemClick: ( + item: InternalDocSearchHit, + event: KeyboardEvent | MouseEvent + ) => void; + onAskAiToggle: (toggle: boolean) => void; + isAskAiActive: boolean; + canHandleAskAi: boolean; + inputRef: React.MutableRefObject; + hitComponent: DocSearchProps['hitComponent']; + indexName: string; + messages: AIMessage[]; + status: AskAiStatus; + askAiError?: Error; + disableUserPersonalization: boolean; + resultsFooterComponent: DocSearchProps['resultsFooterComponent']; + translations: AskAiScreenStateTranslations; + getMissingResultsUrl?: DocSearchProps['getMissingResultsUrl']; + hasCollections: boolean; + onFeedback?: OnAskAiFeedback; + askAiState: AskAiState; + selectAskAiQuestion: (toggle: boolean, query: string) => void; + suggestedQuestions: SuggestedQuestionHit[]; + selectSuggestedQuestion: (question: SuggestedQuestionHit) => void; + onNewConversation: () => void; + resultBadgeKey?: string; + promptBlockingErrorId?: string; +} + +export const AskAiScreenState = React.memo( + ({ + translations = {}, + selectAskAiQuestion, + ...props + }: AskAiScreenStateProps): JSX.Element => { + if ( + props.canHandleAskAi && + props.isAskAiActive && + props.askAiState === 'conversation-history' + ) { + return ( + + ); + } + + if ( + props.canHandleAskAi && + props.isAskAiActive && + props.askAiState === 'new-conversation' + ) { + return ( + + ); + } + + if (props.isAskAiActive && props.canHandleAskAi) { + return ( + + ); + } + + if (props.state?.status === 'error') { + return ; + } + + if (isQueryEmpty(props.state.query)) { + return ( + + ); + } + + if (!props.hasCollections && !props.canHandleAskAi) { + return ( + + ); + } + + return ( + <> + + {props.canHandleAskAi && + !props.hasCollections && ( + // if there's one collection it is the ask ai action, show the no results screen + + )} + + ); + }, + function areEqual(prevProps, nextProps) { + const askAiStateChanged = + prevProps.isAskAiActive !== nextProps.isAskAiActive || + prevProps.askAiState !== nextProps.askAiState || + prevProps.canHandleAskAi !== nextProps.canHandleAskAi; + + // Something triggered an Ask AI related state change, perform a new render + if (askAiStateChanged) { + return false; + } + + const isLoading = + nextProps.state.status === 'loading' || + nextProps.state.status === 'stalled'; + + const isWaitingForCollections = + nextProps.state.status === 'idle' && + !isQueryEmpty(nextProps.state.query) && + prevProps.state.query !== nextProps.state.query && + prevProps.state.collections === nextProps.state.collections; + + return isLoading || isWaitingForCollections; + } +); diff --git a/packages/docsearch-react/src/ConversationHistoryScreen.tsx b/packages/docsearch-react/src/ConversationHistoryScreen.tsx new file mode 100644 index 00000000..b1b03bfe --- /dev/null +++ b/packages/docsearch-react/src/ConversationHistoryScreen.tsx @@ -0,0 +1,69 @@ +import type { JSX } from 'react'; +import React from 'react'; + +import type { AskAiScreenStateProps } from './AskAiScreenState'; +import { CloseIcon, SparklesIcon } from './icons'; +import { Results } from './Results'; +import type { ResultsScreenTranslations } from './ResultsScreen'; +import type { InternalDocSearchHit } from './types'; +import { getCollection } from './utils'; + +type ConversationHistoryScreenProps = Omit< + AskAiScreenStateProps, + 'translations' +> & { + translations?: ResultsScreenTranslations; +}; + +export function ConversationHistoryScreen({ + onAskAiToggle, + ...props +}: ConversationHistoryScreenProps): JSX.Element | null { + const collection = React.useMemo( + () => getCollection(props.state, 'recentConversations'), + [props.state] + ); + + React.useEffect(() => { + if (!collection || collection.items.length === 0) { + onAskAiToggle(true); + } + }, [collection, onAskAiToggle]); + + if (!collection) { + return null; + } + + return ( +
+ ( +
+ +
+ )} + renderAction={({ item }) => ( +
+ +
+ )} + /> +
+ ); +} diff --git a/packages/docsearch-react/src/DocSearch.tsx b/packages/docsearch-react/src/DocSearch.tsx index c769c657..35a49e52 100644 --- a/packages/docsearch-react/src/DocSearch.tsx +++ b/packages/docsearch-react/src/DocSearch.tsx @@ -1,99 +1,204 @@ import type { - AutocompleteState, AutocompleteOptions, + AutocompleteState, } from '@algolia/autocomplete-core'; -import React from 'react'; +import type { SearchResponses } from 'algoliasearch/lite'; +import React, { type JSX } from 'react'; import { createPortal } from 'react-dom'; -import type { SearchClient } from 'typesense'; +import { + DocSearch as DocSearchProvider, + useDocSearch, +} from 'typesense-docsearch-core'; +import type { + DocSearchModalShortcuts, + DocSearchRef, +} from 'typesense-docsearch-core'; import type { ConfigurationOptions as TypesenseConfigurationOptions } from 'typesense/lib/Typesense/Configuration'; -import type { SearchParams as TypesenseSearchParams } from 'typesense/lib/Typesense/Documents'; +import type { + DocumentSchema, + SearchParams as TypesenseSearchParams, +} from 'typesense/lib/Typesense/Documents'; +import type { MultiSearchRequestSchema } from 'typesense/lib/Typesense/Types'; import { DocSearchButton } from './DocSearchButton'; +import type { ButtonTranslations } from './DocSearchButton'; import { DocSearchModal } from './DocSearchModal'; +import type { ModalTranslations } from './DocSearchModal'; import type { DocSearchHit, + DocSearchTheme, InternalDocSearchHit, StoredDocSearchHit, } from './types'; -import { useDocSearchKeyboardEvents } from './useDocSearchKeyboardEvents'; -import type { ButtonTranslations, ModalTranslations } from '.'; +export type { DocSearchRef } from 'typesense-docsearch-core'; export type DocSearchTranslations = Partial<{ button: ButtonTranslations; modal: ModalTranslations; }>; +// The minimal implementation required for the Typesense client, when using the +// `transformSearchClient` option. +export type TypesenseDocsearchTransformClient = { + search: (searchMethodParams: { + requests: Array>; + }) => Promise>; +}; + +export interface DocSearchFacet { + key: string; + label?: string; +} + +export interface HitComponentProps { + hit: InternalDocSearchHit | StoredDocSearchHit; + children: React.ReactNode; +} + +export interface ResultsFooterComponentProps { + state: AutocompleteState; +} + export interface DocSearchProps { + /** Typesense collection name to query. */ typesenseCollectionName: string; + /** Typesense server configuration for the client. */ typesenseServerConfig: TypesenseConfigurationOptions; - typesenseSearchParameters: TypesenseSearchParams; + /** Additional Typesense search parameters to merge into each query. */ + typesenseSearchParameters?: TypesenseSearchParams< + Record, + string + >; + /** + * Facets to display as keyword-search filter controls. Values are read + * dynamically from the configured Typesense collection. + * + * @default [ ] + */ + facets?: DocSearchFacet[]; + /** Theme overrides applied to the modal and related components. */ + theme?: DocSearchTheme; + /** Placeholder text for the search input. */ placeholder?: string; + /** Maximum number of hits to display per source/group. */ + maxResultsPerGroup?: number; + /** + * Show the hierarchy breadcrumb next to each hit's title. + * + * @default false + */ + showHitBreadcrumbs?: boolean; + /** Hook to post-process hits before rendering. */ transformItems?: (items: DocSearchHit[]) => DocSearchHit[]; - hitComponent?: (props: { - hit: InternalDocSearchHit | StoredDocSearchHit; - children: React.ReactNode; - }) => JSX.Element; - resultsFooterComponent?: (props: { - state: AutocompleteState; - }) => JSX.Element | null; - transformSearchClient?: (searchClient: SearchClient) => SearchClient; + /** Custom component to render an individual hit. */ + hitComponent?: (props: HitComponentProps) => JSX.Element; + /** Custom component rendered at the bottom of the results panel. */ + resultsFooterComponent?: ( + props: ResultsFooterComponentProps + ) => JSX.Element | null; + /** + * A custom action that can be rendered in the Modal's footer before the + * Algolia logo. The component will be rendered as a child of `
`. + */ + footerAction?: React.ReactNode; + /** Hook to wrap or modify the Typesense search client. */ + transformSearchClient?: ( + searchClient: TypesenseDocsearchTransformClient + ) => TypesenseDocsearchTransformClient; + /** Disable storage and usage of recent and favorite searches. */ disableUserPersonalization?: boolean; + /** Query string to prefill when opening the modal. */ initialQuery?: string; + /** Custom navigator for controlling link navigation. */ navigator?: AutocompleteOptions['navigator']; + /** Localized strings for the button and modal ui. */ translations?: DocSearchTranslations; + /** Builds a url to report missing results for a given query. */ getMissingResultsUrl?: ({ query }: { query: string }) => string; + /** Insights client integration options to send analytics events. */ + insights?: AutocompleteOptions['insights']; + /** + * The container element where the modal should be portaled to. Defaults to + * document.body. + */ + portalContainer?: DocumentFragment | Element; + /** + * Limit of how many recent searches should be saved/displayed.. + * + * @default 7 + */ + recentSearchesLimit?: number; + /** + * Limit of how many recent searches should be saved/displayed when there are + * favorited searches.. + * + * @default 4 + */ + recentSearchesWithFavoritesLimit?: number; + /** + * Configuration for keyboard shortcuts. Allows enabling/disabling specific + * shortcuts. + * + * @default `{ 'Ctrl/Cmd+K': true, '/': true }` + */ + keyboardShortcuts?: DocSearchModalShortcuts; + /** + * The key used to render a custom badge for each hit. Key must match a + * property returned in `searchParameters.attributesToRetrieve`. + * + * @example + * 'version'; + * 'hierarchy.lvl1'; + * 'tags[2]'; + * + * @default undefined + */ + resultBadgeKey?: string; } -export function DocSearch(props: DocSearchProps) { - const searchButtonRef = React.useRef(null); - const [isOpen, setIsOpen] = React.useState(false); - const [initialQuery, setInitialQuery] = React.useState( - props?.initialQuery || undefined +function DocSearchComponent( + props: DocSearchProps, + ref: React.ForwardedRef +): JSX.Element { + return ( + + + ); +} - const onOpen = React.useCallback(() => { - setIsOpen(true); - }, [setIsOpen]); - - const onClose = React.useCallback(() => { - setIsOpen(false); - }, [setIsOpen]); - - const onInput = React.useCallback( - (event: KeyboardEvent) => { - setIsOpen(true); - setInitialQuery(event.key); - }, - [setIsOpen, setInitialQuery] - ); +export const DocSearch = React.forwardRef(DocSearchComponent); - useDocSearchKeyboardEvents({ - isOpen, - onOpen, - onClose, - onInput, +export function DocSearchInner(props: DocSearchProps): JSX.Element { + const { searchButtonRef, - }); + keyboardShortcuts, + isModalActive, + initialQuery, + openModal, + closeModal, + } = useDocSearch(); return ( <> - - {isOpen && + {isModalActive && createPortal( , - document.body + props.portalContainer ?? document.body )} ); diff --git a/packages/docsearch-react/src/DocSearchAI.tsx b/packages/docsearch-react/src/DocSearchAI.tsx new file mode 100644 index 00000000..0aeb4b9c --- /dev/null +++ b/packages/docsearch-react/src/DocSearchAI.tsx @@ -0,0 +1,118 @@ +import React, { type JSX } from 'react'; +import { createPortal } from 'react-dom'; +import { + DocSearch as DocSearchProvider, + useDocSearch, +} from 'typesense-docsearch-core'; +import type { + DocSearchRef, + InitialAskAiMessage, +} from 'typesense-docsearch-core'; + +import type { DocSearchProps } from './DocSearch'; +import { DocSearchAskAiModal } from './DocSearchAskAiModal'; +import { DocSearchButton } from './DocSearchButton'; +import type { TypesenseAskAiSearchParameters } from './types/AskiAi'; + +export interface DocSearchAskAi { + /** Typesense conversational model id. */ + conversationModelId: string; + /** + * Collection to query for conversational retrieval. Defaults to + * `typesenseCollectionName`. + */ + collection?: string; + /** + * Query field to use for conversational retrieval. + * + * @default 'embedding' + */ + queryBy?: string; + /** + * Fields excluded from the conversational payload. + * + * @default 'embedding' + */ + excludeFields?: string; + /** + * Additional Typesense search parameters for the conversational retrieval + * request. + */ + searchParameters?: TypesenseAskAiSearchParameters; + /** + * Static suggested questions shown in Ask AI entry points. + * + * TODO: Replace this with a Typesense-backed suggestions source instead of + * shipping questions directly in frontend config. + */ + suggestedQuestions?: string[]; +} + +export interface DocSearchAIProps extends DocSearchProps { + /** + * Configuration to enable ask ai mode. Pass a bare Typesense conversation + * model id or a full config object. + */ + askAi: DocSearchAskAi | string; + /** + * Intercept Ask AI requests (e.g. Submitting a prompt or selecting a + * suggested question). + * + * Return `true` to prevent the default modal Ask AI flow (no toggle, no + * sendMessage). Useful to route Ask AI into a different UI (e.g. + * `typesense-docsearch-sidepanel-js`) without flicker. + */ + interceptAskAiEvent?: (initialMessage: InitialAskAiMessage) => boolean | void; +} + +function DocSearchAIComponent( + props: DocSearchAIProps, + ref: React.ForwardedRef +): JSX.Element { + return ( + + + + ); +} + +export const DocSearchAI = React.forwardRef(DocSearchAIComponent); + +export function DocSearchAIInner(props: DocSearchAIProps): JSX.Element { + const { + searchButtonRef, + keyboardShortcuts, + isModalActive, + isAskAiActive, + initialQuery, + onAskAiToggle, + openModal, + closeModal, + isHybridModeSupported, + } = useDocSearch(); + + return ( + <> + + {isModalActive && + createPortal( + , + props.portalContainer ?? document.body + )} + + ); +} diff --git a/packages/docsearch-react/src/DocSearchAskAiModal.tsx b/packages/docsearch-react/src/DocSearchAskAiModal.tsx new file mode 100644 index 00000000..2b81900d --- /dev/null +++ b/packages/docsearch-react/src/DocSearchAskAiModal.tsx @@ -0,0 +1,695 @@ +import { createAutocomplete } from '@algolia/autocomplete-core'; +import React, { type JSX } from 'react'; +import type { + InitialAskAiMessage, + OnAskAiToggle, +} from 'typesense-docsearch-core'; + +import type { AskAiScreenStateTranslations } from './AskAiScreenState'; +import { AskAiScreenState } from './AskAiScreenState'; +import type { AskAiSearchBoxTranslations } from './components/AskAiSearchBox'; +import { AskAiSearchBox } from './components/AskAiSearchBox'; +import type { FacetBarTranslations } from './components/FacetBar'; +import { FacetBar } from './components/FacetBar'; +import { ModalShell } from './components/ui/ModalShell'; +import type { DocSearchAIProps, DocSearchAskAi } from './DocSearchAI'; +import type { FooterTranslations } from './Footer'; +import { Footer } from './Footer'; +import { Hit } from './Hit'; +import { useDocSearchFacets } from './hooks/useDocSearchFacets'; +import { useSendItemClickEvent } from './hooks/useDocSearchInsights'; +import { useInitialModalQuery } from './hooks/useInitialModalQuery'; +import { useModalEnvironment } from './hooks/useModalEnvironment'; +import { useModalRefs } from './hooks/useModalRefs'; +import { useRefreshOnInitialQuery } from './hooks/useRefreshOnInitialQuery'; +import { useSaveRecentSearch } from './hooks/useSaveRecentSearch'; +import { useStoredDocSearches } from './hooks/useStoredDocSearches'; +import type { NewConversationTranslations } from './NewConversationScreen'; +import type { + DocSearchState, + InternalDocSearchHit, + StoredAskAiMessage, + StoredAskAiState, + SuggestedQuestionHit, +} from './types'; +import { type AskAiState } from './types/AskiAi'; +import { useAskAi } from './useAskAi'; +import { useSearchClient } from './useSearchClient'; +import { + identity, + isModifierEvent, + isQueryEmpty, + noop, + scrollTo as scrollToUtils, + SOURCE_IDS, +} from './utils'; +import { + buildDummyAskAiHit, + isAskAiPromptBlockingError, + isThreadDepthError, +} from './utils/ai'; +import { + buildAskAiActionSources, + buildRecentConversationSources, +} from './utils/createAskAiSources'; +import { + buildNoQuerySources, + buildTypesenseQuerySources, + type BuildQuerySourcesState, +} from './utils/createDocSearchSources'; + +export type DocSearchAskAiModalTranslations = AskAiScreenStateTranslations & + Partial<{ + searchBox: AskAiSearchBoxTranslations; + newConversation: NewConversationTranslations; + footer: FooterTranslations; + facets: FacetBarTranslations; + }>; + +export type DocSearchAskAiModalProps = DocSearchAIProps & { + initialScrollY: number; + onAskAiToggle: OnAskAiToggle; + onClose?: () => void; + isAskAiActive?: boolean; + translations?: DocSearchAskAiModalTranslations; + isHybridModeSupported?: boolean; +}; + +export function DocSearchAskAiModal({ + typesenseCollectionName, + typesenseServerConfig, + typesenseSearchParameters, + askAi, + maxResultsPerGroup, + theme, + onClose = noop, + transformItems = identity, + hitComponent = Hit, + resultsFooterComponent = (): JSX.Element | null => null, + navigator, + initialScrollY = 0, + transformSearchClient = identity, + disableUserPersonalization = false, + initialQuery: initialQueryFromProp = '', + translations = {}, + getMissingResultsUrl, + insights = false, + onAskAiToggle, + interceptAskAiEvent, + isAskAiActive = false, + recentSearchesLimit = 7, + recentSearchesWithFavoritesLimit = 4, + facets, + isHybridModeSupported = false, + footerAction, + ...props +}: DocSearchAskAiModalProps): JSX.Element { + const { + footer: footerTranslations, + searchBox: searchBoxTranslations, + facets: facetBarTranslations, + ...screenStateTranslations + } = translations; + const [state, setState] = React.useState< + DocSearchState + >({ + query: '', + collections: [], + completion: null, + context: {}, + isOpen: false, + activeItemId: null, + status: 'idle', + }); + + // check if the instance is configured to handle ask ai + const canHandleAskAi = Boolean(askAi); + + let placeholder = + translations?.searchBox?.placeholderText || + props.placeholder || + 'Search docs'; + + if (canHandleAskAi) { + placeholder = + translations?.searchBox?.placeholderText || + 'Search docs or ask AI a question'; + } + + if (isAskAiActive) { + placeholder = + translations?.searchBox?.placeholderTextAskAi || + 'Ask another question...'; + } + + const { containerRef, modalRef, formElementRef, dropdownRef, inputRef } = + useModalRefs(); + const promptBlockingErrorId = React.useId(); + const { initialQuery, initialQueryFromSelection } = + useInitialModalQuery(initialQueryFromProp); + + const searchClient = useSearchClient( + transformSearchClient, + typesenseServerConfig + ); + + // `askAi` accepts a bare conversation model id as a shorthand for the full + // config object. + const askAiConfig: DocSearchAskAi = React.useMemo( + () => (typeof askAi === 'object' ? askAi : { conversationModelId: askAi }), + [askAi] + ); + const [askAiState, setAskAiState] = React.useState('initial'); + + const defaultIndexName = typesenseCollectionName; + + const autocompleteRef = + React.useRef< + ReturnType< + typeof createAutocomplete< + InternalDocSearchHit, + React.FormEvent, + React.MouseEvent, + React.KeyboardEvent + > + > + >(undefined); + + const { + visibleFacets, + facetSelections, + facetSelectionsRef, + handleFacetSelectionChange, + clearFacetSelections, + } = useDocSearchFacets({ + facets, + typesenseCollectionName, + searchClient, + onSelectionsChange: () => autocompleteRef.current?.refresh(), + }); + + const { favoriteSearches, recentSearches } = useStoredDocSearches({ + defaultIndexName, + recentSearchesLimit, + recentSearchesWithFavoritesLimit, + }); + + const [stoppedStream, setStoppedStream] = React.useState(false); + + const { + chatId, + messages, + status, + setMessages, + sendMessage, + stopAskAiStreaming, + askAiError, + conversations, + startNewConversation, + restoreConversation, + } = useAskAi({ + typesenseServerConfig, + storageKey: `__DOCSEARCH_ASKAI_CONVERSATIONS__${typesenseCollectionName}`, + collection: askAiConfig.collection || typesenseCollectionName, + conversationModelId: askAiConfig.conversationModelId, + queryBy: askAiConfig.queryBy || 'embedding', + excludeFields: askAiConfig.excludeFields || 'embedding', + searchParameters: askAiConfig.searchParameters, + }); + + const suggestedQuestions: SuggestedQuestionHit[] = React.useMemo(() => { + const staticQuestions = askAiConfig.suggestedQuestions ?? []; + + return staticQuestions.map((question, index) => ({ + objectID: `suggested-question-${index}`, + question, + })); + }, [askAiConfig.suggestedQuestions]); + + const prevStatus = React.useRef(status); + React.useEffect(() => { + if (disableUserPersonalization) { + return; + } + // if we just transitioned from "streaming" → "ready", persist + if (prevStatus.current === 'streaming' && status === 'ready') { + // if we stopped the stream, store it on the most recent message + if (stoppedStream && messages.at(-1)) { + messages.at(-1)!.metadata = { + ...messages.at(-1)!.metadata, + stopped: true, + }; + } + + for (const part of messages[0].parts) { + if (part.type === 'text') { + conversations.add(buildDummyAskAiHit(part.text, messages, chatId)); + } + } + } + prevStatus.current = status; + }, [ + status, + messages, + conversations, + disableUserPersonalization, + stoppedStream, + chatId, + ]); + + const hasPromptBlockingError = React.useMemo(() => { + return ( + status === 'error' && + isAskAiPromptBlockingError(askAiError as Error | undefined) + ); + }, [status, askAiError]); + const shouldBlockPrompt = + hasPromptBlockingError && + (!isThreadDepthError(askAiError) || + messages.some((message) => message.role === 'assistant')); + + const promptBlockingChrome = React.useMemo(() => { + if ( + !shouldBlockPrompt || + askAiState === 'new-conversation' || + askAiState === 'conversation-history' + ) { + return undefined; + } + + return 'full' as const; + }, [askAiState, shouldBlockPrompt]); + + const saveRecentSearch = useSaveRecentSearch({ + favoriteSearches, + recentSearches, + disableUserPersonalization, + }); + const sendItemClickEvent = useSendItemClickEvent(state); + + const handleSelectAskAiQuestion = React.useCallback( + ( + toggle: boolean, + query: string, + suggestedQuestion: SuggestedQuestionHit | undefined = undefined + ) => { + if (toggle) { + const initialMessage: InitialAskAiMessage = { + query, + suggestedQuestionId: suggestedQuestion?.objectID, + }; + + if (interceptAskAiEvent?.(initialMessage)) { + // Consumer handled it. Avoid *all* default Ask AI behavior. + if (autocompleteRef.current) { + autocompleteRef.current.setQuery(''); + } + return; + } + } + + if (toggle && askAiState === 'new-conversation') { + setAskAiState('initial'); + } + + onAskAiToggle(toggle, { + query, + suggestedQuestionId: suggestedQuestion?.objectID, + }); + + // If we're in hybrid mode, we don't need to send the message, + // it will be handled by the Sidepanel. + if (isHybridModeSupported) return; + + setStoppedStream(false); + + sendMessage( + query, + suggestedQuestion + ? { suggestedQuestionId: suggestedQuestion.objectID } + : undefined + ).catch(noop); + + // clear the query + if (autocompleteRef.current) { + autocompleteRef.current.setQuery(''); + } + }, + [ + askAiState, + onAskAiToggle, + isHybridModeSupported, + sendMessage, + interceptAskAiEvent, + ] + ); + + if (!autocompleteRef.current) { + autocompleteRef.current = createAutocomplete({ + id: 'docsearch', + defaultActiveItemId: 0, + openOnFocus: true, + initialState: { + query: initialQuery, + context: { + searchSuggestions: [], + }, + }, + insights: Boolean(insights), + navigator, + onStateChange(changes) { + setState(changes.state); + }, + async getSources({ query, state: sourcesState, setContext, setStatus }) { + if (isQueryEmpty(query)) { + const noQuerySources = buildNoQuerySources({ + recentSearches, + favoriteSearches, + saveRecentSearch, + onClose, + disableUserPersonalization, + }); + + const recentConversationSource = canHandleAskAi + ? buildRecentConversationSources({ + conversations, + disableUserPersonalization, + setMessages, + onAskAiToggle, + }) + : []; + return [...recentConversationSource, ...noQuerySources]; + } + + const querySourcesState: BuildQuerySourcesState = { + context: sourcesState.context, + }; + + const keywordSourcesPromise = buildTypesenseQuerySources({ + query, + state: querySourcesState, + setContext, + setStatus, + searchClient, + typesenseCollectionName, + typesenseSearchParameters, + maxResultsPerGroup, + transformItems, + saveRecentSearch, + onClose, + facetSelections: facetSelectionsRef, + }); + + const askAiSources = canHandleAskAi + ? buildAskAiActionSources({ query, handleSelectAskAiQuestion }) + : []; + + const keywordSources = await keywordSourcesPromise; + + // Combine keyword results (once resolved) with the Ask AI source + return [...askAiSources, ...keywordSources]; + }, + }); + } + + const autocomplete = autocompleteRef.current; + + const { getEnvironmentProps, getRootProps, refresh } = autocomplete; + + useModalEnvironment({ + getEnvironmentProps, + containerRef, + dropdownRef, + formElementRef, + inputRef, + initialScrollY, + modalRef, + theme, + }); + + React.useEffect(() => { + if (dropdownRef.current && !isAskAiActive) { + scrollToUtils(dropdownRef.current); + } + }, [state.query, isAskAiActive, dropdownRef]); + + // Follow the conversation: when a new user message is added, scroll to it + const userMessageCount = messages.filter( + (message) => message.role === 'user' + ).length; + + // Auto-follow is pinned until the user scrolls up; scrolling back to the + // bottom re-pins it + const isPinnedToBottomRef = React.useRef(true); + + React.useEffect(() => { + const el = dropdownRef.current; + if (!el || !isAskAiActive) { + return undefined; + } + const handleWheel = (event: WheelEvent): void => { + if (event.deltaY < 0) { + isPinnedToBottomRef.current = false; + } + }; + const handleTouchMove = (): void => { + isPinnedToBottomRef.current = false; + }; + const handleScroll = (): void => { + if (el.scrollHeight - el.scrollTop - el.clientHeight < 80) { + isPinnedToBottomRef.current = true; + } + }; + el.addEventListener('wheel', handleWheel, { passive: true }); + el.addEventListener('touchmove', handleTouchMove, { passive: true }); + el.addEventListener('scroll', handleScroll, { passive: true }); + return () => { + el.removeEventListener('wheel', handleWheel); + el.removeEventListener('touchmove', handleTouchMove); + el.removeEventListener('scroll', handleScroll); + }; + }, [isAskAiActive, dropdownRef]); + + React.useEffect(() => { + if (!isAskAiActive || userMessageCount === 0 || !dropdownRef.current) { + return; + } + isPinnedToBottomRef.current = true; + const el = dropdownRef.current; + // some test environments (like jsdom) don't implement element.scrollTo + if (typeof el.scrollTo === 'function') { + el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' }); + } else { + // fallback for environments without scrollTo support + el.scrollTop = el.scrollHeight; + } + }, [userMessageCount, isAskAiActive, dropdownRef]); + + // Keep the newest tokens in view while the answer streams in + React.useEffect(() => { + const el = dropdownRef.current; + if ( + !isAskAiActive || + status !== 'streaming' || + messages.length === 0 || + !isPinnedToBottomRef.current || + !el + ) { + return; + } + el.scrollTop = el.scrollHeight; + }, [messages, status, isAskAiActive, dropdownRef]); + + useRefreshOnInitialQuery({ initialQuery, inputRef, refresh }); + + const hasCurrentMessages = messages.length > 0; + const previousIsAskAiActive = React.useRef(isAskAiActive); + + // Refresh the autocomplete results when ask ai is toggled off + // helps return to the previous ac state and start screen + React.useEffect(() => { + const wasAskAiActive = previousIsAskAiActive.current; + previousIsAskAiActive.current = isAskAiActive; + + if (!isAskAiActive) { + autocomplete.refresh(); + + // Reset only after leaving Ask AI, not when its first message arrives + // before the parent toggle update commits. + if (wasAskAiActive && hasCurrentMessages) { + startNewConversation(); + } + } + }, [isAskAiActive, autocomplete, startNewConversation, hasCurrentMessages]); + + // Track external state in order to manage internal askAiState + React.useEffect(() => { + setAskAiState('initial'); + }, [isAskAiActive, setAskAiState]); + + const onStopAskAiStreaming = async (): Promise => { + setStoppedStream(true); + + await stopAskAiStreaming(); + }; + + const handleNewConversation = (): void => { + startNewConversation(); + setAskAiState('new-conversation'); + }; + + const handleViewConversationHistory = (): void => { + setAskAiState('conversation-history'); + }; + + const selectSuggestedQuestion = ( + suggestedQuestion: SuggestedQuestionHit + ): void => { + handleSelectAskAiQuestion( + true, + suggestedQuestion.question, + suggestedQuestion + ); + }; + + // hide the dropdown on idle and no collections + let showDocsearchDropdown = true; + const hasCollections = state.collections.some( + (collection) => + collection.source.sourceId !== SOURCE_IDS.askAI && + collection.items.length > 0 + ); + if ( + state.status === 'idle' && + hasCollections === false && + isQueryEmpty(state.query) && + !isAskAiActive + ) { + showDocsearchDropdown = false; + } + + return ( + { + handleSelectAskAiQuestion(true, query); + }} + onStopAskAiStreaming={onStopAskAiStreaming} + onNewConversation={handleNewConversation} + onViewConversationHistory={handleViewConversationHistory} + /> + } + filterBar={ + !isAskAiActive && !isQueryEmpty(state.query) ? ( + + ) : null + } + screenState={ + { + if (item.type === 'askAI' && item.query) { + if (item.anchor === 'stored' && 'messages' in item) { + const hitMessages = item.messages as StoredAskAiMessage[]; + restoreConversation( + hitMessages, + (item as StoredAskAiState).chatId + ); + const initialMessage: InitialAskAiMessage = { + query: item.query, + messageId: hitMessages[0].id, + }; + + if (interceptAskAiEvent?.(initialMessage)) { + if (autocompleteRef.current) { + autocompleteRef.current.setQuery(''); + } + event.preventDefault(); + return; + } + + onAskAiToggle(true, initialMessage); + } else { + handleSelectAskAiQuestion(true, item.query); + } + setAskAiState('initial'); + event.preventDefault(); + return; + } + + sendItemClickEvent(item); + saveRecentSearch(item); + if (!isModifierEvent(event)) { + onClose(); + } + }} + /> + } + footer={ +