├── .circleci └── config.yml ├── .codesandbox └── ci.json ├── .editorconfig ├── .env.sample ├── .github ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ ├── Feature_request.md │ └── config.yml ├── logo.svg ├── preview-dark.png ├── preview-light.png ├── screencast.gif └── workflows │ └── renovate.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .stylelintrc.json ├── .yarn └── releases │ └── yarn-4.6.0.cjs ├── .yarnrc.yml ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bundlesize.config.json ├── cypress.config.ts ├── cypress ├── e2e │ └── search │ │ └── actions.spec.ts ├── support │ ├── commands.d.ts │ ├── commands.ts │ └── e2e.ts └── tsconfig.json ├── eslint.config.mjs ├── examples ├── demo-react-18 │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ └── vite.config.ts ├── demo │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ └── vite.config.ts └── js-demo │ ├── index.html │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── app.css │ ├── main.ts │ └── vite-env.d.ts │ └── tsconfig.json ├── global.d.ts ├── lerna.json ├── netlify.toml ├── package.json ├── packages ├── docsearch-css │ ├── README.md │ ├── build-css.js │ ├── package.json │ └── src │ │ ├── _variables.css │ │ ├── button.css │ │ └── modal.css ├── docsearch-js │ ├── README.md │ ├── babel.config.mjs │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── docsearch.tsx │ │ └── index.ts │ └── tsconfig.declaration.json ├── docsearch-react │ ├── README.md │ ├── babel.config.mjs │ ├── button.js │ ├── modal.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── AlgoliaLogo.tsx │ │ ├── DocSearch.tsx │ │ ├── DocSearchButton.tsx │ │ ├── DocSearchModal.tsx │ │ ├── ErrorScreen.tsx │ │ ├── Footer.tsx │ │ ├── Hit.tsx │ │ ├── NoResultsScreen.tsx │ │ ├── Results.tsx │ │ ├── ResultsScreen.tsx │ │ ├── ScreenState.tsx │ │ ├── SearchBox.tsx │ │ ├── Snippet.tsx │ │ ├── StartScreen.tsx │ │ ├── __tests__ │ │ │ └── api.test.tsx │ │ ├── constants.ts │ │ ├── icons │ │ │ ├── ControlKeyIcon.tsx │ │ │ ├── ErrorIcon.tsx │ │ │ ├── GoToExternalIcon.tsx │ │ │ ├── LoadingIcon.tsx │ │ │ ├── NoResultsIcon.tsx │ │ │ ├── RecentIcon.tsx │ │ │ ├── ResetIcon.tsx │ │ │ ├── SearchIcon.tsx │ │ │ ├── SelectIcon.tsx │ │ │ ├── SourceIcon.tsx │ │ │ ├── StarIcon.tsx │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── stored-searches.ts │ │ ├── types │ │ │ ├── DocSearchHit.ts │ │ │ ├── DocSearchState.ts │ │ │ ├── InternalDocSearchHit.ts │ │ │ ├── StoredDocSearchHit.ts │ │ │ └── index.ts │ │ ├── useDocSearchKeyboardEvents.ts │ │ ├── useSearchClient.ts │ │ ├── useTouchEvents.ts │ │ ├── useTrapFocus.ts │ │ ├── utils │ │ │ ├── groupBy.ts │ │ │ ├── identity.ts │ │ │ ├── index.ts │ │ │ ├── isModifierEvent.ts │ │ │ ├── noop.ts │ │ │ └── removeHighlightTags.ts │ │ └── version.ts │ ├── style │ │ ├── button.js │ │ ├── index.js │ │ ├── modal.js │ │ └── variables.js │ └── tsconfig.declaration.json └── website │ ├── .gitignore │ ├── README.md │ ├── babel.config.mjs │ ├── docs │ ├── api.mdx │ ├── crawler.mdx │ ├── docsearch-program.md │ ├── docsearch-v3.mdx │ ├── how-does-it-work.mdx │ ├── integrations.md │ ├── manage-your-crawls.mdx │ ├── migrating-from-legacy.mdx │ ├── migrating-from-v2.md │ ├── record-extractor.md │ ├── required-configuration.mdx │ ├── styling.md │ ├── templates.mdx │ ├── tips.md │ ├── what-is-docsearch.md │ └── who-can-apply.md │ ├── docusaurus.config.mjs │ ├── package.json │ ├── plugins │ ├── my-loaders.mjs │ └── tailwind-loader.mjs │ ├── sidebars.js │ ├── src │ ├── components │ │ ├── ApplyForm.js │ │ ├── DocSearchLogo.js │ │ ├── Home.js │ │ └── showcase-projects.json │ ├── css │ │ ├── custom.css │ │ └── fragments.css │ └── pages │ │ ├── apply.js │ │ └── index.js │ ├── static │ ├── .nojekyll │ ├── _redirects │ └── img │ │ ├── Algolia-logo-blue.svg │ │ ├── Algolia-logo-white.svg │ │ ├── assets │ │ ├── anatomy.svg │ │ ├── configuration.svg │ │ ├── crawler-editor.png │ │ ├── crawler-monitoring.png │ │ ├── crawler-overview.png │ │ ├── crawler-search-preview.png │ │ ├── crawler-url-tester.png │ │ ├── default-colorscheme.png │ │ ├── docsearch-how-it-works.png │ │ ├── docsearch-shadow-dark.png │ │ ├── docsearch-shadow.png │ │ ├── docsearch-ui-anatomy.png │ │ ├── implementation.svg │ │ ├── keyboard.png │ │ ├── new-crawler-creation.png │ │ ├── noResultsScreen.png │ │ ├── playground.png │ │ ├── recommended-layout.png │ │ └── scraping.svg │ │ ├── build_index │ │ ├── how_do_we_build_docsearch_index_1.png │ │ ├── how_do_we_build_docsearch_index_2.png │ │ ├── how_do_we_build_docsearch_index_3.png │ │ ├── how_do_we_build_docsearch_index_4.png │ │ ├── how_do_we_build_docsearch_index_5.png │ │ ├── how_do_we_build_docsearch_index_6.png │ │ ├── how_do_we_build_docsearch_index_7.png │ │ └── how_do_we_build_docsearch_index_8.png │ │ ├── docsearch-logo-white.svg │ │ ├── docsearch-logo.svg │ │ ├── docsearch-mark.svg │ │ ├── docsearch-x-algolia-logo-dark-mode.png │ │ ├── docsearch-x-algolia-logo-light-mode.png │ │ ├── favicon.ico │ │ ├── logo-small.png │ │ ├── logos │ │ ├── angular.png │ │ ├── ant-design.svg │ │ ├── astro-icon-light-gradient.svg │ │ ├── babel.jpg │ │ ├── backstage.jpeg │ │ ├── bootstrap-vue.svg │ │ ├── bootstrap.svg │ │ ├── calico.png │ │ ├── cern.png │ │ ├── cern_logo.jpeg │ │ ├── cheerio.svg │ │ ├── discord.svg │ │ ├── docusaurus.svg │ │ ├── eslint.svg │ │ ├── ethereum.png │ │ ├── expo.jpeg │ │ ├── express.png │ │ ├── flowbite.svg │ │ ├── formik.png │ │ ├── fylgja.svg │ │ ├── gatsby.png │ │ ├── gns3.png │ │ ├── gradle.png │ │ ├── graphql.jpg │ │ ├── home-assistant.svg │ │ ├── homebrew.png │ │ ├── hugo.png │ │ ├── jekyll.png │ │ ├── jest.png │ │ ├── jquery.jpg │ │ ├── laravel.svg │ │ ├── markdoc.svg │ │ ├── material-ui.svg │ │ ├── momentjs.svg │ │ ├── nestjs.png │ │ ├── react-admin.svg │ │ ├── react-bootstrap.svg │ │ ├── react-native.svg │ │ ├── react.jpg │ │ ├── reactnavigation.svg │ │ ├── remix.svg │ │ ├── requests.png │ │ ├── sass.png │ │ ├── scala.svg │ │ ├── scalar.svg │ │ ├── snap.png │ │ ├── socketio.svg │ │ ├── styled-components.png │ │ ├── tachiyomi.svg │ │ ├── tailwindcss.svg │ │ ├── testing-library.png │ │ ├── twilio.png │ │ ├── typescriptlang.svg │ │ ├── vant.png │ │ ├── vite.svg │ │ ├── vitepress.svg │ │ ├── vue.svg │ │ └── webpack.svg │ │ └── ressources │ │ ├── illus1.png │ │ ├── illus2.png │ │ └── illus3.png │ ├── tailwind.config.mjs │ ├── versioned_docs │ └── version-legacy │ │ ├── behavior.md │ │ ├── config-file.md │ │ ├── dropdown.md │ │ ├── faq.md │ │ ├── how-do-we-build-an-index.mdx │ │ ├── inside-the-engine.md │ │ ├── integrations.md │ │ ├── required-configuration.md │ │ ├── run-your-own.md │ │ ├── scraper-overview.md │ │ ├── styling.mdx │ │ └── tips.md │ ├── versioned_sidebars │ └── version-legacy-sidebars.json │ └── versions.json ├── renovate.json ├── rollup.base.config.js ├── scripts ├── getBundleBanner.js └── setupTests.ts ├── ship.config.mjs ├── tsconfig.declaration.json ├── tsconfig.json ├── vite.config.mts └── yarn.lock /.codesandbox/ci.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/docsearch-*"], 3 | "node": "16" 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | GITHUB_TOKEN= 2 | CYPRESS_RECORD_KEY= 3 | PERCY_TOKEN= -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Help us improve DocSearch. 4 | --- 5 | 6 | ## Description 7 | 8 | 9 | 10 | ## Steps to reproduce 11 | 12 | 1. Go to `...` 13 | 2. Click on `...` 14 | 3. Scroll down to `...` 15 | 4. See error 16 | 17 | 18 | 19 | **Live reproduction:** 20 | 21 | - JavaScript: https://codesandbox.io/s/github/algolia/docsearch/tree/main/examples/js-demo 22 | - React: https://codesandbox.io/s/github/algolia/docsearch/tree/main/examples/demo 23 | 24 | ## Expected behavior 25 | 26 | 27 | 28 | ## Environment 29 | 30 | - OS: [e.g. Windows / Linux / macOS / iOS / Android] 31 | - Browser: [e.g. Chrome, Safari] 32 | - DocSearch version: [e.g. 3.0.0] 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for DocSearch. 4 | --- 5 | 6 | ## Describe the problem 7 | 8 | 9 | 10 | ## Describe the solution 11 | 12 | 13 | 14 | ## Alternatives you've considered 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Documentation feedback 5 | url: https://github.com/algolia/docsearch/issues/new 6 | about: Share with us issues about the DocSearch documentation. 7 | 8 | - name: Apply for DocSearch 9 | url: https://docsearch.algolia.com/apply 10 | about: Apply to integrate DocSearch to your website. 11 | -------------------------------------------------------------------------------- /.github/preview-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/.github/preview-dark.png -------------------------------------------------------------------------------- /.github/preview-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/.github/preview-light.png -------------------------------------------------------------------------------- /.github/screencast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/.github/screencast.gif -------------------------------------------------------------------------------- /.github/workflows/renovate.yml: -------------------------------------------------------------------------------- 1 | name: Renovate 2 | 3 | on: 4 | schedule: 5 | - cron: '0 14 * * 5' # At 14:00 on Friday. 6 | workflow_dispatch: 7 | 8 | jobs: 9 | renovate: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Renovate Automatic Branch 13 | uses: bodinsamuel/renovate-automatic-branch@v1 14 | with: 15 | github-token: ${{ secrets.ALGOLIA_BOT_TOKEN }} 16 | repo-owner: algolia 17 | repo-name: docsearch 18 | branch-base: main 19 | pull-request-title: 'chore(deps): dependencies' 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | node_modules/ 3 | .DS_Store 4 | .eslintcache 5 | *.log 6 | coverage/ 7 | .cache 8 | 9 | # Bundle build files 10 | dist/ 11 | 12 | # Deployment build files 13 | /website/stories 14 | 15 | # IDE 16 | .vscode/ 17 | .idea 18 | 19 | # Environment files 20 | .env 21 | 22 | # Cypress Video and Screenshots output 23 | cypress/screenshots/ 24 | cypress/videos/ 25 | 26 | **/.yarn/* 27 | !**/.yarn/releases 28 | !**/.yarn/plugins 29 | !**/.yarn/patches 30 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18.20.5 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | build 4 | .cache 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "proseWrap": "never", 3 | "singleQuote": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "stylelint-prettier", 4 | "stylelint-no-unsupported-browser-features" 5 | ], 6 | "extends": [ 7 | "stylelint-config-standard", 8 | "stylelint-config-sass-guidelines", 9 | "stylelint-order", 10 | "stylelint-prettier/recommended" 11 | ], 12 | "rules": { 13 | "selector-class-pattern": [ 14 | "^DocSearch-[A-Za-z0-9-]*$" 15 | ], 16 | "prettier/prettier": true, 17 | "max-nesting-depth": [ 18 | 2, 19 | { 20 | "ignore": [ 21 | "pseudo-classes" 22 | ], 23 | "ignoreAtRules": [ 24 | "media" 25 | ] 26 | } 27 | ], 28 | "plugin/no-unsupported-browser-features": [ 29 | null, 30 | { 31 | "severity": "warning" 32 | } 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | enableGlobalCache: false 4 | 5 | yarnPath: .yarn/releases/yarn-4.6.0.cjs 6 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file list code owners for the repository, or part of it. 2 | # See https://help.github.com/articles/about-code-owners/ 3 | 4 | * @shortcuts @8bittitan @dylantientcheu @NatanTechofNY @vascobettencourt @pipeline1987 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to DocSearch 2 | 3 | Welcome to the contributing guide for DocSearch! 4 | 5 | If this guide does not contain what you are looking for and thus prevents you from contributing, don't hesitate to leave a message on the [Discord](https://discord.gg/bRTacwYrfX) or to [open an issue](https://github.com/algolia/docsearch/issues). 6 | 7 | ## Reporting an issue 8 | 9 | Opening an issue is very effective way to contribute because many users might also be impacted. We'll make sure to fix it quickly if it's technically feasible and doesn't have important side effects for other users. 10 | 11 | Before reporting an issue, first check that there is not an already open issue for the same topic using the [issues page](https://github.com/algolia/docsearch/issues). Don't hesitate to thumb up an issue that corresponds to the problem you have. 12 | 13 | Another element that will help us go faster at solving the issue is to provide a reproducible test case. We recommend to [use this CodeSandbox template](https://codesandbox.io/s/github/algolia/docsearch/tree/main/examples/demo). 14 | 15 | ## Code contribution 16 | 17 | For any code contribution, you need to: 18 | 19 | - Fork and clone the project 20 | - Create a new branch for what you want to solve (fix/_issue-number_, feat/_name-of-the-feature_) 21 | - Make your changes 22 | - Open a pull request 23 | 24 | Then: 25 | 26 | - A team member will review the pull request 27 | - Automatic checks will be run 28 | 29 | When every check is green and a team member approves, your contribution is merged! 🚀 30 | 31 | ## Commit conventions 32 | 33 | This project follows the [conventional changelog](https://conventionalcommits.org/) approach. This means that all commit messages should be formatted using the following scheme: 34 | 35 | ``` 36 | type(scope): description 37 | ``` 38 | 39 | In most cases, we use the following types: 40 | 41 | - `fix`: for any resolution of an issue (identified or not) 42 | - `feat`: for any new feature 43 | - `refactor`: for any code change that neither adds a feature nor fixes an issue 44 | - `docs`: for any documentation change or addition 45 | - `chore`: for anything that is not related to the library itself (doc, tooling) 46 | 47 | Even though the scope is optional, we try to fill it in as it helps us better understand the impact of a change. 48 | 49 | Finally, if your work is based on an issue on GitHub, please add in the body of the commit message "fix #1234" if it solves the issue #1234 (read "[Closing issues using keywords](https://help.github.com/en/articles/closing-issues-using-keywords)"). 50 | 51 | Some examples of valid commit messages (used as first lines): 52 | 53 | > - fix(searchbox): add `type` input property 54 | > - chore(deps): update dependency rollup-plugin-babel to v3.0.7 55 | > - fix(modal): increase default height 56 | > - docs(contributing): reword release section 57 | 58 | ## Requirements 59 | 60 | To run this project, you will need: 61 | 62 | - Node.js ≥ 18 – [nvm](https://github.com/nvm-sh/nvm#install-script) is recommended 63 | - [Yarn](https://yarnpkg.com) 64 | 65 | ## Release 66 | 67 | ```sh 68 | yarn run release 69 | ``` 70 | 71 | It will create a pull request for the next release. When it's reviewed, approved and merged, then CircleCI will automatically publish it to npm. 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present Algolia, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [![DocSearch](.github/logo.svg)](https://docsearch.algolia.com) 4 | 5 | The easiest way to add search to your documentation – for free. 6 | 7 | [![Netlify Status](https://api.netlify.com/api/v1/badges/30eacc09-d4b2-4a53-879b-04d40aaea454/deploy-status)](https://app.netlify.com/sites/docsearch/deploys) [![npm version](https://img.shields.io/npm/v/@docsearch/js.svg?style=flat-square)](https://www.npmjs.com/package/@docsearch/js/v/alpha) [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](./LICENSE) 8 | 9 |

10 | 11 | Documentation • 12 | JavaScript Playground • 13 | React Playground 14 | 15 |

