diff --git a/README.md b/README.md index 127016f138..f8580cbf2f 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM | Scrubbable/draggable scrollbar | Yes | Yes | | Support RAW (HEIC, HEIF, DNG, Apple ProRaw) | Yes | Yes | | Metadata view (EXIF, map) | Yes | Yes | -| Search by metadata, objects and image tags | Yes | No | +| Search by metadata, objects and CLIP | Yes | No | | Administrative functions (user management) | N/A | Yes | | Background backup | Yes | N/A | | Virtual scroll | Yes | Yes | diff --git a/docs/docs/developer/architecture.md b/docs/docs/developer/architecture.md index 85295c4791..2c0ea51ebc 100644 --- a/docs/docs/developer/architecture.md +++ b/docs/docs/developer/architecture.md @@ -20,7 +20,7 @@ Immich is a full-stack [TypeScript](https://www.typescriptlang.org/) application ### Web - [SvelteKit](https://kit.svelte.dev/) -- [tailwindcss](https://tailwindcss.com/) +- [Tailwindcss](https://tailwindcss.com/) ### Server @@ -34,6 +34,8 @@ Immich is a full-stack [TypeScript](https://www.typescriptlang.org/) application - [PostgreSQL](https://www.postgresql.org/) - [Redis](https://redis.io/) for job queuing. +- [Typesense](https://typesense.org/) for search. + ### Web Server diff --git a/docs/docs/developer/img/app-architecture.png b/docs/docs/developer/img/app-architecture.png index 6d1f15226b..ed91fe40a0 100644 Binary files a/docs/docs/developer/img/app-architecture.png and b/docs/docs/developer/img/app-architecture.png differ diff --git a/docs/docs/features/img/search-ex-2.webp b/docs/docs/features/img/search-ex-2.webp new file mode 100644 index 0000000000..c33285bc67 Binary files /dev/null and b/docs/docs/features/img/search-ex-2.webp differ diff --git a/docs/docs/features/img/search-ex-3.webp b/docs/docs/features/img/search-ex-3.webp new file mode 100644 index 0000000000..6c5e0bfb78 Binary files /dev/null and b/docs/docs/features/img/search-ex-3.webp differ diff --git a/docs/docs/features/search.md b/docs/docs/features/search.md index e6be3521af..954dfabc4c 100644 --- a/docs/docs/features/search.md +++ b/docs/docs/features/search.md @@ -1,16 +1,20 @@ # Search -:::warning Work In Progress -Search is work-in-progress and subject to change. Stay tuned! -::: +Immich uses Typesense as the primary search database to enable high performance search mechanism. -## Search by Place +Typesense is a powerful search engine that can be integrated with popular natural language processing (NLP) models like CLIP and SBERT to provide highly accurate and relevant search results. Here are some benefits of using Typesense integrated search for CLIP and SBERT: -:::info -Searching is currently only implemented in the [Mobile App](/docs/features/mobile-app.mdx) -::: +Improved Search Accuracy: Typesense uses a combination of indexing, querying, and ranking algorithms to quickly and accurately retrieve relevant search results. When integrated with CLIP and SBERT, Typesense can leverage the semantic understanding and deep learning capabilities of these models to further improve the accuracy of search results. -Searching by the name of a city, state, or country is possible for assets with geolocation data and successful [Reverse Geocoding](/docs/features/reverse-geocoding.md). +Faster Search Response Times: Typesense is optimized for lightning-fast search response times, making it ideal for applications that require near-instantaneous search results. By integrating with CLIP and SBERT, Typesense can reduce the time required to process complex search queries, making it even faster and more efficient. - - +Enhanced Semantic Search Capabilities: CLIP and SBERT are powerful NLP models that can extract the semantic meaning from text, enabling more nuanced search queries. By integrating with Typesense, these models can help to improve the accuracy of semantic search, enabling users to find the most relevant results based on the true meaning of their query. + +Greater Search Flexibility: Typesense provides flexible search capabilities, including fuzzy search, partial search, enabling users to find the information they need quickly and easily. When integrated with CLIP and SBERT, Typesense can offer even greater flexibility, allowing users to refine their search queries using natural language and providing more accurate and relevant results. + +(Generated by Chat-GPT4) + +Some search examples: + + + diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index a33446959b..556e60a3a9 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,70 +1,71 @@ // @ts-check // Note: type annotations allow type checking and IDEs autocompletion -const lightCodeTheme = require("prism-react-renderer/themes/github"); -const darkCodeTheme = require("prism-react-renderer/themes/dracula"); +const lightCodeTheme = require('prism-react-renderer/themes/github'); +const darkCodeTheme = require('prism-react-renderer/themes/dracula'); /** @type {import('@docusaurus/types').Config} */ const config = { - title: "Immich", + title: 'Immich', tagline: - "High performance self-hosted photo and video backup solution directly from your mobile phone", - url: "https://documentation.immich.app", - baseUrl: "/", - onBrokenLinks: "throw", - onBrokenMarkdownLinks: "warn", - favicon: "img/favicon.png", + 'High performance self-hosted photo and video backup solution directly from your mobile phone', + url: 'https://documentation.immich.app', + baseUrl: '/', + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + favicon: 'img/favicon.png', // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. - organizationName: "immich-app", // Usually your GitHub org/user name. - projectName: "immich", // Usually your repo name. - deploymentBranch: "main", + organizationName: 'immich-app', // Usually your GitHub org/user name. + projectName: 'immich', // Usually your repo name. + deploymentBranch: 'main', // Even if you don't use internalization, you can use this field to set useful // metadata like html lang. For example, if your site is Chinese, you may want // to replace "en" with "zh-Hans". i18n: { - defaultLocale: "en", - locales: ["en"], + defaultLocale: 'en', + locales: ['en'], }, plugins: [ async function myPlugin(context, options) { return { - name: "docusaurus-tailwindcss", + name: 'docusaurus-tailwindcss', configurePostCss(postcssOptions) { // Appends TailwindCSS and AutoPrefixer. - postcssOptions.plugins.push(require("tailwindcss")); - postcssOptions.plugins.push(require("autoprefixer")); + postcssOptions.plugins.push(require('tailwindcss')); + postcssOptions.plugins.push(require('autoprefixer')); return postcssOptions; }, }; }, + require.resolve('docusaurus-lunr-search'), ], presets: [ [ - "docusaurus-preset-openapi", + 'docusaurus-preset-openapi', /** @type {import('docusaurus-preset-openapi').Options} */ ({ docs: { showLastUpdateAuthor: true, showLastUpdateTime: true, - sidebarPath: require.resolve("./sidebars.js"), + sidebarPath: require.resolve('./sidebars.js'), // Please change this to your repo. // Remove this to remove the "edit this page" links. - editUrl: "https://github.com/immich-app/immich/tree/main/docs/", + editUrl: 'https://github.com/immich-app/immich/tree/main/docs/', }, api: { - path: "../server/immich-openapi-specs.json", - routeBasePath: "/docs/api", + path: '../server/immich-openapi-specs.json', + routeBasePath: '/docs/api', }, // blog: { // showReadingTime: true, // editUrl: "https://github.com/immich-app/immich/tree/main/docs/", // }, theme: { - customCss: require.resolve("./src/css/custom.css"), + customCss: require.resolve('./src/css/custom.css'), }, }), ], @@ -74,13 +75,13 @@ const config = { /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ colorMode: { - defaultMode: "dark", + defaultMode: 'dark', }, announcementBar: { - id: "site_announcement_immich", + id: 'site_announcement_immich', content: `⚠️ The project is under very active development. Expect bugs and changes. Do not use it as the only way to store your photos and videos!`, - backgroundColor: "#593f00", - textColor: "#ffefc9", + backgroundColor: '#593f00', + textColor: '#ffefc9', isCloseable: false, }, docs: { @@ -90,72 +91,72 @@ const config = { }, navbar: { logo: { - alt: "Immich University Logo", - src: "img/color-logo.png", - srcDark: "img/logo.png", + alt: 'Immich University Logo', + src: 'img/color-logo.png', + srcDark: 'img/logo.png', }, items: [ { - to: "/docs/overview/introduction", - position: "right", - label: "Docs", + to: '/docs/overview/introduction', + position: 'right', + label: 'Docs', }, { - to: "/docs/api", - position: "right", - label: "API", + to: '/docs/api', + position: 'right', + label: 'API', }, { - href: "https://github.com/immich-app/immich", - label: "GitHub", - position: "right", + href: 'https://github.com/immich-app/immich', + label: 'GitHub', + position: 'right', }, { - href: "https://github.com/orgs/immich-app/projects/1", - label: "Roadmap", - position: "right", + href: 'https://github.com/orgs/immich-app/projects/1', + label: 'Roadmap', + position: 'right', }, ], }, footer: { - style: "light", + style: 'light', links: [ { - title: "Overview", + title: 'Overview', items: [ { - label: "Welcome", - to: "/docs/overview/introduction", + label: 'Welcome', + to: '/docs/overview/introduction', }, { - label: "Installation", - to: "/docs/install/requirements", + label: 'Installation', + to: '/docs/install/requirements', }, ], }, { - title: "Community", + title: 'Community', items: [ { - label: "Discord", - href: "https://discord.com/invite/D8JsnBEuKb", + label: 'Discord', + href: 'https://discord.com/invite/D8JsnBEuKb', }, ], }, { - title: "Links", + title: 'Links', items: [ // { // label: "Blog", // to: "/blog", // }, { - label: "GitHub", - href: "https://github.com/immich-app/immich", + label: 'GitHub', + href: 'https://github.com/immich-app/immich', }, { - label: "Roadmap", - href: "https://github.com/orgs/immich-app/projects/1", + label: 'Roadmap', + href: 'https://github.com/orgs/immich-app/projects/1', }, ], }, @@ -166,7 +167,7 @@ const config = { theme: lightCodeTheme, darkTheme: darkCodeTheme, }, - image: "overview/img/feature-panel.png", + image: 'overview/img/feature-panel.png', }), }; diff --git a/docs/package-lock.json b/docs/package-lock.json index 58492106af..999d7389d2 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -13,6 +13,7 @@ "@mdx-js/react": "^1.6.22", "autoprefixer": "^10.4.13", "clsx": "^1.2.1", + "docusaurus-lunr-search": "^2.3.2", "docusaurus-preset-openapi": "^0.6.3", "postcss": "^8.4.20", "prism-react-renderer": "^1.3.5", @@ -3634,6 +3635,11 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3903,6 +3909,11 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -3957,6 +3968,14 @@ "node": ">= 4.0.0" } }, + "node_modules/autocomplete.js": { + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.37.1.tgz", + "integrity": "sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ==", + "dependencies": { + "immediate": "^3.2.3" + } + }, "node_modules/autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -4148,6 +4167,15 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/bcp-47-match": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz", + "integrity": "sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -4583,6 +4611,11 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==" }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -4743,6 +4776,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -4887,6 +4928,11 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -5278,6 +5324,11 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-selector-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz", + "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==" + }, "node_modules/css-tree": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", @@ -5664,6 +5715,18 @@ "node": ">=8" } }, + "node_modules/direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -5685,6 +5748,53 @@ "node": ">=6" } }, + "node_modules/docusaurus-lunr-search": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-2.3.2.tgz", + "integrity": "sha512-Ngvm2kXwliWThqAThXI1912rOKHlFL7BjIc+OVNUfzkjpk5ar4TFEh+EUaaMOLw4V0BBko3CW0Ym7prqqm3jLQ==", + "dependencies": { + "autocomplete.js": "^0.37.0", + "classnames": "^2.2.6", + "gauge": "^3.0.0", + "hast-util-select": "^4.0.0", + "hast-util-to-text": "^2.0.0", + "hogan.js": "^3.0.2", + "lunr": "^2.3.8", + "lunr-languages": "^1.4.0", + "minimatch": "^3.0.4", + "object-assign": "^4.1.1", + "rehype-parse": "^7.0.1", + "to-vfile": "^6.1.0", + "unified": "^9.0.0", + "unist-util-is": "^4.0.2" + }, + "engines": { + "node": ">= 8.10.0" + }, + "peerDependencies": { + "@docusaurus/core": "^2.0.0-alpha.60 || ^2.0.0", + "react": "^16.8.4 || ^17", + "react-dom": "^16.8.4 || ^17" + } + }, + "node_modules/docusaurus-lunr-search/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/docusaurus-lunr-search/node_modules/rehype-parse": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", + "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", + "dependencies": { + "hast-util-from-parse5": "^6.0.0", + "parse5": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/docusaurus-plugin-openapi": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.6.3.tgz", @@ -6689,6 +6799,43 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6994,6 +7141,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -7037,6 +7189,24 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-has-property": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz", + "integrity": "sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", + "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-parse-selector": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", @@ -7072,6 +7242,31 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "node_modules/hast-util-select": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-4.0.2.tgz", + "integrity": "sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg==", + "dependencies": { + "bcp-47-match": "^1.0.0", + "comma-separated-tokens": "^1.0.0", + "css-selector-parser": "^1.0.0", + "direction": "^1.0.0", + "hast-util-has-property": "^1.0.0", + "hast-util-is-element": "^1.0.0", + "hast-util-to-string": "^1.0.0", + "hast-util-whitespace": "^1.0.0", + "not": "^0.1.0", + "nth-check": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0", + "unist-util-visit": "^2.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-parse5": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", @@ -7088,6 +7283,38 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-string": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz", + "integrity": "sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz", + "integrity": "sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==", + "dependencies": { + "hast-util-is-element": "^1.0.0", + "repeat-string": "^1.0.0", + "unist-util-find-after": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", + "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hastscript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", @@ -7133,6 +7360,18 @@ "value-equal": "^1.0.1" } }, + "node_modules/hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", + "dependencies": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + }, + "bin": { + "hulk": "bin/hulk" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -7435,6 +7674,11 @@ "node": ">=14.0.0" } }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, "node_modules/immer": { "version": "9.0.16", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", @@ -8232,6 +8476,16 @@ "node": ">=10" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "node_modules/lunr-languages": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.10.0.tgz", + "integrity": "sha512-BBjKKcwrieJlzwwc9M5H/MRXGJ2qyOSDx/NXYiwkuKjiLOOoouh0WsDzeqcLoUWcX31y7i8sb8IgsZKObdUCkw==" + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -8538,6 +8792,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "engines": { + "node": "*" + } + }, "node_modules/monaco-editor": { "version": "0.31.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", @@ -8657,6 +8920,20 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -8684,6 +8961,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/not": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz", + "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==" + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -12774,6 +13056,19 @@ "node": ">=8.0" } }, + "node_modules/to-vfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz", + "integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==", + "dependencies": { + "is-buffer": "^2.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -12989,6 +13284,18 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-find-after": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz", + "integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==", + "dependencies": { + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-generated": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", @@ -13943,6 +14250,32 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -16831,6 +17164,11 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -17031,6 +17369,11 @@ "picomatch": "^2.0.4" } }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, "arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -17076,6 +17419,14 @@ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, + "autocomplete.js": { + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/autocomplete.js/-/autocomplete.js-0.37.1.tgz", + "integrity": "sha512-PgSe9fHYhZEsm/9jggbjtVsGXJkPLvd+9mC7gZJ662vVL5CRWEtm/mIrrzCx0MrNxHVwxD5d00UOn6NsmL2LUQ==", + "requires": { + "immediate": "^3.2.3" + } + }, "autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -17206,6 +17557,11 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "bcp-47-match": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz", + "integrity": "sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -17502,6 +17858,11 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==" }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-css": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", @@ -17624,6 +17985,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -17744,6 +18110,11 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -18000,6 +18371,11 @@ "nth-check": "^2.0.1" } }, + "css-selector-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz", + "integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==" + }, "css-tree": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", @@ -18268,6 +18644,11 @@ "path-type": "^4.0.0" } }, + "direction": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", + "integrity": "sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==" + }, "dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -18286,6 +18667,43 @@ "@leichtgewicht/ip-codec": "^2.0.1" } }, + "docusaurus-lunr-search": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/docusaurus-lunr-search/-/docusaurus-lunr-search-2.3.2.tgz", + "integrity": "sha512-Ngvm2kXwliWThqAThXI1912rOKHlFL7BjIc+OVNUfzkjpk5ar4TFEh+EUaaMOLw4V0BBko3CW0Ym7prqqm3jLQ==", + "requires": { + "autocomplete.js": "^0.37.0", + "classnames": "^2.2.6", + "gauge": "^3.0.0", + "hast-util-select": "^4.0.0", + "hast-util-to-text": "^2.0.0", + "hogan.js": "^3.0.2", + "lunr": "^2.3.8", + "lunr-languages": "^1.4.0", + "minimatch": "^3.0.4", + "object-assign": "^4.1.1", + "rehype-parse": "^7.0.1", + "to-vfile": "^6.1.0", + "unified": "^9.0.0", + "unist-util-is": "^4.0.2" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "rehype-parse": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", + "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", + "requires": { + "hast-util-from-parse5": "^6.0.0", + "parse5": "^6.0.0" + } + } + } + }, "docusaurus-plugin-openapi": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi/-/docusaurus-plugin-openapi-0.6.3.tgz", @@ -19036,6 +19454,39 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -19266,6 +19717,11 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -19298,6 +19754,16 @@ "web-namespaces": "^1.0.0" } }, + "hast-util-has-property": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz", + "integrity": "sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg==" + }, + "hast-util-is-element": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", + "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==" + }, "hast-util-parse-selector": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", @@ -19327,6 +19793,27 @@ } } }, + "hast-util-select": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-4.0.2.tgz", + "integrity": "sha512-8EEG2//bN5rrzboPWD2HdS3ugLijNioS1pqOTIolXNf67xxShYw4SQEmVXd3imiBG+U2bC2nVTySr/iRAA7Cjg==", + "requires": { + "bcp-47-match": "^1.0.0", + "comma-separated-tokens": "^1.0.0", + "css-selector-parser": "^1.0.0", + "direction": "^1.0.0", + "hast-util-has-property": "^1.0.0", + "hast-util-is-element": "^1.0.0", + "hast-util-to-string": "^1.0.0", + "hast-util-whitespace": "^1.0.0", + "not": "^0.1.0", + "nth-check": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0", + "unist-util-visit": "^2.0.0", + "zwitch": "^1.0.0" + } + }, "hast-util-to-parse5": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", @@ -19339,6 +19826,26 @@ "zwitch": "^1.0.0" } }, + "hast-util-to-string": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz", + "integrity": "sha512-eK0MxRX47AV2eZ+Lyr18DCpQgodvaS3fAQO2+b9Two9F5HEoRPhiUMNzoXArMJfZi2yieFzUBMRl3HNJ3Jus3w==" + }, + "hast-util-to-text": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-2.0.1.tgz", + "integrity": "sha512-8nsgCARfs6VkwH2jJU9b8LNTuR4700na+0h3PqCaEk4MAnMDeu5P0tP8mjk9LLNGxIeQRLbiDbZVw6rku+pYsQ==", + "requires": { + "hast-util-is-element": "^1.0.0", + "repeat-string": "^1.0.0", + "unist-util-find-after": "^3.0.0" + } + }, + "hast-util-whitespace": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", + "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==" + }, "hastscript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", @@ -19374,6 +19881,15 @@ "value-equal": "^1.0.1" } }, + "hogan.js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/hogan.js/-/hogan.js-3.0.2.tgz", + "integrity": "sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==", + "requires": { + "mkdirp": "0.3.0", + "nopt": "1.0.10" + } + }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -19589,6 +20105,11 @@ "queue": "6.0.2" } }, + "immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, "immer": { "version": "9.0.16", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", @@ -20162,6 +20683,16 @@ "yallist": "^4.0.0" } }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==" + }, + "lunr-languages": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.10.0.tgz", + "integrity": "sha512-BBjKKcwrieJlzwwc9M5H/MRXGJ2qyOSDx/NXYiwkuKjiLOOoouh0WsDzeqcLoUWcX31y7i8sb8IgsZKObdUCkw==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -20374,6 +20905,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==" + }, "monaco-editor": { "version": "0.31.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.31.1.tgz", @@ -20461,6 +20997,14 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "requires": { + "abbrev": "1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -20476,6 +21020,11 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" }, + "not": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz", + "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==" + }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -23447,6 +23996,15 @@ "is-number": "^7.0.0" } }, + "to-vfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz", + "integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==", + "requires": { + "is-buffer": "^2.0.0", + "vfile": "^4.0.0" + } + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -23588,6 +24146,14 @@ "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==" }, + "unist-util-find-after": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz", + "integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==", + "requires": { + "unist-util-is": "^4.0.0" + } + }, "unist-util-generated": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", @@ -24241,6 +24807,31 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + } + } + }, "widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", diff --git a/docs/package.json b/docs/package.json index 6c7ace1c1e..c358a3338e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,6 +20,7 @@ "@mdx-js/react": "^1.6.22", "autoprefixer": "^10.4.13", "clsx": "^1.2.1", + "docusaurus-lunr-search": "^2.3.2", "docusaurus-preset-openapi": "^0.6.3", "postcss": "^8.4.20", "prism-react-renderer": "^1.3.5", diff --git a/docs/src/theme/SearchBar/DocSearch.js b/docs/src/theme/SearchBar/DocSearch.js new file mode 100644 index 0000000000..ec627d480d --- /dev/null +++ b/docs/src/theme/SearchBar/DocSearch.js @@ -0,0 +1,297 @@ +import Hogan from "hogan.js"; +import LunrSearchAdapter from "./lunar-search"; +import autocomplete from "autocomplete.js"; +import templates from "./templates"; +import utils from "./utils"; +import $ from "autocomplete.js/zepto"; + +class DocSearch { + constructor({ + searchDocs, + searchIndex, + inputSelector, + debug = false, + baseUrl = '/', + queryDataCallback = null, + autocompleteOptions = { + debug: false, + hint: false, + autoselect: true + }, + transformData = false, + queryHook = false, + handleSelected = false, + enhancedSearchInput = false, + layout = "collumns" + }) { + this.input = DocSearch.getInputFromSelector(inputSelector); + this.queryDataCallback = queryDataCallback || null; + const autocompleteOptionsDebug = + autocompleteOptions && autocompleteOptions.debug + ? autocompleteOptions.debug + : false; + // eslint-disable-next-line no-param-reassign + autocompleteOptions.debug = debug || autocompleteOptionsDebug; + this.autocompleteOptions = autocompleteOptions; + this.autocompleteOptions.cssClasses = + this.autocompleteOptions.cssClasses || {}; + this.autocompleteOptions.cssClasses.prefix = + this.autocompleteOptions.cssClasses.prefix || "ds"; + const inputAriaLabel = + this.input && + typeof this.input.attr === "function" && + this.input.attr("aria-label"); + this.autocompleteOptions.ariaLabel = + this.autocompleteOptions.ariaLabel || inputAriaLabel || "search input"; + + this.isSimpleLayout = layout === "simple"; + + this.client = new LunrSearchAdapter(searchDocs, searchIndex, baseUrl); + + if (enhancedSearchInput) { + this.input = DocSearch.injectSearchBox(this.input); + } + this.autocomplete = autocomplete(this.input, autocompleteOptions, [ + { + source: this.getAutocompleteSource(transformData, queryHook), + templates: { + suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout), + footer: templates.footer, + empty: DocSearch.getEmptyTemplate() + } + } + ]); + + const customHandleSelected = handleSelected; + this.handleSelected = customHandleSelected || this.handleSelected; + + // We prevent default link clicking if a custom handleSelected is defined + if (customHandleSelected) { + $(".algolia-autocomplete").on("click", ".ds-suggestions a", event => { + event.preventDefault(); + }); + } + + this.autocomplete.on( + "autocomplete:selected", + this.handleSelected.bind(null, this.autocomplete.autocomplete) + ); + + this.autocomplete.on( + "autocomplete:shown", + this.handleShown.bind(null, this.input) + ); + + if (enhancedSearchInput) { + DocSearch.bindSearchBoxEvent(); + } + } + + static injectSearchBox(input) { + input.before(templates.searchBox); + const newInput = input + .prev() + .prev() + .find("input"); + input.remove(); + return newInput; + } + + static bindSearchBoxEvent() { + $('.searchbox [type="reset"]').on("click", function () { + $("input#docsearch").focus(); + $(this).addClass("hide"); + autocomplete.autocomplete.setVal(""); + }); + + $("input#docsearch").on("keyup", () => { + const searchbox = document.querySelector("input#docsearch"); + const reset = document.querySelector('.searchbox [type="reset"]'); + reset.className = "searchbox__reset"; + if (searchbox.value.length === 0) { + reset.className += " hide"; + } + }); + } + + /** + * Returns the matching input from a CSS selector, null if none matches + * @function getInputFromSelector + * @param {string} selector CSS selector that matches the search + * input of the page + * @returns {void} + */ + static getInputFromSelector(selector) { + const input = $(selector).filter("input"); + return input.length ? $(input[0]) : null; + } + + /** + * Returns the `source` method to be passed to autocomplete.js. It will query + * the Algolia index and call the callbacks with the formatted hits. + * @function getAutocompleteSource + * @param {function} transformData An optional function to transform the hits + * @param {function} queryHook An optional function to transform the query + * @returns {function} Method to be passed as the `source` option of + * autocomplete + */ + getAutocompleteSource(transformData, queryHook) { + return (query, callback) => { + if (queryHook) { + // eslint-disable-next-line no-param-reassign + query = queryHook(query) || query; + } + this.client.search(query).then(hits => { + if ( + this.queryDataCallback && + typeof this.queryDataCallback == "function" + ) { + this.queryDataCallback(hits); + } + if (transformData) { + hits = transformData(hits) || hits; + } + callback(DocSearch.formatHits(hits)); + }); + }; + } + + // Given a list of hits returned by the API, will reformat them to be used in + // a Hogan template + static formatHits(receivedHits) { + const clonedHits = utils.deepClone(receivedHits); + const hits = clonedHits.map(hit => { + if (hit._highlightResult) { + // eslint-disable-next-line no-param-reassign + hit._highlightResult = utils.mergeKeyWithParent( + hit._highlightResult, + "hierarchy" + ); + } + return utils.mergeKeyWithParent(hit, "hierarchy"); + }); + + // Group hits by category / subcategory + let groupedHits = utils.groupBy(hits, "lvl0"); + $.each(groupedHits, (level, collection) => { + const groupedHitsByLvl1 = utils.groupBy(collection, "lvl1"); + const flattenedHits = utils.flattenAndFlagFirst( + groupedHitsByLvl1, + "isSubCategoryHeader" + ); + groupedHits[level] = flattenedHits; + }); + groupedHits = utils.flattenAndFlagFirst(groupedHits, "isCategoryHeader"); + + // Translate hits into smaller objects to be send to the template + return groupedHits.map(hit => { + const url = DocSearch.formatURL(hit); + const category = utils.getHighlightedValue(hit, "lvl0"); + const subcategory = utils.getHighlightedValue(hit, "lvl1") || category; + const displayTitle = utils + .compact([ + utils.getHighlightedValue(hit, "lvl2") || subcategory, + utils.getHighlightedValue(hit, "lvl3"), + utils.getHighlightedValue(hit, "lvl4"), + utils.getHighlightedValue(hit, "lvl5"), + utils.getHighlightedValue(hit, "lvl6") + ]) + .join( + '' + ); + const text = utils.getSnippetedValue(hit, "content"); + const isTextOrSubcategoryNonEmpty = + (subcategory && subcategory !== "") || + (displayTitle && displayTitle !== ""); + const isLvl1EmptyOrDuplicate = + !subcategory || subcategory === "" || subcategory === category; + const isLvl2 = + displayTitle && displayTitle !== "" && displayTitle !== subcategory; + const isLvl1 = + !isLvl2 && + (subcategory && subcategory !== "" && subcategory !== category); + const isLvl0 = !isLvl1 && !isLvl2; + + return { + isLvl0, + isLvl1, + isLvl2, + isLvl1EmptyOrDuplicate, + isCategoryHeader: hit.isCategoryHeader, + isSubCategoryHeader: hit.isSubCategoryHeader, + isTextOrSubcategoryNonEmpty, + category, + subcategory, + title: displayTitle, + text, + url + }; + }); + } + + static formatURL(hit) { + const { url, anchor } = hit; + if (url) { + const containsAnchor = url.indexOf("#") !== -1; + if (containsAnchor) return url; + else if (anchor) return `${hit.url}#${hit.anchor}`; + return url; + } else if (anchor) return `#${hit.anchor}`; + /* eslint-disable */ + console.warn("no anchor nor url for : ", JSON.stringify(hit)); + /* eslint-enable */ + return null; + } + + static getEmptyTemplate() { + return args => Hogan.compile(templates.empty).render(args); + } + + static getSuggestionTemplate(isSimpleLayout) { + const stringTemplate = isSimpleLayout + ? templates.suggestionSimple + : templates.suggestion; + const template = Hogan.compile(stringTemplate); + return suggestion => template.render(suggestion); + } + + handleSelected(input, event, suggestion, datasetNumber, context = {}) { + // Do nothing if click on the suggestion, as it's already a , the + // browser will take care of it. This allow Ctrl-Clicking on results and not + // having the main window being redirected as well + if (context.selectionMethod === "click") { + return; + } + + input.setVal(""); + window.location.assign(suggestion.url); + } + + handleShown(input) { + const middleOfInput = input.offset().left + input.width() / 2; + let middleOfWindow = $(document).width() / 2; + + if (isNaN(middleOfWindow)) { + middleOfWindow = 900; + } + + const alignClass = + middleOfInput - middleOfWindow >= 0 + ? "algolia-autocomplete-right" + : "algolia-autocomplete-left"; + const otherAlignClass = + middleOfInput - middleOfWindow < 0 + ? "algolia-autocomplete-right" + : "algolia-autocomplete-left"; + const autocompleteWrapper = $(".algolia-autocomplete"); + if (!autocompleteWrapper.hasClass(alignClass)) { + autocompleteWrapper.addClass(alignClass); + } + + if (autocompleteWrapper.hasClass(otherAlignClass)) { + autocompleteWrapper.removeClass(otherAlignClass); + } + } +} + +export default DocSearch; diff --git a/docs/src/theme/SearchBar/algolia.css b/docs/src/theme/SearchBar/algolia.css new file mode 100644 index 0000000000..c5590cfa48 --- /dev/null +++ b/docs/src/theme/SearchBar/algolia.css @@ -0,0 +1,526 @@ +/* Bottom border of each suggestion */ +.algolia-docsearch-suggestion { + border-bottom-color: #3a3dd1; +} +/* Main category headers */ +.algolia-docsearch-suggestion--category-header { + background-color: #4b54de; +} +/* Highlighted search terms */ +.algolia-docsearch-suggestion--highlight { + color: #3a33d1; +} +/* Highligted search terms in the main category headers */ +.algolia-docsearch-suggestion--category-header + .algolia-docsearch-suggestion--highlight { + background-color: #4d47d5; +} +/* Currently selected suggestion */ +.aa-cursor .algolia-docsearch-suggestion--content { + color: #272296; +} +.aa-cursor .algolia-docsearch-suggestion { + background: #ebebfb; +} + +/* For bigger screens, when displaying results in two columns */ +@media (min-width: 768px) { + /* Bottom border of each suggestion */ + .algolia-docsearch-suggestion { + border-bottom-color: #7671df; + } + /* Left column, with secondary category header */ + .algolia-docsearch-suggestion--subcategory-column { + border-right-color: #7671df; + color: #4e4726; + } +} + +.searchbox { + display: inline-block; + position: relative; + width: 200px; + height: 32px !important; + white-space: nowrap; + box-sizing: border-box; + visibility: visible !important; +} + +.searchbox .algolia-autocomplete { + display: block; + width: 100%; + height: 100%; +} + +.searchbox__wrapper { + width: 100%; + height: 100%; + z-index: 999; + position: relative; +} + +.searchbox__input { + display: inline-block; + box-sizing: border-box; + -webkit-transition: box-shadow 0.4s ease, background 0.4s ease; + transition: box-shadow 0.4s ease, background 0.4s ease; + border: 0; + border-radius: 16px; + box-shadow: inset 0 0 0 1px #cccccc; + background: #ffffff !important; + padding: 0; + padding-right: 26px; + padding-left: 32px; + width: 100%; + height: 100%; + vertical-align: middle; + white-space: normal; + font-size: 12px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.searchbox__input::-webkit-search-decoration, +.searchbox__input::-webkit-search-cancel-button, +.searchbox__input::-webkit-search-results-button, +.searchbox__input::-webkit-search-results-decoration { + display: none; +} + +.searchbox__input:hover { + box-shadow: inset 0 0 0 1px #b3b3b3; +} + +.searchbox__input:focus, +.searchbox__input:active { + outline: 0; + box-shadow: inset 0 0 0 1px #aaaaaa; + background: #ffffff; +} + +.searchbox__input::-webkit-input-placeholder { + color: #aaaaaa; +} + +.searchbox__input::-moz-placeholder { + color: #aaaaaa; +} + +.searchbox__input:-ms-input-placeholder { + color: #aaaaaa; +} + +.searchbox__input::placeholder { + color: #aaaaaa; +} + +.searchbox__submit { + position: absolute; + top: 0; + margin: 0; + border: 0; + border-radius: 16px 0 0 16px; + background-color: rgba(69, 142, 225, 0); + padding: 0; + width: 32px; + height: 100%; + vertical-align: middle; + text-align: center; + font-size: inherit; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + right: inherit; + left: 0; +} + +.searchbox__submit::before { + display: inline-block; + margin-right: -4px; + height: 100%; + vertical-align: middle; + content: ''; +} + +.searchbox__submit:hover, +.searchbox__submit:active { + cursor: pointer; +} + +.searchbox__submit:focus { + outline: 0; +} + +.searchbox__submit svg { + width: 14px; + height: 14px; + vertical-align: middle; + fill: #6d7e96; +} + +.searchbox__reset { + display: block; + position: absolute; + top: 8px; + right: 8px; + margin: 0; + border: 0; + background: none; + cursor: pointer; + padding: 0; + font-size: inherit; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + fill: rgba(0, 0, 0, 0.5); +} + +.searchbox__reset.hide { + display: none; +} + +.searchbox__reset:focus { + outline: 0; +} + +.searchbox__reset svg { + display: block; + margin: 4px; + width: 8px; + height: 8px; +} + +.searchbox__input:valid ~ .searchbox__reset { + display: block; + -webkit-animation-name: sbx-reset-in; + animation-name: sbx-reset-in; + -webkit-animation-duration: 0.15s; + animation-duration: 0.15s; +} + +@-webkit-keyframes sbx-reset-in { + 0% { + -webkit-transform: translate3d(-20%, 0, 0); + transform: translate3d(-20%, 0, 0); + opacity: 0; + } + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +@keyframes sbx-reset-in { + 0% { + -webkit-transform: translate3d(-20%, 0, 0); + transform: translate3d(-20%, 0, 0); + opacity: 0; + } + 100% { + -webkit-transform: none; + transform: none; + opacity: 1; + } +} + +.algolia-autocomplete .ds-dropdown-menu:before { + display: block; + position: absolute; + content: ''; + width: 14px; + height: 14px; + background: #373940; + z-index: 1000; + top: -7px; + border-top: 1px solid #373940; + border-right: 1px solid #373940; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + border-radius: 2px; +} + +.algolia-autocomplete .ds-dropdown-menu { + box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2), 0 2px 3px 0 rgba(0, 0, 0, 0.1); +} + +@media (min-width: 601px) { + .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu { + right: 0 !important; + left: inherit !important; + } + + .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before { + right: 48px; + } + + .algolia-autocomplete .ds-dropdown-menu { + position: relative; + top: -6px; + border-radius: 4px; + margin: 6px 0 0; + padding: 0; + text-align: left; + height: auto; + position: relative; + background: transparent; + border: none; + z-index: 999; + max-width: 600px; + min-width: 500px; + } +} + +@media (max-width: 600px) { + .algolia-autocomplete .ds-dropdown-menu { + z-index: 100; + position: fixed !important; + top: 50px !important; + left: auto !important; + right: 1rem !important; + width: 600px; + max-width: calc(100% - 2rem); + max-height: calc(100% - 5rem); + display: block; + } + + .algolia-autocomplete .ds-dropdown-menu:before { + right: 6rem; + } +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestions { + position: relative; + z-index: 1000; +} + +.algolia-autocomplete .ds-dropdown-menu .ds-suggestion { + cursor: pointer; +} + +.algolia-autocomplete .ds-dropdown-menu [class^='ds-dataset-'] { + position: relative; + border-radius: 4px; + overflow: auto; + padding: 0; + background: #ffffff; +} + +.algolia-autocomplete .ds-dropdown-menu * { + box-sizing: border-box; +} + +.algolia-autocomplete .algolia-docsearch-suggestion { + display: block; + position: relative; + padding: 0; + overflow: hidden; + text-decoration: none; +} + +.algolia-autocomplete .ds-cursor .algolia-docsearch-suggestion--wrapper { + background: #f1f1f1; + box-shadow: inset -2px 0 0 #61dafb; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--highlight { + background: #ffe564; + padding: 0.1em 0.05em; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--category-header + .algolia-docsearch-suggestion--category-header-lvl0 + .algolia-docsearch-suggestion--highlight, +.algolia-autocomplete + .algolia-docsearch-suggestion--category-header + .algolia-docsearch-suggestion--category-header-lvl1 + .algolia-docsearch-suggestion--highlight { + color: inherit; + background: inherit; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--text + .algolia-docsearch-suggestion--highlight { + padding: 0 0 1px; + background: inherit; + box-shadow: inset 0 -2px 0 0 rgba(69, 142, 225, 0.8); + color: inherit; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content { + display: block; + float: right; + width: 70%; + position: relative; + padding: 5.33333px 0 5.33333px 10.66667px; + cursor: pointer; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--content:before { + content: ''; + position: absolute; + display: block; + top: 0; + height: 100%; + width: 1px; + background: #ececec; + left: -1px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--category-header { + position: relative; + display: none; + font-size: 14px; + letter-spacing: 0.08em; + font-weight: 700; + background-color: #373940; + text-transform: uppercase; + color: #fff; + margin: 0; + padding: 5px 8px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--wrapper { + background-color: #fff; + width: 100%; + float: left; + padding: 8px 0 0 0; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { + float: left; + width: 30%; + display: none; + padding-left: 0; + text-align: right; + position: relative; + padding: 5.33333px 10.66667px; + color: #777; + font-size: 0.9em; + word-wrap: break-word; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before { + content: ''; + position: absolute; + display: block; + top: 0; + height: 100%; + width: 1px; + background: #ececec; + right: 0; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main + .algolia-docsearch-suggestion--category-header, +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary { + display: block; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--subcategory-column + .algolia-docsearch-suggestion--highlight { + background-color: inherit; + color: inherit; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline { + display: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--title { + margin-bottom: 4px; + color: #02060c; + font-size: 0.9em; + font-weight: bold; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--text { + display: block; + line-height: 1.2em; + font-size: 0.85em; + color: #63676d; + padding-right: 2px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--no-results { + width: 100%; + padding: 8px 0; + text-align: center; + font-size: 1.2em; + background-color: #373940; + margin-top: -8px; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion--no-results + .algolia-docsearch-suggestion--text { + color: #ffffff; + margin-top: 4px; +} + +.algolia-autocomplete .algolia-docsearch-suggestion--no-results::before { + display: none; +} + +.algolia-autocomplete .algolia-docsearch-suggestion code { + padding: 1px 5px; + font-size: 90%; + border: none; + color: #222222; + background-color: #ebebeb; + border-radius: 3px; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion + code + .algolia-docsearch-suggestion--highlight { + background: none; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main + .algolia-docsearch-suggestion--category-header { + color: white; + display: block; +} + +.algolia-autocomplete + .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary + .algolia-docsearch-suggestion--subcategory-column { + display: block; +} + +.algolia-autocomplete .algolia-docsearch-footer { + background-color: #fff; + width: 100%; + height: 30px; + z-index: 2000; + float: right; + font-size: 0; + line-height: 0; +} + +.algolia-autocomplete .algolia-docsearch-footer--logo { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 130 18'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='-36.87%25' x2='129.43%25' y1='134.94%25' y2='-27.7%25'%3E%3Cstop stop-color='%252300AEFF' offset='0%25'/%3E%3Cstop stop-color='%25233369E7' offset='100%25'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='url(%2523a)' d='M59.4.02h13.3a2.37 2.37 0 0 1 2.38 2.37V15.6a2.37 2.37 0 0 1-2.38 2.36H59.4a2.37 2.37 0 0 1-2.38-2.36V2.38A2.37 2.37 0 0 1 59.4.02z'/%3E%3Cpath fill='%2523FFF' d='M66.26 4.56c-2.82 0-5.1 2.27-5.1 5.08 0 2.8 2.28 5.07 5.1 5.07 2.8 0 5.1-2.26 5.1-5.07 0-2.8-2.28-5.07-5.1-5.07zm0 8.65c-2 0-3.6-1.6-3.6-3.56 0-1.97 1.6-3.58 3.6-3.58 1.98 0 3.6 1.6 3.6 3.58a3.58 3.58 0 0 1-3.6 3.57zm0-6.4v2.66c0 .07.08.13.15.1l2.4-1.24c.04-.02.06-.1.03-.14a2.96 2.96 0 0 0-2.46-1.5c-.06 0-.1.05-.1.1zm-3.33-1.96l-.3-.3a.78.78 0 0 0-1.12 0l-.36.36a.77.77 0 0 0 0 1.1l.3.3c.05.05.13.04.17 0 .2-.25.4-.5.6-.7.23-.23.46-.43.7-.6.07-.04.07-.1.03-.16zm5-.8V3.4a.78.78 0 0 0-.78-.78h-1.83a.78.78 0 0 0-.78.78v.63c0 .07.06.12.14.1a5.74 5.74 0 0 1 1.58-.22c.52 0 1.04.07 1.54.2a.1.1 0 0 0 .13-.1z'/%3E%3Cpath fill='%2523182359' d='M102.16 13.76c0 1.46-.37 2.52-1.12 3.2-.75.67-1.9 1-3.44 1-.56 0-1.74-.1-2.67-.3l.34-1.7c.78.17 1.82.2 2.36.2.86 0 1.48-.16 1.84-.5.37-.36.55-.88.55-1.57v-.35a6.37 6.37 0 0 1-.84.3 4.15 4.15 0 0 1-1.2.17 4.5 4.5 0 0 1-1.6-.28 3.38 3.38 0 0 1-1.26-.82 3.74 3.74 0 0 1-.8-1.35c-.2-.54-.3-1.5-.3-2.2 0-.67.1-1.5.3-2.06a3.92 3.92 0 0 1 .9-1.43 4.12 4.12 0 0 1 1.45-.92 5.3 5.3 0 0 1 1.94-.37c.7 0 1.35.1 1.97.2a15.86 15.86 0 0 1 1.6.33v8.46zm-5.95-4.2c0 .9.2 1.88.6 2.3.4.4.9.62 1.53.62.34 0 .66-.05.96-.15a2.75 2.75 0 0 0 .73-.33V6.7a8.53 8.53 0 0 0-1.42-.17c-.76-.02-1.36.3-1.77.8-.4.5-.62 1.4-.62 2.23zm16.13 0c0 .72-.1 1.26-.32 1.85a4.4 4.4 0 0 1-.9 1.53c-.38.42-.85.75-1.4.98-.54.24-1.4.37-1.8.37-.43 0-1.27-.13-1.8-.36a4.1 4.1 0 0 1-1.4-.97 4.5 4.5 0 0 1-.92-1.52 5.04 5.04 0 0 1-.33-1.84c0-.72.1-1.4.32-2 .22-.6.53-1.1.92-1.5.4-.43.86-.75 1.4-.98a4.55 4.55 0 0 1 1.78-.34 4.7 4.7 0 0 1 1.8.34c.54.23 1 .55 1.4.97.38.42.68.92.9 1.5.23.6.35 1.3.35 2zm-2.2 0c0-.92-.2-1.7-.6-2.22-.38-.54-.94-.8-1.64-.8-.72 0-1.27.26-1.67.8-.4.54-.58 1.3-.58 2.22 0 .93.2 1.56.6 2.1.38.54.94.8 1.64.8s1.25-.26 1.65-.8c.4-.55.6-1.17.6-2.1zm6.97 4.7c-3.5.02-3.5-2.8-3.5-3.27L113.57.92l2.15-.34v10c0 .25 0 1.87 1.37 1.88v1.8zm3.77 0h-2.15v-9.2l2.15-.33v9.54zM119.8 3.74c.7 0 1.3-.58 1.3-1.3 0-.7-.58-1.3-1.3-1.3-.73 0-1.3.6-1.3 1.3 0 .72.58 1.3 1.3 1.3zm6.43 1c.7 0 1.3.1 1.78.27.5.18.88.42 1.17.73.28.3.5.74.6 1.18.13.46.2.95.2 1.5v5.47a25.24 25.24 0 0 1-1.5.25c-.67.1-1.42.15-2.25.15a6.83 6.83 0 0 1-1.52-.16 3.2 3.2 0 0 1-1.18-.5 2.46 2.46 0 0 1-.76-.9c-.18-.37-.27-.9-.27-1.44 0-.52.1-.85.3-1.2.2-.37.48-.67.83-.9a3.6 3.6 0 0 1 1.23-.5 7.07 7.07 0 0 1 2.2-.1l.83.16v-.35c0-.25-.03-.48-.1-.7a1.5 1.5 0 0 0-.3-.58c-.15-.18-.34-.3-.58-.4a2.54 2.54 0 0 0-.92-.17c-.5 0-.94.06-1.35.13-.4.08-.75.16-1 .25l-.27-1.74c.27-.1.67-.18 1.2-.28a9.34 9.34 0 0 1 1.65-.14zm.18 7.74c.66 0 1.15-.04 1.5-.1V10.2a5.1 5.1 0 0 0-2-.1c-.23.03-.45.1-.64.2a1.17 1.17 0 0 0-.47.38c-.13.17-.18.26-.18.52 0 .5.17.8.5.98.32.2.74.3 1.3.3zM84.1 4.8c.72 0 1.3.08 1.8.26.48.17.87.42 1.15.73.3.3.5.72.6 1.17.14.45.2.94.2 1.47v5.48a25.24 25.24 0 0 1-1.5.26c-.67.1-1.42.14-2.25.14a6.83 6.83 0 0 1-1.52-.16 3.2 3.2 0 0 1-1.18-.5 2.46 2.46 0 0 1-.76-.9c-.18-.38-.27-.9-.27-1.44 0-.53.1-.86.3-1.22.2-.36.5-.65.84-.88a3.6 3.6 0 0 1 1.24-.5 7.07 7.07 0 0 1 2.2-.1c.26.03.54.08.84.15v-.35c0-.24-.03-.48-.1-.7a1.5 1.5 0 0 0-.3-.58c-.15-.17-.34-.3-.58-.4a2.54 2.54 0 0 0-.9-.15c-.5 0-.96.05-1.37.12-.4.07-.75.15-1 .24l-.26-1.75c.27-.08.67-.17 1.18-.26a8.9 8.9 0 0 1 1.66-.15zm.2 7.73c.65 0 1.14-.04 1.48-.1v-2.17a5.1 5.1 0 0 0-1.98-.1c-.24.03-.46.1-.65.18a1.17 1.17 0 0 0-.47.4c-.12.17-.17.26-.17.52 0 .5.18.8.5.98.32.2.75.3 1.3.3zm8.68 1.74c-3.5 0-3.5-2.82-3.5-3.28L89.45.92 91.6.6v10c0 .25 0 1.87 1.38 1.88v1.8z'/%3E%3Cpath fill='%25231D3657' d='M5.03 11.03c0 .7-.26 1.24-.76 1.64-.5.4-1.2.6-2.1.6-.88 0-1.6-.14-2.17-.42v-1.2c.36.16.74.3 1.14.38.4.1.78.15 1.13.15.5 0 .88-.1 1.12-.3a.94.94 0 0 0 .35-.77.98.98 0 0 0-.33-.74c-.22-.2-.68-.44-1.37-.72-.72-.3-1.22-.62-1.52-1C.23 8.27.1 7.82.1 7.3c0-.65.22-1.17.7-1.55.46-.37 1.08-.56 1.86-.56.76 0 1.5.16 2.25.48l-.4 1.05c-.7-.3-1.32-.44-1.87-.44-.4 0-.73.08-.94.26a.9.9 0 0 0-.33.72c0 .2.04.38.12.52.08.15.22.3.42.4.2.14.55.3 1.06.52.58.24 1 .47 1.27.67.27.2.47.44.6.7.12.26.18.57.18.92zM9 13.27c-.92 0-1.64-.27-2.16-.8-.52-.55-.78-1.3-.78-2.24 0-.97.24-1.73.72-2.3.5-.54 1.15-.82 2-.82.78 0 1.4.25 1.85.72.46.48.7 1.14.7 1.97v.67H7.35c0 .58.17 1.02.46 1.33.3.3.7.47 1.24.47.36 0 .68-.04.98-.1a5.1 5.1 0 0 0 .98-.33v1.02a3.87 3.87 0 0 1-.94.32 5.72 5.72 0 0 1-1.08.1zm-.22-5.2c-.4 0-.73.12-.97.38s-.37.62-.42 1.1h2.7c0-.48-.13-.85-.36-1.1-.23-.26-.54-.38-.94-.38zm7.7 5.1l-.26-.84h-.05c-.28.36-.57.6-.86.74-.28.13-.65.2-1.1.2-.6 0-1.05-.16-1.38-.48-.32-.32-.5-.77-.5-1.34 0-.62.24-1.08.7-1.4.45-.3 1.14-.47 2.07-.5l1.02-.03V9.2c0-.37-.1-.65-.27-.84-.17-.2-.45-.28-.82-.28-.3 0-.6.04-.88.13a6.68 6.68 0 0 0-.8.33l-.4-.9a4.4 4.4 0 0 1 1.05-.4 4.86 4.86 0 0 1 1.08-.12c.76 0 1.33.18 1.7.5.4.33.6.85.6 1.56v4h-.9zm-1.9-.87c.47 0 .83-.13 1.1-.38.3-.26.43-.62.43-1.08v-.52l-.76.03c-.6.03-1.02.13-1.3.3s-.4.45-.4.82c0 .26.08.47.24.6.16.16.4.23.7.23zm7.57-5.2c.25 0 .46.03.62.06l-.12 1.18a2.38 2.38 0 0 0-.56-.06c-.5 0-.92.16-1.24.5-.3.32-.47.75-.47 1.27v3.1h-1.27V7.23h1l.16 1.05h.05c.2-.36.45-.64.77-.85a1.83 1.83 0 0 1 1.02-.3zm4.12 6.17c-.9 0-1.58-.27-2.05-.8-.47-.52-.7-1.27-.7-2.25 0-1 .24-1.77.73-2.3.5-.54 1.2-.8 2.12-.8.63 0 1.2.1 1.7.34l-.4 1c-.52-.2-.96-.3-1.3-.3-1.04 0-1.55.68-1.55 2.05 0 .67.13 1.17.38 1.5.26.34.64.5 1.13.5a3.23 3.23 0 0 0 1.6-.4v1.1a2.53 2.53 0 0 1-.73.28 4.36 4.36 0 0 1-.93.08zm8.28-.1h-1.27V9.5c0-.45-.1-.8-.28-1.02-.18-.23-.47-.34-.88-.34-.53 0-.9.16-1.16.48-.25.3-.38.85-.38 1.6v2.94h-1.26V4.8h1.26v2.12c0 .34-.02.7-.06 1.1h.08a1.76 1.76 0 0 1 .72-.67c.3-.16.66-.24 1.07-.24 1.43 0 2.15.74 2.15 2.2v3.86zM42.2 7.1c.74 0 1.32.28 1.73.82.4.53.62 1.3.62 2.26 0 .97-.2 1.73-.63 2.27-.42.54-1 .82-1.75.82s-1.33-.27-1.75-.8h-.08l-.23.7h-.94V4.8h1.26v2l-.02.64-.03.56h.05c.4-.6 1-.9 1.78-.9zm-.33 1.04c-.5 0-.88.15-1.1.45-.22.3-.34.8-.35 1.5v.08c0 .72.12 1.24.35 1.57.23.32.6.48 1.12.48.44 0 .78-.17 1-.53.24-.35.36-.87.36-1.53 0-1.35-.47-2.03-1.4-2.03zm3.24-.92h1.4l1.2 3.37c.18.47.3.92.36 1.34h.04l.18-.72 1.37-4H51l-2.53 6.73c-.46 1.23-1.23 1.85-2.3 1.85-.3 0-.56-.03-.83-.1v-1c.2.05.4.08.65.08.6 0 1.03-.36 1.28-1.06l.22-.56-2.4-5.94z'/%3E%3C/g%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: center; + background-size: 100%; + overflow: hidden; + text-indent: -9000px; + width: 110px; + height: 100%; + display: block; + margin-left: auto; + margin-right: 5px; +} diff --git a/docs/src/theme/SearchBar/index.js b/docs/src/theme/SearchBar/index.js new file mode 100644 index 0000000000..3c59678f87 --- /dev/null +++ b/docs/src/theme/SearchBar/index.js @@ -0,0 +1,114 @@ +import React, { useRef, useCallback, useState } from "react"; +import classnames from "classnames"; +import { useHistory } from "@docusaurus/router"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import { usePluginData } from '@docusaurus/useGlobalData'; +import useIsBrowser from "@docusaurus/useIsBrowser"; +const Search = props => { + const initialized = useRef(false); + const searchBarRef = useRef(null); + const [indexReady, setIndexReady] = useState(false); + const history = useHistory(); + const { siteConfig = {} } = useDocusaurusContext(); + const isBrowser = useIsBrowser(); + const { baseUrl } = siteConfig; + const initAlgolia = (searchDocs, searchIndex, DocSearch) => { + new DocSearch({ + searchDocs, + searchIndex, + baseUrl, + inputSelector: "#search_input_react", + // Override algolia's default selection event, allowing us to do client-side + // navigation and avoiding a full page refresh. + handleSelected: (_input, _event, suggestion) => { + const url = suggestion.url || "/"; + // Use an anchor tag to parse the absolute url into a relative url + // Alternatively, we can use new URL(suggestion.url) but its not supported in IE + const a = document.createElement("a"); + a.href = url; + // Algolia use closest parent element id #__docusaurus when a h1 page title does not have an id + // So, we can safely remove it. See https://github.com/facebook/docusaurus/issues/1828 for more details. + + history.push(url); + } + }); + }; + + const pluginData = usePluginData('docusaurus-lunr-search'); + const getSearchDoc = () => + process.env.NODE_ENV === "production" + ? fetch(`${baseUrl}${pluginData.fileNames.searchDoc}`).then((content) => content.json()) + : Promise.resolve([]); + + const getLunrIndex = () => + process.env.NODE_ENV === "production" + ? fetch(`${baseUrl}${pluginData.fileNames.lunrIndex}`).then((content) => content.json()) + : Promise.resolve([]); + + const loadAlgolia = () => { + if (!initialized.current) { + Promise.all([ + getSearchDoc(), + getLunrIndex(), + import("./DocSearch"), + import("./algolia.css") + ]).then(([searchDocs, searchIndex, { default: DocSearch }]) => { + if (searchDocs.length === 0) { + return; + } + initAlgolia(searchDocs, searchIndex, DocSearch); + setIndexReady(true); + }); + initialized.current = true; + } + }; + + const toggleSearchIconClick = useCallback( + e => { + if (!searchBarRef.current.contains(e.target)) { + searchBarRef.current.focus(); + } + + props.handleSearchBarToggle && props.handleSearchBarToggle(!props.isSearchBarExpanded); + }, + [props.isSearchBarExpanded] + ); + + if (isBrowser) { + loadAlgolia(); + } + + return ( +
+ + +
+ ); +}; + +export default Search; diff --git a/docs/src/theme/SearchBar/lunar-search.js b/docs/src/theme/SearchBar/lunar-search.js new file mode 100644 index 0000000000..25681f4862 --- /dev/null +++ b/docs/src/theme/SearchBar/lunar-search.js @@ -0,0 +1,147 @@ +import lunr from "@generated/lunr.client"; +lunr.tokenizer.separator = /[\s\-/]+/; + +class LunrSearchAdapter { + constructor(searchDocs, searchIndex, baseUrl = '/') { + this.searchDocs = searchDocs; + this.lunrIndex = lunr.Index.load(searchIndex); + this.baseUrl = baseUrl; + } + + getLunrResult(input) { + return this.lunrIndex.query(function (query) { + const tokens = lunr.tokenizer(input); + query.term(tokens, { + boost: 10 + }); + query.term(tokens, { + wildcard: lunr.Query.wildcard.TRAILING + }); + }); + } + + getHit(doc, formattedTitle, formattedContent) { + return { + hierarchy: { + lvl0: doc.pageTitle || doc.title, + lvl1: doc.type === 0 ? null : doc.title + }, + url: doc.url, + _snippetResult: formattedContent ? { + content: { + value: formattedContent, + matchLevel: "full" + } + } : null, + _highlightResult: { + hierarchy: { + lvl0: { + value: doc.type === 0 ? formattedTitle || doc.title : doc.pageTitle, + }, + lvl1: + doc.type === 0 + ? null + : { + value: formattedTitle || doc.title + } + } + } + }; + } + getTitleHit(doc, position, length) { + const start = position[0]; + const end = position[0] + length; + let formattedTitle = doc.title.substring(0, start) + '' + doc.title.substring(start, end) + '' + doc.title.substring(end, doc.title.length); + return this.getHit(doc, formattedTitle) + } + + getKeywordHit(doc, position, length) { + const start = position[0]; + const end = position[0] + length; + let formattedTitle = doc.title + '
Keywords: ' + doc.keywords.substring(0, start) + '' + doc.keywords.substring(start, end) + '' + doc.keywords.substring(end, doc.keywords.length) + '' + return this.getHit(doc, formattedTitle) + } + + getContentHit(doc, position) { + const start = position[0]; + const end = position[0] + position[1]; + let previewStart = start; + let previewEnd = end; + let ellipsesBefore = true; + let ellipsesAfter = true; + for (let k = 0; k < 3; k++) { + const nextSpace = doc.content.lastIndexOf(' ', previewStart - 2); + const nextDot = doc.content.lastIndexOf('.', previewStart - 2); + if ((nextDot > 0) && (nextDot > nextSpace)) { + previewStart = nextDot + 1; + ellipsesBefore = false; + break; + } + if (nextSpace < 0) { + previewStart = 0; + ellipsesBefore = false; + break; + } + previewStart = nextSpace + 1; + } + for (let k = 0; k < 10; k++) { + const nextSpace = doc.content.indexOf(' ', previewEnd + 1); + const nextDot = doc.content.indexOf('.', previewEnd + 1); + if ((nextDot > 0) && (nextDot < nextSpace)) { + previewEnd = nextDot; + ellipsesAfter = false; + break; + } + if (nextSpace < 0) { + previewEnd = doc.content.length; + ellipsesAfter = false; + break; + } + previewEnd = nextSpace; + } + let preview = doc.content.substring(previewStart, start); + if (ellipsesBefore) { + preview = '... ' + preview; + } + preview += '' + doc.content.substring(start, end) + ''; + preview += doc.content.substring(end, previewEnd); + if (ellipsesAfter) { + preview += ' ...'; + } + return this.getHit(doc, null, preview); + + } + search(input) { + return new Promise((resolve, rej) => { + const results = this.getLunrResult(input); + const hits = []; + results.length > 5 && (results.length = 5); + this.titleHitsRes = [] + this.contentHitsRes = [] + results.forEach(result => { + const doc = this.searchDocs[result.ref]; + const { metadata } = result.matchData; + for (let i in metadata) { + if (metadata[i].title) { + if (!this.titleHitsRes.includes(result.ref)) { + const position = metadata[i].title.position[0] + hits.push(this.getTitleHit(doc, position, input.length)); + this.titleHitsRes.push(result.ref); + } + } else if (metadata[i].content) { + const position = metadata[i].content.position[0] + hits.push(this.getContentHit(doc, position)) + } else if (metadata[i].keywords) { + const position = metadata[i].keywords.position[0] + hits.push(this.getKeywordHit(doc, position, input.length)); + this.titleHitsRes.push(result.ref); + } + } + }); + hits.length > 5 && (hits.length = 5); + resolve(hits); + }); + } +} + +export default LunrSearchAdapter; diff --git a/docs/src/theme/SearchBar/styles.css b/docs/src/theme/SearchBar/styles.css new file mode 100644 index 0000000000..8dc3b0a12b --- /dev/null +++ b/docs/src/theme/SearchBar/styles.css @@ -0,0 +1,33 @@ +.search-icon { + background-image: var(--ifm-navbar-search-input-icon); + height: auto; + width: 24px; + cursor: pointer; + padding: 8px; + line-height: 32px; + background-repeat: no-repeat; + background-position: center; + display: none; +} + +.search-icon-hidden { + visibility: hidden; +} + +@media (max-width: 360px) { + .search-bar { + width: 0 !important; + background: none !important; + padding: 0 !important; + transition: none !important; + } + + .search-bar-expanded { + width: 9rem !important; + } + + .search-icon { + display: inline; + vertical-align: sub; + } +} diff --git a/docs/src/theme/SearchBar/templates.js b/docs/src/theme/SearchBar/templates.js new file mode 100644 index 0000000000..1485e7858d --- /dev/null +++ b/docs/src/theme/SearchBar/templates.js @@ -0,0 +1,112 @@ +const prefix = 'algolia-docsearch'; +const suggestionPrefix = `${prefix}-suggestion`; +const footerPrefix = `${prefix}-footer`; + +const templates = { + suggestion: ` +
+
+ {{{category}}} +
+
+
+ {{{subcategory}}} +
+ {{#isTextOrSubcategoryNonEmpty}} +
+
{{{subcategory}}}
+
{{{title}}}
+ {{#text}}
{{{text}}}
{{/text}} +
+ {{/isTextOrSubcategoryNonEmpty}} +
+
+ `, + suggestionSimple: ` +
+
+ {{^isLvl0}} + {{{category}}} + {{^isLvl1}} + {{^isLvl1EmptyOrDuplicate}} + + {{{subcategory}}} + + {{/isLvl1EmptyOrDuplicate}} + {{/isLvl1}} + {{/isLvl0}} +
+ {{#isLvl2}} + {{{title}}} + {{/isLvl2}} + {{#isLvl1}} + {{{subcategory}}} + {{/isLvl1}} + {{#isLvl0}} + {{{category}}} + {{/isLvl0}} +
+
+
+ {{#text}} +
+
{{{text}}}
+
+ {{/text}} +
+
+ `, + footer: ` +
+
+ `, + empty: ` +
+
+
+
+
+ No results found for query "{{query}}" +
+
+
+
+
+ `, + searchBox: ` + + + + `, +}; + +export default templates; diff --git a/docs/src/theme/SearchBar/utils.js b/docs/src/theme/SearchBar/utils.js new file mode 100644 index 0000000000..0807500d76 --- /dev/null +++ b/docs/src/theme/SearchBar/utils.js @@ -0,0 +1,270 @@ +import $ from "autocomplete.js/zepto"; + +const utils = { + /* + * Move the content of an object key one level higher. + * eg. + * { + * name: 'My name', + * hierarchy: { + * lvl0: 'Foo', + * lvl1: 'Bar' + * } + * } + * Will be converted to + * { + * name: 'My name', + * lvl0: 'Foo', + * lvl1: 'Bar' + * } + * @param {Object} object Main object + * @param {String} property Main object key to move up + * @return {Object} + * @throws Error when key is not an attribute of Object or is not an object itself + */ + mergeKeyWithParent(object, property) { + if (object[property] === undefined) { + return object; + } + if (typeof object[property] !== 'object') { + return object; + } + const newObject = $.extend({}, object, object[property]); + delete newObject[property]; + return newObject; + }, + /* + * Group all objects of a collection by the value of the specified attribute + * If the attribute is a string, use the lowercase form. + * + * eg. + * groupBy([ + * {name: 'Tim', category: 'dev'}, + * {name: 'Vincent', category: 'dev'}, + * {name: 'Ben', category: 'sales'}, + * {name: 'Jeremy', category: 'sales'}, + * {name: 'AlexS', category: 'dev'}, + * {name: 'AlexK', category: 'sales'} + * ], 'category'); + * => + * { + * 'devs': [ + * {name: 'Tim', category: 'dev'}, + * {name: 'Vincent', category: 'dev'}, + * {name: 'AlexS', category: 'dev'} + * ], + * 'sales': [ + * {name: 'Ben', category: 'sales'}, + * {name: 'Jeremy', category: 'sales'}, + * {name: 'AlexK', category: 'sales'} + * ] + * } + * @param {array} collection Array of objects to group + * @param {String} property The attribute on which apply the grouping + * @return {array} + * @throws Error when one of the element does not have the specified property + */ + groupBy(collection, property) { + const newCollection = {}; + $.each(collection, (index, item) => { + if (item[property] === undefined) { + throw new Error(`[groupBy]: Object has no key ${property}`); + } + let key = item[property]; + if (typeof key === 'string') { + key = key.toLowerCase(); + } + // fix #171 the given data type of docsearch hits might be conflict with the properties of the native Object, + // such as the constructor, so we need to do this check. + if (!Object.prototype.hasOwnProperty.call(newCollection, key)) { + newCollection[key] = []; + } + newCollection[key].push(item); + }); + return newCollection; + }, + /* + * Return an array of all the values of the specified object + * eg. + * values({ + * foo: 42, + * bar: true, + * baz: 'yep' + * }) + * => + * [42, true, yep] + * @param {object} object Object to extract values from + * @return {array} + */ + values(object) { + return Object.keys(object).map(key => object[key]); + }, + /* + * Flattens an array + * eg. + * flatten([1, 2, [3, 4], [5, 6]]) + * => + * [1, 2, 3, 4, 5, 6] + * @param {array} array Array to flatten + * @return {array} + */ + flatten(array) { + const results = []; + array.forEach(value => { + if (!Array.isArray(value)) { + results.push(value); + return; + } + value.forEach(subvalue => { + results.push(subvalue); + }); + }); + return results; + }, + /* + * Flatten all values of an object into an array, marking each first element of + * each group with a specific flag + * eg. + * flattenAndFlagFirst({ + * 'devs': [ + * {name: 'Tim', category: 'dev'}, + * {name: 'Vincent', category: 'dev'}, + * {name: 'AlexS', category: 'dev'} + * ], + * 'sales': [ + * {name: 'Ben', category: 'sales'}, + * {name: 'Jeremy', category: 'sales'}, + * {name: 'AlexK', category: 'sales'} + * ] + * , 'isTop'); + * => + * [ + * {name: 'Tim', category: 'dev', isTop: true}, + * {name: 'Vincent', category: 'dev', isTop: false}, + * {name: 'AlexS', category: 'dev', isTop: false}, + * {name: 'Ben', category: 'sales', isTop: true}, + * {name: 'Jeremy', category: 'sales', isTop: false}, + * {name: 'AlexK', category: 'sales', isTop: false} + * ] + * @param {object} object Object to flatten + * @param {string} flag Flag to set to true on first element of each group + * @return {array} + */ + flattenAndFlagFirst(object, flag) { + const values = this.values(object).map(collection => + collection.map((item, index) => { + // eslint-disable-next-line no-param-reassign + item[flag] = index === 0; + return item; + }) + ); + return this.flatten(values); + }, + /* + * Removes all empty strings, null, false and undefined elements array + * eg. + * compact([42, false, null, undefined, '', [], 'foo']); + * => + * [42, [], 'foo'] + * @param {array} array Array to compact + * @return {array} + */ + compact(array) { + const results = []; + array.forEach(value => { + if (!value) { + return; + } + results.push(value); + }); + return results; + }, + /* + * Returns the highlighted value of the specified key in the specified object. + * If no highlighted value is available, will return the key value directly + * eg. + * getHighlightedValue({ + * _highlightResult: { + * text: { + * value: 'foo' + * } + * }, + * text: 'foo' + * }, 'text'); + * => + * 'foo' + * @param {object} object Hit object returned by the Algolia API + * @param {string} property Object key to look for + * @return {string} + **/ + getHighlightedValue(object, property) { + if ( + object._highlightResult && + object._highlightResult.hierarchy_camel && + object._highlightResult.hierarchy_camel[property] && + object._highlightResult.hierarchy_camel[property].matchLevel && + object._highlightResult.hierarchy_camel[property].matchLevel !== 'none' && + object._highlightResult.hierarchy_camel[property].value + ) { + return object._highlightResult.hierarchy_camel[property].value; + } + if ( + object._highlightResult && + object._highlightResult && + object._highlightResult[property] && + object._highlightResult[property].value + ) { + return object._highlightResult[property].value; + } + return object[property]; + }, + /* + * Returns the snippeted value of the specified key in the specified object. + * If no highlighted value is available, will return the key value directly. + * Will add starting and ending ellipsis (…) if we detect that a sentence is + * incomplete + * eg. + * getSnippetedValue({ + * _snippetResult: { + * text: { + * value: 'This is an unfinished sentence' + * } + * }, + * text: 'This is an unfinished sentence' + * }, 'text'); + * => + * 'This is an unfinished sentence…' + * @param {object} object Hit object returned by the Algolia API + * @param {string} property Object key to look for + * @return {string} + **/ + getSnippetedValue(object, property) { + if ( + !object._snippetResult || + !object._snippetResult[property] || + !object._snippetResult[property].value + ) { + return object[property]; + } + let snippet = object._snippetResult[property].value; + + if (snippet[0] !== snippet[0].toUpperCase()) { + snippet = `…${snippet}`; + } + if (['.', '!', '?'].indexOf(snippet[snippet.length - 1]) === -1) { + snippet = `${snippet}…`; + } + return snippet; + }, + /* + * Deep clone an object. + * Note: This will not clone functions and dates + * @param {object} object Object to clone + * @return {object} + */ + deepClone(object) { + return JSON.parse(JSON.stringify(object)); + }, +}; + +export default utils;