mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
fix: let the site search land on cards
The search splits a page at its headings. Emulator cards were titled in bold and platform systems folded into blocks under one heading, so a pasted hash opened the top of a page or the systems overview of a 100-block platform page. Each file card is now a level 4 heading kept out of the table of contents, and each platform system a heading. Card lines that list other pages (platforms, packs, source citations, collection paths) leave the search index: 24.7 MB down to 18.1 MB, the names, hashes and notes still searchable.
This commit is contained in:
1 parent
32dcfcf85a
commit
a67265e903
7 files changed
+189
-34
No files matched your search
+20
-3
@@ -109,6 +109,24 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* A card's title is a heading so the search lands on it, set as body text */
|
||||
.md-typeset .rb-file-title {
|
||||
margin: 0 0 0.3rem;
|
||||
font-size: inherit;
|
||||
font-weight: 400;
|
||||
letter-spacing: normal;
|
||||
scroll-margin-top: 4rem;
|
||||
}
|
||||
|
||||
.md-typeset .rb-file-title code {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Lines the search skips continue the card's list */
|
||||
.rb-search-skip > ul {
|
||||
margin-top: 0.1rem;
|
||||
}
|
||||
|
||||
.rb-file-entry ul,
|
||||
.rb-sys-file ul {
|
||||
margin: 0.3rem 0 0;
|
||||
@@ -265,8 +283,7 @@
|
||||
.rb-permalink:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
.rb-file-entry:target,
|
||||
.rb-sys-file:target {
|
||||
.rb-file-entry:has(> .rb-file-title:target),
|
||||
.rb-sys-file:has(> .rb-file-title:target) {
|
||||
background: var(--rb-surface);
|
||||
scroll-margin-top: 4rem;
|
||||
}
|
||||
@@ -82,6 +82,9 @@ markdown_extensions:
|
||||
- tables
|
||||
- toc:
|
||||
permalink: true
|
||||
# File cards are level 4 headings: the search lands on them, and a
|
||||
# page of a hundred cards keeps a readable table of contents.
|
||||
toc_depth: 3
|
||||
- pymdownx.details
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
|
||||
+77
-27
@@ -1728,16 +1728,19 @@ def _render_platform_systems(
|
||||
sys_link = _system_link(sys_id, "../")
|
||||
|
||||
anchor = sys_id.replace(" ", "-")
|
||||
# Every system is a heading with the same anchor in both layouts:
|
||||
# emulator cards link here, and the site search splits a page at its
|
||||
# headings. Folded into blocks under one heading, every hash pasted
|
||||
# in the search led to the systems overview of a 100-block page.
|
||||
if use_collapsible:
|
||||
status_tag = "OK" if ok_count == total else _count(total - ok_count, "issue")
|
||||
lines.append(f'<a id="{anchor}"></a>')
|
||||
lines.append(f'??? note "{sys_id} ({ok_count}/{total} - {status_tag})"')
|
||||
lines.append(f"### {sys_link} {{ #{anchor} }}")
|
||||
lines.append("")
|
||||
lines.append(f'??? note "{_count(total, "file")} ({status_tag})"')
|
||||
lines.append("")
|
||||
pad = " "
|
||||
else:
|
||||
# The same anchor in both layouts: emulator cards link here.
|
||||
lines.append(f'<a id="{anchor}"></a>')
|
||||
lines.append(f"## {sys_link}")
|
||||
lines.append(f"## {sys_link} {{ #{anchor} }}")
|
||||
lines.append("")
|
||||
pad = ""
|
||||
|
||||
@@ -2528,22 +2531,18 @@ def _system_file_card(
|
||||
anchor = ""
|
||||
else:
|
||||
anchored.add(anchor)
|
||||
id_attr = f' id="{anchor}"' if anchor else ""
|
||||
lines = [f'<div class="rb-sys-file"{id_attr} markdown>', ""]
|
||||
# The anchor is this page's, the second link is the file's own address,
|
||||
# resolved by content wherever its card moves.
|
||||
lines = ['<div class="rb-sys-file" markdown>', ""]
|
||||
# The heading's own link is this page's, the second link is the file's
|
||||
# own address, resolved by content wherever its card moves.
|
||||
permalink = (
|
||||
f' <a class="rb-permalink" href="#{anchor}" title="Link to this card">#</a>'
|
||||
f' <a class="rb-permalink" href="../../files/?sha1={sha1_full}" '
|
||||
f'title="Permanent link, by content">permalink</a>'
|
||||
) if anchor else ""
|
||||
lines.append(f'**`{name}`** <span class="rb-badge rb-badge-muted">{size}</span>{permalink}')
|
||||
lines.append(_card_title(
|
||||
f'{_code(name)} <span class="rb-badge rb-badge-muted">{size}</span>{permalink}',
|
||||
anchor,
|
||||
))
|
||||
lines.append("")
|
||||
if f.get("path"):
|
||||
# The card named a file, its hashes and provenance, but not where to
|
||||
# get it: a user missing scph5501.bin found the reference and no way
|
||||
# to the bytes.
|
||||
lines.append(f"- Collection path: {_code(f['path'])} ([download]({_fetch_url(f['path'])}))")
|
||||
# Every hash in full: the site search indexes text, not a title
|
||||
# attribute, so a pasted md5 or crc32 (what clrmamepro, RomVault or an
|
||||
# emulator log prints) found nothing behind a 12-character prefix.
|
||||
@@ -2551,6 +2550,16 @@ def _system_file_card(
|
||||
("CRC32", f.get("crc32")), ("SHA256", f.get("sha256"))):
|
||||
if value and value != "unknown":
|
||||
lines.append(f'- {label}: <span class="rb-hash">{_code(value)}</span>')
|
||||
if emus:
|
||||
lines.append(f"- Emulators: {', '.join(_emulator_links(sha1_full, consumers))}")
|
||||
provenance = f.get("provenance", {})
|
||||
if provenance:
|
||||
lines.extend(_provenance_lines(provenance, "../"))
|
||||
if f.get("path"):
|
||||
# The card named a file, its hashes and provenance, but not where to
|
||||
# get it: a user missing scph5501.bin found the reference and no way
|
||||
# to the bytes.
|
||||
lines.append(f"- Collection path: {_code(f['path'])} ([download]({_fetch_url(f['path'])}))")
|
||||
if plats:
|
||||
# Each link lands on the platform's system section, and says where
|
||||
# that platform puts the file: from the card, retrodeck sent the
|
||||
@@ -2562,15 +2571,53 @@ def _system_file_card(
|
||||
# looking up scph5501.bin found every platform but RetroBat, whose
|
||||
# pack installs it for six cores.
|
||||
lines.append(f"- Also installed by: {', '.join(installed)}")
|
||||
if emus:
|
||||
lines.append(f"- Emulators: {', '.join(_emulator_links(sha1_full, consumers))}")
|
||||
provenance = f.get("provenance", {})
|
||||
if provenance:
|
||||
lines.extend(_provenance_lines(provenance, "../"))
|
||||
lines[2:] = _search_skipped_apart(lines[2:])
|
||||
lines.extend(["", "</div>", ""])
|
||||
return lines
|
||||
|
||||
|
||||
# Card lines the site search leaves out. They list where a file goes and
|
||||
# who cites it, links to other pages that index the same names, and they
|
||||
# were 6.6 of the 24.7 MB the browser downloads and indexes before the first
|
||||
# result. The name, the hashes, the entry's path and the notes stay
|
||||
# searchable.
|
||||
SEARCH_SKIPPED = (
|
||||
"- Platforms:", "- Also installed by:", "- Source:", "- Asked for by:",
|
||||
"- Collection path:", "- Collection copy:", "- Collection copies:",
|
||||
"- Installed by the platform's own package on:",
|
||||
"- Not in the platform's package", "- Never opened on these platforms",
|
||||
)
|
||||
|
||||
|
||||
def _search_skipped_apart(lines: list[str]) -> list[str]:
|
||||
"""Card list lines, each run the search skips in a block of its own."""
|
||||
out: list[str] = []
|
||||
skipping = False
|
||||
for line in lines:
|
||||
skip = line.startswith(SEARCH_SKIPPED) or (skipping and line.startswith(" "))
|
||||
if skip and not skipping:
|
||||
out.extend(["", '<div class="rb-search-skip" data-search-exclude markdown>', ""])
|
||||
elif skipping and not skip:
|
||||
out.extend(["", "</div>", ""])
|
||||
skipping = skip
|
||||
out.append(line)
|
||||
if skipping:
|
||||
out.extend(["", "</div>", ""])
|
||||
return out
|
||||
|
||||
|
||||
def _card_title(text: str, anchor: str) -> str:
|
||||
"""A file card's heading, so the site search lands on the card.
|
||||
|
||||
The search splits a page at its headings: a card titled in bold sent a
|
||||
pasted md5 to the top of a page of a hundred cards. Level 4 stays out
|
||||
of the table of contents (toc_depth 3), and a card without an anchor
|
||||
keeps the id the toc gives it.
|
||||
"""
|
||||
attributes = f"#{anchor} .rb-file-title" if anchor else ".rb-file-title"
|
||||
return f"#### {text} {{ {attributes} }}"
|
||||
|
||||
|
||||
def generate_system_page(
|
||||
manufacturer: str,
|
||||
consoles: dict[str, list],
|
||||
@@ -2863,14 +2910,13 @@ def _render_emulator_file(
|
||||
|
||||
badges = _file_badges(f, in_repo)
|
||||
badge_str = " ".join(badges)
|
||||
id_attr = f' id="{anchor}"' if anchor else ""
|
||||
lines.append(f'<div class="rb-file-entry"{id_attr} markdown>')
|
||||
lines.append('<div class="rb-file-entry" markdown>')
|
||||
lines.append("")
|
||||
lines.append(_card_title(f"{_code(fname)} {badge_str}", anchor))
|
||||
lines.append("")
|
||||
permalink = f' <a class="rb-permalink" href="#{anchor}" title="Link to this file">#</a>' if anchor else ""
|
||||
lines.append(f"**`{fname}`** {badge_str}{permalink}")
|
||||
if desc:
|
||||
lines.append(f"<br>{_escape_tags(str(desc))}")
|
||||
lines.append("")
|
||||
lines.extend([_escape_tags(str(desc)), ""])
|
||||
body_start = len(lines)
|
||||
|
||||
details = []
|
||||
if fpath and fpath != fname:
|
||||
@@ -3022,6 +3068,7 @@ def _render_emulator_file(
|
||||
lines.append(f" - `{c.get('name', '')}`{extra}")
|
||||
else:
|
||||
lines.append(f" - {c}")
|
||||
lines[body_start:] = _search_skipped_apart(lines[body_start:])
|
||||
lines.append("")
|
||||
lines.append("</div>")
|
||||
lines.append("")
|
||||
@@ -5224,6 +5271,9 @@ markdown_extensions:
|
||||
- tables
|
||||
- toc:
|
||||
permalink: true
|
||||
# File cards are level 4 headings: the search lands on them, and a
|
||||
# page of a hundred cards keeps a readable table of contents.
|
||||
toc_depth: 3
|
||||
- pymdownx.details
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
|
||||
@@ -4098,8 +4098,9 @@ class AFileCardSaysAllItKnows(unittest.TestCase):
|
||||
text = "\n".join(_render_emulator_file(
|
||||
entry, {"files": [entry]}, None, [entry], lambda f: True, "file-neogeo-zip",
|
||||
))
|
||||
self.assertIn('id="file-neogeo-zip"', text)
|
||||
self.assertIn('href="#file-neogeo-zip"', text)
|
||||
# The card is a heading with this id, linked by the heading's own
|
||||
# permalink.
|
||||
self.assertIn("{ #file-neogeo-zip .rb-file-title }", text)
|
||||
self.assertIn("Standalone path: `Firmware/neogeo.zip`", text)
|
||||
self.assertIn("`m11.bin`", text)
|
||||
self.assertIn("CRC32 `0000000b`", text)
|
||||
@@ -4819,7 +4820,7 @@ class VersionAnchorsFollowContent(unittest.TestCase):
|
||||
"raze", {"emulator": "Raze", "type": "standalone", "files": files},
|
||||
{"files": {}, "indexes": {}},
|
||||
)
|
||||
return re.findall(r'id="(file-[^"]+)"', page)
|
||||
return re.findall(r"\{ #(file-[^ ]+) \.rb-file-title \}", page)
|
||||
|
||||
def test_inserting_a_version_keeps_the_others(self):
|
||||
a = {"name": "DUKE3D.GRP", "path": "DUKE3D.GRP", "crc32": "bbc9ce44"}
|
||||
|
||||
@@ -337,5 +337,79 @@ class TheDatHoldsEveryCopy(unittest.TestCase):
|
||||
self.assertEqual(dat.count("<rom "), 2)
|
||||
|
||||
|
||||
class TheSearchLandsOnTheCard(unittest.TestCase):
|
||||
"""The site search splits a page at its headings. Emulator cards were
|
||||
titled in bold and platform systems folded into blocks under one
|
||||
heading, so a pasted hash led to the page top or to the systems
|
||||
overview of a 100-block page."""
|
||||
|
||||
def test_a_platform_system_is_a_heading(self):
|
||||
import generate_site
|
||||
|
||||
by_system = {f"system-{i:02d}": [{"name": f"f{i}.bin", "status": "ok"}]
|
||||
for i in range(11)}
|
||||
lines = generate_site._render_platform_systems(by_system, {}, {})
|
||||
headings = [line for line in lines if line.startswith("### ")]
|
||||
self.assertEqual(len(headings), 11)
|
||||
self.assertTrue(headings[0].endswith("{ #system-00 }"))
|
||||
self.assertNotIn('<a id="system-00"></a>', lines)
|
||||
self.assertIn('??? note "1 file (OK)"', lines)
|
||||
|
||||
def test_an_emulator_card_is_a_heading_with_its_anchor(self):
|
||||
import generate_site
|
||||
|
||||
entry = {"name": "scph5501.bin", "required": True}
|
||||
lines = generate_site._render_emulator_file(
|
||||
entry, {"files": [entry]}, None, [entry], lambda f: True, "file-scph5501-bin")
|
||||
self.assertTrue(lines[2].startswith("#### `scph5501.bin` "))
|
||||
self.assertTrue(lines[2].endswith("{ #file-scph5501-bin .rb-file-title }"))
|
||||
|
||||
def test_a_system_card_is_a_heading_with_its_anchor(self):
|
||||
import generate_site
|
||||
from cross_reference import FileConsumers
|
||||
|
||||
entry = {"name": "a.bin", "sha1": "a" * 40, "md5": "b" * 32, "size": 3}
|
||||
lines = generate_site._system_file_card(entry, FileConsumers({"files": {}}))
|
||||
self.assertTrue(lines[2].startswith("#### `a.bin` "))
|
||||
self.assertTrue(lines[2].endswith("{ #sha1-aaaaaaaaaaaa .rb-file-title }"))
|
||||
|
||||
|
||||
class TheSearchSkipsLinkLists(unittest.TestCase):
|
||||
"""The search index was 24.7 MB, read and indexed before the first
|
||||
result. Lists of platforms, copies and source citations on every card
|
||||
made 6.6 MB of it, names already indexed on their own pages."""
|
||||
|
||||
def test_a_system_card_keeps_its_hashes_and_skips_its_platforms(self):
|
||||
import generate_site
|
||||
from cross_reference import FileConsumers
|
||||
|
||||
consumers = FileConsumers({"files": {}})
|
||||
sha1 = "a" * 40
|
||||
consumers.platforms = {sha1: {"retroarch"}}
|
||||
consumers.installed = {sha1: {"batocera": {"bios/a.bin"}}}
|
||||
consumers.emulators = {sha1: {"duckstation"}}
|
||||
entry = {"name": "a.bin", "sha1": sha1, "md5": "b" * 32, "size": 3}
|
||||
text = "\n".join(generate_site._system_file_card(entry, consumers))
|
||||
searched, skipped = text.split('<div class="rb-search-skip" data-search-exclude markdown>')
|
||||
self.assertIn("- MD5:", searched)
|
||||
self.assertIn("- Emulators:", searched)
|
||||
self.assertIn("- Platforms:", skipped)
|
||||
self.assertIn("- Also installed by:", skipped)
|
||||
self.assertEqual(text.count("</div>"), 2)
|
||||
|
||||
def test_runs_alternate_in_card_order(self):
|
||||
import generate_site
|
||||
|
||||
lines = ["- SHA1: x", "- Source: `a.c:1`", "- Platforms: p",
|
||||
"- Destination: `d`", "- Contents (1 members):", " - `m`"]
|
||||
out = [line for line in generate_site._search_skipped_apart(lines) if line]
|
||||
self.assertEqual(out, [
|
||||
"- SHA1: x",
|
||||
'<div class="rb-search-skip" data-search-exclude markdown>',
|
||||
"- Source: `a.c:1`", "- Platforms: p", "</div>",
|
||||
"- Destination: `d`", "- Contents (1 members):", " - `m`",
|
||||
])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -143,7 +143,7 @@ class AnIdNamesOneCardPerPage(unittest.TestCase):
|
||||
"path": "bios/Nintendo/DS/a.bin"}
|
||||
page = gs.generate_system_page(
|
||||
"Nintendo", {"DS": [entry], "DSi": [entry]}, FileConsumers({"files": {}}))
|
||||
self.assertEqual(page.count('id="sha1-aaaaaaaaaaaa"'), 1)
|
||||
self.assertEqual(page.count("#sha1-aaaaaaaaaaaa .rb-file-title"), 1)
|
||||
self.assertEqual(page.count("rb-sys-file"), 2)
|
||||
|
||||
|
||||
|
||||
@@ -66,6 +66,16 @@ dist/ generated packs (gitignored)
|
||||
`platforms/`, `emulators/`, `database.json`, and the `wiki/` sources.
|
||||
Documentation changes belong in `wiki/` or in the generator.
|
||||
|
||||
The site search splits each page at its headings and sends a result to the
|
||||
heading above the match. Every file card is therefore a level 4 heading with
|
||||
the card's anchor, and every platform system a heading of its own: a hash
|
||||
pasted in the search opens that card or that system, not the top of the page.
|
||||
Level 4 stays out of the table of contents (`toc_depth: 3`). The lines that
|
||||
list other pages (platforms, packs, source citations, collection paths) sit
|
||||
in a `data-search-exclude` block, since the browser downloads and indexes the
|
||||
whole search index before it shows a first result. File names, hashes, the
|
||||
paths an emulator reads and the notes stay searchable.
|
||||
|
||||
## Data flow
|
||||
|
||||
```
|
||||
|
||||
Reference in new issue
Block a user