16 | 17 |
18 | 19 | --- 20 | 21 | DocSearch crawls your documentation, pushes the content to an Algolia index and provides a dropdown search experience on your website. 22 | 23 | ## Preview 24 | 25 | ![Screencast](.github/screencast.gif) 26 | 27 | | Light | Dark | 28 | | --- | --- | 29 | | ![Light preview](.github/preview-light.png) | ![Dark preview](.github/preview-dark.png) | 30 | 31 | ## Usage 32 | 33 | > Don't have your Algolia credentials yet? [Apply to DocSearch](https://docsearch.algolia.com/apply)! 34 | 35 | ### JavaScript 36 | 37 | #### Installation 38 | 39 | ```sh 40 | yarn add @docsearch/js@3 41 | # or 42 | npm install @docsearch/js@3 43 | ``` 44 | 45 | If you don’t want to use a package manager, you can use a standalone endpoint: 46 | 47 | ```html 48 | 49 | ``` 50 | 51 | #### Get started 52 | 53 | To get started, you need a [`container`](https://docsearch.algolia.com/docs/api#container) for your DocSearch component to go in. If you don’t have one already, you can insert one into your markup: 54 | 55 | ```html 56 |
57 | ``` 58 | 59 | Then, insert DocSearch into it by calling the [`docsearch`](https://docsearch.algolia.com/docs/api) function and providing the container. It can be a [CSS selector](https://developer.mozilla.org/en-us/docs/web/css/css_selectors) or an [Element](https://developer.mozilla.org/en-us/docs/web/api/htmlelement). 60 | 61 | Make sure to provide a [`container`](https://docsearch.algolia.com/docs/api#container) (for example, a `div`), not an `input`. DocSearch generates a fully accessible search box for you. 62 | 63 | ```js app.js 64 | import docsearch from '@docsearch/js'; 65 | 66 | import '@docsearch/css'; 67 | 68 | docsearch({ 69 | container: '#docsearch', 70 | appId: 'YOUR_APP_ID', 71 | indexName: 'YOUR_INDEX_NAME', 72 | apiKey: 'YOUR_SEARCH_API_KEY', 73 | }); 74 | ``` 75 | 76 | ### React 77 | 78 | #### Installation 79 | 80 | ```bash 81 | yarn add @docsearch/react@3 82 | # or 83 | npm install @docsearch/react@3 84 | ``` 85 | 86 | If you don’t want to use a package manager, you can use a standalone endpoint: 87 | 88 | ```html 89 | 90 | ``` 91 | 92 | #### Get started 93 | 94 | DocSearch generates a fully accessible search box for you. 95 | 96 | ```jsx App.js 97 | import { DocSearch } from '@docsearch/react'; 98 | 99 | import '@docsearch/css'; 100 | 101 | function App() { 102 | return ( 103 | 108 | ); 109 | } 110 | 111 | export default App; 112 | ``` 113 | 114 | ## Styling 115 | 116 | [Read documentation →](https://docsearch.algolia.com/docs/styling) 117 | 118 | ## Related projects 119 | 120 | DocSearch is made of the following repositories: 121 | 122 | - **[algolia/docsearch](https://github.com/algolia/docsearch)**: DocSearch source code. 123 | - **[algolia/docsearch/packages/website](https://github.com/algolia/docsearch/tree/main/packages/website)**: DocSearch website and documentation. 124 | - **[algolia/docsearch-configs](https://github.com/algolia/docsearch-configs)**: DocSearch websites configurations that DocSearch powers. 125 | - **[algolia/docsearch-scraper](https://github.com/algolia/docsearch-scraper)**: DocSearch crawler that extracts data from your documentation. 126 | 127 | ## License 128 | 129 | [MIT](LICENSE) 130 | -------------------------------------------------------------------------------- /bundlesize.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "path": "packages/docsearch-css/dist/style.css", 5 | "maxSize": "3.25 kB" 6 | }, 7 | { 8 | "path": "packages/docsearch-react/dist/umd/index.js", 9 | "maxSize": "27 kB" 10 | }, 11 | { 12 | "path": "packages/docsearch-js/dist/umd/index.js", 13 | "maxSize": "35.5 kB" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'cypress'; 2 | 3 | export default defineConfig({ 4 | projectId: 'nf9rdc', 5 | e2e: { 6 | baseUrl: 'http://localhost:3000', 7 | specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}', 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /cypress/support/commands.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare namespace Cypress { 4 | interface Chainable { 5 | /** 6 | * Ensures the modal is visible and focused. 7 | */ 8 | modalIsVisibleAndFocused: () => void; 9 | /** 10 | * Ensures the modal not visible. 11 | */ 12 | modalIsNotVisible: () => void; 13 | /** 14 | * Toggles the dark mode on the preview website. 15 | */ 16 | darkmode: () => void; 17 | /** 18 | * Wait for the page to load. 19 | */ 20 | waitLoad: () => void; 21 | /** 22 | * Opens the DocSearch modal. 23 | */ 24 | openModal: () => void; 25 | /** 26 | * Closes the DocSearch modal. 27 | */ 28 | closeModal: () => void; 29 | /** 30 | * Search for a given query. 31 | */ 32 | search: (query: string) => void; 33 | /** 34 | * Types a query that returns results. 35 | */ 36 | typeQueryMatching: () => void; 37 | /** 38 | * Types a query that returns no results. 39 | */ 40 | typeQueryNotMatching: () => void; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | Cypress.Commands.add('modalIsVisibleAndFocused', () => { 2 | cy.get('.DocSearch-Modal', { timeout: 10000 }).should('be.visible'); 3 | cy.get('.DocSearch-Input').should('be.focus'); 4 | }); 5 | 6 | Cypress.Commands.add('modalIsNotVisible', () => { 7 | cy.get('body').should('not.have.class', 'DocSearch--active'); 8 | cy.get('.DocSearch-Modal').should('not.exist'); 9 | }); 10 | 11 | Cypress.Commands.add('darkmode', () => { 12 | cy.get('.react-toggle').click({ force: true }); 13 | cy.get('.react-toggle-screenreader-only').blur(); 14 | cy.get('html.dark').should('be.visible'); 15 | }); 16 | 17 | Cypress.Commands.add('waitLoad', () => { 18 | cy.get('.DocSearch-Button', { timeout: 10000 }).should('be.visible'); 19 | }); 20 | 21 | Cypress.Commands.add('openModal', () => { 22 | cy.get('.DocSearch-Button').should('be.visible').click(); 23 | cy.modalIsVisibleAndFocused(); 24 | }); 25 | 26 | Cypress.Commands.add('closeModal', () => { 27 | cy.get('body').type('{esc}'); 28 | cy.modalIsNotVisible(); 29 | }); 30 | 31 | Cypress.Commands.add('search', (query: string) => { 32 | cy.get('.DocSearch-Input').should('be.visible').type(query); 33 | }); 34 | 35 | Cypress.Commands.add('typeQueryMatching', () => { 36 | cy.search('g'); 37 | }); 38 | 39 | Cypress.Commands.add('typeQueryNotMatching', () => { 40 | cy.search('zzz'); 41 | }); 42 | -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["cypress"] 5 | }, 6 | "exclude": [] 7 | } 8 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import algolia from 'eslint-config-algolia/flat/base.js'; 2 | import algoliaReact from 'eslint-config-algolia/flat/react.js'; 3 | import algoliaTypescript from 'eslint-config-algolia/flat/typescript.js'; 4 | import cypresss from 'eslint-plugin-cypress/flat'; // eslint-disable-line import/no-unresolved 5 | import reactRefresh from 'eslint-plugin-react-refresh'; 6 | import tseslint from 'typescript-eslint'; // eslint-disable-line import/no-unresolved 7 | 8 | export default [ 9 | ...algolia, 10 | ...algoliaReact, 11 | ...algoliaTypescript, 12 | { 13 | ignores: ['**/node_modules/', '**/dist/', '**/build/', '.yarn/', '**/.docusaurus'], 14 | }, 15 | { 16 | plugins: { 17 | 'react-refresh': reactRefresh, 18 | }, 19 | languageOptions: { 20 | parserOptions: { 21 | projectService: true, 22 | }, 23 | }, 24 | settings: { 25 | react: { 26 | pragma: 'React', 27 | version: 'detect', 28 | }, 29 | 'import/resolver': { 30 | node: { 31 | extensions: ['.js', '.ts', '.tsx'], 32 | }, 33 | }, 34 | }, 35 | rules: { 36 | 'no-param-reassign': 0, 37 | 'valid-jsdoc': 0, 38 | 'no-shadow': 0, 39 | 'prefer-template': 0, 40 | 'react/prop-types': 0, 41 | 'react/no-unescaped-entities': 0, 42 | 'import/extensions': 0, 43 | 'no-unused-expressions': 0, 44 | complexity: 0, 45 | 'import/order': [ 46 | 'error', 47 | { 48 | alphabetize: { 49 | order: 'asc', 50 | caseInsensitive: true, 51 | }, 52 | 'newlines-between': 'always', 53 | groups: ['builtin', 'external', 'parent', 'sibling', 'index'], 54 | pathGroups: [ 55 | { 56 | pattern: '@/**/*', 57 | group: 'parent', 58 | position: 'before', 59 | }, 60 | ], 61 | pathGroupsExcludedImportTypes: ['builtin'], 62 | }, 63 | ], 64 | 65 | // TMP 66 | 'react/function-component-definition': ['off'], 67 | 'react/jsx-filename-extension': ['off'], 68 | 'jsdoc/check-examples': ['off'], 69 | }, 70 | }, 71 | { 72 | files: ['**/*.js'], 73 | }, 74 | tseslint.configs.disableTypeChecked, 75 | { 76 | files: ['cypress/**/*'], 77 | plugins: { 78 | cypresss, 79 | }, 80 | rules: { 81 | '@typescript-eslint/triple-slash-reference': 0, 82 | }, 83 | }, 84 | { 85 | files: ['packages/website/**/*'], 86 | rules: { 87 | 'import/no-unresolved': 0, 88 | 'import/no-extraneous-dependencies': 0, 89 | }, 90 | }, 91 | { 92 | files: ['examples/demo/**/*'], 93 | rules: { 94 | 'react/react-in-jsx-scope': 0, 95 | }, 96 | }, 97 | ]; 98 | -------------------------------------------------------------------------------- /examples/demo-react-18/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DocSearch v3 - React 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/demo-react-18/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/react-18-example", 3 | "description": "DocSearch v3 React 18 example", 4 | "version": "3.9.0", 5 | "private": true, 6 | "license": "MIT", 7 | "type": "module", 8 | "scripts": { 9 | "dev": "vite", 10 | "build": "tsc -b && vite build", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@docsearch/css": "workspace:*", 15 | "@docsearch/react": "workspace:*", 16 | "react": "^18.0.0", 17 | "react-dom": "^18.0.0" 18 | }, 19 | "devDependencies": { 20 | "@vitejs/plugin-react": "^4.3.4", 21 | "vite": "^6.0.7" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/demo-react-18/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/examples/demo-react-18/public/favicon.ico -------------------------------------------------------------------------------- /examples/demo-react-18/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 3 | Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 4 | } 5 | -------------------------------------------------------------------------------- /examples/demo-react-18/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { DocSearch } from '@docsearch/react'; 2 | import React from 'react'; 3 | import './App.css'; 4 | import '@docsearch/css/dist/style.css'; 5 | 6 | function App(): React.JSX.Element { 7 | return ( 8 |
9 |

DocSearch v3 - React - 18

10 | 11 |
12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /examples/demo-react-18/src/main.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/react-in-jsx-scope */ 2 | import { StrictMode } from 'react'; 3 | import { createRoot } from 'react-dom/client'; 4 | 5 | import App from './App.tsx'; 6 | 7 | createRoot(document.getElementById('root')!).render( 8 | 9 | 10 | , 11 | ); 12 | -------------------------------------------------------------------------------- /examples/demo-react-18/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/demo-react-18/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": [ 7 | "ES2020", 8 | "DOM", 9 | "DOM.Iterable" 10 | ], 11 | "module": "ESNext", 12 | "skipLibCheck": true, 13 | /* Bundler mode */ 14 | "moduleResolution": "Bundler", 15 | "allowImportingTsExtensions": true, 16 | "isolatedModules": true, 17 | "moduleDetection": "force", 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true, 25 | "noUncheckedSideEffectImports": true 26 | }, 27 | "include": [ 28 | "src", 29 | "vite.config.ts" 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /examples/demo-react-18/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import { defineConfig } from 'vite'; 3 | 4 | // https://vite.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /examples/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DocSearch v3 - React 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/react-example", 3 | "description": "DocSearch v3 React example", 4 | "version": "3.9.0", 5 | "private": true, 6 | "license": "MIT", 7 | "type": "module", 8 | "scripts": { 9 | "dev": "vite", 10 | "build": "tsc -b && vite build", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@docsearch/css": "3.9.0", 15 | "@docsearch/react": "3.9.0", 16 | "react": "^19.0.0", 17 | "react-dom": "^19.0.0" 18 | }, 19 | "devDependencies": { 20 | "@vitejs/plugin-react": "^4.3.4", 21 | "vite": "^6.0.7" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/examples/demo/public/favicon.ico -------------------------------------------------------------------------------- /examples/demo/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 3 | Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 4 | } 5 | -------------------------------------------------------------------------------- /examples/demo/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { DocSearch } from '@docsearch/react'; 2 | import type { JSX } from 'react'; 3 | import './App.css'; 4 | import '@docsearch/css/dist/style.css'; 5 | 6 | function App(): JSX.Element { 7 | return ( 8 |
9 |

DocSearch v3 - React

10 | 11 |
12 | ); 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /examples/demo/src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | 4 | import App from './App.tsx'; 5 | 6 | createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ); 11 | -------------------------------------------------------------------------------- /examples/demo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": [ 7 | "ES2020", 8 | "DOM", 9 | "DOM.Iterable" 10 | ], 11 | "module": "ESNext", 12 | "skipLibCheck": true, 13 | /* Bundler mode */ 14 | "moduleResolution": "Bundler", 15 | "allowImportingTsExtensions": true, 16 | "isolatedModules": true, 17 | "moduleDetection": "force", 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true, 25 | "noUncheckedSideEffectImports": true 26 | }, 27 | "include": [ 28 | "src", 29 | "vite.config.ts" 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /examples/demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react'; 2 | import { defineConfig } from 'vite'; 3 | 4 | // https://vite.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /examples/js-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DocSearch v3 - Vanilla JavaScript 9 | 10 | 11 | 12 |
13 |

DocSearch v3 - Vanilla JavaScript

