snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
61
docs/site/src/theme/MDXComponents.tsx
Normal file
61
docs/site/src/theme/MDXComponents.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
// Globally-available MDX components. Authors can write <Note>...</Note>
|
||||
// in any .mdx file without an import.
|
||||
|
||||
import MDXComponents from '@theme-original/MDXComponents';
|
||||
import {Note, Tip, Important, Attention, Warning, Security} from '@site/src/components/Callout';
|
||||
import PlanBadge from '@site/src/components/PlanBadge';
|
||||
import PlanAvailability from '@site/src/components/PlanAvailability';
|
||||
import EditionAvailability from '@site/src/components/EditionAvailability';
|
||||
import DeploymentAvailability from '@site/src/components/DeploymentAvailability';
|
||||
import AttestationStatus from '@site/src/components/AttestationStatus';
|
||||
import DeploymentOnly from '@site/src/components/DeploymentOnly';
|
||||
import IMEDiagram from '@site/src/components/IMEDiagram';
|
||||
import DeploymentArchitectureBuilder from '@site/src/components/DeploymentArchitectureBuilder';
|
||||
import CompassIcon from '@site/src/components/CompassIcon';
|
||||
import Hero from '@site/src/components/Hero';
|
||||
import Eyebrow from '@site/src/components/Eyebrow';
|
||||
import StatStrip from '@site/src/components/StatStrip';
|
||||
import MethodLegend from '@site/src/components/MethodLegend';
|
||||
import CardGrid from '@site/src/components/CardGrid';
|
||||
import UpgradeNotesFilter from '@site/src/components/UpgradeNotesFilter';
|
||||
import SecurityUpdates from '@site/src/components/SecurityUpdates';
|
||||
import PluginGoDocs from '@site/src/components/PluginGoDocs';
|
||||
import PluginGoExample from '@site/src/components/PluginGoExample';
|
||||
import PluginJsDocs from '@site/src/components/PluginJsDocs';
|
||||
import PluginManifestDocs from '@site/src/components/PluginManifestDocs';
|
||||
// Globally available so migrated developer docs (Hugo `tabs` shortcode)
|
||||
// can use them without imports.
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
export default {
|
||||
...MDXComponents,
|
||||
Note,
|
||||
Tip,
|
||||
Important,
|
||||
Attention,
|
||||
Warning,
|
||||
Security,
|
||||
PlanBadge,
|
||||
PlanAvailability,
|
||||
EditionAvailability,
|
||||
DeploymentAvailability,
|
||||
AttestationStatus,
|
||||
DeploymentOnly,
|
||||
IMEDiagram,
|
||||
DeploymentArchitectureBuilder,
|
||||
CompassIcon,
|
||||
Hero,
|
||||
Eyebrow,
|
||||
StatStrip,
|
||||
MethodLegend,
|
||||
CardGrid,
|
||||
UpgradeNotesFilter,
|
||||
SecurityUpdates,
|
||||
PluginGoDocs,
|
||||
PluginGoExample,
|
||||
PluginJsDocs,
|
||||
PluginManifestDocs,
|
||||
Tabs,
|
||||
TabItem,
|
||||
};
|
||||
623
docs/site/src/theme/SearchPage/index.tsx
Normal file
623
docs/site/src/theme/SearchPage/index.tsx
Normal file
@@ -0,0 +1,623 @@
|
||||
/* eslint-disable jsx-a11y/no-autofocus */
|
||||
|
||||
// Ejected from @docusaurus/theme-search-algolia@3.10.1 (`SearchPage`).
|
||||
//
|
||||
// Upstream never forwards `themeConfig.algolia.searchParameters` to this
|
||||
// component — it destructures only {appId, apiKey, indexName,
|
||||
// contextualSearch} and builds the query helper from a hardcoded parameter
|
||||
// set. The DocSearch modal does honour `searchParameters`, so the two search
|
||||
// surfaces returned differently ordered results for the same query. There is
|
||||
// no config option to fix this, and Algolia Rules aren't available on our
|
||||
// plan, so swizzling is the only route.
|
||||
//
|
||||
// The ONLY delta from upstream is the three edits marked "Mattermost delta"
|
||||
// below: `searchParameters` added to the `useAlgoliaThemeConfig()`
|
||||
// destructure, `...searchParameters` spread last into the
|
||||
// `algoliaSearchHelper` options, and the `PlainSearchParameters` type import
|
||||
// the spread's cast needs. Everything else is verbatim upstream, including
|
||||
// the swizzle-stripped MIT license header. Filter values and scores are
|
||||
// deliberately not duplicated here — docusaurus.config.ts stays the single
|
||||
// source of truth so the modal and this page cannot drift apart again.
|
||||
//
|
||||
// On a Docusaurus upgrade, re-eject from the new version and re-apply those
|
||||
// three edits rather than diffing this whole file:
|
||||
// npm run swizzle @docusaurus/theme-search-algolia SearchPage -- \
|
||||
// --eject --danger --typescript
|
||||
// (the theme is only registered when ALGOLIA_APP_ID and
|
||||
// ALGOLIA_SEARCH_API_KEY are set, so export placeholders first). Drop this
|
||||
// file entirely once upstream passes `searchParameters` through.
|
||||
|
||||
import React, {
|
||||
type ReactNode,
|
||||
useEffect,
|
||||
useReducer,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import algoliaSearchHelper, {
|
||||
type PlainSearchParameters,
|
||||
} from 'algoliasearch-helper';
|
||||
import {liteClient} from 'algoliasearch/lite';
|
||||
|
||||
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||
import Head from '@docusaurus/Head';
|
||||
import Link from '@docusaurus/Link';
|
||||
import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
|
||||
import {
|
||||
HtmlClassNameProvider,
|
||||
PageMetadata,
|
||||
useEvent,
|
||||
usePluralForm,
|
||||
useSearchQueryString,
|
||||
} from '@docusaurus/theme-common';
|
||||
import Translate, {translate} from '@docusaurus/Translate';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import {
|
||||
useAlgoliaThemeConfig,
|
||||
useSearchResultUrlProcessor,
|
||||
} from '@docusaurus/theme-search-algolia/client';
|
||||
import Layout from '@theme/Layout';
|
||||
import Heading from '@theme/Heading';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
// Very simple pluralization: probably good enough for now
|
||||
function useDocumentsFoundPlural() {
|
||||
const {selectMessage} = usePluralForm();
|
||||
return (count: number) =>
|
||||
selectMessage(
|
||||
count,
|
||||
translate(
|
||||
{
|
||||
id: 'theme.SearchPage.documentsFound.plurals',
|
||||
description:
|
||||
'Pluralized label for "{count} documents found". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',
|
||||
message: 'One document found|{count} documents found',
|
||||
},
|
||||
{count},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function useDocsSearchVersionsHelpers() {
|
||||
const allDocsData = useAllDocsData();
|
||||
|
||||
// State of the version select menus / algolia facet filters
|
||||
// docsPluginId -> versionName map
|
||||
const [searchVersions, setSearchVersions] = useState<{
|
||||
[pluginId: string]: string;
|
||||
}>(() =>
|
||||
Object.entries(allDocsData).reduce(
|
||||
(acc, [pluginId, pluginData]) => ({
|
||||
...acc,
|
||||
[pluginId]: pluginData.versions[0]!.name,
|
||||
}),
|
||||
{},
|
||||
),
|
||||
);
|
||||
|
||||
// Set the value of a single select menu
|
||||
const setSearchVersion = (pluginId: string, searchVersion: string) =>
|
||||
setSearchVersions((s) => ({...s, [pluginId]: searchVersion}));
|
||||
|
||||
const versioningEnabled = Object.values(allDocsData).some(
|
||||
(docsData) => docsData.versions.length > 1,
|
||||
);
|
||||
|
||||
return {
|
||||
allDocsData,
|
||||
versioningEnabled,
|
||||
searchVersions,
|
||||
setSearchVersion,
|
||||
};
|
||||
}
|
||||
|
||||
// We want to display one select per versioned docs plugin instance
|
||||
function SearchVersionSelectList({
|
||||
docsSearchVersionsHelpers,
|
||||
}: {
|
||||
docsSearchVersionsHelpers: ReturnType<typeof useDocsSearchVersionsHelpers>;
|
||||
}) {
|
||||
const versionedPluginEntries = Object.entries(
|
||||
docsSearchVersionsHelpers.allDocsData,
|
||||
)
|
||||
// Do not show a version select for unversioned docs plugin instances
|
||||
.filter(([, docsData]) => docsData.versions.length > 1);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'col',
|
||||
'col--3',
|
||||
'padding-left--none',
|
||||
styles.searchVersionColumn,
|
||||
)}>
|
||||
{versionedPluginEntries.map(([pluginId, docsData]) => {
|
||||
const labelPrefix =
|
||||
versionedPluginEntries.length > 1 ? `${pluginId}: ` : '';
|
||||
return (
|
||||
<select
|
||||
key={pluginId}
|
||||
onChange={(e) =>
|
||||
docsSearchVersionsHelpers.setSearchVersion(
|
||||
pluginId,
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
defaultValue={docsSearchVersionsHelpers.searchVersions[pluginId]}
|
||||
className={styles.searchVersionInput}>
|
||||
{docsData.versions.map((version, i) => (
|
||||
<option
|
||||
key={i}
|
||||
label={`${labelPrefix}${version.label}`}
|
||||
value={version.name}
|
||||
/>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AlgoliaLogo(): ReactNode {
|
||||
return (
|
||||
<svg
|
||||
width="80"
|
||||
height="24"
|
||||
aria-label="Algolia"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 2196.2 500"
|
||||
style={{maxWidth: '150px'}}>
|
||||
<defs>
|
||||
{/* eslint-disable-next-line @docusaurus/no-untranslated-text */}
|
||||
<style>{`.cls-1,.cls-2{fill:#003dff}.cls-2{fill-rule:evenodd}`}</style>
|
||||
</defs>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1070.38,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
|
||||
/>
|
||||
<rect
|
||||
className="cls-1"
|
||||
x="1845.88"
|
||||
y="104.73"
|
||||
width="62.58"
|
||||
height="277.9"
|
||||
rx="5.9"
|
||||
ry="5.9"
|
||||
/>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1851.78,71.38h50.77c3.26,0,5.9-2.64,5.9-5.9V5.9c0-3.62-3.24-6.39-6.82-5.83l-50.77,7.95c-2.87,.45-4.99,2.92-4.99,5.83v51.62c0,3.26,2.64,5.9,5.9,5.9Z"
|
||||
/>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1764.03,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
|
||||
/>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1631.95,142.72c-11.14-12.25-24.83-21.65-40.78-28.31-15.92-6.53-33.26-9.85-52.07-9.85-18.78,0-36.15,3.17-51.92,9.85-15.59,6.66-29.29,16.05-40.76,28.31-11.47,12.23-20.38,26.87-26.76,44.03-6.38,17.17-9.24,37.37-9.24,58.36,0,20.99,3.19,36.87,9.55,54.21,6.38,17.32,15.14,32.11,26.45,44.36,11.29,12.23,24.83,21.62,40.6,28.46,15.77,6.83,40.12,10.33,52.4,10.48,12.25,0,36.78-3.82,52.7-10.48,15.92-6.68,29.46-16.23,40.78-28.46,11.29-12.25,20.05-27.04,26.25-44.36,6.22-17.34,9.24-33.22,9.24-54.21,0-20.99-3.34-41.19-10.03-58.36-6.38-17.17-15.14-31.8-26.43-44.03Zm-44.43,163.75c-11.47,15.75-27.56,23.7-48.09,23.7-20.55,0-36.63-7.8-48.1-23.7-11.47-15.75-17.21-34.01-17.21-61.2,0-26.89,5.59-49.14,17.06-64.87,11.45-15.75,27.54-23.52,48.07-23.52,20.55,0,36.63,7.78,48.09,23.52,11.47,15.57,17.36,37.98,17.36,64.87,0,27.19-5.72,45.3-17.19,61.2Z"
|
||||
/>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M894.42,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
|
||||
/>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M2133.97,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-14.52,22.58-22.99,49.63-22.99,78.73,0,44.89,20.13,84.92,51.59,111.1,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47,1.23,0,2.46-.03,3.68-.09,.36-.02,.71-.05,1.07-.07,.87-.05,1.75-.11,2.62-.2,.34-.03,.68-.08,1.02-.12,.91-.1,1.82-.21,2.73-.34,.21-.03,.42-.07,.63-.1,32.89-5.07,61.56-30.82,70.9-62.81v57.83c0,3.26,2.64,5.9,5.9,5.9h50.42c3.26,0,5.9-2.64,5.9-5.9V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,206.92c-12.2,10.16-27.97,13.98-44.84,15.12-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-42.24,0-77.12-35.89-77.12-79.37,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33v142.83Z"
|
||||
/>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1314.05,104.73h-49.33c-48.36,0-90.91,25.48-115.75,64.1-11.79,18.34-19.6,39.64-22.11,62.59-.58,5.3-.88,10.68-.88,16.14s.31,11.15,.93,16.59c4.28,38.09,23.14,71.61,50.66,94.52,2.93,2.6,6.05,4.98,9.31,7.14,12.86,8.49,28.11,13.47,44.52,13.47h0c17.99,0,34.61-5.93,48.16-15.97,16.29-11.58,28.88-28.54,34.48-47.75v50.26h-.11v11.08c0,21.84-5.71,38.27-17.34,49.36-11.61,11.08-31.04,16.63-58.25,16.63-11.12,0-28.79-.59-46.6-2.41-2.83-.29-5.46,1.5-6.27,4.22l-12.78,43.11c-1.02,3.46,1.27,7.02,4.83,7.53,21.52,3.08,42.52,4.68,54.65,4.68,48.91,0,85.16-10.75,108.89-32.21,21.48-19.41,33.15-48.89,35.2-88.52V110.63c0-3.26-2.64-5.9-5.9-5.9h-56.32Zm0,64.1s.65,139.13,0,143.36c-12.08,9.77-27.11,13.59-43.49,14.7-.16,.01-.33,.03-.49,.04-1.12,.07-2.24,.1-3.36,.1-1.32,0-2.63-.03-3.94-.1-40.41-2.11-74.52-37.26-74.52-79.38,0-10.25,1.96-20.01,5.42-28.98,11.22-29.12,38.77-49.74,71.06-49.74h49.33Z"
|
||||
/>
|
||||
<path
|
||||
className="cls-1"
|
||||
d="M249.83,0C113.3,0,2,110.09,.03,246.16c-2,138.19,110.12,252.7,248.33,253.5,42.68,.25,83.79-10.19,120.3-30.03,3.56-1.93,4.11-6.83,1.08-9.51l-23.38-20.72c-4.75-4.21-11.51-5.4-17.36-2.92-25.48,10.84-53.17,16.38-81.71,16.03-111.68-1.37-201.91-94.29-200.13-205.96,1.76-110.26,92-199.41,202.67-199.41h202.69V407.41l-115-102.18c-3.72-3.31-9.42-2.66-12.42,1.31-18.46,24.44-48.53,39.64-81.93,37.34-46.33-3.2-83.87-40.5-87.34-86.81-4.15-55.24,39.63-101.52,94-101.52,49.18,0,89.68,37.85,93.91,85.95,.38,4.28,2.31,8.27,5.52,11.12l29.95,26.55c3.4,3.01,8.79,1.17,9.63-3.3,2.16-11.55,2.92-23.58,2.07-35.92-4.82-70.34-61.8-126.93-132.17-131.26-80.68-4.97-148.13,58.14-150.27,137.25-2.09,77.1,61.08,143.56,138.19,145.26,32.19,.71,62.03-9.41,86.14-26.95l150.26,133.2c6.44,5.71,16.61,1.14,16.61-7.47V9.48C499.66,4.25,495.42,0,490.18,0H249.83Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
type ResultDispatcherState = {
|
||||
items: {
|
||||
title: string;
|
||||
url: string;
|
||||
summary: string;
|
||||
breadcrumbs: string[];
|
||||
}[];
|
||||
query: string | null;
|
||||
totalResults: number | null;
|
||||
totalPages: number | null;
|
||||
lastPage: number | null;
|
||||
hasMore: boolean | null;
|
||||
loading: boolean | null;
|
||||
};
|
||||
|
||||
type ResultDispatcher =
|
||||
| {type: 'reset'; value?: undefined}
|
||||
| {type: 'loading'; value?: undefined}
|
||||
| {type: 'update'; value: ResultDispatcherState}
|
||||
| {type: 'advance'; value?: undefined};
|
||||
|
||||
function getSearchPageTitle(searchQuery: string | undefined): string {
|
||||
return searchQuery
|
||||
? translate(
|
||||
{
|
||||
id: 'theme.SearchPage.existingResultsTitle',
|
||||
message: 'Search results for "{query}"',
|
||||
description: 'The search page title for non-empty query',
|
||||
},
|
||||
{
|
||||
query: searchQuery,
|
||||
},
|
||||
)
|
||||
: translate({
|
||||
id: 'theme.SearchPage.emptyResultsTitle',
|
||||
message: 'Search the documentation',
|
||||
description: 'The search page title for empty query',
|
||||
});
|
||||
}
|
||||
|
||||
function SearchPageContent(): ReactNode {
|
||||
const {
|
||||
i18n: {currentLocale},
|
||||
} = useDocusaurusContext();
|
||||
const {
|
||||
// Mattermost delta: `searchParameters` added (upstream omits it).
|
||||
algolia: {appId, apiKey, indexName, contextualSearch, searchParameters},
|
||||
} = useAlgoliaThemeConfig();
|
||||
const processSearchResultUrl = useSearchResultUrlProcessor();
|
||||
const documentsFoundPlural = useDocumentsFoundPlural();
|
||||
|
||||
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
||||
const [searchQuery, setSearchQuery] = useSearchQueryString();
|
||||
const pageTitle = getSearchPageTitle(searchQuery);
|
||||
|
||||
const initialSearchResultState: ResultDispatcherState = {
|
||||
items: [],
|
||||
query: null,
|
||||
totalResults: null,
|
||||
totalPages: null,
|
||||
lastPage: null,
|
||||
hasMore: null,
|
||||
loading: null,
|
||||
};
|
||||
const [searchResultState, searchResultStateDispatcher] = useReducer(
|
||||
(prevState: ResultDispatcherState, data: ResultDispatcher) => {
|
||||
switch (data.type) {
|
||||
case 'reset': {
|
||||
return initialSearchResultState;
|
||||
}
|
||||
case 'loading': {
|
||||
return {...prevState, loading: true};
|
||||
}
|
||||
case 'update': {
|
||||
if (searchQuery !== data.value.query) {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
return {
|
||||
...data.value,
|
||||
items:
|
||||
data.value.lastPage === 0
|
||||
? data.value.items
|
||||
: prevState.items.concat(data.value.items),
|
||||
};
|
||||
}
|
||||
case 'advance': {
|
||||
const hasMore = prevState.totalPages! > prevState.lastPage! + 1;
|
||||
|
||||
return {
|
||||
...prevState,
|
||||
lastPage: hasMore ? prevState.lastPage! + 1 : prevState.lastPage,
|
||||
hasMore,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return prevState;
|
||||
}
|
||||
},
|
||||
initialSearchResultState,
|
||||
);
|
||||
|
||||
// respect settings from the theme config for facets
|
||||
const disjunctiveFacets = contextualSearch
|
||||
? ['language', 'docusaurus_tag']
|
||||
: [];
|
||||
|
||||
const algoliaClient = liteClient(appId, apiKey);
|
||||
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: why errors happens after upgrading to TS 5.5 ?
|
||||
hitsPerPage: 15,
|
||||
advancedSyntax: true,
|
||||
disjunctiveFacets,
|
||||
// Mattermost delta: spread last so theme config wins on conflicts. The
|
||||
// cast bridges DocSearch's `SearchOptions` (where `optionalFilters` also
|
||||
// accepts a bare string) and the helper's narrower array-only type.
|
||||
...(searchParameters as PlainSearchParameters),
|
||||
});
|
||||
|
||||
algoliaHelper.on(
|
||||
'result',
|
||||
({results: {query, hits, page, nbHits, nbPages}}) => {
|
||||
if (query === '' || !Array.isArray(hits)) {
|
||||
searchResultStateDispatcher({type: 'reset'});
|
||||
return;
|
||||
}
|
||||
|
||||
const sanitizeValue = (value: string) =>
|
||||
value.replace(
|
||||
/algolia-docsearch-suggestion--highlight/g,
|
||||
'search-result-match',
|
||||
);
|
||||
|
||||
const items = hits.map(
|
||||
({
|
||||
url,
|
||||
_highlightResult: {hierarchy},
|
||||
_snippetResult: snippet = {},
|
||||
}: {
|
||||
url: string;
|
||||
_highlightResult: {hierarchy: {[key: string]: {value: string}}};
|
||||
_snippetResult: {content?: {value: string}};
|
||||
}) => {
|
||||
const titles = Object.keys(hierarchy).map((key) =>
|
||||
sanitizeValue(hierarchy[key]!.value),
|
||||
);
|
||||
return {
|
||||
title: titles.pop()!,
|
||||
url: processSearchResultUrl(url),
|
||||
summary: snippet.content
|
||||
? `${sanitizeValue(snippet.content.value)}...`
|
||||
: '',
|
||||
breadcrumbs: titles,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
searchResultStateDispatcher({
|
||||
type: 'update',
|
||||
value: {
|
||||
items,
|
||||
query,
|
||||
totalResults: nbHits,
|
||||
totalPages: nbPages,
|
||||
lastPage: page,
|
||||
hasMore: nbPages > page + 1,
|
||||
loading: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const [loaderRef, setLoaderRef] = useState<HTMLDivElement | null>(null);
|
||||
const prevY = useRef(0);
|
||||
const observer = useRef(
|
||||
ExecutionEnvironment.canUseIntersectionObserver &&
|
||||
new IntersectionObserver(
|
||||
// TODO need to fix this React Compiler lint error
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
(entries) => {
|
||||
const {
|
||||
isIntersecting,
|
||||
boundingClientRect: {y: currentY},
|
||||
} = entries[0]!;
|
||||
|
||||
if (isIntersecting && prevY.current > currentY) {
|
||||
searchResultStateDispatcher({type: 'advance'});
|
||||
}
|
||||
|
||||
prevY.current = currentY;
|
||||
},
|
||||
{threshold: 1},
|
||||
),
|
||||
);
|
||||
|
||||
const makeSearch = useEvent((page: number = 0) => {
|
||||
if (contextualSearch) {
|
||||
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
|
||||
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
||||
|
||||
Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
|
||||
([pluginId, searchVersion]) => {
|
||||
algoliaHelper.addDisjunctiveFacetRefinement(
|
||||
'docusaurus_tag',
|
||||
`docs-${pluginId}-${searchVersion}`,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!loaderRef) {
|
||||
return undefined;
|
||||
}
|
||||
const currentObserver = observer.current;
|
||||
if (currentObserver) {
|
||||
currentObserver.observe(loaderRef);
|
||||
return () => currentObserver.unobserve(loaderRef);
|
||||
}
|
||||
return () => true;
|
||||
}, [loaderRef]);
|
||||
|
||||
useEffect(() => {
|
||||
searchResultStateDispatcher({type: 'reset'});
|
||||
|
||||
if (searchQuery) {
|
||||
searchResultStateDispatcher({type: 'loading'});
|
||||
|
||||
setTimeout(() => {
|
||||
makeSearch();
|
||||
}, 300);
|
||||
}
|
||||
}, [searchQuery, docsSearchVersionsHelpers.searchVersions, makeSearch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchResultState.lastPage || searchResultState.lastPage === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
makeSearch(searchResultState.lastPage);
|
||||
}, [makeSearch, searchResultState.lastPage]);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<PageMetadata title={pageTitle} />
|
||||
|
||||
<Head>
|
||||
{/*
|
||||
We should not index search pages
|
||||
See https://github.com/facebook/docusaurus/pull/3233
|
||||
*/}
|
||||
<meta property="robots" content="noindex, follow" />
|
||||
</Head>
|
||||
|
||||
<div className="container margin-vert--lg">
|
||||
<Heading as="h1">{pageTitle}</Heading>
|
||||
|
||||
<form className="row" onSubmit={(e) => e.preventDefault()}>
|
||||
<div
|
||||
className={clsx('col', styles.searchQueryColumn, {
|
||||
'col--9': docsSearchVersionsHelpers.versioningEnabled,
|
||||
'col--12': !docsSearchVersionsHelpers.versioningEnabled,
|
||||
})}>
|
||||
<input
|
||||
type="search"
|
||||
name="q"
|
||||
className={styles.searchQueryInput}
|
||||
placeholder={translate({
|
||||
id: 'theme.SearchPage.inputPlaceholder',
|
||||
message: 'Type your search here',
|
||||
description: 'The placeholder for search page input',
|
||||
})}
|
||||
aria-label={translate({
|
||||
id: 'theme.SearchPage.inputLabel',
|
||||
message: 'Search',
|
||||
description: 'The ARIA label for search page input',
|
||||
})}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
value={searchQuery}
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
{contextualSearch && docsSearchVersionsHelpers.versioningEnabled && (
|
||||
<SearchVersionSelectList
|
||||
docsSearchVersionsHelpers={docsSearchVersionsHelpers}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
|
||||
<div className="row">
|
||||
<div className={clsx('col', 'col--8', styles.searchResultsColumn)}>
|
||||
{!!searchResultState.totalResults &&
|
||||
documentsFoundPlural(searchResultState.totalResults)}
|
||||
</div>
|
||||
|
||||
<div className={clsx('col', 'col--4', styles.searchLogoColumn)}>
|
||||
<span>
|
||||
{translate({
|
||||
id: 'theme.SearchPage.algoliaLabel',
|
||||
message: 'Powered by',
|
||||
description:
|
||||
'The text explain that the search powered by Algolia',
|
||||
})}
|
||||
</span>
|
||||
<Link
|
||||
to="https://www.algolia.com/"
|
||||
aria-label={translate({
|
||||
id: 'theme.SearchPage.algoliaLabel',
|
||||
message: 'Powered by Algolia',
|
||||
description: 'The description label for Algolia mention',
|
||||
})}>
|
||||
<AlgoliaLogo />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{searchResultState.items.length > 0 ? (
|
||||
<main>
|
||||
{searchResultState.items.map(
|
||||
({title, url, summary, breadcrumbs}, i) => (
|
||||
<article key={i} className={styles.searchResultItem}>
|
||||
<Heading as="h2" className={styles.searchResultItemHeading}>
|
||||
<Link to={url} dangerouslySetInnerHTML={{__html: title}} />
|
||||
</Heading>
|
||||
|
||||
{breadcrumbs.length > 0 && (
|
||||
<nav aria-label="breadcrumbs">
|
||||
<ul
|
||||
className={clsx(
|
||||
'breadcrumbs',
|
||||
styles.searchResultItemPath,
|
||||
)}>
|
||||
{breadcrumbs.map((html, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="breadcrumbs__item"
|
||||
// Developer provided the HTML, so assume it's safe.
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{__html: html}}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
)}
|
||||
|
||||
{summary && (
|
||||
<p
|
||||
className={styles.searchResultItemSummary}
|
||||
// Developer provided the HTML, so assume it's safe.
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{__html: summary}}
|
||||
/>
|
||||
)}
|
||||
</article>
|
||||
),
|
||||
)}
|
||||
</main>
|
||||
) : (
|
||||
[
|
||||
searchQuery && !searchResultState.loading && (
|
||||
<p key="no-results">
|
||||
<Translate
|
||||
id="theme.SearchPage.noResultsText"
|
||||
description="The paragraph for empty search result">
|
||||
No results were found
|
||||
</Translate>
|
||||
</p>
|
||||
),
|
||||
!!searchResultState.loading && (
|
||||
<div key="spinner" className={styles.loadingSpinner} />
|
||||
),
|
||||
]
|
||||
)}
|
||||
|
||||
{searchResultState.hasMore && (
|
||||
<div className={styles.loader} ref={setLoaderRef}>
|
||||
<Translate
|
||||
id="theme.SearchPage.fetchingNewResults"
|
||||
description="The paragraph for fetching new search results">
|
||||
Fetching new results...
|
||||
</Translate>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SearchPage(): ReactNode {
|
||||
return (
|
||||
<HtmlClassNameProvider className="search-page-wrapper">
|
||||
<SearchPageContent />
|
||||
</HtmlClassNameProvider>
|
||||
);
|
||||
}
|
||||
120
docs/site/src/theme/SearchPage/styles.module.css
Normal file
120
docs/site/src/theme/SearchPage/styles.module.css
Normal file
@@ -0,0 +1,120 @@
|
||||
.searchQueryInput,
|
||||
.searchVersionInput {
|
||||
border-radius: var(--ifm-global-radius);
|
||||
border: 2px solid var(--ifm-toc-border-color);
|
||||
font: var(--ifm-font-size-base) var(--ifm-font-family-base);
|
||||
padding: 0.8rem;
|
||||
width: 100%;
|
||||
background: var(--docsearch-searchbox-focus-background);
|
||||
color: var(--docsearch-text-color);
|
||||
margin-bottom: 0.5rem;
|
||||
transition: border var(--ifm-transition-fast) ease;
|
||||
}
|
||||
|
||||
.searchQueryInput:focus,
|
||||
.searchVersionInput:focus {
|
||||
border-color: var(--docsearch-primary-color);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.searchQueryInput::placeholder {
|
||||
color: var(--docsearch-muted-color);
|
||||
}
|
||||
|
||||
.searchResultsColumn {
|
||||
font-size: 0.9rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.searchLogoColumn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.searchLogoColumn a {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.searchLogoColumn span {
|
||||
color: var(--docsearch-muted-color);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.searchResultItem {
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid var(--ifm-toc-border-color);
|
||||
}
|
||||
|
||||
.searchResultItemHeading {
|
||||
font-weight: 400;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.searchResultItemPath {
|
||||
font-size: 0.8rem;
|
||||
color: var(--ifm-color-content-secondary);
|
||||
--ifm-breadcrumb-separator-size-multiplier: 1;
|
||||
}
|
||||
|
||||
.searchResultItemSummary {
|
||||
margin: 0.5rem 0 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 996px) {
|
||||
.searchQueryColumn {
|
||||
max-width: 60% !important;
|
||||
}
|
||||
|
||||
.searchVersionColumn {
|
||||
max-width: 40% !important;
|
||||
}
|
||||
|
||||
.searchResultsColumn {
|
||||
max-width: 60% !important;
|
||||
}
|
||||
|
||||
.searchLogoColumn {
|
||||
max-width: 40% !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 576px) {
|
||||
.searchQueryColumn {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.searchVersionColumn {
|
||||
max-width: 100% !important;
|
||||
padding-left: var(--ifm-spacing-horizontal) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.loadingSpinner {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: 0.4em solid #eee;
|
||||
border-top-color: var(--ifm-color-primary);
|
||||
border-radius: 50%;
|
||||
animation: loading-spin 1s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes loading-spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loader {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
:global(.search-result-match) {
|
||||
color: var(--docsearch-hit-color);
|
||||
background: rgb(255 215 142 / 25%);
|
||||
padding: 0.09em 0;
|
||||
}
|
||||
Reference in New Issue
Block a user