This is an automated email from the ASF dual-hosted git repository.
jeffreyh pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/doris-website.git
The following commit(s) were added to refs/heads/develop by this push:
new d7950004aa7 Some bugfix (#1460)
d7950004aa7 is described below
commit d7950004aa7fea27a10af0888f23c3037209c5cd
Author: yangon <[email protected]>
AuthorDate: Thu Dec 5 20:14:28 2024 +0800
Some bugfix (#1460)
1. Fixed some issues with the search box.
2. Some style optimizations.
---------
Co-authored-by: liyang <[email protected]>
---
.../docusaurus-plugin-content-docs/current.json | 8 +
.../version-1.2.json | 8 +
.../version-2.0.json | 8 +
.../version-2.1.json | 8 +
.../version-3.0.json | 8 +
shared/interfaces.js | 8 +
src/constant/common.tsx | 3 +
src/scss/components/markdown.scss | 1 +
src/scss/components/navbar.scss | 3 -
src/scss/components/toc.scss | 1 +
src/shared/interfaces.js | 8 +
src/theme/Admonition/Layout/styles.module.css | 11 +-
src/theme/DocSidebarItem/Category/style.scss | 2 +-
src/theme/Footer/index.tsx | 38 +-
src/theme/Footer/styles.scss | 29 +-
src/theme/Navbar/Content/index.tsx | 16 +-
src/theme/SearchBar/EmptyTemplate.js | 12 +
src/theme/SearchBar/SearchBar.jsx | 402 +++++++++++++++++++++
src/theme/SearchBar/SearchBar.module.css | 298 +++++++++++++++
src/theme/SearchBar/SuggestionTemplate.js | 54 +++
src/theme/SearchBar/__mocks__/icons.js | 7 +
src/theme/SearchBar/icons.js | 7 +
src/theme/SearchBar/index.js | 13 +-
src/theme/SearchBar/searchByWorker.js | 24 ++
src/theme/SearchBar/worker.js | 94 +++++
src/theme/TOC/index.tsx | 14 +-
src/utils/normalizeContextByPath.js | 24 ++
static/images/toc-icon/concat.svg | 21 ++
static/images/toc-icon/github-active.svg | 12 -
static/images/toc-icon/home-active.svg | 5 -
static/images/toc-icon/pdf-active.svg | 5 -
31 files changed, 1055 insertions(+), 97 deletions(-)
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
index e9287466d10..08715a162ae 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
@@ -23,6 +23,14 @@
"message": "版本发布",
"description": "The label for category Releases in sidebar docs"
},
+ "sidebar.docs.category.Tutorials": {
+ "message": "使用教程",
+ "description": "The label for category Tutorials in sidebar docs"
+ },
+ "sidebar.docs.category.Building lakehouse": {
+ "message": "构建 lakehouse",
+ "description": "The label for category Building lakehouse in sidebar docs"
+ },
"sidebar.docs.category.Quick Start": {
"message": "快速体验",
"description": "The label for category Quick Start in sidebar docs"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-1.2.json
b/i18n/zh-CN/docusaurus-plugin-content-docs/version-1.2.json
index 4986b459b70..682456cb51b 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-1.2.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-1.2.json
@@ -27,6 +27,14 @@
"message": "Doris 介绍",
"description": "The label for category Doris Introduction in sidebar docs"
},
+ "sidebar.docs.category.Tutorials": {
+ "message": "使用教程",
+ "description": "The label for category Tutorials in sidebar docs"
+ },
+ "sidebar.docs.category.Building lakehouse": {
+ "message": "构建 lakehouse",
+ "description": "The label for category Building lakehouse in sidebar docs"
+ },
"sidebar.docs.category.Install And Deploy": {
"message": "安装部署",
"description": "The label for category Install And Deploy in sidebar docs"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.0.json
b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.0.json
index cbd36011592..94048c4dff2 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.0.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.0.json
@@ -27,6 +27,14 @@
"message": "快速体验",
"description": "The label for category Quick Start in sidebar docs"
},
+ "sidebar.docs.category.Tutorials": {
+ "message": "使用教程",
+ "description": "The label for category Tutorials in sidebar docs"
+ },
+ "sidebar.docs.category.Building lakehouse": {
+ "message": "构建 lakehouse",
+ "description": "The label for category Building lakehouse in sidebar docs"
+ },
"sidebar.docs.category.Installation and Deployment": {
"message": "安装部署",
"description": "The label for category Installation and Deployment in
sidebar docs"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.1.json
b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.1.json
index 247ea98c9ed..fb787a4de59 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.1.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-2.1.json
@@ -27,6 +27,14 @@
"message": "快速体验",
"description": "The label for category Quick Start in sidebar docs"
},
+ "sidebar.docs.category.Tutorials": {
+ "message": "使用教程",
+ "description": "The label for category Tutorials in sidebar docs"
+ },
+ "sidebar.docs.category.Building lakehouse": {
+ "message": "构建 lakehouse",
+ "description": "The label for category Building lakehouse in sidebar docs"
+ },
"sidebar.docs.category.Installation and Deployment": {
"message": "安装部署",
"description": "The label for category Install and Deploy in sidebar docs"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.0.json
b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.0.json
index 631798c3d4e..678f65519d4 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.0.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.0.json
@@ -27,6 +27,14 @@
"message": "快速体验",
"description": "The label for category Quick Start in sidebar docs"
},
+ "sidebar.docs.category.Tutorials": {
+ "message": "使用教程",
+ "description": "The label for category Tutorials in sidebar docs"
+ },
+ "sidebar.docs.category.Building lakehouse": {
+ "message": "构建 lakehouse",
+ "description": "The label for category Building lakehouse in sidebar docs"
+ },
"sidebar.docs.category.Installation and Deployment": {
"message": "安装部署",
"description": "The label for category Installation and Deployment in
sidebar docs"
diff --git a/shared/interfaces.js b/shared/interfaces.js
new file mode 100644
index 00000000000..f4037df8f9d
--- /dev/null
+++ b/shared/interfaces.js
@@ -0,0 +1,8 @@
+export var SearchDocumentType;
+(function (SearchDocumentType) {
+ SearchDocumentType[SearchDocumentType["Title"] = 0] = "Title";
+ SearchDocumentType[SearchDocumentType["Heading"] = 1] = "Heading";
+ SearchDocumentType[SearchDocumentType["Description"] = 2] = "Description";
+ SearchDocumentType[SearchDocumentType["Keywords"] = 3] = "Keywords";
+ SearchDocumentType[SearchDocumentType["Content"] = 4] = "Content";
+})(SearchDocumentType || (SearchDocumentType = {}));
diff --git a/src/constant/common.tsx b/src/constant/common.tsx
index 4d8c292309d..b1f71a6bb9a 100644
--- a/src/constant/common.tsx
+++ b/src/constant/common.tsx
@@ -18,3 +18,6 @@ export const BLOG_TAG_ICONS = {
'Top News': <TopNewsIcon />,
All: <AllBlogIcon />,
};
+
+export const VERSIONS = ['1.2', '2.0', '2.1', '3.0', 'dev'];
+export const DEFAULT_VERSION = '2.1';
\ No newline at end of file
diff --git a/src/scss/components/markdown.scss
b/src/scss/components/markdown.scss
index 1945ab4484d..f9e54982293 100644
--- a/src/scss/components/markdown.scss
+++ b/src/scss/components/markdown.scss
@@ -113,6 +113,7 @@
.theme-admonition {
padding: calc(var(--custom-leading) * 2);
+ box-shadow: none;
}
h2 {
diff --git a/src/scss/components/navbar.scss b/src/scss/components/navbar.scss
index a9700af9bde..0dc57d431e6 100644
--- a/src/scss/components/navbar.scss
+++ b/src/scss/components/navbar.scss
@@ -30,7 +30,6 @@
display: flex !important;
align-items: center;
justify-content: center;
- pointer-events: none;
padding: 5px 7px;
background-color: #fff;
border: none;
@@ -74,8 +73,6 @@
outline: none !important;
}
.navbar__search span span {
- max-height: 580px !important;
- overflow: scroll !important;
top: 2.5rem !important;
}
}
diff --git a/src/scss/components/toc.scss b/src/scss/components/toc.scss
index 3ada29d42e5..54f3b720685 100644
--- a/src/scss/components/toc.scss
+++ b/src/scss/components/toc.scss
@@ -3,6 +3,7 @@
.toc-icon-content {
display: flex;
margin-bottom: 0.5rem;
+ align-items: center;
cursor: pointer;
}
diff --git a/src/shared/interfaces.js b/src/shared/interfaces.js
new file mode 100644
index 00000000000..f4037df8f9d
--- /dev/null
+++ b/src/shared/interfaces.js
@@ -0,0 +1,8 @@
+export var SearchDocumentType;
+(function (SearchDocumentType) {
+ SearchDocumentType[SearchDocumentType["Title"] = 0] = "Title";
+ SearchDocumentType[SearchDocumentType["Heading"] = 1] = "Heading";
+ SearchDocumentType[SearchDocumentType["Description"] = 2] = "Description";
+ SearchDocumentType[SearchDocumentType["Keywords"] = 3] = "Keywords";
+ SearchDocumentType[SearchDocumentType["Content"] = 4] = "Content";
+})(SearchDocumentType || (SearchDocumentType = {}));
diff --git a/src/theme/Admonition/Layout/styles.module.css
b/src/theme/Admonition/Layout/styles.module.css
index 88df7e639be..3f5fe3be285 100644
--- a/src/theme/Admonition/Layout/styles.module.css
+++ b/src/theme/Admonition/Layout/styles.module.css
@@ -3,9 +3,12 @@
}
.admonitionHeading {
- font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) /
- var(--ifm-heading-line-height) var(--ifm-heading-font-family);
+ font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) /
var(--ifm-heading-line-height) var(--ifm-heading-font-family);
text-transform: uppercase;
+ font-size: 1rem;
+ color: #00000A;
+ margin-bottom: 0.75rem !important;
+ font-weight: 600;
}
/* Heading alone without content (does not handle fragment content) */
@@ -30,6 +33,6 @@
fill: var(--ifm-alert-foreground-color);
}
-.admonitionContent > :last-child {
+.admonitionContent> :last-child {
margin-bottom: 0;
-}
+}
\ No newline at end of file
diff --git a/src/theme/DocSidebarItem/Category/style.scss
b/src/theme/DocSidebarItem/Category/style.scss
index c642c4779f4..2398c432995 100644
--- a/src/theme/DocSidebarItem/Category/style.scss
+++ b/src/theme/DocSidebarItem/Category/style.scss
@@ -58,7 +58,7 @@
.divider {
width: calc(100% - 0.625rem);
- border: 0.5px solid #edf2fa;
+ border: 0.5px solid #DFE5F0;
margin: 1rem 0 1rem 0;
position: relative;
left: 0.625rem;
diff --git a/src/theme/Footer/index.tsx b/src/theme/Footer/index.tsx
index 3ce1831341e..097a13a2446 100644
--- a/src/theme/Footer/index.tsx
+++ b/src/theme/Footer/index.tsx
@@ -25,11 +25,14 @@ function Footer(): JSX.Element | null {
}
const { copyright, links, logo, style } = footer;
- const [isDocsPage, setIsDocsPage] = useState(false);
+ const [isDocsPage, setIsDocsPage] = useState(false); // docs page or
community page
useEffect(() => {
if (typeof window !== 'undefined') {
const pathname = location.pathname.split('/')[1];
- const docsPage = pathname === 'docs' ||
location.pathname.includes('zh-CN/docs');
+ const docsPage =
+ pathname === 'docs' ||
+ location.pathname.includes('zh-CN/docs') ||
+ location.pathname.includes('community');
setIsDocsPage(docsPage);
}
}, [typeof window !== 'undefined' && location.pathname]);
@@ -39,30 +42,13 @@ function Footer(): JSX.Element | null {
if (isDocsPage) {
return (
<div className="docs-footer flex-col lg:flex-row">
- <div className="logo w-full lg:w-[var(--doc-sidebar-width)]
pt-28 lg:h-auto">
- <FooterLogo logo={logo} />
- </div>
- <div className="content container">
- <div className="my-7 text-[#8592A6] text-sm">
- {/* border-b border-[#F7F9FE] */}
- <div className="flex flex-col lg:flex-row pb-3
flex-wrap">
- <div className=" w-40 mb-3 lg:mb-0
font-medium">RESOURCES</div>
- {ResourcesItems.map(({ label, href }) => (
- <Link className="w-40 no-underline mb-2"
href={href}>
- {label}
- </Link>
- ))}
- </div>
- <div className="flex flex-col lg:flex-row pt-3
flex-wrap">
- <div className="w-40 mb-3 lg:mb-0
font-medium">COMMUNITY</div>
- {CommunityItems.map(({ label, href }) => (
- <Link className="w-40 no-underline mb-2"
href={href}>
- {label}
- </Link>
- ))}
- </div>
- </div>
- </div>
+ <p className='docs-footer-p'>
+ The contents of this website are © 2024{' '}
+ <Link href="https://www.apache.org/">Apache Software
Foundation</Link> under the terms of the{' '}
+ <Link
href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache License
v2.</Link> Apache
+ Doris, Doris, and the Doris logo are either registered
trademarks or trademarks of The Apache
+ Software Foundation in the United States and other
countries.
+ </p>
</div>
);
}
diff --git a/src/theme/Footer/styles.scss b/src/theme/Footer/styles.scss
index e70eebf6ee3..2320381d3fe 100644
--- a/src/theme/Footer/styles.scss
+++ b/src/theme/Footer/styles.scss
@@ -212,23 +212,20 @@
}
.docs-footer {
- height: 7.625rem;
+ height: 3.25rem;
background-color: #fff;
- display: flex;
- border-top: 1px solid #edf2fa;
- .logo {
- position: relative;
- .footer__logo {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 8.5625rem;
- height: 3.5rem;
- margin-top: 0;
- }
+ padding: 1rem 1.5rem;
+ border-top: 1px solid #EDF2FA;
+ .docs-footer-p {
+ text-align: center;
+ color: rgba(29, 29, 29, 0.4);
+ font-family: Inter;
+ font-size: 9px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 20px;
}
- .content {
- flex: 1;
+ .docs-footer-p a {
+ text-decoration: underline;
}
}
diff --git a/src/theme/Navbar/Content/index.tsx
b/src/theme/Navbar/Content/index.tsx
index 275af09cc8f..0243b53757a 100644
--- a/src/theme/Navbar/Content/index.tsx
+++ b/src/theme/Navbar/Content/index.tsx
@@ -86,11 +86,13 @@ export default function NavbarContent(): JSX.Element {
const [isEN, setIsEN] = useState(true);
const docItems = isEN ? useThemeConfig().docNavbarEN.items :
useThemeConfig().docNavbarZH.items;
const [leftItems, rightItems] = splitNavbarItems(items);
- const [leftDocItems,rightDocItems] = splitNavbarItems(docItems);
- const [isDocsPage, setIsDocsPage] = useState(false);
+ const [leftDocItems, rightDocItems] = splitNavbarItems(docItems);
+ const [isDocsPage, setIsDocsPage] = useState(
+ typeof window !== 'undefined' ? location.pathname.includes('docs') :
false,
+ );
const [isCommunity, setIsCommunity] = useState(false);
const searchBarItem = items.find(item => item.type === 'search');
-
+
const [currentVersion, setCurrentVersion] = useState('');
useEffect(() => {
getGithubStar();
@@ -139,8 +141,7 @@ export default function NavbarContent(): JSX.Element {
function getNavItem(type: string) {
return items.find(item => item.type === type);
}
-
-
+
return (
<NavbarContentLayout
left={
@@ -166,10 +167,7 @@ export default function NavbarContent(): JSX.Element {
{!isDocsPage ? (
<NavbarItems items={leftItems} />
) : (
- <NavbarItems
- items={leftDocItems}
- isDocsPage={isDocsPage}
- />
+ <NavbarItems items={leftDocItems}
isDocsPage={isDocsPage} />
)}
</div>
{/* */}
diff --git a/src/theme/SearchBar/EmptyTemplate.js
b/src/theme/SearchBar/EmptyTemplate.js
new file mode 100644
index 00000000000..0c67ba65f42
--- /dev/null
+++ b/src/theme/SearchBar/EmptyTemplate.js
@@ -0,0 +1,12 @@
+import { translate } from "@docusaurus/Translate";
+import { iconNoResults } from "./icons";
+import styles from "./SearchBar.module.css";
+export function EmptyTemplate() {
+ if (process.env.NODE_ENV === "production") {
+ return `<span class="${styles.noResults}"><span
class="${styles.noResultsIcon}">${iconNoResults}</span><span>${translate({
+ id: "theme.SearchBar.noResultsText",
+ message: "No results",
+ })}</span></span>`;
+ }
+ return `<span class="${styles.noResults}">⚠️ The search index is only
available when you run docusaurus build!</span>`;
+}
diff --git a/src/theme/SearchBar/SearchBar.jsx
b/src/theme/SearchBar/SearchBar.jsx
new file mode 100644
index 00000000000..3272a111f72
--- /dev/null
+++ b/src/theme/SearchBar/SearchBar.jsx
@@ -0,0 +1,402 @@
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import clsx from 'clsx';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import useIsBrowser from '@docusaurus/useIsBrowser';
+import { useHistory, useLocation } from '@docusaurus/router';
+import { translate } from '@docusaurus/Translate';
+import { ReactContextError, useDocsPreferredVersion } from
'@docusaurus/theme-common';
+import { useActivePlugin } from '@docusaurus/plugin-content-docs/client';
+import { fetchIndexesByWorker, searchByWorker } from './searchByWorker';
+import { SuggestionTemplate } from './SuggestionTemplate';
+import { EmptyTemplate } from './EmptyTemplate';
+import {
+ Mark,
+ searchBarShortcut,
+ searchBarShortcutHint,
+ searchBarPosition,
+ docsPluginIdForPreferredVersion,
+ indexDocs,
+ searchContextByPaths,
+ hideSearchBarWithNoSearchContext,
+ useAllContextsWithNoSearchContext,
+} from '../../utils/proxiedGenerated';
+import LoadingRing from '../LoadingRing/LoadingRing';
+import styles from './SearchBar.module.css';
+import { normalizeContextByPath } from '../../utils/normalizeContextByPath';
+import useIsDocPage from '@site/src/hooks/use-is-doc';
+import { VERSIONS } from '@site/src/constant/common';
+async function fetchAutoCompleteJS() {
+ const autoCompleteModule = await import('@easyops-cn/autocomplete.js');
+ const autoComplete = autoCompleteModule.default;
+ if (autoComplete.noConflict) {
+ // For webpack v5 since docusaurus v2.0.0-alpha.75
+ autoComplete.noConflict();
+ } else if (autoCompleteModule.noConflict) {
+ // For webpack v4 before docusaurus v2.0.0-alpha.74
+ autoCompleteModule.noConflict();
+ }
+ return autoComplete;
+}
+const SEARCH_PARAM_HIGHLIGHT = '_highlight';
+export default function SearchBar({ handleSearchBarToggle }) {
+ const isBrowser = useIsBrowser();
+ const location = useLocation();
+ const {
+ siteConfig: { baseUrl },
+ i18n: { currentLocale },
+ } = useDocusaurusContext();
+ // It returns undefined for non-docs pages
+ const activePlugin = useActivePlugin();
+ const [isDocsPage] = useIsDocPage(false);
+ let versionUrl = baseUrl;
+ if (location?.pathname && location.pathname.includes('zh-CN') &&
!versionUrl.includes('zh-CN')) {
+ versionUrl = baseUrl + 'zh-CN/';
+ }
+ if (location?.pathname) {
+ VERSIONS.forEach(version => {
+ if (location.pathname.includes(version)) {
+ versionUrl += `docs/${version}/`;
+ }
+ });
+ }
+
+ // For non-docs pages while using plugin-content-docs with custom ids,
+ // this will throw an error of:
+ // > Docusaurus plugin global data not found for
"docusaurus-plugin-content-docs" plugin with id "default".
+ // It seems that we can not get the correct id for non-docs pages.
+ // try {
+ // // The try-catch is a hack because useDocsPreferredVersion just
throws an
+ // // exception when versions are not used.
+ // // The same hack is used in SearchPage.tsx
+ // // eslint-disable-next-line react-hooks/rules-of-hooks
+ // const { preferredVersion } =
useDocsPreferredVersion(activePlugin?.pluginId ??
docsPluginIdForPreferredVersion);
+ // console.log('preferredVersion',preferredVersion);
+
+ // if (preferredVersion && !preferredVersion.isLast) {
+ // versionUrl = preferredVersion.path + "/";
+ // }
+ // }
+ // catch (e) {
+ // if (indexDocs) {
+ // if (e instanceof ReactContextError) {
+ // /* ignore, happens when website doesn't use versions */
+ // }
+ // else {
+ // throw e;
+ // }
+ // }
+ // }
+
+ const history = useHistory();
+ const searchBarRef = useRef(null);
+ const indexStateMap = useRef(new Map());
+ // Should the input be focused after the index is loaded?
+ const focusAfterIndexLoaded = useRef(false);
+ const [loading, setLoading] = useState(false);
+ const [inputChanged, setInputChanged] = useState(false);
+ const [inputValue, setInputValue] = useState('');
+ const search = useRef(null);
+ const prevSearchContext = useRef('');
+ const [searchContext, setSearchContext] = useState('');
+ useEffect(() => {
+ if (!Array.isArray(searchContextByPaths)) {
+ return;
+ }
+ let nextSearchContext = '';
+ if (location.pathname.startsWith(versionUrl)) {
+ const uri = location.pathname.substring(versionUrl.length);
+ let matchedPath;
+ for (const _path of searchContextByPaths) {
+ const path = typeof _path === 'string' ? _path : _path.path;
+ if (uri === path || uri.startsWith(`${path}/`)) {
+ matchedPath = path;
+ break;
+ }
+ }
+ if (matchedPath) {
+ nextSearchContext = matchedPath;
+ }
+ }
+ if (prevSearchContext.current !== nextSearchContext) {
+ // Reset index state map once search context is changed.
+ indexStateMap.current.delete(nextSearchContext);
+ prevSearchContext.current = nextSearchContext;
+ }
+ setSearchContext(nextSearchContext);
+ }, [location.pathname, versionUrl]);
+ const hidden = !!hideSearchBarWithNoSearchContext &&
Array.isArray(searchContextByPaths) && searchContext === '';
+ const loadIndex = useCallback(async () => {
+ if (hidden || indexStateMap.current.get(searchContext)) {
+ // Do not load the index (again) if its already loaded or in the
process of being loaded.
+ return;
+ }
+ indexStateMap.current.set(searchContext, 'loading');
+ search.current?.autocomplete.destroy();
+ setLoading(true);
+ const [autoComplete] = await Promise.all([
+ fetchAutoCompleteJS(),
+ fetchIndexesByWorker(versionUrl, searchContext),
+ ]);
+ const searchFooterLinkElement = ({ query, isEmpty }) => {
+ const a = document.createElement('a');
+ const params = new URLSearchParams();
+ params.set('q', query);
+ let linkText;
+ if (searchContext) {
+ const detailedSearchContext =
+ searchContext && Array.isArray(searchContextByPaths)
+ ? searchContextByPaths.find(item =>
+ typeof item === 'string' ? item ===
searchContext : item.path === searchContext,
+ )
+ : searchContext;
+ const translatedSearchContext = detailedSearchContext
+ ? normalizeContextByPath(detailedSearchContext,
currentLocale).label
+ : searchContext;
+ if (useAllContextsWithNoSearchContext && isEmpty) {
+ linkText = translate(
+ {
+ id: 'theme.SearchBar.seeAllOutsideContext',
+ message: 'See all results outside "{context}"',
+ },
+ { context: translatedSearchContext },
+ );
+ } else {
+ linkText = translate(
+ {
+ id: 'theme.SearchBar.searchInContext',
+ message: 'See all results within "{context}"',
+ },
+ { context: translatedSearchContext },
+ );
+ }
+ } else {
+ linkText = translate({
+ id: 'theme.SearchBar.seeAll',
+ message: 'See all results',
+ });
+ }
+ if (
+ searchContext &&
+ Array.isArray(searchContextByPaths) &&
+ (!useAllContextsWithNoSearchContext || !isEmpty)
+ ) {
+ params.set('ctx', searchContext);
+ }
+ if (versionUrl !== baseUrl) {
+ if (!versionUrl.startsWith(baseUrl)) {
+ throw new Error(
+ `Version url '${versionUrl}' does not start with base
url '${baseUrl}', this is a bug of \`@easyops-cn/docusaurus-search-local\`,
please report it.`,
+ );
+ }
+ params.set('version', versionUrl.substring(baseUrl.length));
+ }
+ const url = `${baseUrl}search/?${params.toString()}`;
+ a.href = url;
+ a.textContent = linkText;
+ a.addEventListener('click', e => {
+ if (!e.ctrlKey && !e.metaKey) {
+ e.preventDefault();
+ search.current?.autocomplete.close();
+ history.push(url);
+ }
+ });
+ return a;
+ };
+ search.current = autoComplete(
+ searchBarRef.current,
+ {
+ hint: false,
+ autoselect: true,
+ openOnFocus: true,
+ cssClasses: {
+ root: clsx(styles.searchBar, {
+ [styles.searchBarLeft]: searchBarPosition === 'left',
+ }),
+ noPrefix: true,
+ dropdownMenu: styles.dropdownMenu,
+ input: styles.input,
+ hint: styles.hint,
+ suggestions: styles.suggestions,
+ suggestion: styles.suggestion,
+ cursor: styles.cursor,
+ dataset: styles.dataset,
+ empty: styles.empty,
+ },
+ },
+ [
+ {
+ source: async (input, callback) => {
+ const result = await searchByWorker(versionUrl,
searchContext, input);
+ callback(result);
+ },
+ templates: {
+ suggestion: SuggestionTemplate,
+ empty: EmptyTemplate,
+ footer: ({ query, isEmpty }) => {
+ if (isEmpty && (!searchContext ||
!useAllContextsWithNoSearchContext)) {
+ return;
+ }
+ const a = searchFooterLinkElement({ query, isEmpty
});
+ const div = document.createElement('div');
+ div.className = styles.hitFooter;
+ div.appendChild(a);
+ return div;
+ },
+ },
+ },
+ ],
+ )
+ .on('autocomplete:selected', function (event, { document: { u, h
}, tokens }) {
+ searchBarRef.current?.blur();
+ let url = u;
+ if (Mark && tokens.length > 0) {
+ const params = new URLSearchParams();
+ for (const token of tokens) {
+ params.append(SEARCH_PARAM_HIGHLIGHT, token);
+ }
+ url += `?${params.toString()}`;
+ }
+ if (h) {
+ url += h;
+ }
+ history.push(url);
+ })
+ .on('autocomplete:closed', () => {
+ searchBarRef.current?.blur();
+ });
+ indexStateMap.current.set(searchContext, 'done');
+ setLoading(false);
+ if (focusAfterIndexLoaded.current) {
+ const input = searchBarRef.current;
+ if (input.value) {
+ search.current?.autocomplete.open();
+ }
+ input.focus();
+ }
+ }, [hidden, searchContext, versionUrl, baseUrl, history]);
+ useEffect(() => {
+ if (!Mark) {
+ return;
+ }
+ const keywords = isBrowser ? new
URLSearchParams(location.search).getAll(SEARCH_PARAM_HIGHLIGHT) : [];
+ // A workaround to fix an issue of highlighting in code blocks.
+ // See https://github.com/easyops-cn/docusaurus-search-local/issues/92
+ // Code blocks will be re-rendered after this `useEffect` ran.
+ // So we make the marking run after a macro task.
+ setTimeout(() => {
+ const root = document.querySelector('article');
+ if (!root) {
+ return;
+ }
+ const mark = new Mark(root);
+ mark.unmark();
+ if (keywords.length !== 0) {
+ mark.mark(keywords);
+ }
+ // Apply any keywords to the search input so that we can clear
marks in case we loaded a page with a highlight in the url
+ setInputValue(keywords.join(' '));
+ search.current?.autocomplete.setVal(keywords.join(' '));
+ });
+ }, [isBrowser, location.search, location.pathname]);
+ const [focused, setFocused] = useState(false);
+ const onInputFocus = useCallback(() => {
+ focusAfterIndexLoaded.current = true;
+ loadIndex();
+ setFocused(true);
+ handleSearchBarToggle?.(true);
+ }, [handleSearchBarToggle, loadIndex]);
+ const onInputBlur = useCallback(() => {
+ setFocused(false);
+ handleSearchBarToggle?.(false);
+ }, [handleSearchBarToggle]);
+ const onInputMouseEnter = useCallback(() => {
+ loadIndex();
+ }, [loadIndex]);
+ const onInputChange = useCallback(event => {
+ setInputValue(event.target.value);
+ if (event.target.value) {
+ setInputChanged(true);
+ }
+ }, []);
+ // Implement hint icons for the search shortcuts on mac and the rest
operating systems.
+ const isMac = isBrowser ? /mac/i.test(navigator.userAgentData?.platform ??
navigator.platform) : false;
+ useEffect(() => {
+ if (!searchBarShortcut) {
+ return;
+ }
+ // Add shortcuts command/ctrl + K
+ const handleShortcut = event => {
+ if ((isMac ? event.metaKey : event.ctrlKey) && (event.key === 'k'
|| event.key === 'K')) {
+ event.preventDefault();
+ searchBarRef.current?.focus();
+ onInputFocus();
+ }
+ };
+ document.addEventListener('keydown', handleShortcut);
+ return () => {
+ document.removeEventListener('keydown', handleShortcut);
+ };
+ }, [isMac, onInputFocus]);
+
+ useEffect(() => {
+ if (isDocsPage) {
+ loadIndex();
+ }
+ }, [location.pathname, isDocsPage]);
+
+ const onClearSearch = useCallback(() => {
+ const params = new URLSearchParams(location.search);
+ params.delete(SEARCH_PARAM_HIGHLIGHT);
+ const paramsStr = params.toString();
+ const searchUrl = location.pathname + (paramsStr != '' ?
`?${paramsStr}` : '') + location.hash;
+ if (searchUrl != location.pathname + location.search + location.hash) {
+ history.push(searchUrl);
+ }
+ // We always clear these here because in case no match was selected
the above history push wont happen
+ setInputValue('');
+ search.current?.autocomplete.setVal('');
+ }, [location.pathname, location.search, location.hash, history]);
+
+ return (
+ <div
+ className={clsx('navbar__search', styles.searchBarContainer, {
+ [styles.searchIndexLoading]: loading && inputChanged,
+ [styles.focused]: focused,
+ })}
+ hidden={hidden}
+ // Manually make the search bar be LTR even if in RTL
+ dir="ltr"
+ >
+ <input
+ placeholder={translate({
+ id: 'theme.SearchBar.label',
+ message: 'Search',
+ description: 'The ARIA label and placeholder for search
button',
+ })}
+ aria-label="Search"
+ className={clsx('navbar__search-input',
styles.navbarSearchInput)}
+ onMouseEnter={onInputMouseEnter}
+ onFocus={onInputFocus}
+ onBlur={onInputBlur}
+ onChange={onInputChange}
+ ref={searchBarRef}
+ value={inputValue}
+ />
+ <LoadingRing className={styles.searchBarLoadingRing} />
+ {searchBarShortcut &&
+ searchBarShortcutHint &&
+ (inputValue !== '' ? (
+ <button className={styles.searchClearButton}
onClick={onClearSearch}>
+ ✕
+ </button>
+ ) : (
+ isBrowser && (
+ <div className={styles.searchHintContainer}>
+ <kbd className={styles.searchHint}>{isMac ? '⌘' :
'ctrl'}</kbd>
+ <kbd className={styles.searchHint}>K</kbd>
+ </div>
+ )
+ ))}
+ </div>
+ );
+}
diff --git a/src/theme/SearchBar/SearchBar.module.css
b/src/theme/SearchBar/SearchBar.module.css
new file mode 100644
index 00000000000..c5d9dfbb682
--- /dev/null
+++ b/src/theme/SearchBar/SearchBar.module.css
@@ -0,0 +1,298 @@
+.searchBar .dropdownMenu {
+ left: auto !important;
+ right: 0 !important;
+
+ background: var(--search-local-modal-background, #f5f6f7);
+ border-radius: 6px;
+ box-shadow: var(
+ --search-local-modal-shadow,
+ inset 1px 1px 0 0 hsla(0, 0%, 100%, 0.5),
+ 0 3px 8px 0 #555a64
+ );
+ margin-top: 8px;
+ width: var(--search-local-modal-width, 560px);
+ position: relative;
+
+ padding: var(--search-local-spacing, 12px);
+}
+
+.navbarSearchInput {
+ border: none !important;
+ background-color: #f7f9fe !important;
+ background-image: url('/static/images/search-icon.svg');
+ height: 2.5rem !important;
+ background-position: 0.625rem center !important;
+ padding-left: 2.25rem !important;
+ color: #4c576c !important;
+ width: 15.43rem !important;
+ border-radius: 0.5rem;
+}
+
+
+@media not (max-width: 996px) {
+ .searchBar.searchBarLeft .dropdownMenu {
+ left: 0 !important;
+ right: auto !important;
+ }
+}
+
+@media (max-width: 576px) {
+ :global(.navbar__search-input):not(:focus) {
+ width: 2rem;
+ }
+
+ .searchBar .dropdownMenu {
+ width: var(--search-local-modal-width-sm, 340px);
+ max-width: calc(100vw - var(--ifm-navbar-padding-horizontal) * 2);
+ }
+}
+
+html[data-theme="dark"] .searchBar .dropdownMenu {
+ background: var(--search-local-modal-background,
var(--ifm-background-color));
+ box-shadow: var(
+ --search-local-modal-shadow,
+ inset 1px 1px 0 0 #2c2e40,
+ 0 3px 8px 0 #000309
+ );
+}
+
+.searchBar .dropdownMenu .suggestion {
+ cursor: pointer;
+ background: var(--search-local-hit-background, #fff);
+ border-radius: 4px;
+ box-shadow: var(--search-local-hit-shadow, 0 1px 3px 0 #d4d9e1);
+ padding: 0 var(--search-local-spacing, 12px);
+ width: 100%;
+
+ align-items: center;
+ color: var(--search-local-hit-color, #444950);
+ display: flex;
+ flex-direction: row;
+ height: var(--search-local-hit-height, 56px);
+}
+
+html[data-theme="dark"] .dropdownMenu .suggestion {
+ background: var(--search-local-hit-background,
var(--ifm-color-emphasis-100));
+ box-shadow: var(--search-local-hit-shadow, none);
+ color: var(--search-local-hit-color, var(--ifm-font-color-base));
+}
+
+.searchBar .dropdownMenu .suggestion:not(:last-child) {
+ margin-bottom: 4px;
+}
+
+.searchBar .dropdownMenu .suggestion.cursor {
+ background-color: var(
+ --search-local-highlight-color,
+ var(--ifm-color-primary)
+ );
+}
+
+.hitTree,
+.hitIcon,
+.hitPath,
+.noResultsIcon,
+.hitFooter a {
+ color: var(--search-local-muted-color, #969faf);
+}
+
+html[data-theme="dark"] .hitTree,
+html[data-theme="dark"] .hitIcon,
+html[data-theme="dark"] .hitPath,
+html[data-theme="dark"] .noResultsIcon {
+ color: var(--search-local-muted-color, var(--ifm-color-secondary-darkest));
+}
+
+.hitTree {
+ display: flex;
+ align-items: center;
+}
+
+.hitTree > svg {
+ height: var(--search-local-hit-height, 56px);
+ opacity: 0.5;
+ stroke-width: var(--search-local-icon-stroke-width, 1.4);
+ width: 24px;
+}
+
+.hitIcon {
+ stroke-width: var(--search-local-icon-stroke-width, 1.4);
+
+ height: 20px;
+ width: 20px;
+}
+
+.hitWrapper {
+ flex: 1 1 auto;
+ display: flex;
+ flex-direction: column;
+ font-weight: 500;
+ justify-content: center;
+ margin: 0 8px;
+ overflow-x: hidden;
+ width: 80%;
+}
+
+.hitWrapper mark {
+ background: none;
+ color: var(--search-local-highlight-color, var(--ifm-color-primary));
+}
+
+.hitTitle {
+ font-size: 0.9em;
+}
+
+.hitPath {
+ font-size: 0.75em;
+}
+
+.hitPath,
+.hitTitle {
+ white-space: nowrap;
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+}
+
+.hitAction {
+ height: 20px;
+ width: 20px;
+}
+
+.hideAction > svg {
+ display: none;
+}
+
+.noResults {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: var(--search-local-spacing, 12px) 0;
+}
+
+.noResultsIcon {
+ margin-bottom: var(--search-local-spacing, 12px);
+}
+
+.hitFooter {
+ text-align: center;
+ margin-top: var(--search-local-spacing, 12px);
+ font-size: 0.85em;
+}
+
+.hitFooter a {
+ text-decoration: underline;
+}
+
+.cursor .hideAction > svg {
+ display: block;
+}
+
+.suggestion.cursor,
+.suggestion.cursor mark,
+.suggestion.cursor .hitTree,
+.suggestion.cursor .hitIcon,
+.suggestion.cursor .hitPath {
+ color: var(
+ --search-local-hit-active-color,
+ var(--ifm-color-white)
+ ) !important;
+}
+
+.suggestion.cursor mark {
+ text-decoration: underline;
+}
+
+.searchBarContainer {
+ margin-left: 16px;
+}
+
+.searchBarContainer .searchBarLoadingRing {
+ display: none;
+ position: absolute;
+ left: 10px;
+ top: 6px;
+}
+
+.searchBarContainer .searchClearButton {
+ position: absolute;
+ right: 0.8rem;
+ top: 50%;
+ transform: translate(0, -50%);
+ padding: 0;
+ background: none;
+ border: none;
+ line-height: 1rem;
+}
+
+:global(.navbar__search) {
+ position: relative;
+}
+
+.searchIndexLoading :global(.navbar__search-input) {
+ background-image: none;
+}
+
+.searchBarContainer.searchIndexLoading .searchBarLoadingRing {
+ display: inline-block;
+}
+
+.searchHintContainer {
+ position: absolute;
+ right: 10px;
+ top: 0px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ pointer-events: none;
+ gap: 4px;
+}
+
+.searchHint {
+ color: var(--ifm-navbar-search-input-placeholder-color);
+ background-color: var(--ifm-navbar-search-input-background-color);
+ border: 1px solid var(--ifm-color-emphasis-500);
+ box-shadow: inset 0 -1px 0 var(--ifm-color-emphasis-500);
+}
+
+@media (max-width: 576px) {
+ .searchBarContainer:not(.focused) .searchClearButton,
+ .searchHintContainer {
+ display: none;
+ }
+}
+
+html[dir="rtl"] .searchHintContainer {
+ right: auto;
+ left: 10px;
+}
+
+html[dir="rtl"] .searchBarContainer .searchClearButton {
+ right: auto;
+ left: 0.8rem;
+}
+
+html[dir="rtl"] .searchBarContainer .searchBarLoadingRing {
+ left: auto;
+ right: 10px;
+}
+
+html[dir="rtl"] :global(.navbar__search-input) {
+ padding: 0 2.25em 0 0.5em;
+}
+
+/* For autocomplete.js only. */
+.input {
+}
+.hint {
+}
+.suggestions {
+ max-height: 500px !important;
+ overflow: scroll !important;
+}
+.dataset {
+}
+.empty {
+}
+/**/
diff --git a/src/theme/SearchBar/SuggestionTemplate.js
b/src/theme/SearchBar/SuggestionTemplate.js
new file mode 100644
index 00000000000..427a6a6b225
--- /dev/null
+++ b/src/theme/SearchBar/SuggestionTemplate.js
@@ -0,0 +1,54 @@
+import { SearchDocumentType, } from "../../../shared/interfaces";
+import { concatDocumentPath } from "../../utils/concatDocumentPath";
+import { getStemmedPositions } from "../../utils/getStemmedPositions";
+import { highlight } from "../../utils/highlight";
+import { highlightStemmed } from "../../utils/highlightStemmed";
+import { explicitSearchResultPath } from "../../utils/proxiedGenerated";
+import { iconAction, iconContent, iconHeading, iconTitle, iconTreeInter,
iconTreeLast, } from "./icons";
+import styles from "./SearchBar.module.css";
+export function SuggestionTemplate({ document, type, page, metadata, tokens,
isInterOfTree, isLastOfTree, }) {
+ const isTitle = type === SearchDocumentType.Title;
+ const isKeywords = type === SearchDocumentType.Keywords;
+ const isTitleRelated = isTitle || isKeywords;
+ const isHeading = type === SearchDocumentType.Heading;
+ const tree = [];
+ if (isInterOfTree) {
+ tree.push(iconTreeInter);
+ }
+ else if (isLastOfTree) {
+ tree.push(iconTreeLast);
+ }
+ const treeWrapper = tree.map((item) => `<span
class="${styles.hitTree}">${item}</span>`);
+ const icon = `<span class="${styles.hitIcon}">${isTitleRelated ? iconTitle
: isHeading ? iconHeading : iconContent}</span>`;
+ const wrapped = [
+ `<span class="${styles.hitTitle}">${isKeywords
+ ? highlight(document.s, tokens)
+ : highlightStemmed(document.t, getStemmedPositions(metadata, "t"),
tokens)}</span>`,
+ ];
+ const needsExplicitHitPath = !isInterOfTree && !isLastOfTree &&
explicitSearchResultPath;
+ if (needsExplicitHitPath) {
+ const pathItems = page
+ ? page.b
+ ?.concat(page.t)
+ .concat(!document.s || document.s === page.t ? [] : document.s)
+ : document.b;
+ wrapped.push(`<span
class="${styles.hitPath}">${concatDocumentPath(pathItems ?? [])}</span>`);
+ }
+ else if (!isTitleRelated) {
+ wrapped.push(`<span class="${styles.hitPath}">${highlight(page.t ||
+ // Todo(weareoutman): This is for EasyOps only.
+ // istanbul ignore next
+ (document.u.startsWith("/docs/api-reference/")
+ ? "API Reference"
+ : ""), tokens)}</span>`);
+ }
+ const action = `<span class="${styles.hitAction}">${iconAction}</span>`;
+ return [
+ ...treeWrapper,
+ icon,
+ `<span class="${styles.hitWrapper}">`,
+ ...wrapped,
+ "</span>",
+ action,
+ ].join("");
+}
diff --git a/src/theme/SearchBar/__mocks__/icons.js
b/src/theme/SearchBar/__mocks__/icons.js
new file mode 100644
index 00000000000..7c2f79e1343
--- /dev/null
+++ b/src/theme/SearchBar/__mocks__/icons.js
@@ -0,0 +1,7 @@
+export const iconTitle = '<svg class="icon-title"></svg>';
+export const iconHeading = '<svg class="icon-heading"></svg>';
+export const iconContent = '<svg class="icon-content"></svg>';
+export const iconAction = '<svg class="icon-action"></svg>';
+export const iconNoResults = '<svg class="icon-no-results"></svg>';
+export const iconTreeInter = '<svg class="icon-tree-inner"></svg>';
+export const iconTreeLast = '<svg class="icon-tree-last"></svg>';
diff --git a/src/theme/SearchBar/icons.js b/src/theme/SearchBar/icons.js
new file mode 100644
index 00000000000..d5380213e98
--- /dev/null
+++ b/src/theme/SearchBar/icons.js
@@ -0,0 +1,7 @@
+export const iconTitle = '<svg width="20" height="20" viewBox="0 0 20
20"><path d="M17 6v12c0 .52-.2 1-1 1H4c-.7 0-1-.33-1-1V2c0-.55.42-1 1-1h8l5
5zM14 8h-3.13c-.51 0-.87-.34-.87-.87V4" stroke="currentColor" fill="none"
fill-rule="evenodd" stroke-linejoin="round"></path></svg>';
+export const iconHeading = '<svg width="20" height="20" viewBox="0 0 20
20"><path d="M13 13h4-4V8H7v5h6v4-4H7V8H3h4V3v5h6V3v5h4-4v5zm-6 0v4-4H3h4z"
stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round"
stroke-linejoin="round"></path></svg>';
+export const iconContent = '<svg width="20" height="20" viewBox="0 0 20
20"><path d="M17 5H3h14zm0 5H3h14zm0 5H3h14z" stroke="currentColor" fill="none"
fill-rule="evenodd" stroke-linejoin="round"></path></svg>';
+export const iconAction = '<svg width="20" height="20" viewBox="0 0 20 20"><g
stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round"
stroke-linejoin="round"><path d="M18 3v4c0 2-2 4-4 4H2"></path><path d="M8
17l-6-6 6-6"></path></g></svg>';
+export const iconNoResults = '<svg width="40" height="40" viewBox="0 0 20 20"
fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round"><path d="M15.5 4.8c2 3 1.7 7-1 9.7h0l4.3
4.3-4.3-4.3a7.8 7.8 0 01-9.8 1m-2.2-2.2A7.8 7.8 0 0113.2 2.4M2 18L18
2"></path></svg>';
+export const iconTreeInter = '<svg viewBox="0 0 24 54"><g
stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round"
stroke-linejoin="round"><path d="M8 6v42M20 27H8.3"></path></g></svg>';
+export const iconTreeLast = '<svg viewBox="0 0 24 54"><g stroke="currentColor"
fill="none" fill-rule="evenodd" stroke-linecap="round"
stroke-linejoin="round"><path d="M8 6v21M20 27H8.3"></path></g></svg>';
diff --git a/src/theme/SearchBar/index.js b/src/theme/SearchBar/index.js
index 381d9012c05..369df710bfd 100644
--- a/src/theme/SearchBar/index.js
+++ b/src/theme/SearchBar/index.js
@@ -1,10 +1,3 @@
-import React from 'react';
-import SearchBar from '@theme-original/SearchBar';
-
-export default function SearchBarWrapper(props) {
- return (
- <>
- <SearchBar {...props} />
- </>
- );
-}
+import "../../utils/proxiedGenerated";
+import SearchBar from "./SearchBar";
+export default SearchBar;
diff --git a/src/theme/SearchBar/searchByWorker.js
b/src/theme/SearchBar/searchByWorker.js
new file mode 100644
index 00000000000..cd53fa72b97
--- /dev/null
+++ b/src/theme/SearchBar/searchByWorker.js
@@ -0,0 +1,24 @@
+import * as Comlink from "comlink";
+let remoteWorkerPromise;
+function getRemoteWorker() {
+ if (process.env.NODE_ENV === "production" && !remoteWorkerPromise) {
+ remoteWorkerPromise = (async () => {
+ const Remote = Comlink.wrap(new Worker(new URL("./worker.js",
import.meta.url)));
+ return await new Remote();
+ })();
+ }
+ return remoteWorkerPromise;
+}
+export async function fetchIndexesByWorker(baseUrl, searchContext) {
+ if (process.env.NODE_ENV === "production") {
+ const remoteWorker = await getRemoteWorker();
+ await remoteWorker.fetchIndexes(baseUrl, searchContext);
+ }
+}
+export async function searchByWorker(baseUrl, searchContext, input) {
+ if (process.env.NODE_ENV === "production") {
+ const remoteWorker = await getRemoteWorker();
+ return remoteWorker.search(baseUrl, searchContext, input);
+ }
+ return [];
+}
diff --git a/src/theme/SearchBar/worker.js b/src/theme/SearchBar/worker.js
new file mode 100644
index 00000000000..b2d3d11d08a
--- /dev/null
+++ b/src/theme/SearchBar/worker.js
@@ -0,0 +1,94 @@
+import * as Comlink from "comlink";
+import lunr from "lunr";
+import { searchIndexUrl, searchResultLimits, language } from
"@easyops-cn/docusaurus-search-local/dist/client/client/utils/proxiedGeneratedConstants";
+import { tokenize } from
"@easyops-cn/docusaurus-search-local/dist/client/client/utils/tokenize";
+import { smartQueries } from
"@easyops-cn/docusaurus-search-local/dist/client/client/utils/smartQueries";
+import { SearchDocumentType, } from "../../shared/interfaces";
+import { sortSearchResults } from
"@easyops-cn/docusaurus-search-local/dist/client/client/utils/sortSearchResults";
+import { processTreeStatusOfSearchResults } from
"@easyops-cn/docusaurus-search-local/dist/client/client/utils/processTreeStatusOfSearchResults";
+const cache = new Map();
+export class SearchWorker {
+ async fetchIndexes(baseUrl, searchContext) {
+ await this.lowLevelFetchIndexes(baseUrl, searchContext);
+ }
+ async lowLevelFetchIndexes(baseUrl, searchContext) {
+ const cacheKey = `${baseUrl}${searchContext}`;
+ let promise = cache.get(cacheKey);
+ if (!promise) {
+ promise = legacyFetchIndexes(baseUrl, searchContext);
+ cache.set(cacheKey, promise);
+ }
+ return promise;
+ }
+ async search(baseUrl, searchContext, input) {
+ const rawTokens = tokenize(input, language);
+ if (rawTokens.length === 0) {
+ return [];
+ }
+ const { wrappedIndexes, zhDictionary } = await
this.lowLevelFetchIndexes(baseUrl, searchContext);
+ const queries = smartQueries(rawTokens, zhDictionary);
+ const results = [];
+ search: for (const { term, tokens } of queries) {
+ for (const { documents, index, type } of wrappedIndexes) {
+ results.push(...index
+ .query((query) => {
+ for (const item of term) {
+ query.term(item.value, {
+ wildcard: item.wildcard,
+ presence: item.presence,
+ });
+ }
+ })
+ .slice(0, searchResultLimits)
+ // Remove duplicated results.
+ .filter((result) => !results.some((item) =>
item.document.i.toString() === result.ref))
+ .slice(0, searchResultLimits - results.length)
+ .map((result) => {
+ const document = documents.find((doc) => doc.i.toString()
=== result.ref);
+ return {
+ document,
+ type,
+ page: type !== SearchDocumentType.Title &&
+ wrappedIndexes[0].documents.find((doc) => doc.i
=== document.p),
+ metadata: result.matchData.metadata,
+ tokens,
+ score: result.score,
+ };
+ }));
+ if (results.length >= searchResultLimits) {
+ break search;
+ }
+ }
+ }
+ sortSearchResults(results);
+ processTreeStatusOfSearchResults(results);
+ return results;
+ }
+}
+async function legacyFetchIndexes(baseUrl, searchContext) {
+ const url = `${baseUrl}${searchIndexUrl.replace("{dir}", searchContext ?
`-${searchContext.replace(/\//g, "-")}` : "")}`;
+ // Catch potential attacks.
+ const fullUrl = new URL(url, location.origin);
+ if (fullUrl.origin !== location.origin) {
+ throw new Error("Unexpected version url");
+ }
+ const json = (await (await fetch(url)).json());
+ const wrappedIndexes = json.map(({ documents, index }, type) => ({
+ type: type,
+ documents,
+ index: lunr.Index.load(index),
+ }));
+ const zhDictionary = json.reduce((acc, item) => {
+ for (const tuple of item.index.invertedIndex) {
+ if (/\p{Unified_Ideograph}/u.test(tuple[0][0])) {
+ acc.add(tuple[0]);
+ }
+ }
+ return acc;
+ }, new Set());
+ return {
+ wrappedIndexes,
+ zhDictionary: Array.from(zhDictionary),
+ };
+}
+Comlink.expose(SearchWorker);
diff --git a/src/theme/TOC/index.tsx b/src/theme/TOC/index.tsx
index d08817f4abd..5571d8e1fa7 100644
--- a/src/theme/TOC/index.tsx
+++ b/src/theme/TOC/index.tsx
@@ -6,7 +6,9 @@ import useDocusaurusContext from
'@docusaurus/useDocusaurusContext';
import HomeIcon from '@site/static/images/toc-icon/home.svg';
import PdfIcon from '@site/static/images/toc-icon/pdf.svg';
import GithubIcon from '@site/static/images/toc-icon/github.svg';
+import ConcatIcon from '@site/static/images/toc-icon/concat.svg';
import { DOWNLOAD_PDFS } from '@site/src/constant/download.data';
+import { VERSIONS } from '@site/src/constant/common';
import Link from '@docusaurus/Link';
import styles from './styles.module.css';
@@ -37,14 +39,14 @@ export default function TOC({ className, ...props }:
Props): JSX.Element {
const [currentVersion, setCurrentVersion] = useState(DEFAULT_VERSION);
const handleMouseEnter = (id: string) => {
const dom = document.getElementById(id);
- dom.style.color = '#444FD9';
- dom.firstChild.style.fill = '#444FD9';
+ dom!.style.color = '#444FD9';
+ dom!.firstChild!.style.fill = '#444FD9';
};
const handleMouseLeave = (id: string) => {
const dom = document.getElementById(id);
- dom.style.color = '#1F1F26';
- dom.firstChild.style.fill = '#7F7F83';
+ dom!.style.color = '#1F1F26';
+ dom!.firstChild!.style.fill = '#7F7F83';
};
useEffect(() => {
@@ -55,7 +57,7 @@ export default function TOC({ className, ...props }: Props):
JSX.Element {
const secPath = location.pathname.includes('zh-CN/docs')
? location.pathname.split('/')[3]
: location.pathname.split('/')[2];
- if (pathname === 'docs' && ['dev', '3.0', '2.0',
'1.2'].includes(secPath)) {
+ if (pathname === 'docs' && VERSIONS.includes(secPath)) {
setCurrentVersion(secPath);
} else {
setCurrentVersion(DEFAULT_VERSION);
@@ -100,7 +102,7 @@ export default function TOC({ className, ...props }:
Props): JSX.Element {
onMouseEnter={() => handleMouseEnter('toc-icon-github')}
onMouseLeave={() => handleMouseLeave('toc-icon-github')}
>
- <GithubIcon />
+ {isCN ? <ConcatIcon /> : <GithubIcon />}
<span>{isCN ? '技术论坛' : 'Ask Questions on
Discussion'}</span>
</Link>
</div>
diff --git a/src/utils/normalizeContextByPath.js
b/src/utils/normalizeContextByPath.js
new file mode 100644
index 00000000000..99c29d64034
--- /dev/null
+++ b/src/utils/normalizeContextByPath.js
@@ -0,0 +1,24 @@
+export function normalizeContextByPath(context, currentLocale) {
+ if (typeof context === "string") {
+ return {
+ label: context,
+ path: context,
+ };
+ }
+ else {
+ const { label, path } = context;
+ if (typeof label === "string") {
+ return { label, path };
+ }
+ if (Object.prototype.hasOwnProperty.call(label, currentLocale)) {
+ return {
+ label: label[currentLocale],
+ path,
+ };
+ }
+ return {
+ label: path,
+ path,
+ };
+ }
+}
diff --git a/static/images/toc-icon/concat.svg
b/static/images/toc-icon/concat.svg
new file mode 100644
index 00000000000..7b7754644a4
--- /dev/null
+++ b/static/images/toc-icon/concat.svg
@@ -0,0 +1,21 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="#7F7F83"
xmlns="http://www.w3.org/2000/svg">
+ <g clip-path="url(#clip0_8016_3647)">
+ <path
+ d="M3.04395 4.7041C3.04395 4.28989 3.37973 3.9541 3.79395
3.9541H16.2575C16.6717 3.9541 17.0075 4.28989 17.0075 4.7041V13.1767C17.0075
13.591 16.6717 13.9267 16.2575 13.9267H10.0218C9.86403 13.9267 9.71031 13.9765
9.58246 14.0689L5.67114 16.8957L6.09631 13.9267H3.79395C3.37973 13.9267 3.04395
13.591 3.04395 13.1767V4.7041Z"
+ />
+ <path
+ d="M6.5 9.93701C7.05228 9.93701 7.5 9.4893 7.5 8.93701C7.5 8.38473
7.05228 7.93701 6.5 7.93701C5.94772 7.93701 5.5 8.38473 5.5 8.93701C5.5 9.4893
5.94772 9.93701 6.5 9.93701Z"
+ fill="white" />
+ <path
+ d="M10 9.93701C10.5523 9.93701 11 9.4893 11 8.93701C11 8.38473
10.5523 7.93701 10 7.93701C9.44772 7.93701 9 8.38473 9 8.93701C9 9.4893 9.44772
9.93701 10 9.93701Z"
+ fill="white" />
+ <path
+ d="M13.5 9.93701C14.0523 9.93701 14.5 9.4893 14.5 8.93701C14.5
8.38473 14.0523 7.93701 13.5 7.93701C12.9477 7.93701 12.5 8.38473 12.5
8.93701C12.5 9.4893 12.9477 9.93701 13.5 9.93701Z"
+ fill="white" />
+ </g>
+ <defs>
+ <clipPath id="clip0_8016_3647">
+ <rect width="20" height="20" />
+ </clipPath>
+ </defs>
+</svg>
\ No newline at end of file
diff --git a/static/images/toc-icon/github-active.svg
b/static/images/toc-icon/github-active.svg
deleted file mode 100644
index 904b6428759..00000000000
--- a/static/images/toc-icon/github-active.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-<svg width="21" height="20" viewBox="0 0 21 20" fill="none"
xmlns="http://www.w3.org/2000/svg">
- <g clip-path="url(#clip0_8019_4572)">
- <path
- d="M11.0061 3C14.9792 2.99839 18.1973 6.21483 18.1973
10.1848C18.1973 13.3241 16.1842 15.9927 13.3806 16.9727C13.0031 17.0675 13.0609
16.7992 13.0609 16.616V15.3709C15.2411 15.6263 15.3295 14.1836 15.4757
13.9426C15.7713 13.4381 16.4702 13.3096 16.2613 13.0686C15.7649 12.8132 15.2588
13.1329 14.6724 13.9989C14.2482 14.627 13.4208 14.521 13.0015 14.4166C12.9099
14.039 12.7139 13.7016 12.444 13.4398C14.7029 13.0349 15.6444 11.6564 15.6444
10.0177C15.6444 9.2224 15.3825 8.49139 [...]
- fill="#636CDF" />
- </g>
- <defs>
- <clipPath id="clip0_8019_4572">
- <rect width="21" height="20" fill="white" />
- </clipPath>
- </defs>
-</svg>
\ No newline at end of file
diff --git a/static/images/toc-icon/home-active.svg
b/static/images/toc-icon/home-active.svg
deleted file mode 100644
index 1cb9a98ae42..00000000000
--- a/static/images/toc-icon/home-active.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<svg width="21" height="20" viewBox="0 0 21 20" fill="none"
xmlns="http://www.w3.org/2000/svg">
- <path fill-rule="evenodd" clip-rule="evenodd"
- d="M3.33983 6.44003C3.06163 6.62549 2.89453 6.93773 2.89453
7.27208V16.2106C2.89453 16.6466 3.24799 17.0001 3.684 17.0001H16.3156C16.7516
17.0001 17.1051 16.6466 17.1051 16.2106V7.27208C17.1051 6.93773 16.938 6.62549
16.6598 6.44003L10.5545 2.36985C10.2186 2.14592 9.78099 2.14592 9.44509
2.36985L3.33983 6.44003ZM10.6001 10.6001C10.6001 10.2687 10.3315 10.0001
10.0001 10.0001C9.66873 10.0001 9.4001 10.2687 9.4001 10.6001L9.4001
13.9001C9.4001 14.2314 9.66873 14.5001 10.0001 14.500 [...]
- fill="#636CDF" />
-</svg>
\ No newline at end of file
diff --git a/static/images/toc-icon/pdf-active.svg
b/static/images/toc-icon/pdf-active.svg
deleted file mode 100644
index 3e9bf42eb7f..00000000000
--- a/static/images/toc-icon/pdf-active.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-<svg width="21" height="20" viewBox="0 0 21 20" fill="none"
xmlns="http://www.w3.org/2000/svg">
- <path
- d="M15.1754 4H4.28648C3.43093 4 2.73096 4.70001 2.73096
5.55559V16.4445C2.73096 17.3 3.43093 18 4.28648 18H15.1754C16.031 18 16.731
17.3 16.731 16.4445V5.55559C16.7309 4.70005 16.0309 4 15.1754 4ZM9.38379
9.99791C9.20496 10.5735 8.9996 11.4635 8.69752 12.1952C8.54177 12.5909 8.54931
12.522 8.43222 12.7719L8.53333 12.7391C9.57248 12.4483 10.3 12.3762 10.9767
12.2517C10.8409 12.1445 10.7229 12.0616 10.6295 11.9761C10.1646 11.4614 10.0023
11.3648 9.38379 9.99791ZM14.4834 13.2945C14. [...]
- fill="#636CDF" />
-</svg>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]