14 | 15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /examples/js-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/js-example", 3 | "description": "DocSearch v3 Vanilla JavaScript example", 4 | "version": "3.9.0", 5 | "private": true, 6 | "license": "MIT", 7 | "scripts": { 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@docsearch/css": "3.9.0", 14 | "@docsearch/js": "3.9.0" 15 | }, 16 | "devDependencies": { 17 | "vite": "^6.0.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/js-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/examples/js-demo/public/favicon.ico -------------------------------------------------------------------------------- /examples/js-demo/src/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 3 | Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 4 | } 5 | -------------------------------------------------------------------------------- /examples/js-demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import docsearch from '@docsearch/js'; 2 | 3 | import './app.css'; 4 | import '@docsearch/css/dist/style.css'; 5 | 6 | docsearch({ 7 | container: '#docsearch', 8 | indexName: 'docsearch', 9 | appId: 'R2IYF7ETH7', 10 | apiKey: '599cec31baffa4868cae4e79f180729b', 11 | }); 12 | -------------------------------------------------------------------------------- /examples/js-demo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/js-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": [ 7 | "ES2020", 8 | "DOM", 9 | "DOM.Iterable" 10 | ], 11 | "skipLibCheck": true, 12 | /* Bundler mode */ 13 | "moduleResolution": "Bundler", 14 | "allowImportingTsExtensions": true, 15 | "isolatedModules": true, 16 | "moduleDetection": "force", 17 | "noEmit": true, 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noUncheckedSideEffectImports": true 24 | }, 25 | "include": [ 26 | "src", 27 | "vite.config.ts" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/naming-convention 2 | declare const __DEV__: boolean; 3 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*", 4 | "examples/*" 5 | ], 6 | "version": "3.9.0", 7 | "npmClient": "yarn" 8 | } -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command="yarn build && yarn website:build" 3 | publish="packages/website/build" 4 | ignore="git diff --quiet $COMMIT_REF $CACHED_COMMIT_REF -- packages/website/ yarn.lock" 5 | 6 | [build.environment] 7 | YARN_VERSION = "1.22.5" 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/monorepo", 3 | "private": true, 4 | "workspaces": [ 5 | "packages/*", 6 | "examples/*" 7 | ], 8 | "scripts": { 9 | "build:clean": "lerna run build:clean", 10 | "build:types": "lerna run build:types", 11 | "build": "lerna run build", 12 | "cy:clean": "rm -rf cypress/screenshots", 13 | "cy:info": "cypress info", 14 | "cy:run:chrome": "yarn run cy:run --browser chrome", 15 | "cy:run:edge": "yarn run cy:run --browser edge", 16 | "cy:run:firefox": "yarn run cy:run --browser firefox", 17 | "cy:run": "start-server-and-test 'yarn website:test' http://localhost:3000 'cypress run --headless'", 18 | "cy:verify": "cypress verify", 19 | "lint:css": "stylelint **/src/**/*.css", 20 | "lint": "eslint .", 21 | "playground:build": "yarn workspace @docsearch/react-example build", 22 | "playground:start": "yarn workspace @docsearch/react-example dev --host", 23 | "playground-js:start": "yarn workspace @docsearch/js-example dev --host", 24 | "release": "shipjs prepare", 25 | "start": "yarn run watch", 26 | "test:size": "bundlesize", 27 | "test:types": "tsc --noEmit", 28 | "test": "vitest", 29 | "watch": "lerna run watch --parallel", 30 | "website:build": "yarn workspace @docsearch/website build", 31 | "website:start": "yarn workspace @docsearch/website start --host 0.0.0.0", 32 | "website:test": "yarn workspace @docsearch/website start --no-open" 33 | }, 34 | "devDependencies": { 35 | "@babel/plugin-transform-react-jsx": "7.25.9", 36 | "@babel/preset-env": "7.26.0", 37 | "@babel/preset-typescript": "7.26.0", 38 | "@eslint-community/eslint-plugin-eslint-comments": "4.4.1", 39 | "@rollup/plugin-babel": "6.0.4", 40 | "@rollup/plugin-json": "6.1.0", 41 | "@rollup/plugin-node-resolve": "16.0.0", 42 | "@rollup/plugin-replace": "6.0.2", 43 | "@rollup/plugin-terser": "0.4.4", 44 | "@stylistic/eslint-plugin": "2.13.0", 45 | "@testing-library/dom": "10.4.0", 46 | "@types/react": "^19.0.0", 47 | "@types/react-dom": "^19.0.0", 48 | "@typescript-eslint/eslint-plugin": "8.20.0", 49 | "@typescript-eslint/parser": "8.20.0", 50 | "babel-plugin-module-resolver": "5.0.2", 51 | "bundlesize2": "0.0.32", 52 | "cssnano": "7.0.6", 53 | "cypress": "13.17.0", 54 | "eslint": "8.57.1", 55 | "eslint-config-algolia": "23.2.1", 56 | "eslint-config-prettier": "10.0.1", 57 | "eslint-plugin-cypress": "4.1.0", 58 | "eslint-plugin-import": "2.31.0", 59 | "eslint-plugin-jsdoc": "50.6.2", 60 | "eslint-plugin-jsx-a11y": "6.10.2", 61 | "eslint-plugin-prettier": "5.2.3", 62 | "eslint-plugin-react": "7.37.4", 63 | "eslint-plugin-react-hooks": "5.1.0", 64 | "eslint-plugin-react-refresh": "0.4.18", 65 | "jsdom": "26.0.0", 66 | "lerna": "8.1.9", 67 | "postcss": "8.5.1", 68 | "prettier": "3.4.2", 69 | "react": "^19.0.0", 70 | "react-dom": "^19.0.0", 71 | "rollup": "4.31.0", 72 | "rollup-plugin-dts": "6.1.1", 73 | "rollup-plugin-filesize": "10.0.0", 74 | "shipjs": "0.27.0", 75 | "start-server-and-test": "2.0.10", 76 | "stylelint": "16.13.2", 77 | "stylelint-config-sass-guidelines": "12.1.0", 78 | "stylelint-config-standard": "36.0.1", 79 | "stylelint-no-unsupported-browser-features": "8.0.2", 80 | "stylelint-order": "6.0.4", 81 | "stylelint-prettier": "5.0.2", 82 | "typescript": "5.7.3", 83 | "typescript-eslint": "8.20.0", 84 | "vitest": "3.0.2", 85 | "watch": "1.0.2" 86 | }, 87 | "packageManager": "yarn@4.6.0" 88 | } 89 | -------------------------------------------------------------------------------- /packages/docsearch-css/README.md: -------------------------------------------------------------------------------- 1 | # @docsearch/css 2 | 3 | Style package for [DocSearch](http://docsearch.algolia.com/), the best search experience for docs. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | yarn add @docsearch/css@3 9 | # or 10 | npm install @docsearch/css@3 11 | ``` 12 | 13 | If you don’t want to use a package manager, you can use a standalone endpoint: 14 | 15 | ```html 16 | 17 | ``` 18 | 19 | ## Get started 20 | 21 | ```js 22 | import '@docsearch/css'; 23 | ``` 24 | 25 | ## Documentation 26 | 27 | [Read documentation →](https://docsearch.algolia.com/docs/styling) 28 | -------------------------------------------------------------------------------- /packages/docsearch-css/build-css.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | /* eslint-disable import/no-commonjs, import/no-extraneous-dependencies */ 3 | 4 | const { execSync } = require('child_process'); 5 | const fs = require('fs'); 6 | const util = require('util'); 7 | 8 | const cssnano = require('cssnano'); 9 | const postcss = require('postcss'); 10 | 11 | const pkg = require('./package.json'); 12 | 13 | const readFile = util.promisify(fs.readFile); 14 | 15 | function getBundleBanner(_pkg) { 16 | const lastCommitHash = execSync('git rev-parse --short HEAD').toString().trim(); 17 | const version = process.env.SHIPJS ? _pkg.version : `${_pkg.version} (UNRELEASED ${lastCommitHash})`; 18 | const authors = '© Algolia, Inc. and contributors'; 19 | 20 | return `/*! ${_pkg.name} ${version} | MIT License | ${authors} | ${_pkg.homepage} */`; 21 | } 22 | 23 | function build({ input, output, banner }) { 24 | fs.readFile(input, (error, css) => { 25 | if (error) { 26 | throw error; 27 | } 28 | 29 | postcss([cssnano]) 30 | .process(css, { from: input, to: output }) 31 | .then((result) => { 32 | fs.writeFile(output, [banner, result.css].join('\n'), () => true); 33 | }); 34 | }); 35 | } 36 | 37 | build({ 38 | input: 'src/_variables.css', 39 | output: 'dist/_variables.css', 40 | banner: getBundleBanner({ ...pkg, name: `${pkg.name} Variables` }), 41 | }); 42 | build({ 43 | input: 'src/button.css', 44 | output: 'dist/button.css', 45 | banner: getBundleBanner({ ...pkg, name: `${pkg.name} Button` }), 46 | }); 47 | build({ 48 | input: 'src/modal.css', 49 | output: 'dist/modal.css', 50 | banner: getBundleBanner({ ...pkg, name: `${pkg.name} Modal` }), 51 | }); 52 | 53 | async function buildStyle() { 54 | const variablesCss = await readFile('src/_variables.css'); 55 | const buttonCss = await readFile('src/button.css'); 56 | const modalCss = await readFile('src/modal.css'); 57 | 58 | const variablesOutput = await postcss([cssnano]).process(variablesCss, { 59 | from: undefined, 60 | }); 61 | const buttonOutput = await postcss([cssnano]).process(buttonCss, { 62 | from: undefined, 63 | }); 64 | const modalOutput = await postcss([cssnano]).process(modalCss, { 65 | from: undefined, 66 | }); 67 | 68 | fs.writeFile( 69 | 'dist/style.css', 70 | [getBundleBanner(pkg), [variablesOutput.css, buttonOutput.css, modalOutput.css].join('')].join('\n'), 71 | () => true, 72 | ); 73 | 74 | fs.writeFile( 75 | 'dist/style.scss', 76 | [getBundleBanner(pkg), [variablesOutput.css, buttonOutput.css, modalOutput.css].join('')].join('\n'), 77 | () => true, 78 | ); 79 | } 80 | 81 | buildStyle(); 82 | -------------------------------------------------------------------------------- /packages/docsearch-css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/css", 3 | "description": "Styles for DocSearch.", 4 | "version": "3.9.0", 5 | "license": "MIT", 6 | "homepage": "https://docsearch.algolia.com", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/algolia/docsearch.git", 10 | "directory": "packages/docsearch-css" 11 | }, 12 | "author": { 13 | "name": "Algolia, Inc.", 14 | "url": "https://www.algolia.com" 15 | }, 16 | "files": [ 17 | "dist/" 18 | ], 19 | "main": "dist/style.css", 20 | "unpkg": "dist/style.css", 21 | "jsdelivr": "dist/style.css", 22 | "scripts": { 23 | "build:clean": "rm -rf ./dist", 24 | "build:css": "node build-css.js", 25 | "build": "yarn build:clean && mkdir dist && yarn build:css", 26 | "watch": "nodemon --watch 'src/**/*.css' --exec 'yarn build:css' --ext css" 27 | }, 28 | "devDependencies": { 29 | "nodemon": "^3.1.9" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/docsearch-css/src/_variables.css: -------------------------------------------------------------------------------- 1 | /* Variables */ 2 | 3 | :root { 4 | --docsearch-primary-color: rgb(84, 104, 255); 5 | --docsearch-text-color: rgb(28, 30, 33); 6 | --docsearch-spacing: 12px; 7 | --docsearch-icon-stroke-width: 1.4; 8 | --docsearch-highlight-color: var(--docsearch-primary-color); 9 | --docsearch-muted-color: rgb(150, 159, 175); 10 | --docsearch-container-background: rgba(101, 108, 133, 0.8); 11 | --docsearch-logo-color: rgba(84, 104, 255); 12 | 13 | /* modal */ 14 | --docsearch-modal-width: 560px; 15 | --docsearch-modal-height: 600px; 16 | --docsearch-modal-background: rgb(245, 246, 247); 17 | --docsearch-modal-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.5), 18 | 0 3px 8px 0 rgba(85, 90, 100, 1); 19 | 20 | /* searchbox */ 21 | --docsearch-searchbox-height: 56px; 22 | --docsearch-searchbox-background: rgb(235, 237, 240); 23 | --docsearch-searchbox-focus-background: #fff; 24 | --docsearch-searchbox-shadow: inset 0 0 0 2px var(--docsearch-primary-color); 25 | 26 | /* hit */ 27 | --docsearch-hit-height: 56px; 28 | --docsearch-hit-color: rgb(68, 73, 80); 29 | --docsearch-hit-active-color: #fff; 30 | --docsearch-hit-background: #fff; 31 | --docsearch-hit-shadow: 0 1px 3px 0 rgb(212, 217, 225); 32 | 33 | /* key */ 34 | --docsearch-key-gradient: linear-gradient( 35 | -225deg, 36 | rgb(213, 219, 228) 0%, 37 | rgb(248, 248, 248) 100% 38 | ); 39 | --docsearch-key-shadow: inset 0 -2px 0 0 rgb(205, 205, 230), 40 | inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4); 41 | --docsearch-key-pressed-shadow: inset 0 -2px 0 0 #cdcde6, 42 | inset 0 0 1px 1px #fff, 0 1px 1px 0 rgba(30, 35, 90, 0.4); 43 | 44 | /* footer */ 45 | --docsearch-footer-height: 44px; 46 | --docsearch-footer-background: #fff; 47 | --docsearch-footer-shadow: 0 -1px 0 0 rgb(224, 227, 232), 48 | 0 -3px 6px 0 rgba(69, 98, 155, 0.12); 49 | } 50 | 51 | /* Darkmode */ 52 | 53 | html[data-theme='dark'] { 54 | --docsearch-text-color: rgb(245, 246, 247); 55 | --docsearch-container-background: rgba(9, 10, 17, 0.8); 56 | --docsearch-modal-background: rgb(21, 23, 42); 57 | --docsearch-modal-shadow: inset 1px 1px 0 0 rgb(44, 46, 64), 58 | 0 3px 8px 0 rgb(0, 3, 9); 59 | --docsearch-searchbox-background: rgb(9, 10, 17); 60 | --docsearch-searchbox-focus-background: #000; 61 | --docsearch-hit-color: rgb(190, 195, 201); 62 | --docsearch-hit-shadow: none; 63 | --docsearch-hit-background: rgb(9, 10, 17); 64 | --docsearch-key-gradient: linear-gradient( 65 | -26.5deg, 66 | rgb(86, 88, 114) 0%, 67 | rgb(49, 53, 91) 100% 68 | ); 69 | --docsearch-key-shadow: inset 0 -2px 0 0 rgb(40, 45, 85), 70 | inset 0 0 1px 1px rgb(81, 87, 125), 0 2px 2px 0 rgba(3, 4, 9, 0.3); 71 | --docsearch-key-pressed-shadow: inset 0 -2px 0 0 #282d55, 72 | inset 0 0 1px 1px #51577d, 0 1px 1px 0 #0304094d; 73 | --docsearch-footer-background: rgb(30, 33, 54); 74 | --docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 75 | 0 -4px 8px 0 rgba(0, 0, 0, 0.2); 76 | --docsearch-logo-color: rgb(255, 255, 255); 77 | --docsearch-muted-color: rgb(127, 132, 151); 78 | } 79 | -------------------------------------------------------------------------------- /packages/docsearch-css/src/button.css: -------------------------------------------------------------------------------- 1 | .DocSearch-Button { 2 | align-items: center; 3 | background: var(--docsearch-searchbox-background); 4 | border: 0; 5 | border-radius: 40px; 6 | color: var(--docsearch-muted-color); 7 | cursor: pointer; 8 | display: flex; 9 | font-weight: 500; 10 | height: 36px; 11 | justify-content: space-between; 12 | margin: 0 0 0 16px; 13 | padding: 0 8px; 14 | user-select: none; 15 | } 16 | 17 | .DocSearch-Button:hover, 18 | .DocSearch-Button:active, 19 | .DocSearch-Button:focus { 20 | background: var(--docsearch-searchbox-focus-background); 21 | box-shadow: var(--docsearch-searchbox-shadow); 22 | color: var(--docsearch-text-color); 23 | outline: none; 24 | } 25 | 26 | .DocSearch-Button-Container { 27 | align-items: center; 28 | display: flex; 29 | } 30 | 31 | .DocSearch-Search-Icon { 32 | stroke-width: 1.6; 33 | } 34 | 35 | .DocSearch-Button .DocSearch-Search-Icon { 36 | color: var(--docsearch-text-color); 37 | } 38 | 39 | .DocSearch-Button-Placeholder { 40 | font-size: 1rem; 41 | padding: 0 12px 0 6px; 42 | } 43 | 44 | .DocSearch-Button-Keys { 45 | display: flex; 46 | min-width: calc(2 * 20px + 2 * 0.4em); 47 | } 48 | 49 | .DocSearch-Button-Key { 50 | align-items: center; 51 | background: var(--docsearch-key-gradient); 52 | border-radius: 3px; 53 | box-shadow: var(--docsearch-key-shadow); 54 | color: var(--docsearch-muted-color); 55 | display: flex; 56 | height: 18px; 57 | justify-content: center; 58 | margin-right: 0.4em; 59 | position: relative; 60 | padding: 0 0 2px; 61 | border: 0; 62 | top: -1px; 63 | width: 20px; 64 | transition-property: all; 65 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 66 | transition-duration: 100ms; 67 | } 68 | 69 | @media (prefers-reduced-motion) { 70 | .DocSearch-Button-Key { 71 | transition: none; 72 | } 73 | } 74 | 75 | .DocSearch-Button-Key--pressed { 76 | transform: translate3d(0, 1px, 0); 77 | box-shadow: var(--docsearch-key-pressed-shadow); 78 | } 79 | 80 | @media (max-width: 768px) { 81 | .DocSearch-Button-Keys, 82 | .DocSearch-Button-Placeholder { 83 | display: none; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/docsearch-js/README.md: -------------------------------------------------------------------------------- 1 | # @docsearch/js 2 | 3 | JavaScript package for [DocSearch](http://docsearch.algolia.com/), the best search experience for docs. 4 | 5 | ## Installation 6 | 7 | ```sh 8 | yarn add @docsearch/js@3 9 | # or 10 | npm install @docsearch/js@3 11 | ``` 12 | 13 | ## Get started 14 | 15 | If you don’t want to use a package manager, you can use a standalone endpoint: 16 | 17 | ```html 18 | 19 | ``` 20 | 21 | To get started, you need a [`container`](https://docsearch.algolia.com/docs/api#container) for your DocSearch component to go in. If you don’t have one already, you can insert one into your markup: 22 | 23 | ```html 24 |
25 | ``` 26 | 27 | Then, insert DocSearch into it by calling the [`docsearch`](https://docsearch.algolia.com/docs/api) function and providing the container. It can be a [CSS selector](https://developer.mozilla.org/en-us/docs/web/css/css_selectors) or an [Element](https://developer.mozilla.org/en-us/docs/web/api/htmlelement). 28 | 29 | Make sure to provide a [`container`](https://docsearch.algolia.com/docs/api#container) (for example, a `div`), not an `input`. DocSearch generates a fully accessible search box for you. 30 | 31 | ```js app.js 32 | import docsearch from '@docsearch/js'; 33 | 34 | import '@docsearch/css'; 35 | 36 | docsearch({ 37 | container: '#docsearch', 38 | appId: 'YOUR_APP_ID', 39 | indexName: 'YOUR_INDEX_NAME', 40 | apiKey: 'YOUR_SEARCH_API_KEY', 41 | }); 42 | ``` 43 | 44 | ## Documentation 45 | 46 | [Read documentation →](https://docsearch.algolia.com/docs/docsearch-v3) 47 | -------------------------------------------------------------------------------- /packages/docsearch-js/babel.config.mjs: -------------------------------------------------------------------------------- 1 | export default (api) => { 2 | const isTest = api.env('test'); 3 | const targets = {}; 4 | 5 | if (isTest) { 6 | targets.node = true; 7 | } else { 8 | targets.browsers = ['last 2 versions', 'ie >= 11']; 9 | } 10 | 11 | return { 12 | presets: [ 13 | '@babel/preset-typescript', 14 | [ 15 | '@babel/preset-env', 16 | { 17 | targets, 18 | }, 19 | ], 20 | ], 21 | plugins: [ 22 | ['@babel/plugin-transform-react-jsx'], 23 | [ 24 | 'module-resolver', 25 | { 26 | root: ['./src'], 27 | alias: { 28 | react: 'preact/compat', 29 | 'react-dom': 'preact/compat', 30 | }, 31 | }, 32 | ], 33 | ], 34 | }; 35 | }; 36 | -------------------------------------------------------------------------------- /packages/docsearch-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/js", 3 | "description": "JavaScript package for DocSearch, the best search experience for docs.", 4 | "version": "3.9.0", 5 | "license": "MIT", 6 | "homepage": "https://docsearch.algolia.com", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/algolia/docsearch.git", 10 | "directory": "packages/docsearch-js" 11 | }, 12 | "author": { 13 | "name": "Algolia, Inc.", 14 | "url": "https://www.algolia.com" 15 | }, 16 | "sideEffects": false, 17 | "files": [ 18 | "dist/" 19 | ], 20 | "source": "src/index.ts", 21 | "types": "dist/esm/index.d.ts", 22 | "module": "dist/esm/index.js", 23 | "main": "dist/umd/index.js", 24 | "umd:main": "dist/umd/index.js", 25 | "unpkg": "dist/umd/index.js", 26 | "jsdelivr": "dist/umd/index.js", 27 | "scripts": { 28 | "build:clean": "rm -rf ./dist", 29 | "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm/types", 30 | "build:clean-types": "rm -rf ./dist/esm/types", 31 | "build": "yarn build:clean && yarn build:types && rollup --config --bundleConfigAsCjs && yarn build:clean-types", 32 | "on:change": "yarn build", 33 | "watch": "nodemon --watch src --exec \"yarn on:change\" --ignore dist/ --ext ts,tsx" 34 | }, 35 | "dependencies": { 36 | "@docsearch/react": "3.9.0", 37 | "preact": "^10.0.0" 38 | }, 39 | "devDependencies": { 40 | "@rollup/plugin-replace": "6.0.2", 41 | "nodemon": "^3.1.9" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/docsearch-js/rollup.config.js: -------------------------------------------------------------------------------- 1 | import replace from '@rollup/plugin-replace'; 2 | 3 | import { plugins, typesConfig } from '../../rollup.base.config'; 4 | import { getBundleBanner } from '../../scripts/getBundleBanner'; 5 | 6 | import pkg from './package.json'; 7 | 8 | export default [ 9 | { 10 | input: 'src/index.ts', 11 | output: [ 12 | { 13 | file: 'dist/umd/index.js', 14 | format: 'umd', 15 | sourcemap: true, 16 | name: 'docsearch', 17 | banner: getBundleBanner(pkg), 18 | }, 19 | { 20 | file: 'dist/esm/index.js', 21 | format: 'es', 22 | sourcemap: true, 23 | banner: getBundleBanner(pkg), 24 | plugins: [...plugins], 25 | }, 26 | ], 27 | plugins: [ 28 | ...plugins, 29 | replace({ 30 | preventAssignment: true, 31 | 'process.env.NODE_ENV': JSON.stringify('production'), 32 | }), 33 | ], 34 | }, 35 | typesConfig, 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/docsearch-js/src/docsearch.tsx: -------------------------------------------------------------------------------- 1 | import type { DocSearchProps as DocSearchComponentProps } from '@docsearch/react'; 2 | import { DocSearch, version } from '@docsearch/react'; 3 | import React, { render } from 'preact/compat'; 4 | 5 | function getHTMLElement(value: HTMLElement | string, environment: DocSearchProps['environment'] = window): HTMLElement { 6 | if (typeof value === 'string') { 7 | return environment.document.querySelector(value)!; 8 | } 9 | 10 | return value; 11 | } 12 | 13 | interface DocSearchProps extends DocSearchComponentProps { 14 | container: HTMLElement | string; 15 | environment?: typeof window; 16 | } 17 | 18 | export function docsearch(props: DocSearchProps): void { 19 | render( 20 | { 23 | searchClient.addAlgoliaAgent('docsearch.js', version); 24 | 25 | return props.transformSearchClient ? props.transformSearchClient(searchClient) : searchClient; 26 | }} 27 | />, 28 | getHTMLElement(props.container, props.environment), 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/docsearch-js/src/index.ts: -------------------------------------------------------------------------------- 1 | export { docsearch as default } from './docsearch'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-js/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.declaration" 3 | } 4 | -------------------------------------------------------------------------------- /packages/docsearch-react/README.md: -------------------------------------------------------------------------------- 1 | # @docsearch/react 2 | 3 | React package for [DocSearch](http://docsearch.algolia.com/), the best search experience for docs. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | yarn add @docsearch/react@3 9 | # or 10 | npm install @docsearch/react@3 11 | ``` 12 | 13 | If you don’t want to use a package manager, you can use a standalone endpoint: 14 | 15 | ```html 16 | 17 | ``` 18 | 19 | ## Get started 20 | 21 | DocSearch generates a fully accessible search box for you. 22 | 23 | ```jsx App.js 24 | import { DocSearch } from '@docsearch/react'; 25 | 26 | import '@docsearch/css'; 27 | 28 | function App() { 29 | return ( 30 | 35 | ); 36 | } 37 | 38 | export default App; 39 | ``` 40 | 41 | ## Documentation 42 | 43 | [Read documentation →](https://docsearch.algolia.com/docs/docsearch-v3) 44 | -------------------------------------------------------------------------------- /packages/docsearch-react/babel.config.mjs: -------------------------------------------------------------------------------- 1 | export default (api) => { 2 | const isTest = api.env('test'); 3 | const targets = {}; 4 | 5 | if (isTest) { 6 | targets.node = true; 7 | } else { 8 | targets.browsers = ['last 2 versions', 'ie >= 9']; 9 | } 10 | 11 | return { 12 | presets: [ 13 | '@babel/preset-typescript', 14 | [ 15 | '@babel/preset-env', 16 | { 17 | targets, 18 | }, 19 | ], 20 | ], 21 | plugins: [['@babel/plugin-transform-react-jsx']], 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/docsearch-react/button.js: -------------------------------------------------------------------------------- 1 | export { DocSearchButton } from './dist/esm'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/modal.js: -------------------------------------------------------------------------------- 1 | export { DocSearchModal } from './dist/esm'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/react", 3 | "description": "React package for DocSearch, the best search experience for docs.", 4 | "version": "3.9.0", 5 | "license": "MIT", 6 | "homepage": "https://docsearch.algolia.com", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/algolia/docsearch.git", 10 | "directory": "packages/docsearch-react" 11 | }, 12 | "author": { 13 | "name": "Algolia, Inc.", 14 | "url": "https://www.algolia.com" 15 | }, 16 | "sideEffects": false, 17 | "files": [ 18 | "dist/", 19 | "style/", 20 | "button.js", 21 | "modal.js" 22 | ], 23 | "source": "src/index.ts", 24 | "types": "dist/esm/index.d.ts", 25 | "module": "dist/esm/index.js", 26 | "main": "dist/umd/index.js", 27 | "umd:main": "dist/umd/index.js", 28 | "unpkg": "dist/umd/index.js", 29 | "jsdelivr": "dist/umd/index.js", 30 | "scripts": { 31 | "build:clean": "rm -rf ./dist", 32 | "build:clean-types": "rm -rf ./dist/esm/types", 33 | "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm/types", 34 | "build": "yarn build:clean && yarn build:types && rollup --config --bundleConfigAsCjs && yarn build:clean-types", 35 | "on:change": "yarn build", 36 | "watch": "nodemon --watch src --ext ts,tsx,js,jsx,json --ignore dist/ --ignore node_modules/ --verbose --delay 250ms --exec \"yarn on:change\"" 37 | }, 38 | "dependencies": { 39 | "@algolia/autocomplete-core": "1.18.1", 40 | "@algolia/autocomplete-preset-algolia": "1.18.1", 41 | "@docsearch/css": "3.9.0", 42 | "algoliasearch": "^5.14.2" 43 | }, 44 | "devDependencies": { 45 | "@rollup/plugin-replace": "6.0.2", 46 | "@testing-library/jest-dom": "6.6.3", 47 | "@testing-library/react": "16.2.0", 48 | "nodemon": "^3.1.0", 49 | "vitest": "3.0.2" 50 | }, 51 | "peerDependencies": { 52 | "@types/react": ">= 16.8.0 < 20.0.0", 53 | "react": ">= 16.8.0 < 20.0.0", 54 | "react-dom": ">= 16.8.0 < 20.0.0", 55 | "search-insights": ">= 1 < 3" 56 | }, 57 | "peerDependenciesMeta": { 58 | "@types/react": { 59 | "optional": true 60 | }, 61 | "react": { 62 | "optional": true 63 | }, 64 | "react-dom": { 65 | "optional": true 66 | }, 67 | "search-insights": { 68 | "optional": true 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/docsearch-react/rollup.config.js: -------------------------------------------------------------------------------- 1 | import replace from '@rollup/plugin-replace'; 2 | 3 | import { plugins, typesConfig } from '../../rollup.base.config'; 4 | import { getBundleBanner } from '../../scripts/getBundleBanner'; 5 | 6 | import pkg from './package.json'; 7 | 8 | export default [ 9 | { 10 | input: 'src/index.ts', 11 | external: ['react', 'react-dom'], 12 | output: [ 13 | { 14 | globals: { 15 | react: 'React', 16 | 'react-dom': 'ReactDOM', 17 | }, 18 | file: 'dist/umd/index.js', 19 | format: 'umd', 20 | sourcemap: true, 21 | name: pkg.name, 22 | banner: getBundleBanner(pkg), 23 | }, 24 | { dir: 'dist/esm', format: 'es' }, 25 | ], 26 | plugins: [ 27 | ...plugins, 28 | replace({ 29 | preventAssignment: true, 30 | 'process.env.NODE_ENV': JSON.stringify('production'), 31 | }), 32 | ], 33 | }, 34 | typesConfig, 35 | ]; 36 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/DocSearch.tsx: -------------------------------------------------------------------------------- 1 | import type { AutocompleteState, AutocompleteOptions } from '@algolia/autocomplete-core'; 2 | import type { LiteClient, SearchParamsObject } from 'algoliasearch/lite'; 3 | import React, { type JSX } from 'react'; 4 | import { createPortal } from 'react-dom'; 5 | 6 | import { DocSearchButton } from './DocSearchButton'; 7 | import { DocSearchModal } from './DocSearchModal'; 8 | import type { DocSearchHit, InternalDocSearchHit, StoredDocSearchHit } from './types'; 9 | import { useDocSearchKeyboardEvents } from './useDocSearchKeyboardEvents'; 10 | 11 | import type { ButtonTranslations, ModalTranslations } from '.'; 12 | 13 | export type DocSearchTranslations = Partial<{ 14 | button: ButtonTranslations; 15 | modal: ModalTranslations; 16 | }>; 17 | 18 | // The interface that describes the minimal implementation required for the algoliasearch client, when using the [`transformSearchClient`](https://docsearch.algolia.com/docs/api/#transformsearchclient) option. 19 | export type DocSearchTransformClient = { 20 | search: LiteClient['search']; 21 | addAlgoliaAgent: LiteClient['addAlgoliaAgent']; 22 | transporter: Pick; 23 | }; 24 | 25 | export interface DocSearchProps { 26 | appId: string; 27 | apiKey: string; 28 | indexName: string; 29 | placeholder?: string; 30 | searchParameters?: SearchParamsObject; 31 | maxResultsPerGroup?: number; 32 | transformItems?: (items: DocSearchHit[]) => DocSearchHit[]; 33 | hitComponent?: (props: { hit: InternalDocSearchHit | StoredDocSearchHit; children: React.ReactNode }) => JSX.Element; 34 | resultsFooterComponent?: (props: { state: AutocompleteState }) => JSX.Element | null; 35 | transformSearchClient?: (searchClient: DocSearchTransformClient) => DocSearchTransformClient; 36 | disableUserPersonalization?: boolean; 37 | initialQuery?: string; 38 | navigator?: AutocompleteOptions['navigator']; 39 | translations?: DocSearchTranslations; 40 | getMissingResultsUrl?: ({ query }: { query: string }) => string; 41 | insights?: AutocompleteOptions['insights']; 42 | } 43 | 44 | export function DocSearch(props: DocSearchProps): JSX.Element { 45 | const searchButtonRef = React.useRef(null); 46 | const [isOpen, setIsOpen] = React.useState(false); 47 | const [initialQuery, setInitialQuery] = React.useState(props?.initialQuery || undefined); 48 | 49 | const onOpen = React.useCallback(() => { 50 | setIsOpen(true); 51 | }, [setIsOpen]); 52 | 53 | const onClose = React.useCallback(() => { 54 | setIsOpen(false); 55 | setInitialQuery(props?.initialQuery); 56 | }, [setIsOpen, props.initialQuery]); 57 | 58 | const onInput = React.useCallback( 59 | (event: KeyboardEvent) => { 60 | setIsOpen(true); 61 | setInitialQuery(event.key); 62 | }, 63 | [setIsOpen, setInitialQuery], 64 | ); 65 | 66 | useDocSearchKeyboardEvents({ 67 | isOpen, 68 | onOpen, 69 | onClose, 70 | onInput, 71 | searchButtonRef, 72 | }); 73 | 74 | return ( 75 | <> 76 | 77 | 78 | {isOpen && 79 | createPortal( 80 | , 87 | document.body, 88 | )} 89 | 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/DocSearchButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX, useEffect, useState } from 'react'; 2 | 3 | import { ControlKeyIcon } from './icons/ControlKeyIcon'; 4 | import { SearchIcon } from './icons/SearchIcon'; 5 | 6 | export type ButtonTranslations = Partial<{ 7 | buttonText: string; 8 | buttonAriaLabel: string; 9 | }>; 10 | 11 | export type DocSearchButtonProps = React.ComponentProps<'button'> & { 12 | translations?: ButtonTranslations; 13 | }; 14 | 15 | const ACTION_KEY_DEFAULT = 'Ctrl' as const; 16 | const ACTION_KEY_APPLE = '⌘' as const; 17 | 18 | function isAppleDevice(): boolean { 19 | return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); 20 | } 21 | 22 | export const DocSearchButton = React.forwardRef( 23 | ({ translations = {}, ...props }, ref) => { 24 | const { buttonText = 'Search', buttonAriaLabel = 'Search' } = translations; 25 | 26 | const [key, setKey] = useState(null); 27 | 28 | useEffect(() => { 29 | if (typeof navigator !== 'undefined') { 30 | isAppleDevice() ? setKey(ACTION_KEY_APPLE) : setKey(ACTION_KEY_DEFAULT); 31 | } 32 | }, []); 33 | 34 | const [actionKeyReactsTo, actionKeyAltText, actionKeyChild] = 35 | key === ACTION_KEY_DEFAULT 36 | ? // eslint-disable-next-line react/jsx-key -- false flag 37 | ([ACTION_KEY_DEFAULT, 'Control', ] as const) 38 | : (['Meta', 'Meta', key] as const); 39 | 40 | const shortcut = `${actionKeyAltText}+k`; 41 | 42 | return ( 43 | 65 | ); 66 | }, 67 | ); 68 | 69 | type DocSearchButtonKeyProps = { 70 | reactsToKey?: string; 71 | }; 72 | 73 | function DocSearchButtonKey({ reactsToKey, children }: React.PropsWithChildren): JSX.Element { 74 | const [isKeyDown, setIsKeyDown] = useState(false); 75 | 76 | useEffect(() => { 77 | if (!reactsToKey) { 78 | return undefined; 79 | } 80 | 81 | function handleKeyDown(e: KeyboardEvent): void { 82 | if (e.key === reactsToKey) { 83 | setIsKeyDown(true); 84 | } 85 | } 86 | 87 | function handleKeyUp(e: KeyboardEvent): void { 88 | if ( 89 | e.key === reactsToKey || 90 | // keyup doesn't fire when Command is held down, 91 | // workaround is to mark key as also released when Command is released 92 | // See https://stackoverflow.com/a/73419500 93 | e.key === 'Meta' 94 | ) { 95 | setIsKeyDown(false); 96 | } 97 | } 98 | 99 | window.addEventListener('keydown', handleKeyDown); 100 | window.addEventListener('keyup', handleKeyUp); 101 | 102 | return (): void => { 103 | window.removeEventListener('keydown', handleKeyDown); 104 | window.removeEventListener('keyup', handleKeyUp); 105 | }; 106 | }, [reactsToKey]); 107 | 108 | return ( 109 | 110 | {children} 111 | 112 | ); 113 | } 114 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/ErrorScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | import { ErrorIcon } from './icons'; 4 | 5 | export type ErrorScreenTranslations = Partial<{ 6 | titleText: string; 7 | helpText: string; 8 | }>; 9 | 10 | type ErrorScreenProps = { 11 | translations?: ErrorScreenTranslations; 12 | }; 13 | 14 | export function ErrorScreen({ translations = {} }: ErrorScreenProps): JSX.Element { 15 | const { titleText = 'Unable to fetch results', helpText = 'You might want to check your network connection.' } = 16 | translations; 17 | return ( 18 |
19 |
20 | 21 |
22 |

