fix: name systems and keep the picker filter

This commit is contained in:
Abdessamad Derraz committed 2026-10-22 19:16:36 +02:00
1 parent 1c898ba389
commit de5b517579
4 files changed
+53 -13

No files matched your search

+6 -1
View File
@@ -6864,7 +6864,12 @@ def _generate(args: argparse.Namespace) -> None:
json.dumps(sitestarlight.sidebar(nav, present), indent=1) + "\n")
write_if_changed(str(generated / "lastmod.json"),
json.dumps(dict(sorted(lastmod.items())), indent=1) + "\n")
sitestarlight.picker_data(_REPO_ROOT / "install", public / "data" / "picker")
mfr_names = {mfr.lower().replace(" ", "-"): mfr for mfr in manufacturers}
titles = {}
for sys_id, (slug, console) in _system_page_map.items():
mfr = mfr_names.get(slug, "")
titles[sys_id] = console if console.startswith(mfr) else f"{mfr} {console}"
sitestarlight.picker_data(_REPO_ROOT / "install", public / "data" / "picker", titles)
total_pages = len(present)
print(f"\nGenerated {total_pages} pages in {args.docs_dir}/")
if stale:
+4 -2
View File
@@ -494,9 +494,10 @@ def _links(items: list[dict]) -> Iterator[dict]:
# The picker's data
def picker_data(install: Path, out: Path) -> int:
def picker_data(install: Path, out: Path, titles: dict[str, str]) -> int:
"""Each manifest, compacted to what the file picker reads: per file its
destination, size and systems, the systems install.py --system accepts.
`titles` names a system id the way its system page does.
"""
out.mkdir(parents=True, exist_ok=True)
index = []
@@ -512,7 +513,8 @@ def picker_data(install: Path, out: Path) -> int:
"name": data.get("display_name", data["platform"]),
"folder": (data.get("detect") or [{}])[0].get("bios_path", ""),
"generated": data.get("generated", ""),
"systems": [{"id": s, "native": names.get(s, [])} for s in systems],
"systems": [{"id": s, "title": titles.get(s, ""), "native": names.get(s, [])}
for s in systems],
"other": any(not f.get("systems") for f in data["files"]),
"files": [[f["dest"], f.get("size", 0), [position[s] for s in f.get("systems") or []]]
for f in sorted(data["files"], key=lambda f: f["dest"])],
+24 -1
View File
@@ -42,7 +42,8 @@ class PickerMatchesInstaller(unittest.TestCase):
self.folder = tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp" if (REPO_ROOT / "tmp").is_dir() else None)
self.out = Path(self.folder.name)
self.count = sitestarlight.picker_data(REPO_ROOT / "install", self.out)
self.count = sitestarlight.picker_data(REPO_ROOT / "install", self.out,
{"sony-playstation": "Sony PlayStation"})
def tearDown(self):
self.folder.cleanup()
@@ -86,6 +87,28 @@ class PickerMatchesInstaller(unittest.TestCase):
self.assertIn("picked.includes('other')", source)
self.assertRegex(source, re.escape("--system ${picked.join(',')}"))
def test_systems_carry_their_page_title(self):
"""A beginner picks "Sony PlayStation". The id stays for the command."""
data = json.loads((self.out / "batocera.json").read_text(encoding="utf-8"))
psx = next(s for s in data["systems"] if s["id"] == "sony-playstation")
self.assertEqual(psx["title"], "Sony PlayStation")
self.assertIn("s.title || s.id", PICKER.read_text(encoding="utf-8"))
def test_filter_hides_rows_and_survives_a_platform_change(self):
"""A display rule on the rows overrides the hidden attribute, and a new
list must keep the query typed: otherwise None clears rows no one sees."""
source = PICKER.read_text(encoding="utf-8")
self.assertIn(".rb-pick-list label[hidden] { display: none; }", source)
load = source[source.index("const load = async"):source.index("const applyFilter")]
self.assertIn("applyFilter();", load)
def test_sizes_use_the_installer_units(self):
"""The total the page shows is the one install.py prints for the same files."""
source = PICKER.read_text(encoding="utf-8")
self.assertNotRegex(source, r"\b1e[369]\b")
self.assertEqual(install.format_size(338_165_350), "322.5 MB")
self.assertIn("(bytes / 1024 ** 2).toFixed(1)} MB", source)
if __name__ == "__main__":
unittest.main()
+19 -9
View File
@@ -44,7 +44,7 @@ const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
name: string;
folder: string;
generated: string;
systems: { id: string; native: string[] }[];
systems: { id: string; title: string; native: string[] }[];
other: boolean;
files: [string, number, number[]][];
};
@@ -53,9 +53,9 @@ const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
const MAX_ROWS = 500;
const size = (bytes: number) =>
bytes >= 1e9 ? `${(bytes / 1e9).toFixed(1)} GB`
: bytes >= 1e6 ? `${(bytes / 1e6).toFixed(1)} MB`
: bytes >= 1e3 ? `${(bytes / 1e3).toFixed(1)} KB`
bytes >= 1024 ** 3 ? `${(bytes / 1024 ** 3).toFixed(1)} GB`
: bytes >= 1024 ** 2 ? `${(bytes / 1024 ** 2).toFixed(1)} MB`
: bytes >= 1024 ? `${(bytes / 1024).toFixed(1)} KB`
: `${bytes} B`;
for (const root of document.querySelectorAll<HTMLElement>('.rb-picker')) {
@@ -123,28 +123,36 @@ const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
if (!platform) { result.textContent = ''; return; }
result.textContent = 'Loading the file list...';
data = await fetch(`${base}data/picker/${platform}.json`).then((r) => r.json());
const choices = data!.systems.map((s) => ({ id: s.id, label: s.native.length ? `${s.id} (${s.native.join(', ')})` : s.id }));
if (data!.other) choices.push({ id: 'other', label: 'other (files no system owns: engines, computers)' });
const choices = data!.systems.map((s) => ({
id: s.id,
title: s.title || s.id,
detail: [s.id, ...s.native].filter((n) => n !== s.title).join(', '),
}));
if (data!.other) choices.push({ id: 'other', title: 'Other files', detail: 'engines and computers no system owns' });
for (const choice of choices) {
const label = document.createElement('label');
const box = document.createElement('input');
box.type = 'checkbox';
box.value = choice.id;
box.checked = preset ? preset.includes(choice.id) : true;
label.append(box, ` ${choice.label}`);
const detail = document.createElement('small');
detail.textContent = choice.detail;
label.append(box, ` ${choice.title} `, detail);
list.append(label);
}
applyFilter();
render();
};
select.addEventListener('change', () => load(select.value, null));
list.addEventListener('change', render);
filter.addEventListener('input', () => {
const applyFilter = () => {
const query = filter.value.trim().toLowerCase();
for (const label of list.querySelectorAll<HTMLElement>('label')) {
label.hidden = !!query && !label.textContent!.toLowerCase().includes(query);
}
});
};
filter.addEventListener('input', applyFilter);
root.querySelector('[data-all]')!.addEventListener('click', () => {
list.querySelectorAll<HTMLInputElement>('label:not([hidden]) input').forEach((box) => (box.checked = true));
render();
@@ -175,6 +183,8 @@ const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
border: 1px solid var(--rb-border);
border-radius: 4px;
}
.rb-picker .rb-pick-list label[hidden] { display: none; }
.rb-picker .rb-pick-list small { color: var(--sl-color-gray-3); }
.rb-picker .rb-pick-list label { display: flex; gap: 0.5rem; align-items: center; min-height: 32px; margin: 0; font-size: 0.9rem; }
.sl-markdown-content .rb-picker .rb-pick-list input { min-height: auto; width: 1.1rem; height: 1.1rem; margin: 0; flex: none; }
.rb-picker .rb-pick-systems-head { display: flex; gap: 0.5rem; align-items: center; }