diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 079203b2..8d5b7943 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,6 +31,9 @@ importers: '@gitea-docs/content-loader': specifier: workspace:* version: link:../../packages/content-loader + '@pagefind/default-ui': + specifier: ^1.5.2 + version: 1.5.2 astro: specifier: 7.2.0 version: 7.2.0(@astrojs/markdown-remark@7.2.2(supports-color@7.2.0))(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.13.3)(yaml@2.9.0) diff --git a/sites/docs/README.md b/sites/docs/README.md index ded4a3d8..8a9a0107 100644 --- a/sites/docs/README.md +++ b/sites/docs/README.md @@ -101,6 +101,25 @@ into facets and `src/config/docsearch.ts` filters the search on the product, version and language being read. `cloudflare/docsearch-crawler.json` holds the crawler configuration and only indexes the versions people read. +Pagefind builds one index for the whole site, so the same scope has to be +applied on its side as well, otherwise a search started in the 1.23 docs answers +with the pages of the latest release: + +- `src/components/MarkdownContent.astro` wraps the content in the + `data-pagefind-filter` elements that put `product` and `version` into the + index. One filter per element, pagefind reads the whole attribute as a single + `name:value` pair. +- the same scope is written to the `gitea:search-filters` meta tag by + `src/lib/search.ts`. +- starlight builds the search ui with a build time configuration and has no + option for per page filters, so its `@pagefind/default-ui` import is + redirected to the subclass in `src/lib/pagefind-ui.ts` by the + `gitea-pagefind-filters` plugin in `astro.config.mjs`. It selects the filters + of the page the modal was opened on and adds the "Search all versions" + checkbox, which drops them again and labels the results with their version. + A starlight upgrade that moves the import fails the build, the + `gitea-pagefind-filters-check` integration verifies that the redirect ran. + ## Deployment `cloudflare/_headers` and `cloudflare/_redirects` are copied next to the build diff --git a/sites/docs/astro.config.mjs b/sites/docs/astro.config.mjs index 626ea49e..a4122dea 100644 --- a/sites/docs/astro.config.mjs +++ b/sites/docs/astro.config.mjs @@ -18,6 +18,11 @@ const useDocSearch = Boolean( process.env.PUBLIC_DOCSEARCH_APP_ID && process.env.PUBLIC_DOCSEARCH_API_KEY, ); +// set by the `gitea-pagefind-filters` plugin below, checked after the build so +// a starlight upgrade that moves the import fails loudly instead of silently +// serving an unscoped search again +let pagefindUiRedirected = false; + export default defineConfig({ site: 'https://docs.gitea.com', trailingSlash: 'always', @@ -44,6 +49,26 @@ export default defineConfig({ return null; }, }, + { + // Pagefind indexes every version into one index and starlight builds + // the search ui with a build time configuration, so a search cannot be + // scoped to the version being read. Its `@pagefind/default-ui` import + // is redirected to the subclass in src/lib/pagefind-ui.ts, which + // selects the filters of the current page. + name: 'gitea-pagefind-filters', + enforce: 'pre', + resolveId(source, importer) { + if ( + !useDocSearch && + source === '@pagefind/default-ui' && + importer?.includes('starlight/components/Search.astro') + ) { + pagefindUiRedirected = true; + return path.join(repoRoot, 'sites/docs/src/lib/pagefind-ui.ts'); + } + return null; + }, + }, ], }, // languages the sources tag code blocks with that shiki does not know @@ -62,6 +87,19 @@ export default defineConfig({ }, integrations: [ giteaPostBuild(), + { + name: 'gitea-pagefind-filters-check', + hooks: { + 'astro:build:done': () => { + if (useDocSearch || pagefindUiRedirected) return; + throw new Error( + 'the pagefind search ui was not replaced by src/lib/pagefind-ui.ts, ' + + 'the search would return results from every version: check the ' + + "`gitea-pagefind-filters` plugin against starlight's Search.astro", + ); + }, + }, + }, starlight({ title: 'Gitea Documentation', description: 'Git with a cup of tea', diff --git a/sites/docs/package.json b/sites/docs/package.json index 64ed6939..4cc29854 100644 --- a/sites/docs/package.json +++ b/sites/docs/package.json @@ -14,6 +14,7 @@ "@astrojs/starlight": "0.41.7", "@astrojs/starlight-docsearch": "0.7.0", "@gitea-docs/content-loader": "workspace:*", + "@pagefind/default-ui": "^1.5.2", "astro": "7.2.0", "sharp": "0.34.5", "starlight-openapi": "0.26.0" diff --git a/sites/docs/src/components/MarkdownContent.astro b/sites/docs/src/components/MarkdownContent.astro index 75b3f569..bc21810f 100644 --- a/sites/docs/src/components/MarkdownContent.astro +++ b/sites/docs/src/components/MarkdownContent.astro @@ -1,17 +1,25 @@ --- import Default from '@astrojs/starlight/components/MarkdownContent.astro'; import { t } from '../config/strings'; +import { searchFilters } from '../lib/search'; /** * Translated pages carry the notice the docusaurus site showed: a translation * can lag behind the english original, so point at it and at the translation * guide. Fallback pages already get starlight's own notice. + * + * The wrappers carry the pagefind filters of the page. They sit inside the + * `data-pagefind-body` element starlight puts on `
` and outside the + * `.sl-markdown-content` the default component renders, so they are picked up + * by the index without taking part in the content styles. One filter per + * element: pagefind reads the whole attribute as a single `name:value` pair. */ const route = Astro.locals.starlightRoute; const meta = route.entry.data.gitea; const translated = Boolean(meta) && meta!.locale !== 'en-us' && !route.isFallback; const editUrl = route.editUrl?.href; const strings = t(meta?.locale ?? 'en-us'); +const filters = meta ? searchFilters(meta) : undefined; --- { @@ -22,7 +30,11 @@ const strings = t(meta?.locale ?? 'en-us'); ) } - +
+
+ +
+