{titleText}

23 |

{helpText}

24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | import { AlgoliaLogo } from './AlgoliaLogo'; 4 | 5 | export type FooterTranslations = Partial<{ 6 | selectText: string; 7 | selectKeyAriaLabel: string; 8 | navigateText: string; 9 | navigateUpKeyAriaLabel: string; 10 | navigateDownKeyAriaLabel: string; 11 | closeText: string; 12 | closeKeyAriaLabel: string; 13 | searchByText: string; 14 | }>; 15 | 16 | type FooterProps = Partial<{ 17 | translations: FooterTranslations; 18 | }>; 19 | 20 | interface CommandIconProps { 21 | children: React.ReactNode; 22 | ariaLabel: string; 23 | } 24 | 25 | function CommandIcon(props: CommandIconProps): JSX.Element { 26 | return ( 27 | 28 | 29 | {props.children} 30 | 31 | 32 | ); 33 | } 34 | 35 | export function Footer({ translations = {} }: FooterProps): JSX.Element { 36 | const { 37 | selectText = 'to select', 38 | selectKeyAriaLabel = 'Enter key', 39 | navigateText = 'to navigate', 40 | navigateUpKeyAriaLabel = 'Arrow up', 41 | navigateDownKeyAriaLabel = 'Arrow down', 42 | closeText = 'to close', 43 | closeKeyAriaLabel = 'Escape key', 44 | searchByText = 'Search by', 45 | } = translations; 46 | 47 | return ( 48 | <> 49 |
50 | 51 |
52 |
    53 |
  • 54 | 55 | 56 | 57 | 58 | 59 | {selectText} 60 |
  • 61 |
  • 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {navigateText} 73 |
  • 74 |
  • 75 | 76 | 77 | 78 | 79 | 80 | {closeText} 81 |
  • 82 |
83 | 84 | ); 85 | } 86 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/Hit.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | import type { InternalDocSearchHit, StoredDocSearchHit } from './types'; 4 | 5 | interface HitProps { 6 | hit: InternalDocSearchHit | StoredDocSearchHit; 7 | children: React.ReactNode; 8 | } 9 | 10 | export function Hit({ hit, children }: HitProps): JSX.Element { 11 | return {children}; 12 | } 13 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/NoResultsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | import { NoResultsIcon } from './icons'; 4 | import type { ScreenStateProps } from './ScreenState'; 5 | import type { InternalDocSearchHit } from './types'; 6 | 7 | export type NoResultsScreenTranslations = Partial<{ 8 | noResultsText: string; 9 | suggestedQueryText: string; 10 | reportMissingResultsText: string; 11 | reportMissingResultsLinkText: string; 12 | }>; 13 | 14 | type NoResultsScreenProps = Omit, 'translations'> & { 15 | translations?: NoResultsScreenTranslations; 16 | }; 17 | 18 | export function NoResultsScreen({ translations = {}, ...props }: NoResultsScreenProps): JSX.Element { 19 | const { 20 | noResultsText = 'No results for', 21 | suggestedQueryText = 'Try searching for', 22 | reportMissingResultsText = 'Believe this query should return results?', 23 | reportMissingResultsLinkText = 'Let us know.', 24 | } = translations; 25 | const searchSuggestions: string[] | undefined = props.state.context.searchSuggestions as string[]; 26 | 27 | return ( 28 |
29 |
30 | 31 |
32 |

33 | {noResultsText} "{props.state.query}" 34 |

35 | 36 | {searchSuggestions && searchSuggestions.length > 0 && ( 37 |
38 |

{suggestedQueryText}:

39 |
    40 | {searchSuggestions.slice(0, 3).reduce( 41 | (acc, search) => [ 42 | ...acc, 43 |
  • 44 | 56 |
  • , 57 | ], 58 | [], 59 | )} 60 |
61 |
62 | )} 63 | 64 | {props.getMissingResultsUrl && ( 65 |

66 | {`${reportMissingResultsText} `} 67 | 68 | {reportMissingResultsLinkText} 69 | 70 |

71 | )} 72 |
73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/ResultsScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | import { SelectIcon, SourceIcon } from './icons'; 4 | import { Results } from './Results'; 5 | import type { ScreenStateProps } from './ScreenState'; 6 | import type { InternalDocSearchHit } from './types'; 7 | import { removeHighlightTags } from './utils'; 8 | 9 | type ResultsScreenProps = Omit, 'translations'>; 10 | 11 | export function ResultsScreen(props: ResultsScreenProps): JSX.Element { 12 | return ( 13 |
14 | {props.state.collections.map((collection) => { 15 | if (collection.items.length === 0) { 16 | return null; 17 | } 18 | 19 | const title = removeHighlightTags(collection.items[0]); 20 | 21 | return ( 22 | ( 28 | <> 29 | {item.__docsearch_parent && ( 30 | 31 | 38 | {item.__docsearch_parent !== collection.items[index + 1]?.__docsearch_parent ? ( 39 | 40 | ) : ( 41 | 42 | )} 43 | 44 | 45 | )} 46 | 47 |
48 | 49 |
50 | 51 | )} 52 | renderAction={() => ( 53 |
54 | 55 |
56 | )} 57 | /> 58 | ); 59 | })} 60 | 61 | {props.resultsFooterComponent && ( 62 |
63 | 64 |
65 | )} 66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/ScreenState.tsx: -------------------------------------------------------------------------------- 1 | import type { AutocompleteApi, AutocompleteState, BaseItem } from '@algolia/autocomplete-core'; 2 | import React from 'react'; 3 | 4 | import type { DocSearchProps } from './DocSearch'; 5 | import type { ErrorScreenTranslations } from './ErrorScreen'; 6 | import { ErrorScreen } from './ErrorScreen'; 7 | import type { NoResultsScreenTranslations } from './NoResultsScreen'; 8 | import { NoResultsScreen } from './NoResultsScreen'; 9 | import { ResultsScreen } from './ResultsScreen'; 10 | import type { StartScreenTranslations } from './StartScreen'; 11 | import { StartScreen } from './StartScreen'; 12 | import type { StoredSearchPlugin } from './stored-searches'; 13 | import type { InternalDocSearchHit, StoredDocSearchHit } from './types'; 14 | 15 | export type ScreenStateTranslations = Partial<{ 16 | errorScreen: ErrorScreenTranslations; 17 | startScreen: StartScreenTranslations; 18 | noResultsScreen: NoResultsScreenTranslations; 19 | }>; 20 | 21 | export interface ScreenStateProps 22 | extends AutocompleteApi { 23 | state: AutocompleteState; 24 | recentSearches: StoredSearchPlugin; 25 | favoriteSearches: StoredSearchPlugin; 26 | onItemClick: (item: InternalDocSearchHit, event: KeyboardEvent | MouseEvent) => void; 27 | inputRef: React.MutableRefObject; 28 | hitComponent: DocSearchProps['hitComponent']; 29 | indexName: DocSearchProps['indexName']; 30 | disableUserPersonalization: boolean; 31 | resultsFooterComponent: DocSearchProps['resultsFooterComponent']; 32 | translations: ScreenStateTranslations; 33 | getMissingResultsUrl?: DocSearchProps['getMissingResultsUrl']; 34 | } 35 | 36 | export const ScreenState = React.memo( 37 | ({ translations = {}, ...props }: ScreenStateProps) => { 38 | if (props.state.status === 'error') { 39 | return ; 40 | } 41 | 42 | const hasCollections = props.state.collections.some((collection) => collection.items.length > 0); 43 | 44 | if (!props.state.query) { 45 | return ; 46 | } 47 | 48 | if (hasCollections === false) { 49 | return ; 50 | } 51 | 52 | return ; 53 | }, 54 | function areEqual(_prevProps, nextProps) { 55 | // We don't update the screen when Autocomplete is loading or stalled to 56 | // avoid UI flashes: 57 | // - Empty screen → Results screen 58 | // - NoResults screen → NoResults screen with another query 59 | return nextProps.state.status === 'loading' || nextProps.state.status === 'stalled'; 60 | }, 61 | ); 62 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/SearchBox.tsx: -------------------------------------------------------------------------------- 1 | import type { AutocompleteApi, AutocompleteState } from '@algolia/autocomplete-core'; 2 | import React, { type JSX, type RefObject } from 'react'; 3 | 4 | import { MAX_QUERY_SIZE } from './constants'; 5 | import { LoadingIcon } from './icons/LoadingIcon'; 6 | import { ResetIcon } from './icons/ResetIcon'; 7 | import { SearchIcon } from './icons/SearchIcon'; 8 | import type { InternalDocSearchHit } from './types'; 9 | 10 | export type SearchBoxTranslations = Partial<{ 11 | resetButtonTitle: string; 12 | resetButtonAriaLabel: string; 13 | cancelButtonText: string; 14 | cancelButtonAriaLabel: string; 15 | searchInputLabel: string; 16 | }>; 17 | 18 | interface SearchBoxProps 19 | extends AutocompleteApi { 20 | state: AutocompleteState; 21 | autoFocus: boolean; 22 | inputRef: RefObject; 23 | onClose: () => void; 24 | isFromSelection: boolean; 25 | translations?: SearchBoxTranslations; 26 | } 27 | 28 | export function SearchBox({ translations = {}, ...props }: SearchBoxProps): JSX.Element { 29 | const { 30 | resetButtonTitle = 'Clear the query', 31 | resetButtonAriaLabel = 'Clear the query', 32 | cancelButtonText = 'Cancel', 33 | cancelButtonAriaLabel = 'Cancel', 34 | searchInputLabel = 'Search', 35 | } = translations; 36 | const { onReset } = props.getFormProps({ 37 | inputElement: props.inputRef.current, 38 | }); 39 | 40 | React.useEffect(() => { 41 | if (props.autoFocus && props.inputRef.current) { 42 | props.inputRef.current.focus(); 43 | } 44 | }, [props.autoFocus, props.inputRef]); 45 | 46 | React.useEffect(() => { 47 | if (props.isFromSelection && props.inputRef.current) { 48 | props.inputRef.current.select(); 49 | } 50 | }, [props.isFromSelection, props.inputRef]); 51 | 52 | return ( 53 | <> 54 |
{ 57 | event.preventDefault(); 58 | }} 59 | onReset={onReset} 60 | > 61 | 65 | 66 |
67 | 68 |
69 | 70 | 79 | 80 | 89 |
90 | 91 | 94 | 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/Snippet.tsx: -------------------------------------------------------------------------------- 1 | import { type JSX, createElement } from 'react'; 2 | 3 | import type { StoredDocSearchHit } from './types'; 4 | 5 | function getPropertyByPath(object: Record, path: string): any { 6 | const parts = path.split('.'); 7 | 8 | return parts.reduce((prev, current) => { 9 | if (prev?.[current]) return prev[current]; 10 | return null; 11 | }, object); 12 | } 13 | 14 | interface SnippetProps { 15 | hit: TItem; 16 | attribute: string; 17 | tagName?: string; 18 | [prop: string]: unknown; 19 | } 20 | 21 | export function Snippet({ 22 | hit, 23 | attribute, 24 | tagName = 'span', 25 | ...rest 26 | }: SnippetProps): JSX.Element { 27 | return createElement(tagName, { 28 | ...rest, 29 | dangerouslySetInnerHTML: { 30 | __html: getPropertyByPath(hit, `_snippetResult.${attribute}.value`) || getPropertyByPath(hit, attribute), 31 | }, 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const MAX_QUERY_SIZE = 64; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/ControlKeyIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function ControlKeyIcon(): JSX.Element { 4 | return ( 5 | 6 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/ErrorIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function ErrorIcon(): JSX.Element { 4 | return ( 5 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/GoToExternalIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function GoToExternal(): JSX.Element { 4 | return ( 5 | 6 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/LoadingIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function LoadingIcon(): JSX.Element { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/NoResultsIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function NoResultsIcon(): JSX.Element { 4 | return ( 5 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/RecentIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function RecentIcon(): JSX.Element { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/ResetIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function ResetIcon(): JSX.Element { 4 | return ( 5 | 6 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/SearchIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function SearchIcon(): JSX.Element { 4 | return ( 5 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/SelectIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function SelectIcon(): JSX.Element { 4 | return ( 5 | 6 | 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/SourceIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | const LvlIcon: React.FC = () => { 4 | return ( 5 | 6 | 13 | 14 | ); 15 | }; 16 | 17 | export function SourceIcon(props: { type: string }): JSX.Element { 18 | switch (props.type) { 19 | case 'lvl1': 20 | return ; 21 | case 'content': 22 | return ; 23 | default: 24 | return ; 25 | } 26 | } 27 | 28 | function AnchorIcon(): JSX.Element { 29 | return ( 30 | 31 | 39 | 40 | ); 41 | } 42 | 43 | function ContentIcon(): JSX.Element { 44 | return ( 45 | 46 | 53 | 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/StarIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, { type JSX } from 'react'; 2 | 3 | export function StarIcon(): JSX.Element { 4 | return ( 5 | 6 | 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/icons/index.ts: -------------------------------------------------------------------------------- 1 | export * from './GoToExternalIcon'; 2 | export * from './LoadingIcon'; 3 | export * from './RecentIcon'; 4 | export * from './ResetIcon'; 5 | export * from './SearchIcon'; 6 | export * from './SelectIcon'; 7 | export * from './SourceIcon'; 8 | export * from './StarIcon'; 9 | export * from './ErrorIcon'; 10 | export * from './NoResultsIcon'; 11 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DocSearch'; 2 | export * from './DocSearchButton'; 3 | export * from './DocSearchModal'; 4 | export * from './useDocSearchKeyboardEvents'; 5 | export * from './version'; 6 | export * from './types'; 7 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/stored-searches.ts: -------------------------------------------------------------------------------- 1 | import type { DocSearchHit, StoredDocSearchHit } from './types'; 2 | 3 | function isLocalStorageSupported(): boolean { 4 | const key = '__TEST_KEY__'; 5 | 6 | try { 7 | localStorage.setItem(key, ''); 8 | localStorage.removeItem(key); 9 | 10 | return true; 11 | } catch { 12 | return false; 13 | } 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 17 | function createStorage(key: string) { 18 | if (isLocalStorageSupported() === false) { 19 | return { 20 | setItem(): void {}, 21 | getItem(): TItem[] { 22 | return []; 23 | }, 24 | }; 25 | } 26 | 27 | return { 28 | setItem(item: TItem[]): void { 29 | return window.localStorage.setItem(key, JSON.stringify(item)); 30 | }, 31 | getItem(): TItem[] { 32 | const item = window.localStorage.getItem(key); 33 | 34 | return item ? JSON.parse(item) : []; 35 | }, 36 | }; 37 | } 38 | 39 | type CreateStoredSearchesOptions = { 40 | key: string; 41 | limit?: number; 42 | }; 43 | 44 | export type StoredSearchPlugin = { 45 | add: (item: TItem) => void; 46 | remove: (item: TItem) => void; 47 | getAll: () => TItem[]; 48 | }; 49 | 50 | export function createStoredSearches({ 51 | key, 52 | limit = 5, 53 | }: CreateStoredSearchesOptions): StoredSearchPlugin { 54 | const storage = createStorage(key); 55 | let items = storage.getItem().slice(0, limit); 56 | 57 | return { 58 | add(item: TItem): void { 59 | const { _highlightResult, _snippetResult, ...hit } = item as unknown as DocSearchHit; 60 | 61 | const isQueryAlreadySaved = items.findIndex((x) => x.objectID === hit.objectID); 62 | 63 | if (isQueryAlreadySaved > -1) { 64 | items.splice(isQueryAlreadySaved, 1); 65 | } 66 | 67 | items.unshift(hit as TItem); 68 | items = items.slice(0, limit); 69 | 70 | storage.setItem(items); 71 | }, 72 | remove(item: TItem): void { 73 | items = items.filter((x) => x.objectID !== item.objectID); 74 | 75 | storage.setItem(items); 76 | }, 77 | getAll(): TItem[] { 78 | return items; 79 | }, 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/types/DocSearchHit.ts: -------------------------------------------------------------------------------- 1 | type ContentType = 'content' | 'lvl0' | 'lvl1' | 'lvl2' | 'lvl3' | 'lvl4' | 'lvl5' | 'lvl6'; 2 | 3 | interface DocSearchHitAttributeHighlightResult { 4 | value: string; 5 | matchLevel: 'full' | 'none' | 'partial'; 6 | matchedWords: string[]; 7 | fullyHighlighted?: boolean; 8 | } 9 | 10 | interface DocSearchHitHighlightResultHierarchy { 11 | lvl0: DocSearchHitAttributeHighlightResult; 12 | lvl1: DocSearchHitAttributeHighlightResult; 13 | lvl2: DocSearchHitAttributeHighlightResult; 14 | lvl3: DocSearchHitAttributeHighlightResult; 15 | lvl4: DocSearchHitAttributeHighlightResult; 16 | lvl5: DocSearchHitAttributeHighlightResult; 17 | lvl6: DocSearchHitAttributeHighlightResult; 18 | } 19 | 20 | interface DocSearchHitHighlightResult { 21 | content: DocSearchHitAttributeHighlightResult; 22 | hierarchy: DocSearchHitHighlightResultHierarchy; 23 | hierarchy_camel: DocSearchHitHighlightResultHierarchy[]; 24 | } 25 | 26 | interface DocSearchHitAttributeSnippetResult { 27 | value: string; 28 | matchLevel: 'full' | 'none' | 'partial'; 29 | } 30 | 31 | interface DocSearchHitSnippetResult { 32 | content: DocSearchHitAttributeSnippetResult; 33 | hierarchy: DocSearchHitHighlightResultHierarchy; 34 | hierarchy_camel: DocSearchHitHighlightResultHierarchy[]; 35 | } 36 | 37 | export declare type DocSearchHit = { 38 | objectID: string; 39 | content: string | null; 40 | url: string; 41 | url_without_anchor: string; 42 | type: ContentType; 43 | anchor: string | null; 44 | hierarchy: { 45 | lvl0: string; 46 | lvl1: string; 47 | lvl2: string | null; 48 | lvl3: string | null; 49 | lvl4: string | null; 50 | lvl5: string | null; 51 | lvl6: string | null; 52 | }; 53 | _highlightResult: DocSearchHitHighlightResult; 54 | _snippetResult: DocSearchHitSnippetResult; 55 | _rankingInfo?: { 56 | promoted: boolean; 57 | nbTypos: number; 58 | firstMatchedWord: number; 59 | proximityDistance?: number; 60 | geoDistance: number; 61 | geoPrecision?: number; 62 | nbExactWords: number; 63 | words: number; 64 | filters: number; 65 | userScore: number; 66 | matchedGeoLocation?: { 67 | lat: number; 68 | lng: number; 69 | distance: number; 70 | }; 71 | }; 72 | _distinctSeqID?: number; 73 | __autocomplete_indexName?: string; 74 | __autocomplete_queryID?: string; 75 | __autocomplete_algoliaCredentials?: { 76 | appId: string; 77 | apiKey: string; 78 | }; 79 | __autocomplete_id?: number; 80 | }; 81 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/types/DocSearchState.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | AutocompleteContext, 3 | AutocompleteInsightsApi, 4 | AutocompleteState, 5 | BaseItem, 6 | } from '@algolia/autocomplete-core'; 7 | 8 | interface DocSearchContext extends AutocompleteContext { 9 | algoliaInsightsPlugin?: { 10 | insights: AutocompleteInsightsApi; 11 | }; 12 | } 13 | 14 | export interface DocSearchState extends AutocompleteState { 15 | context: DocSearchContext; 16 | } 17 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/types/InternalDocSearchHit.ts: -------------------------------------------------------------------------------- 1 | import type { DocSearchHit } from './DocSearchHit'; 2 | 3 | export type InternalDocSearchHit = DocSearchHit & { 4 | __docsearch_parent: InternalDocSearchHit | null; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/types/StoredDocSearchHit.ts: -------------------------------------------------------------------------------- 1 | import type { DocSearchHit } from './DocSearchHit'; 2 | 3 | export type StoredDocSearchHit = Omit; 4 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DocSearchHit'; 2 | export * from './DocSearchState'; 3 | export * from './InternalDocSearchHit'; 4 | export * from './StoredDocSearchHit'; 5 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/useDocSearchKeyboardEvents.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export interface UseDocSearchKeyboardEventsProps { 4 | isOpen: boolean; 5 | onOpen: () => void; 6 | onClose: () => void; 7 | onInput?: (event: KeyboardEvent) => void; 8 | searchButtonRef: React.RefObject; 9 | } 10 | 11 | function isEditingContent(event: KeyboardEvent): boolean { 12 | const element = event.composedPath()[0] as HTMLElement; 13 | const tagName = element.tagName; 14 | 15 | return element.isContentEditable || tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA'; 16 | } 17 | 18 | export function useDocSearchKeyboardEvents({ 19 | isOpen, 20 | onOpen, 21 | onClose, 22 | onInput, 23 | searchButtonRef, 24 | }: UseDocSearchKeyboardEventsProps): void { 25 | React.useEffect(() => { 26 | function onKeyDown(event: KeyboardEvent): void { 27 | if ( 28 | (event.code === 'Escape' && isOpen) || 29 | // The `Cmd+K` shortcut both opens and closes the modal. 30 | // We need to check for `event.key` because it can be `undefined` with 31 | // Chrome's autofill feature. 32 | // See https://github.com/paperjs/paper.js/issues/1398 33 | (event.key?.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) || 34 | // The `/` shortcut opens but doesn't close the modal because it's 35 | // a character. 36 | (!isEditingContent(event) && event.key === '/' && !isOpen) 37 | ) { 38 | event.preventDefault(); 39 | 40 | if (isOpen) { 41 | onClose(); 42 | } else if (!document.body.classList.contains('DocSearch--active')) { 43 | // We check that no other DocSearch modal is showing before opening 44 | // another one. 45 | onOpen(); 46 | } 47 | 48 | return; 49 | } 50 | 51 | if (searchButtonRef && searchButtonRef.current === document.activeElement && onInput) { 52 | if (/[a-zA-Z0-9]/.test(String.fromCharCode(event.keyCode))) { 53 | onInput(event); 54 | } 55 | } 56 | } 57 | 58 | window.addEventListener('keydown', onKeyDown); 59 | 60 | return (): void => { 61 | window.removeEventListener('keydown', onKeyDown); 62 | }; 63 | }, [isOpen, onOpen, onClose, onInput, searchButtonRef]); 64 | } 65 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/useSearchClient.ts: -------------------------------------------------------------------------------- 1 | import { liteClient } from 'algoliasearch/lite'; 2 | import React from 'react'; 3 | 4 | import type { DocSearchTransformClient } from './DocSearch'; 5 | import { version } from './version'; 6 | 7 | export function useSearchClient( 8 | appId: string, 9 | apiKey: string, 10 | transformSearchClient: (searchClient: DocSearchTransformClient) => DocSearchTransformClient, 11 | ): DocSearchTransformClient { 12 | const searchClient = React.useMemo(() => { 13 | const client = liteClient(appId, apiKey); 14 | client.addAlgoliaAgent('docsearch', version); 15 | 16 | // Since DocSearch.js relies on DocSearch React with an alias to Preact, 17 | // we cannot add the `docsearch-react` user agent by default, otherwise 18 | // it would also be sent on a DocSearch.js integration. 19 | // We therefore only add the `docsearch-react` user agent if `docsearch.js` 20 | // is not present. 21 | if (/docsearch.js \(.*\)/.test(client.transporter.algoliaAgent.value) === false) { 22 | client.addAlgoliaAgent('docsearch-react', version); 23 | } 24 | 25 | return transformSearchClient(client); 26 | }, [appId, apiKey, transformSearchClient]); 27 | 28 | return searchClient; 29 | } 30 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/useTouchEvents.ts: -------------------------------------------------------------------------------- 1 | import type { AutocompleteApi } from '@algolia/autocomplete-core'; 2 | import React from 'react'; 3 | 4 | interface UseTouchEventsProps { 5 | getEnvironmentProps: AutocompleteApi['getEnvironmentProps']; 6 | panelElement: HTMLDivElement | null; 7 | formElement: HTMLDivElement | null; 8 | inputElement: HTMLInputElement | null; 9 | } 10 | 11 | export function useTouchEvents({ 12 | getEnvironmentProps, 13 | panelElement, 14 | formElement, 15 | inputElement, 16 | }: UseTouchEventsProps): void { 17 | React.useEffect(() => { 18 | if (!(panelElement && formElement && inputElement)) { 19 | return undefined; 20 | } 21 | 22 | const { onTouchStart, onTouchMove } = getEnvironmentProps({ 23 | panelElement, 24 | formElement, 25 | inputElement, 26 | }); 27 | 28 | window.addEventListener('touchstart', onTouchStart); 29 | window.addEventListener('touchmove', onTouchMove); 30 | 31 | return (): void => { 32 | window.removeEventListener('touchstart', onTouchStart); 33 | window.removeEventListener('touchmove', onTouchMove); 34 | }; 35 | }, [getEnvironmentProps, panelElement, formElement, inputElement]); 36 | } 37 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/useTrapFocus.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface UseTrapFocusProps { 4 | container: HTMLElement | null; 5 | } 6 | 7 | export function useTrapFocus({ container }: UseTrapFocusProps): void { 8 | React.useEffect(() => { 9 | if (!container) { 10 | return undefined; 11 | } 12 | 13 | const focusableElements = container.querySelectorAll( 14 | 'a[href]:not([disabled]), button:not([disabled]), input:not([disabled])', 15 | ); 16 | const firstElement = focusableElements[0]; 17 | const lastElement = focusableElements[focusableElements.length - 1]; 18 | 19 | function trapFocus(event: KeyboardEvent): void { 20 | if (event.key !== 'Tab') { 21 | return; 22 | } 23 | 24 | if (event.shiftKey) { 25 | if (document.activeElement === firstElement) { 26 | event.preventDefault(); 27 | lastElement.focus(); 28 | } 29 | } else if (document.activeElement === lastElement) { 30 | event.preventDefault(); 31 | firstElement.focus(); 32 | } 33 | } 34 | 35 | container.addEventListener('keydown', trapFocus); 36 | 37 | return (): void => { 38 | container.removeEventListener('keydown', trapFocus); 39 | }; 40 | }, [container]); 41 | } 42 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/utils/groupBy.ts: -------------------------------------------------------------------------------- 1 | export function groupBy>( 2 | values: TValue[], 3 | predicate: (value: TValue) => string, 4 | maxResultsPerGroup?: number, 5 | ): Record { 6 | return values.reduce>((acc, item) => { 7 | const key = predicate(item); 8 | 9 | if (!acc.hasOwnProperty(key)) { 10 | acc[key] = []; 11 | } 12 | 13 | // We limit each section to show 5 hits maximum. 14 | // This acts as a frontend alternative to `distinct`. 15 | if (acc[key].length < (maxResultsPerGroup || 5)) { 16 | acc[key].push(item); 17 | } 18 | 19 | return acc; 20 | }, {}); 21 | } 22 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/utils/identity.ts: -------------------------------------------------------------------------------- 1 | export function identity(x: TParam): TParam { 2 | return x; 3 | } 4 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './groupBy'; 2 | export * from './identity'; 3 | export * from './isModifierEvent'; 4 | export * from './noop'; 5 | export * from './removeHighlightTags'; 6 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/utils/isModifierEvent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Detect when an event is modified with a special key to let the browser 3 | * trigger its default behavior. 4 | */ 5 | export function isModifierEvent(event: TEvent): boolean { 6 | const isMiddleClick = (event as MouseEvent).button === 1; 7 | 8 | return isMiddleClick || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; 9 | } 10 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/utils/noop.ts: -------------------------------------------------------------------------------- 1 | export function noop(..._args: any[]): void {} 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/utils/removeHighlightTags.ts: -------------------------------------------------------------------------------- 1 | import type { DocSearchHit, InternalDocSearchHit } from '../types'; 2 | 3 | const regexHighlightTags = /(|<\/mark>)/g; 4 | const regexHasHighlightTags = RegExp(regexHighlightTags.source); 5 | 6 | export function removeHighlightTags(hit: DocSearchHit | InternalDocSearchHit): string { 7 | const internalDocSearchHit = hit as InternalDocSearchHit; 8 | 9 | if (!internalDocSearchHit.__docsearch_parent && !hit._highlightResult) { 10 | return hit.hierarchy.lvl0; 11 | } 12 | 13 | const lvl0 = internalDocSearchHit.__docsearch_parent 14 | ? internalDocSearchHit.__docsearch_parent?._highlightResult?.hierarchy?.lvl0 15 | : hit._highlightResult?.hierarchy?.lvl0; 16 | 17 | if (!lvl0) { 18 | return hit.hierarchy.lvl0; 19 | } 20 | 21 | return lvl0.value && regexHasHighlightTags.test(lvl0.value) ? lvl0.value.replace(regexHighlightTags, '') : lvl0.value; 22 | } 23 | -------------------------------------------------------------------------------- /packages/docsearch-react/src/version.ts: -------------------------------------------------------------------------------- 1 | export const version = '3.9.0'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/style/button.js: -------------------------------------------------------------------------------- 1 | export * from '@docsearch/css/dist/button.css'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/style/index.js: -------------------------------------------------------------------------------- 1 | export * from '@docsearch/css'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/style/modal.js: -------------------------------------------------------------------------------- 1 | export * from '@docsearch/css/dist/modal.css'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/style/variables.js: -------------------------------------------------------------------------------- 1 | export * from '@docsearch/css/dist/_variables.css'; 2 | -------------------------------------------------------------------------------- /packages/docsearch-react/tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.declaration", 3 | } 4 | -------------------------------------------------------------------------------- /packages/website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /packages/website/README.md: -------------------------------------------------------------------------------- 1 | [![DocSearch][1]][2] 2 | 3 | The easiest way to add search to your documentation. For free. 4 | 5 | [![Netlify Status][3]][4] 6 | 7 | Check out our [website][2] for a complete explanation and documentation. 8 | 9 | # Installation 10 | 11 | 1. In the root of this repository (two level above this directory), do `yarn install` and `yarn build` 12 | 1. In this directory, do `yarn start`. 13 | 1. A browser window will open up, pointing to the docs. 14 | 15 | # Deployment 16 | 17 | Netlify handles the deployment of this website. If you are part of the DocSearch core team. [Access the Netlify Dashboard][11]. 18 | 19 | [1]: ./static/img/docsearch-logo.svg 20 | [2]: https://docsearch.algolia.com/ 21 | [3]: https://api.netlify.com/api/v1/badges/30eacc09-d4b2-4a53-879b-04d40aaea454/deploy-status 22 | [4]: https://app.netlify.com/sites/docsearch/deploys 23 | [6]: https://github.com/algolia/docsearch 24 | [7]: https://github.com/algolia/docsearch-configs 25 | [8]: https://github.com/algolia/docsearch-scraper 26 | [9]: https://github.com/algolia/docsearch-website 27 | [10]: https://v2.docusaurus.io/ 28 | [11]: https://app.netlify.com/sites/docsearch/overview 29 | -------------------------------------------------------------------------------- /packages/website/babel.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/website/docs/how-does-it-work.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: How does it work? 3 | --- 4 | 5 | import useBaseUrl from '@docusaurus/useBaseUrl'; 6 | 7 | Getting up and ready with DocSearch is a straightforward process that requires three steps: you apply, we configure the crawler and the Algolia app for you, and you integrate our UI in your frontend. You only need to copy and paste a JavaScript snippet. 8 | 9 | How it works 13 | 14 | ## You apply 15 | 16 | The first thing you'll need to do is to apply for DocSearch by [filling out the form on this page][1] (double check first that [you qualify][2]). We are receiving a lot of requests, so this form makes sure we won't be forgetting anyone. 17 | 18 | We guarantee that we will answer every request, but as we receive a lot of applications, please give us a couple of days to get back to you :) 19 | 20 | ## We create your Algolia application and a dedicated crawler 21 | 22 | Once we receive [your application][1], we'll have a look at your website, create an Algolia application and a dedicated [crawler][5] for it. Your crawler comes with [a configuration file][6] which defines which URLs we should crawl or ignore, as well as the specific CSS selectors to use for selecting headers, subheaders, etc. 23 | 24 | This step still requires some manual work and human brain, but thanks to the +4,000 configs we already created, we're able to automate most of it. Once this creation finishes, we'll run a first indexing of your website and have it run automatically at a random time of the week. 25 | 26 | **With the Crawler, comes [a dedicated interface][8] for you to:** 27 | 28 | - Start, schedule and monitor your crawls 29 | - Edit and test your config file directly with [DocSearch v3][7] 30 | 31 | **With the Algolia application comes access to the dashboard for you to:** 32 | 33 | - Browse your index and see how your content is indexed 34 | - Various analytics to understand how your search performs and ensure that your users are able to find what they’re searching for 35 | - Trials for other Algolia features 36 | - Team management 37 | 38 | ## You update your website 39 | 40 | We'll then get back to you with the JavaScript snippet you'll need to add to your website. This will bind your [DocSearch component][7] to display results from your Algolia index on each keystroke in a pop-up modal. 41 | 42 | Now that DocSearch is set, you don't have anything else to do. We'll keep crawling your website and update your search results automatically. All we ask is that you keep the "Search by Algolia" logo next to your search results. 43 | 44 | [1]: /apply 45 | [2]: /docs/who-can-apply 46 | [3]: https://github.com/algolia/docsearch-configs/tree/master/configs 47 | [4]: /docs/styling 48 | [5]: https://www.algolia.com/products/search-and-discovery/crawler/ 49 | [6]: https://www.algolia.com/doc/tools/crawler/apis/configuration/ 50 | [7]: /docs/docsearch-v3 51 | [8]: https://crawler.algolia.com/ 52 | -------------------------------------------------------------------------------- /packages/website/docs/integrations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Supported Integrations 3 | --- 4 | 5 | We worked with **documentation website generators** to have DocSearch directly embedded as a first class citizen in the websites they produce. 6 | 7 | ## Our great integrations 8 | 9 | So, if you're using one of the following tools, checkout their documentation to see how to enable DocSearch on your website: 10 | 11 | - [Docusaurus v1][1] - [How to enable search][2] 12 | - [Docusaurus v2 & v3][3] - [Using Algolia DocSearch][4] 13 | - [VuePress][5] - [Algolia Search][6] 14 | - [VitePress][21] - [Search][22] 15 | - [Starlight][7] - [Algolia Search][8] 16 | - [LaRecipe][9] - [Algolia Search][10] 17 | - [Orchid][11] - [Algolia Search][12] 18 | - [Smooth DOC][13] - [DocSearch][14] 19 | - [Docsy][15] - [Configure Algolia DocSearch][16] 20 | - [Lotus Docs][19] - [Enabling the DocSearch Plugin][20] 21 | - [Sphinx](https://www.sphinx-doc.org/en/master/) - [Algolia DocSearch for Sphinx](https://sphinx-docsearch.readthedocs.io/) 22 | 23 | If you're maintaining a similar tool and want us to add you to the list, [feel free to make a pull request](https://github.com/algolia/docsearch/edit/main/packages/website/docs/integrations.md) and [contribute to Code Exchange](https://www.algolia.com/developers/code-exchange/contribute/). We're happy to help. 24 | 25 | [1]: https://v1.docusaurus.io/ 26 | [2]: https://v1.docusaurus.io/docs/en/search 27 | [3]: https://docusaurus.io/ 28 | [4]: https://docusaurus.io/docs/search#using-algolia-docsearch 29 | [5]: https://vuepress.vuejs.org/ 30 | [6]: https://vuepress.vuejs.org/theme/default-theme-config.html#algolia-search 31 | [7]: https://starlight.astro.build/ 32 | [8]: https://starlight.astro.build/guides/site-search/#algolia-docsearch 33 | [9]: https://larecipe.saleem.dev/docs/2.2/overview 34 | [10]: https://larecipe.saleem.dev/docs/2.2/search#available-engines 35 | [11]: https://orchid.run 36 | [12]: https://orchid.run/plugins/orchidsearch#algolia-docsearch 37 | [13]: https://next-smooth-doc.vercel.app/ 38 | [14]: https://next-smooth-doc.vercel.app/docs/docsearch/ 39 | [15]: https://www.docsy.dev/ 40 | [16]: https://www.docsy.dev/docs/adding-content/search/#algolia-docsearch 41 | [19]: https://lotusdocs.dev/docs/ 42 | [20]: https://lotusdocs.dev/docs/guides/features/docsearch/#enabling-the-docsearch-plugin 43 | [21]: https://vitepress.dev/ 44 | [22]: https://vitepress.dev/reference/default-theme-search#algolia-search 45 | -------------------------------------------------------------------------------- /packages/website/docs/manage-your-crawls.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Manage your crawls 3 | --- 4 | 5 | import useBaseUrl from '@docusaurus/useBaseUrl'; 6 | 7 | DocSearch comes with the [Algolia Crawler web interface](https://crawler.algolia.com/) that allows you to configure how and when your Algolia index will be populated. 8 | 9 | ## Trigger a new crawl 10 | 11 | Head over to the `Overview` section to `start`, `restart` or `pause` your crawls and view a real-time summary. 12 | 13 |
14 | Algolia Crawler creation page 18 |
19 | 20 | ## Monitor your crawls 21 | 22 | The `monitoring` section helps you find crawl errors or improve your search results. 23 | 24 |
25 | Algolia Crawler creation page 29 |
30 | 31 | ## Update your config 32 | 33 | The live editor allows you to update your config file and test your URLs (`URL tester`). 34 | 35 |
36 | Algolia Crawler creation page 40 |
41 | 42 | ## Search preview 43 | 44 | From the [`editor`](#update-your-config), you have access to a `Search preview` tab to browse search results with [`DocSearch v3`](/docs/docsearch-v3). 45 | 46 |
47 | Algolia Crawler creation page 51 |
52 | 53 | ## URL tester 54 | 55 | From the [`editor`](#update-your-config), your can use the URL tester to [debug selectors](https://www.algolia.com/doc/tools/crawler/getting-started/crawler-configuration/#debugging-selectors) or how we crawl your website. 56 | 57 |
58 | Algolia Crawler URL tester 62 |
63 | -------------------------------------------------------------------------------- /packages/website/docs/migrating-from-v2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Migrating from DocSearch v2 3 | --- 4 | 5 | This page lists the differences between the [DocSearch v2](/docs/legacy/dropdown) and [DocSearch v3](/docs/docsearch-v3) API, you can also take a look at [the exhaustive `API reference` list](/docs/api) and [the `Getting started` guide](/docs/docsearch-v3). 6 | 7 | ```diff 8 | docsearch({ 9 | indexName: 'YOUR_INDEX_NAME', 10 | apiKey: 'YOUR_SEARCH_API_KEY', 11 | 12 | - inputSelector: '', 13 | + container: '', -> We now require a container to be provided 14 | 15 | - appId: '', 16 | + appId: '', -> `appId` is now required 17 | 18 | - transformData: function(hits) {}, 19 | + transformItems: function(items) {}, 20 | 21 | - algoliaOptions: {}, 22 | + searchParameters: {}, 23 | }); 24 | ``` 25 | -------------------------------------------------------------------------------- /packages/website/docs/styling.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Styling 3 | --- 4 | 5 | :::info 6 | 7 | The following content is for **[DocSearch v3][2]**. If you are using **[DocSearch v2][3]**, see the **[legacy][4]** documentation. 8 | 9 | ::: 10 | 11 | :::caution 12 | 13 | This documentation is in progress. 14 | 15 | ::: 16 | 17 | ## Introduction 18 | 19 | DocSearch v3 comes with a theme package called `@docsearch/css`, which offers a sleek out of the box theme! 20 | 21 | :::note 22 | 23 | This package is a dependency of [`@docsearch/js`][1] and [`@docsearch/react`][1], you don't need to install it if you are using a package manager! 24 | 25 | ::: 26 | 27 | ## Installation 28 | 29 | ```bash 30 | yarn add @docsearch/css@3 31 | # or 32 | npm install @docsearch/css@3 33 | ``` 34 | 35 | If you don’t want to use a package manager, you can use a standalone endpoint: 36 | 37 | ```html 38 | 39 | ``` 40 | 41 | ## Files 42 | 43 | ``` 44 | @docsearch/css 45 | ├── dist/style.css # all styles 46 | ├── dist/_variables.css # CSS variables 47 | ├── dist/button.css # CSS for the button 48 | └── dist/modal.css # CSS for the modal 49 | ``` 50 | 51 | [1]: /docs/docsearch-v3 52 | [2]: https://github.com/algolia/docsearch/ 53 | [3]: https://github.com/algolia/docsearch/tree/master 54 | [4]: /docs/legacy/dropdown 55 | -------------------------------------------------------------------------------- /packages/website/docs/what-is-docsearch.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: What is DocSearch? 3 | sidebar_label: What is DocSearch? 4 | --- 5 | 6 | ## Why? 7 | 8 | We created DocSearch because we are scratching our own itch. As developers, we spend a lot of time reading documentation, and it can be hard to find relevant information in large documentations. We're not blaming anyone here: building good search is a challenge. 9 | 10 | It happens that we are a search company and we actually have a lot of experience building search interfaces. We wanted to use those skills to help others. That's why we created a way to automatically extract content from tech documentation and make it available to everyone from the first keystroke. 11 | 12 | ## Quick description 13 | 14 | We split DocSearch into a crawler and a frontend library. 15 | 16 | - Crawls are handled by the [Algolia Crawler][4] and scheduled to run once a week by default, you can then trigger new crawls yourself and monitor them directly from the [Crawler interface][5], which also offers a live editor where you can maintain your config. 17 | - The frontend library is built on top of [Algolia Autocomplete][6] and provides an immersive search experience through its modal. 18 | 19 | ## How to feature DocSearch? 20 | 21 | DocSearch is entirely free and automated. The one thing we'll need from you is to read [our checklist][2] and apply! After that, we'll share with you the snippet needed to add DocSearch to your website. We ask that you keep the "Search by Algolia" link displayed. 22 | 23 | DocSearch is [one of our ways][1] to give back to the open source community for everything it did for us already. 24 | 25 | You can now [apply to the program][3] 26 | 27 | [1]: https://opencollective.com/algolia 28 | [2]: /docs/who-can-apply 29 | [3]: /apply 30 | [4]: https://www.algolia.com/products/search-and-discovery/crawler/ 31 | [5]: https://crawler.algolia.com/ 32 | [6]: https://www.algolia.com/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete/ 33 | -------------------------------------------------------------------------------- /packages/website/docs/who-can-apply.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Who can apply? 3 | --- 4 | 5 | **Open for all developer documentation and technical blogs.** 6 | 7 | We built DocSearch from the ground up with the idea of improving search on large technical documentations. For this reason, we are offering a free hosting version to all online technical documentations and technical blogs. 8 | 9 | We usually turn down applications when they are not production ready or have non-technical content on the website. 10 | 11 | ## The checklist 12 | 13 | To have your request validated, we’ll ask that you check all the following points. 14 | 15 | - Your website must be a **technical documentation or a technical blog**. 16 | 17 | - You must be the **owner** of the website, or at least have the permissions to update its content. You'll need to [include a JavaScript snippet][1] to your frontend to implement DocSearch. 18 | 19 | - Your website must be **publicly available**. We do not host search indices for websites that are only available after authentication or are hosted on a private network. 20 | 21 | - Your website must be **production ready**. We won't index empty websites nor those filled with placeholder content. Please, wait until you have written some documentation before applying. We would be happy to help you as soon as you have a steady design. 22 | 23 | If in doubt, don't hesitate to [apply][2] and we'll figure it out together. 24 | 25 | Even if we cannot accept your request, this does not mean that you cannot enjoy great search on your website. Legacy DocSearch is entirely open source and [you can run it yourself][3], or use any of [our other API clients][4] to take advantage of Algolia's features. 26 | 27 | ## Priority 28 | 29 | We're receiving dozens of requests every day, and while we strive to answer them all as fast as we can, we sometimes give priority to some based on the following criteria: 30 | 31 | - 🙂 If you're using one of our [official integrations][5], creating your config will be much faster for us. 32 | 33 | - 🙁 If we need to render your website in the browser through JavaScript, it means that we'll have to crawl it through a much slower browser emulation. We highly recommend that you put in place server-side rendering for the useful textual content. 34 | 35 | ## Process duration 36 | 37 | As stated in [the priority section](#priority), we receive a lot of requests every day. To ensure they all match [our checklist](#the-checklist), we manually review each of them, which means it can take a bit of time before you get an answer from us. 38 | 39 | The full process (from application to deployment) can take **up to two weeks**, applying multiple times or opening issues **only slows the process**, please be patient 🙏. 40 | 41 | [1]: /docs/docsearch-v3 42 | [2]: /apply 43 | [3]: /docs/legacy/run-your-own 44 | [4]: https://www.algolia.com/doc/api-client/getting-started/install/javascript/?client=javascript 45 | [5]: integrations.md 46 | -------------------------------------------------------------------------------- /packages/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@docsearch/website", 3 | "version": "3.9.0", 4 | "private": true, 5 | "homepage": "https://docsearch.algolia.com/", 6 | "scripts": { 7 | "build:clean": "rm -rf dist build .docusaurus node_modules", 8 | "docusaurus": "docusaurus", 9 | "start": "docusaurus start", 10 | "build": "docusaurus build", 11 | "swizzle": "docusaurus swizzle", 12 | "deploy": "docusaurus deploy", 13 | "clear": "docusaurus clear", 14 | "serve": "docusaurus serve", 15 | "write-translations": "docusaurus write-translations", 16 | "write-heading-ids": "docusaurus write-heading-ids" 17 | }, 18 | "dependencies": { 19 | "@algolia/ui-library": "5.86.0", 20 | "@docsearch/react": "3.9.0", 21 | "@docusaurus/core": "3.7.0", 22 | "@docusaurus/preset-classic": "3.7.0", 23 | "@mdx-js/react": "^3.1.0", 24 | "file-loader": "6.2.0", 25 | "postcss": "8.5.1", 26 | "postcss-import": "16.1.0", 27 | "postcss-preset-env": "10.1.3", 28 | "prism-react-renderer": "2.4.1", 29 | "react": "^18.0.0", 30 | "react-dom": "^18.0.0", 31 | "react-google-recaptcha": "^3.1.0" 32 | }, 33 | "devDependencies": { 34 | "tailwindcss": "3.4.17" 35 | }, 36 | "browserslist": { 37 | "production": [ 38 | ">0.5%", 39 | "not dead", 40 | "not op_mini all" 41 | ], 42 | "development": [ 43 | "last 1 chrome version", 44 | "last 1 firefox version", 45 | "last 1 safari version" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/website/plugins/my-loaders.mjs: -------------------------------------------------------------------------------- 1 | export default function () { 2 | return { 3 | name: 'loaders', 4 | configureWebpack() { 5 | return { 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.(gif|png|jpe?g|svg)$/i, 10 | exclude: /\.(mdx?)$/i, 11 | use: ['file-loader', { loader: 'image-webpack-loader' }], 12 | }, 13 | ], 14 | }, 15 | }; 16 | }, 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /packages/website/plugins/tailwind-loader.mjs: -------------------------------------------------------------------------------- 1 | import postcssImport from 'postcss-import'; 2 | import postcssPresetEnv from 'postcss-preset-env'; 3 | import tailwindcss from 'tailwindcss'; 4 | 5 | export default function () { 6 | return { 7 | name: 'postcss-tailwindcss-loader', 8 | configurePostCss(postcssOptions) { 9 | postcssOptions.plugins.push( 10 | postcssImport, 11 | tailwindcss, 12 | postcssPresetEnv({ 13 | autoprefixer: { 14 | flexbox: 'no-2009', 15 | }, 16 | stage: 4, 17 | }), 18 | ); 19 | return postcssOptions; 20 | }, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /packages/website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | * - create an ordered group of docs 4 | * - render a sidebar for each doc of that group 5 | * - provide next/previous navigation. 6 | * 7 | * The sidebars can be generated from the filesystem, or explicitly defined here. 8 | * 9 | * Create as many sidebars as you want. 10 | */ 11 | 12 | export default { 13 | docs: [ 14 | { 15 | type: 'category', 16 | label: 'Introduction', 17 | items: ['what-is-docsearch', 'who-can-apply', 'migrating-from-legacy'], 18 | }, 19 | { 20 | type: 'category', 21 | label: 'DocSearch v3', 22 | items: ['docsearch-v3', 'api', 'styling', 'migrating-from-v2'], 23 | }, 24 | { 25 | type: 'category', 26 | label: 'Algolia Crawler', 27 | items: ['record-extractor', 'templates', 'manage-your-crawls'], 28 | }, 29 | { 30 | type: 'category', 31 | label: 'Requirements, tips, FAQ', 32 | items: [ 33 | { 34 | type: 'category', 35 | label: 'FAQ', 36 | items: ['crawler', 'docsearch-program'], 37 | }, 38 | { 39 | type: 'doc', 40 | id: 'tips', 41 | }, 42 | { 43 | type: 'doc', 44 | id: 'integrations', 45 | }, 46 | ], 47 | }, 48 | { 49 | type: 'category', 50 | label: 'Under the hood', 51 | items: ['how-does-it-work', 'required-configuration'], 52 | }, 53 | ], 54 | }; 55 | -------------------------------------------------------------------------------- /packages/website/src/pages/apply.js: -------------------------------------------------------------------------------- 1 | import { Hero } from '@algolia/ui-library'; 2 | import Layout from '@theme/Layout'; 3 | import React from 'react'; 4 | 5 | import ApplyForm from '../components/ApplyForm.js'; 6 | import DocSearchLogo from '../components/DocSearchLogo'; 7 | 8 | function ApplyPage() { 9 | return ( 10 | 14 |
15 | } background="curves" /> 16 | 17 |
18 |
19 | ); 20 | } 21 | 22 | export default ApplyPage; 23 | -------------------------------------------------------------------------------- /packages/website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import Layout from '@theme/Layout'; 2 | import React from 'react'; 3 | 4 | import Home from '../components/Home'; 5 | 6 | function HomePage() { 7 | return ( 8 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default HomePage; 18 | -------------------------------------------------------------------------------- /packages/website/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/.nojekyll -------------------------------------------------------------------------------- /packages/website/static/_redirects: -------------------------------------------------------------------------------- 1 | /docs/run-your-own /docs/legacy/run-your-own 2 | /docs/config-file /docs/legacy/config-file 3 | /docs/apply /apply 4 | /docs/dropdown /docs/legacy/dropdown -------------------------------------------------------------------------------- /packages/website/static/img/assets/crawler-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/crawler-editor.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/crawler-monitoring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/crawler-monitoring.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/crawler-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/crawler-overview.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/crawler-search-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/crawler-search-preview.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/crawler-url-tester.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/crawler-url-tester.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/default-colorscheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/default-colorscheme.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/docsearch-how-it-works.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/docsearch-how-it-works.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/docsearch-shadow-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/docsearch-shadow-dark.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/docsearch-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/docsearch-shadow.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/docsearch-ui-anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/docsearch-ui-anatomy.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/keyboard.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/new-crawler-creation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/new-crawler-creation.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/noResultsScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/noResultsScreen.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/playground.png -------------------------------------------------------------------------------- /packages/website/static/img/assets/recommended-layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/assets/recommended-layout.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_1.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_2.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_3.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_4.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_5.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_6.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_7.png -------------------------------------------------------------------------------- /packages/website/static/img/build_index/how_do_we_build_docsearch_index_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/build_index/how_do_we_build_docsearch_index_8.png -------------------------------------------------------------------------------- /packages/website/static/img/docsearch-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/docsearch-x-algolia-logo-dark-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/docsearch-x-algolia-logo-dark-mode.png -------------------------------------------------------------------------------- /packages/website/static/img/docsearch-x-algolia-logo-light-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/docsearch-x-algolia-logo-light-mode.png -------------------------------------------------------------------------------- /packages/website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/favicon.ico -------------------------------------------------------------------------------- /packages/website/static/img/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logo-small.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/angular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/angular.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/ant-design.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/astro-icon-light-gradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/babel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/babel.jpg -------------------------------------------------------------------------------- /packages/website/static/img/logos/backstage.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/backstage.jpeg -------------------------------------------------------------------------------- /packages/website/static/img/logos/bootstrap-vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/bootstrap.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/calico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/calico.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/cern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/cern.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/cern_logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/cern_logo.jpeg -------------------------------------------------------------------------------- /packages/website/static/img/logos/cheerio.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/eslint.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/ethereum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/ethereum.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/expo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/expo.jpeg -------------------------------------------------------------------------------- /packages/website/static/img/logos/express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/express.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/flowbite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/formik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/formik.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/fylgja.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/gatsby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/gatsby.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/gns3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/gns3.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/gradle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/gradle.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/graphql.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/graphql.jpg -------------------------------------------------------------------------------- /packages/website/static/img/logos/homebrew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/homebrew.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/hugo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/hugo.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/jekyll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/jekyll.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/jest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/jest.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/jquery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/jquery.jpg -------------------------------------------------------------------------------- /packages/website/static/img/logos/laravel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/markdoc.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/material-ui.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/momentjs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/nestjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/nestjs.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/react-admin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/react-bootstrap.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/react-native.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/react.jpg -------------------------------------------------------------------------------- /packages/website/static/img/logos/remix.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/requests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/requests.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/sass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/sass.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/scala.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/snap.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/socketio.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/styled-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/styled-components.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/tachiyomi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/tailwindcss.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/testing-library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/testing-library.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/twilio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/twilio.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/typescriptlang.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/vant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/logos/vant.png -------------------------------------------------------------------------------- /packages/website/static/img/logos/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/vitepress.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/logos/webpack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/website/static/img/ressources/illus1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/ressources/illus1.png -------------------------------------------------------------------------------- /packages/website/static/img/ressources/illus2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/ressources/illus2.png -------------------------------------------------------------------------------- /packages/website/static/img/ressources/illus3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/docsearch/ba5bdd8a84544b1f92e0d9a88eb12620ce573528/packages/website/static/img/ressources/illus3.png -------------------------------------------------------------------------------- /packages/website/tailwind.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | content: ['./src/**/*.html', './src/**/*.js', './src/**/*.tsx'], 3 | corePlugins: { preflight: false, container: false }, 4 | important: '#tailwind', 5 | theme: { 6 | extend: { 7 | colors: { 8 | algolia: '#1C52FF', 9 | }, 10 | maxWidth: { 11 | xxs: '18rem', 12 | }, 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/website/versioned_docs/version-legacy/dropdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dropdown Search-UI 3 | --- 4 | 5 | Once your Algolia DocSearch index is ready, set up, and filled with the right data, you will need to integrate our dedicated Search-UI. To add the dropdown of results below your search input, you'll have to include our DocSearch library into your website as per the following example. 6 | 7 | ```html 8 | 9 | 13 | 14 | 15 | 16 | 31 | ``` 32 | 33 | You need to integrate this snippet into every page that integrates the dropdown UI. 34 | 35 | ## Testing 36 | 37 | If you're eager to test DocSearch but don't have any credentials of your own yet, you can try out the one we use on this website: 38 | 39 | ```javascript 40 | docsearch({ 41 | appId: 'R2IYF7ETH7', 42 | apiKey: '599cec31baffa4868cae4e79f180729b', 43 | indexName: 'docsearch', 44 | }); 45 | ``` 46 | -------------------------------------------------------------------------------- /packages/website/versioned_docs/version-legacy/how-do-we-build-an-index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: How do we build a DocSearch index? 3 | --- 4 | 5 | import useBaseUrl from '@docusaurus/useBaseUrl'; 6 | 7 | In this section you will learn how we build a DocSearch index from your page. 8 | 9 | ## Everything starts from your page 10 | 11 | 1st step 15 | 16 | ## We extract the payload with to your set of `selectors` 17 | 18 | 2nd step 22 | 23 | We will focus on the highlighted information depending on your selectors. 24 | 25 | ## We iterate through the HTML flow and build the payload 26 | 27 | 3rd step 31 | 32 | This payload will be the only data extracted from your page. 33 | 34 | ## We iterate through the payload and start pushing records 35 | 36 | 4th step 40 | 41 | We index the temporary record when we add an element to it (if `min_indexed_level` equals `0`) 42 | 43 | ## We pile up the elements based on the current temporary record 44 | 45 | 5th step 49 | 50 | Based on the position within the flow, we nest elements as much as possible to keep the context and increase the relevancy. 51 | 52 | ## We iterate until we match a `text` element 53 | 54 | 6th step 58 | 59 | ## We override the text element when we find a newer one 60 | 61 | 7th step 65 | 66 | ## We remove the stashed, deeper elements when we add a higher level 67 | 68 | 8th step 72 | 73 | Contextual information and hierarchy must be updated once we encounter a new level. We are doing that because it highlights a new sub-section not related to the previous one. 74 | 75 | If you need any further information, please connect with us on [Discord][1] or let our [support][2] team know. 76 | 77 | [1]: https://alg.li/discord 78 | [2]: https://support.algolia.com/ 79 | -------------------------------------------------------------------------------- /packages/website/versioned_docs/version-legacy/inside-the-engine.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Inside the engine 3 | --- 4 | 5 | This page explains in more detail how the crawler extracts content from your page, and how it ranks the results. 6 | 7 | ## Crawling 8 | 9 | Each crawl begins its journey at `start_urls` value specified in your config. It will read those pages, recursively extract and follow every link in those pages until it has browsed every compliant page. 10 | 11 | If you have explicitly defined a `sitemap.xml`, our crawler will scrape every provided and compliant page. We recommend using [a sitemap][1] since it explicitly exposes URLs to crawl and avoid missing pages that aren't linked from another page. 12 | 13 | ## Extracting content 14 | 15 | Building records using the scraper is pretty intuitive. Based on your settings, we extract the payload of your web page and index it, preserving your data structure. It achieves this in a simple way: 16 | 17 | - We **read top down** your web page following your HTML flow and pick out your matching elements according to their **levels** based on the `selectors_level` defined. 18 | - We create a record for each paragraph along with its hierarchical path. This construction is based on their **time of appearance** along the flow. 19 | - We **index** these records with the appropriate global settings (e.g. metadata, tags, etc.) 20 | 21 | _**Note:** The above process performs sanity tests as it scrapes to detect errors. If there are any serious warnings, it aborts and hence does not overwrite your current index. These checks ensure that your dedicated index isn't flushed._ 22 | 23 | You can [find more explanations in this dedicated section.][2] 24 | 25 | ## Ranking records 26 | 27 | Algolia always returns the most relevant results first, using a [tie-breaking approach][3]. DocSearch will first search for exact matches in your keywords, and then fallback to partial matches. It sorts those results, once again, on the page hierarchy, as extracted from the `selectors`. 28 | 29 | The default strategy is to promote records having matching words in the highest level first. Thus if two results have the same matching words, the one having them in the highest level (`lvl0`) will be ranked higher. We also use the position of the matching words. The sooner they appear within the HTML flow, the higher the record will be ranked. 30 | 31 | We base relevancy on several factors and customize it according to the Algolia tie-breaking method. 32 | 33 | You can boost pages depending on their URLs. You should use the `start_urls` and its `page_rank` attributes. Its value is a numeric value (defaults to 0). The higher the value is, the higher results from the matching pages are ranked. For example, all pages with a `page_rank` of 5 will be returned before pages with a `page_rank` of 1. 34 | 35 | You could even change the relevancy strategy by [overwriting the default `customRanking`][4] used by the index by using the `custom_settings` option of your config. 36 | 37 | [1]: https://www.sitemaps.org/ 38 | [2]: /docs/legacy/how-do-we-build-an-index 39 | [3]: https://www.algolia.com/doc/guides/ranking/ranking-formula/#tie-breaking-approach 40 | [4]: https://www.algolia.com/doc/guides/ranking/custom-ranking/ 41 | -------------------------------------------------------------------------------- /packages/website/versioned_docs/version-legacy/integrations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Supported Integrations 3 | --- 4 | 5 | We worked with **documentation website generators** to have DocSearch directly embedded as a first class citizen in the websites they produce. 6 | 7 | ## Our great integrations 8 | 9 | If you're using one of the following tools, checkout their documentation to see how to enable DocSearch on your website: 10 | 11 | - [Docusaurus v1][1] - [How to enable search][2] 12 | - [Docusaurus v2][3] - [Using Algolia DocSearch][4] 13 | - [VuePress][5] - [Algolia Search][6] 14 | - [pkgdown][7] - [DocSearch indexing][8] 15 | - [LaRecipe][9] - [Algolia Search][10] 16 | - [Orchid][11] - [Algolia Search][12] 17 | - [Smooth DOC][13] - [DocSearch][14] 18 | - [Docsy][15] - [Configure Algolia DocSearch][16] 19 | 20 | If you're maintaining a similar tool and want us to add you to the list, [get in touch with us on Discord][17]. We'd be happy to help. 21 | 22 | [1]: https://v1.docusaurus.io/ 23 | [2]: https://v1.docusaurus.io/docs/en/search 24 | [3]: https://docusaurus.io/ 25 | [4]: https://docusaurus.io/docs/search#using-algolia-docsearch 26 | [5]: https://vuepress.vuejs.org/ 27 | [6]: https://vuepress.vuejs.org/theme/default-theme-config.html#algolia-search 28 | [7]: https://pkgdown.r-lib.org/ 29 | [8]: https://pkgdown.r-lib.org/articles/search.html 30 | [9]: https://larecipe.binarytorch.com.my/docs/2.2/overview 31 | [10]: https://larecipe.binarytorch.com.my/docs/2.2/configurations#search 32 | [11]: https://orchid.run 33 | [12]: https://orchid.run/plugins/orchidsearch#algolia-docsearch 34 | [13]: https://smooth-doc.com/ 35 | [14]: https://smooth-doc.com/docs/docsearch/ 36 | [15]: https://www.docsy.dev/ 37 | [16]: https://www.docsy.dev/docs/adding-content/navigation/#configure-algolia-docsearch 38 | [17]: https://alg.li/discord 39 | -------------------------------------------------------------------------------- /packages/website/versioned_docs/version-legacy/scraper-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scraper Overview 3 | --- 4 | 5 | ## How? 6 | 7 | The DocSearch scraper is written in Python and heavily inspired by the [Scrapy][1] framework. It goes through all pages of your website and extracts content from the HTML structure to populate an Algolia index. 8 | 9 | It automatically follows every internal link to make sure we are not missing any content, and uses the semantics of your HTML structure to construct its records. This means that `h1`,`h2`, etc., (`selectors`) titles are used as hierarchy, and each `p` is used as a potential result. 10 | 11 | Those CSS selectors can be overwritten, and each website has its own JSON configuration file that describes in more detail how the scraper should behave. You can find the complete list of options in [the related section][2]. 12 | 13 | If you'd like to [run DocSearch on your own][3], all the code is open sourced and even packaged as a Docker image. Download it, and run it with your own credentials. 14 | 15 | [1]: https://scrapy.org/ 16 | [2]: /docs/legacy/config-file 17 | [3]: /docs/legacy/run-your-own 18 | -------------------------------------------------------------------------------- /packages/website/versioned_docs/version-legacy/styling.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Styling DocSearch 3 | --- 4 | 5 | import useBaseUrl from '@docusaurus/useBaseUrl'; 6 | 7 | DocSearch default color scheme comes in a grey theme with blue highlight. 8 | 9 | ## Colorscheme 10 | 11 | Default colorscheme 15 | 16 | This theme works well with most websites, but we encourage you to style it to your own theme. You can achieved it by overriding the CSS classes used by the default theme. 17 | 18 | The following annotated example will help you style each part: 19 | 20 | ```css 21 | /* Main dropdown wrapper */ 22 | .algolia-autocomplete .ds-dropdown-menu { 23 | width: 500px; 24 | } 25 | 26 | /* Main category (eg. Getting Started) */ 27 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header { 28 | color: darkgray; 29 | border: 1px solid gray; 30 | } 31 | 32 | /* Category (eg. Downloads) */ 33 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { 34 | color: gray; 35 | } 36 | 37 | /* Title (eg. Bootstrap CDN) */ 38 | .algolia-autocomplete .algolia-docsearch-suggestion--title { 39 | font-weight: bold; 40 | color: black; 41 | } 42 | 43 | /* Description description (eg. Bootstrap currently works...) */ 44 | .algolia-autocomplete .algolia-docsearch-suggestion--text { 45 | font-size: 0.8rem; 46 | color: gray; 47 | } 48 | 49 | /* Highlighted text */ 50 | .algolia-autocomplete .algolia-docsearch-suggestion--highlight { 51 | color: blue; 52 | } 53 | ``` 54 | 55 | ## Attribution 56 | 57 | We're happy to provide DocSearch free of charge for any documentation website, and you're encouraged to style it to fit your own theming. All we ask is that you keep the "Search by Algolia" logo and link next to your search results. 58 | 59 | The logo is automatically added in the dropdown with the default styling. It's our way to let more people know about what we do, and how they could benefit from a fast and relevant search on their website. 60 | 61 | If you're using your own [paid Algolia account][1] and [run the crawler yourself][2], you don't have to keep the logo. 62 | 63 | ## Debugging 64 | 65 | To inspect the dropdown markup with your browser tools, you should add `debug: true` to your `docsearch` call to prevent it from closing on inspection. 66 | 67 | ```javascript 68 | docsearch({ 69 | […], 70 | debug: true 71 | }); 72 | ``` 73 | 74 | ## Other considerations 75 | 76 | The dropdown wraps selected suggestions in a `.ds-cursor` class. This means that you can use `.ds-cursor .algolia-docsearch-suggestion--content` to style the selected suggestion for example. 77 | 78 | On small screens, DocSearch reverts to a single column layout, while is uses the two-column layout shown in the screenshot on larger screens. You can add media queries (for example `@media (min-width: 768px) {}`) to target different displays. 79 | 80 | ## Advanced styling 81 | 82 | Whether you would like to do more heavy styling, feel free to have a look at the [SCSS source code][3]. `_variables.scss` contains all the default theming, sizing and breakpoints. 83 | 84 | You can generate your own CSS file by cloning the repository and running `yarn run build:css`. It generates the resulting file in `./dist/cdn`, and you should use them instead of the default one. 85 | 86 | [1]: https://www.algolia.com/pricing 87 | [2]: /docs/legacy/scraper-overview 88 | [3]: https://github.com/algolia/docsearch/tree/master/src/styles 89 | -------------------------------------------------------------------------------- /packages/website/versioned_sidebars/version-legacy-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": [ 3 | { 4 | "collapsed": true, 5 | "type": "category", 6 | "label": "Adding DocSearch to your UI", 7 | "items": [ 8 | { 9 | "type": "doc", 10 | "id": "dropdown" 11 | }, 12 | { 13 | "type": "doc", 14 | "id": "styling" 15 | }, 16 | { 17 | "type": "doc", 18 | "id": "behavior" 19 | } 20 | ] 21 | }, 22 | { 23 | "collapsed": true, 24 | "type": "category", 25 | "label": "Requirements, tips, FAQ", 26 | "items": [ 27 | { 28 | "type": "doc", 29 | "id": "required-configuration" 30 | }, 31 | { 32 | "type": "doc", 33 | "id": "tips" 34 | }, 35 | { 36 | "type": "doc", 37 | "id": "faq" 38 | }, 39 | { 40 | "type": "doc", 41 | "id": "integrations" 42 | } 43 | ] 44 | }, 45 | { 46 | "collapsed": true, 47 | "type": "category", 48 | "label": "Under the Hood", 49 | "items": [ 50 | { 51 | "type": "doc", 52 | "id": "inside-the-engine" 53 | }, 54 | { 55 | "type": "doc", 56 | "id": "config-file" 57 | }, 58 | { 59 | "type": "doc", 60 | "id": "how-do-we-build-an-index" 61 | }, 62 | { 63 | "type": "doc", 64 | "id": "run-your-own" 65 | } 66 | ] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /packages/website/versions.json: -------------------------------------------------------------------------------- 1 | ["legacy"] 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:js-lib", 5 | "github>algolia/renovate-config-algolia" 6 | ], 7 | "baseBranches": [ 8 | "chore/renovateBaseBranch" 9 | ], 10 | "packageRules": [ 11 | { 12 | "packagePatterns": [ 13 | "^@types" 14 | ], 15 | "depTypeList": [ 16 | "devDependencies" 17 | ], 18 | "rangeStrategy": "replace" 19 | } 20 | ], 21 | "prHourlyLimit": 20 22 | } 23 | -------------------------------------------------------------------------------- /rollup.base.config.js: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import json from '@rollup/plugin-json'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import replace from '@rollup/plugin-replace'; 5 | import terser from '@rollup/plugin-terser'; 6 | import { dts } from 'rollup-plugin-dts'; 7 | import filesize from 'rollup-plugin-filesize'; 8 | 9 | export const plugins = [ 10 | replace({ 11 | preventAssignment: true, 12 | __DEV__: JSON.stringify(process.env.NODE_ENV === 'development'), 13 | }), 14 | json(), 15 | resolve({ 16 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], 17 | browser: true, 18 | }), 19 | babel({ 20 | babelHelpers: 'bundled', 21 | exclude: 'node_modules/**', 22 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], 23 | rootMode: 'upward', 24 | }), 25 | terser(), 26 | filesize({ 27 | showMinifiedSize: false, 28 | showGzippedSize: true, 29 | }), 30 | ]; 31 | 32 | export const typesConfig = { 33 | input: 'dist/esm/types/index.d.ts', 34 | output: [{ file: 'dist/esm/index.d.ts', format: 'es' }], 35 | plugins: [dts()], 36 | }; 37 | -------------------------------------------------------------------------------- /scripts/getBundleBanner.js: -------------------------------------------------------------------------------- 1 | import { execSync } from 'child_process'; 2 | 3 | export function getBundleBanner(pkg) { 4 | const lastCommitHash = execSync('git rev-parse --short HEAD').toString().trim(); 5 | const version = process.env.SHIPJS ? pkg.version : `${pkg.version} (UNRELEASED ${lastCommitHash})`; 6 | const authors = '© Algolia, Inc. and contributors'; 7 | 8 | return `/*! ${pkg.name} ${version} | MIT License | ${authors} | ${pkg.homepage} */`; 9 | } 10 | -------------------------------------------------------------------------------- /scripts/setupTests.ts: -------------------------------------------------------------------------------- 1 | import type { Mock } from 'vitest'; 2 | import { vi } from 'vitest'; 3 | 4 | type MatchMediaProps = Partial<{ 5 | matches: boolean; 6 | media: string; 7 | onchange: null; 8 | addListener: Mock; 9 | removeListener: Mock; 10 | addEventListener: Mock; 11 | removeEventListener: Mock; 12 | dispatchEvent: Mock; 13 | }>; 14 | 15 | const createMatchMedia = (props: MatchMediaProps): Mock => { 16 | return vi.fn((query) => ({ 17 | matches: false, 18 | media: query, 19 | onchange: null, 20 | addListener: vi.fn(), 21 | removeListener: vi.fn(), 22 | addEventListener: vi.fn(), 23 | removeEventListener: vi.fn(), 24 | dispatchEvent: vi.fn(), 25 | ...props, 26 | })); 27 | }; 28 | 29 | vi.stubGlobal('scrollTo', vi.fn()); 30 | vi.stubGlobal('matchMedia', createMatchMedia({ matches: true })); 31 | -------------------------------------------------------------------------------- /ship.config.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const packages = ['packages/docsearch-css', 'packages/docsearch-react', 'packages/docsearch-js']; 5 | 6 | export default { 7 | monorepo: { 8 | mainVersionFile: 'lerna.json', 9 | // We rely on Lerna to bump our dependencies. 10 | packagesToBump: [], 11 | packagesToPublish: packages, 12 | }, 13 | publishCommand({ tag }) { 14 | return `npm publish --tag ${tag}`; 15 | }, 16 | versionUpdated({ exec, dir, version }) { 17 | // Update package dependencies 18 | exec(`yarn lerna version ${version} --exact --no-git-tag-version --no-push --yes`); 19 | 20 | // Ship.js reads JSON and writes with `fs.writeFileSync(JSON.stringify(json, null, 2))` 21 | // which causes a lint error in the `lerna.json` file. 22 | exec('yarn eslint lerna.json --fix'); 23 | 24 | fs.writeFileSync( 25 | path.resolve(dir, 'packages', 'docsearch-react', 'src', 'version.ts'), 26 | `export const version = '${version}';\n`, 27 | ); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /tsconfig.declaration.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "noEmit": false, 5 | "declaration": true, 6 | "emitDeclarationOnly": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react", 5 | "module": "NodeNext", 6 | "moduleResolution": "NodeNext", 7 | "noFallthroughCasesInSwitch": true, 8 | "noImplicitAny": false, 9 | "noImplicitReturns": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "strict": true, 15 | "target": "ESNEXT" 16 | }, 17 | "exclude": [ 18 | "**/__fixtures__/**/*", 19 | "**/__mocks__/**/*", 20 | "**/dist", 21 | "**/node_modules", 22 | "cypress", 23 | "examples" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /vite.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: 'jsdom', 6 | setupFiles: ['./scripts/setupTests.ts'], 7 | } 8 | }); 9 | --------------------------------------------------------------------------------