fix: rank a console first for its own name

The PlayStation 2 page holds every word of "playstation bios" and pagefind ignores the words a page has that the query lacks, so its own body matches put it first. A console whose every word another system page's consoles also hold is now written again in an `exact` field (weight 20). File names reach the `files` field as their parts: whole names let "ds" reach twelve dsi_* names on the DSi page. `consoles` weighs 100 so an emulator named after its console (Saturn.emu) comes second, and `aliases` 10 so the Nintendo page keeps "gba bios". The gate runs both directions of the PlayStation, Game Boy, DS and Wii pairs and Saturn.
This commit is contained in:
Abdessamad Derraz committed 2026-10-27 06:54:08 +01:00
1 parent 6a611cd77a
commit 07f785cfd7
9 files changed
+191 -25

No files matched your search

+9
View File
@@ -3065,6 +3065,14 @@ def decorate_markdown_pages(docs: Path) -> dict[str, str]:
"""
lastmod: dict[str, str] = {}
terms = sitestarlight.glossary_terms(_REPO_ROOT / WIKI_SRC_DIR / "glossary.md")
# The console words of every system page, decorated or not: a console
# another page's words cover gets a search field of its own.
console_words: dict[str, set[str]] = {}
for path in sorted((docs / "systems").glob("*.md*")):
text = path.read_text(encoding="utf-8")
words = sitestarlight.page_console_words(_front_matter(text), text)
if words:
console_words[path.relative_to(docs).with_suffix("").as_posix()] = words
for path in sorted(docs.rglob("*.md")):
relative = path.relative_to(docs)
if any(part in LOCAL_NOTE_DIRS for part in relative.parts[:-1]):
@@ -3125,6 +3133,7 @@ def decorate_markdown_pages(docs: Path) -> dict[str, str]:
logo=str(authored.get("logo") or ""),
parent=authored.get("parent"),
authored=authored,
words_by_page=console_words,
)
write_decorated(docs / target, text)
# One file per page: a page that gained or lost its components
+49 -4
View File
@@ -16,7 +16,7 @@ import json
import re
import unicodedata
import urllib.parse
from collections.abc import Callable, Iterator
from collections.abc import Callable, Iterator, Mapping
from html import unescape
from pathlib import Path, PurePosixPath
@@ -366,6 +366,8 @@ def split_title(markdown: str) -> tuple[str, str, str]:
_CARD_NAME = re.compile(r'<h3[^>]*class="rb-file-title"(?![^>]*data-pagefind-ignore)[^>]*><code>([^<]+)</code>')
# What separates the parts of a file name: anything but a letter or a digit.
_NAME_PART = re.compile(r"[\W_]+")
def search_field(key: str, words: list[str]) -> str:
@@ -384,9 +386,20 @@ def file_names_meta(body: str) -> str:
field of the page. A page of 250 cards is long, and the search weighs a
long page down: the Sony page, where scph5501.bin is downloaded, came
after every short page citing the name. The field is weighted in the
site's ranking (astro.config.mjs, metaWeights.files)."""
site's ranking (astro.config.mjs, metaWeights.files).
A name is written as its parts (scph5501 bin), each once. The search
matches a word as the start of a longer one and scores every distinct
word it reaches: whole names gave "ds" twelve rare words on the DSi page
(dsi_bios7.bin, DSi_Nand_USA.bin...), which then ranked first for
"nintendo ds bios"."""
names = sorted({unescape(name) for name in _CARD_NAME.findall(body)})
return search_field("files", names)
parts: dict[str, str] = {}
for name in names:
for part in _NAME_PART.split(name):
if part:
parts.setdefault(part.casefold(), part)
return search_field("files", list(parts.values()))
# systems/ page -> the platforms' own ids for the consoles it holds, built
@@ -435,6 +448,33 @@ def console_words(names: list[str]) -> list[str]:
return [f"{name} BIOS" for name in names]
_CONSOLES_META = re.compile(r'data-pagefind-meta="consoles:([^"]*)"')
def page_console_words(authored: dict, text: str) -> set[str]:
"""The words of the consoles a page answers for: its `consoles` front
matter (*authored*) before decoration, its `consoles` field after."""
names = field_words(authored.get("consoles"))
if names:
return {word.casefold() for name in names for word in name.split()}
match = _CONSOLES_META.search(text)
return set(unescape(match[1]).casefold().split()) if match else set()
def covered_names(names: list[str], page: str, words_by_page: Mapping[str, set[str]]) -> list[str]:
"""The names of *names* whose every word another page's consoles also
hold, as a word or the start of one (PlayStation on the PlayStation 2
page, Nintendo DS on the DSi page, Nintendo Super System on the
Nintendo page). The search matches a word as the start of a longer one
and ignores the words a page has that the query lacks: it cannot tell
these pages apart by words, only by a weight the other page lacks."""
held = [words for other, words in words_by_page.items() if other != page]
return [name for name in names
if any(all(any(word.startswith(part) for word in words)
for part in name.casefold().split())
for words in held)]
def _neighbour(rel: str, value: object) -> object:
"""A `prev:` or `next:` the page states, its link resolved like any
other link of the page."""
@@ -476,6 +516,7 @@ def finalize_page(
logo: str = "",
parent: dict | None = None,
authored: dict | None = None,
words_by_page: Mapping[str, set[str]] | None = None,
) -> tuple[str, str]:
"""The Starlight file for a page body: its path under the content
collection and its text. *parent* ({label, link}, the link written
@@ -487,7 +528,10 @@ def finalize_page(
page states its neighbours and its weight), and its `consoles` and
`terms` become search fields (astro.config.mjs, metaWeights): the
consoles a manufacturer page holds sections for, and the words a page
answers that its text does not say, as an error message words them."""
answers that its text does not say, as an error message words them.
*words_by_page* holds the console words of every system page, by path
without extension: a console another page's words cover is written
once more, in `exact`."""
authored = authored or {}
title, title_id, body = split_title(markdown)
body = body.lstrip("\n")
@@ -509,6 +553,7 @@ def finalize_page(
body += file_names_meta(body) + search_field("aliases", page_aliases(rel))
consoles = field_words(authored.get("consoles"))
body += search_field("consoles", consoles)
body += search_field("exact", covered_names(consoles, rel.rsplit(".", 1)[0], words_by_page or {}))
body += search_field("terms", field_words(authored.get("terms")) + console_words(consoles))
components = used_components(body)
stem = rel.rsplit(".", 1)[0]