Skip to content

Commit 9e477fb

Browse files
Replace Sphinx search with Pagefind
Sphinx's stock client-side search stems and splits identifiers like `system.nix` into ubiquitous terms (`system` + `nix`) and stores no positional data, so phrase/identifier ranking is impossible: searching `system.nix` did not rank `guides/recipes/dependency-management.html`, which contains the exact string. Pagefind indexes the rendered HTML post-build and does real phrase/proximity ranking. Searching `system.nix` now ranks that page first. - `pagefind.yml`: scope indexing to the article body, exclude UI chrome. - `default.nix`: add `pkgs.pagefind` to `nativeBuildInputs` (also exposes it in the dev shell via `inputsFrom`). - `Makefile`: run `pagefind --site build/html` after `sphinx-build` in the `html` target for local/CI parity, guarded with a clear error if the binary is missing. - `source/_templates/search.html`: replace the Sphinx search page with a Pagefind UI, dropping the now-unused Sphinx search scripts and pre-filling from `?q=` so the sidebar search box still works. Assisted-by: Claude:claude-opus-4-8
1 parent e23fc1a commit 9e477fb

4 files changed

Lines changed: 58 additions & 0 deletions

File tree

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ html:
6060
# hack to make live-reload on css update work
6161
@cp source/_static/css/custom.css build/html/_static/css/custom.css
6262

63+
# build the Pagefind search index over the rendered HTML (writes build/html/_pagefind/)
64+
@command -v pagefind >/dev/null 2>&1 \
65+
|| { echo "error: 'pagefind' not found on PATH; install it or run inside the Nix dev shell (nix-shell)"; exit 1; }
66+
pagefind --site build/html
67+
6368
@echo
6469
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
6570

default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ let
3636
nativeBuildInputs = [
3737
nix-dev-python-pkgs
3838
nix-dev-latex
39+
pkgs.pagefind
3940
];
4041
buildPhase =
4142
let

pagefind.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Pagefind configuration for nix.dev search.
2+
# `--site build/html` is passed on the CLI (see Makefile / default.nix),
3+
# so this file only holds selectors and tuning.
4+
5+
# Only index the main article content, not navigation/sidebars/footers.
6+
root_selector: "div.bd-article-container"
7+
8+
# Keep search snippets clean by dropping non-content UI bits.
9+
exclude_selectors:
10+
- "a.headerlink"
11+
- "button.copybtn" # sphinx_copybutton buttons
12+
- ".prev-next-footer"
13+
14+
force_language: "en"

source/_templates/search.html

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{# Replace Sphinx's built-in search page with a Pagefind-powered one. #}
2+
{# Overrides the theme's search.html (pydata_sphinx_theme), which itself #}
3+
{# overrides the `docs_body` block; see pydata_sphinx_theme/search.html. #}
4+
{% extends "!search.html" %}
5+
6+
{% block docs_body %}
7+
<div class="bd-search-container">
8+
<h1>{{ _("Search") }}</h1>
9+
<noscript>
10+
<div class="admonition error">
11+
<p class="admonition-title">{% trans %}Error{% endtrans %}</p>
12+
<p>{% trans %}Please activate JavaScript to enable the search functionality.{% endtrans %}</p>
13+
</div>
14+
</noscript>
15+
<link rel="stylesheet" href="{{ pathto('pagefind/pagefind-ui.css', 1) }}">
16+
<div id="search" class="pagefind-search"></div>
17+
<script src="{{ pathto('pagefind/pagefind-ui.js', 1) }}"></script>
18+
<script>
19+
window.addEventListener('DOMContentLoaded', () => {
20+
const search = new PagefindUI({ element: "#search", showSubResults: true, excerptLength: 30 });
21+
// Pre-fill from `?q=` so the theme's sidebar search box (which submits to
22+
// this page) carries the query through to the Pagefind results.
23+
const q = new URLSearchParams(window.location.search).get("q");
24+
if (q) {
25+
search.triggerSearch(q);
26+
}
27+
});
28+
</script>
29+
</div>
30+
{% endblock docs_body %}
31+
32+
{# The theme's search.html pulls in Sphinx's searchtools.js / searchindex.js #}
33+
{# via the `scripts` block. We use Pagefind instead, so emit only the standard #}
34+
{# document scripts (Sphinx's base `scripts` block is just `script()`) and drop #}
35+
{# the now-unused Sphinx search index. #}
36+
{% block scripts %}
37+
{{- script() }}
38+
{% endblock scripts %}

0 commit comments

Comments
 (0)