mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
feat: group tested rows by the file they vary
Rows sit under the file under test. A title names the game and what sets the test apart from the others of its file (build, clock, region, files tried), never cut. Zero counts leave the summary. A legend explains the dots. The chips say depends on the file and same with every file. Ran on shows the short host.
This commit is contained in:
1 parent
e7d0c0183c
commit
b6ed5ba0a1
3 files changed
+190
-38
No files matched your search
+140
-26
@@ -148,6 +148,7 @@ def index_item(entry: mr.Stored, runs: int, source: dict, profiles: dict,
|
||||
"record_url": permalink(source["repo"], source["commit"], mr.records_path(entry.emulator), entry.line),
|
||||
"sqlite": f"downloads/measurements/{entry.emulator}.sqlite",
|
||||
"host_short": short_host(record["host"]),
|
||||
"setup": setup_of(record),
|
||||
"groups": variant_groups(record),
|
||||
"proves": proves(record["id"], profile),
|
||||
"timing": ({"ms_per_frame": record["timing"]["ms_per_frame"]["median"],
|
||||
@@ -557,9 +558,9 @@ RESULT_WORDS = {
|
||||
"crash": ("crash", "danger"),
|
||||
"hang": ("hang", "warning"),
|
||||
"unknown": ("unknown", "muted"),
|
||||
"mixed": ("differs", "info"),
|
||||
"same": ("no difference", "muted"),
|
||||
"differs": ("differs", "info"),
|
||||
"mixed": ("depends on the file", "info"),
|
||||
"same": ("same with every file", "muted"),
|
||||
"differs": ("depends on the file", "info"),
|
||||
"inconclusive": ("inconclusive", "warning"),
|
||||
"no verdict": ("no verdict", "muted"),
|
||||
"ran": ("ran", "muted"),
|
||||
@@ -570,8 +571,7 @@ TERMS = {"bios": "BIOS", "vcd": "Video CD", "cd": "CD", "jp": "JP", "us": "US",
|
||||
# The campaign's own check names, as the block says them.
|
||||
CHECK_NAMES = {"files": "full set", "none": "no BIOS", "negative": "wrong files",
|
||||
"references": "reference run", "variants": "copies", "alias": "other name",
|
||||
"path": "other path", "bench": "benchmark"}
|
||||
TITLE_AXIS_WORDS = 4
|
||||
"path": "other path", "bench": "benchmark", "boot": "boot test", "alone": "file alone"}
|
||||
|
||||
|
||||
def _words(text: str) -> list[str]:
|
||||
@@ -604,13 +604,102 @@ def short_title(item: dict) -> str:
|
||||
word = f"issue {issue[1]}" if issue else CHECK_NAMES.get(word, TERMS.get(word, word))
|
||||
if word not in axis:
|
||||
axis.append(word)
|
||||
if len(axis) > TITLE_AXIS_WORDS:
|
||||
axis = [*axis[:TITLE_AXIS_WORDS], "..."]
|
||||
head = ", ".join(part for part in (f"`{thing}`" if thing else "", game) if part)
|
||||
title = f"{head} ({' '.join(axis)})" if axis and head else head or " ".join(axis)
|
||||
return title or str(item.get("subject") or "")
|
||||
|
||||
|
||||
OBSERVABLE_WORDS = {"video_sha1": "video", "frame_hashes": "screens", "audio_sha1": "audio",
|
||||
"state_sha1": "save state", "frame_sha1": "last frame"}
|
||||
|
||||
|
||||
def setup_of(record: dict) -> dict:
|
||||
"""What told two runs of one test apart, each as the words a title
|
||||
would use: the campaign check, the core build, the core options, the
|
||||
clock, the number of files tried and what was compared."""
|
||||
build = str(record["core"].get("build_command") or "")
|
||||
patched = re.search(r"\b(patch|edit(?:ed|ing)?)\b", build, re.IGNORECASE)
|
||||
named = re.search(r"\b([A-Z]+[0-9]+)\b", build) if patched else None
|
||||
check = record.get("check") or {}
|
||||
first = (_words(record["subject"]) or [""])[0]
|
||||
options = {}
|
||||
for key, value in sorted(record["inputs"].get("options", {}).items()):
|
||||
options[key] = f"{value} region" if key.endswith("_region") else f"{key.rsplit('_', 1)[-1]} {value}"
|
||||
observable = str(check.get("observable") or "")
|
||||
return {
|
||||
"check": CHECK_NAMES.get(first, ""),
|
||||
"build": (f"{named[1]} patch" if named else "patched build") if patched else "published build",
|
||||
"options": options,
|
||||
"clock": "fixed clock" if "FAKETIME" in record["inputs"].get("env", {}) else "running clock",
|
||||
"files": f"{len(record['variants'])} files" if record["kind"] == "check" else "",
|
||||
"observable": (observable.split(":", 1)[1] if observable.startswith("state:")
|
||||
else OBSERVABLE_WORDS.get(observable, observable)),
|
||||
}
|
||||
|
||||
|
||||
def _features(item: dict) -> dict[str, str]:
|
||||
"""A test's setup as named features, the options one by one."""
|
||||
setup = item.get("setup") or {}
|
||||
out = {name: str(setup.get(name) or "") for name in ("build", "clock", "files", "observable")}
|
||||
for key, value in (setup.get("options") or {}).items():
|
||||
out[f"option:{key}"] = value
|
||||
return out
|
||||
|
||||
|
||||
def distinct_titles(items: list[dict]) -> dict[str, str]:
|
||||
"""A title per item: its game and campaign check, then what sets it
|
||||
apart from the other tests of the same file, game and core.
|
||||
|
||||
A test whose setup is the common one of its group is named by its game.
|
||||
Another adds the features where it leaves the common setup (a patched
|
||||
build, a running clock, another region). Two titles still alike take
|
||||
every feature that varies in the group, then the subject's own words.
|
||||
Nothing is cut.
|
||||
"""
|
||||
out: dict[str, str] = {}
|
||||
peers: dict[tuple, list[dict]] = {}
|
||||
for item in items:
|
||||
game = short_content(item.get("content")) or short_title(item)
|
||||
peers.setdefault((item.get("variable") or "", game, item["core"].get("file")), []).append(item)
|
||||
for (_, game, _), group in peers.items():
|
||||
features = [_features(item) for item in group]
|
||||
names = sorted({name for f in features for name in f},
|
||||
key=lambda n: (n.startswith("option:"), ["build", "clock", "files", "observable"].index(n)
|
||||
if not n.startswith("option:") else 0, n))
|
||||
common = {}
|
||||
for name in names:
|
||||
values = [f.get(name, "") for f in features]
|
||||
ranked = sorted(set(values), key=lambda v: -values.count(v))
|
||||
# A tie has no common value: every test names its own.
|
||||
tied = len(ranked) > 1 and values.count(ranked[0]) == values.count(ranked[1])
|
||||
common[name] = None if tied else ranked[0]
|
||||
varying = [n for n in names if len({f.get(n, "") for f in features}) > 1]
|
||||
|
||||
def title(item: dict, own: dict[str, str], every: bool, varying: list[str] = varying,
|
||||
common: dict[str, str] = common, game: str = game) -> str:
|
||||
words = [(item.get("setup") or {}).get("check", "")]
|
||||
for name in varying:
|
||||
value = own.get(name, "")
|
||||
if every or value != common[name]:
|
||||
words.append(value or f"no {name.rsplit('_', 1)[-1]}")
|
||||
return ", ".join([game, *[w for w in words if w]])
|
||||
|
||||
for item, own in zip(group, features):
|
||||
out[item["id"]] = title(item, own, every=False)
|
||||
for every in (True, None):
|
||||
seen: dict[str, int] = {}
|
||||
for item in group:
|
||||
seen[out[item["id"]]] = seen.get(out[item["id"]], 0) + 1
|
||||
for item, own in zip(group, features):
|
||||
if seen[out[item["id"]]] > 1:
|
||||
if every:
|
||||
out[item["id"]] = title(item, own, every=True)
|
||||
else:
|
||||
axis = short_title({**item, "variable": None, "content": None})
|
||||
out[item["id"]] = f"{out[item['id']]}, {axis}"
|
||||
return out
|
||||
|
||||
|
||||
def short_host(host: dict) -> str:
|
||||
"""The computer as a reader names it: the CPU model and the OS."""
|
||||
cpu = str(host.get("cpu_model") or "unknown CPU")
|
||||
@@ -705,15 +794,15 @@ def _details(item: dict) -> list[str]:
|
||||
f"{item['repetitions']} repetition{'s' if item['repetitions'] != 1 else ''} each"
|
||||
if item["flags"]:
|
||||
reps += f", {item['flags']} flag{'s' if item['flags'] != 1 else ''} raised"
|
||||
runs = "1 run" if item["runs"] == 1 else f"{item['runs']} runs, the latest shown"
|
||||
runs = "" if item["runs"] == 1 else f", {item['runs']} runs, the latest shown"
|
||||
lines = [
|
||||
_sentence_html("Claim", item.get("claim")),
|
||||
_sentence_html("Result", item.get("statement")),
|
||||
_sentence_html("Method", item.get("method")),
|
||||
_sentence_html("Proves", ", ".join(item.get("proves") or [])),
|
||||
_sentence_html("Ran on", item["host"]),
|
||||
_sentence_html("Ran on", item.get("host_short") or item["host"]),
|
||||
_sentence_html("Core build", build and f"{build}. {reps}", markup_ready=True),
|
||||
f'<p><a href="{_attr(item["record_url"])}">Full record</a>, {runs}.</p>',
|
||||
f'<p><a href="{_attr(item["record_url"])}">Full record</a>{runs}.</p>',
|
||||
]
|
||||
return [line for line in lines if line]
|
||||
|
||||
@@ -730,9 +819,9 @@ def core_label(item: dict) -> str:
|
||||
return Path(str(item["core"].get("file") or "")).name.split("_libretro")[0]
|
||||
|
||||
|
||||
def _row(item: dict, page_host: str, main_core: str) -> list[str]:
|
||||
def _row(item: dict, title: str, page_host: str, main_core: str) -> list[str]:
|
||||
elsewhere = (item.get("host_short") or item["host"]) != page_host
|
||||
title = _cell(item.get("title") or short_title(item))
|
||||
title = _cell(title)
|
||||
if core_label(item) != main_core:
|
||||
title += f' <small>{html.escape(core_label(item))}</small>'
|
||||
return [
|
||||
@@ -751,25 +840,41 @@ def _row(item: dict, page_host: str, main_core: str) -> list[str]:
|
||||
]
|
||||
|
||||
|
||||
def by_file(items: list[dict]) -> list[tuple[str, list[dict]]]:
|
||||
"""The items under the file each varies, in order of first appearance;
|
||||
the runs that vary none last."""
|
||||
groups: dict[str, list[dict]] = {}
|
||||
for item in items:
|
||||
groups.setdefault(Path(str(item.get("variable") or "")).name, []).append(item)
|
||||
return sorted(groups.items(), key=lambda kv: kv[0] == "")
|
||||
|
||||
|
||||
def summary_line(items: list[dict], page_host: str) -> str:
|
||||
"""N tests, the confirmed and open counts when there are any, the last
|
||||
run and its computer."""
|
||||
counts = tested_counts(items)
|
||||
parts = [f"{counts['tests']} test{'s' if counts['tests'] != 1 else ''}"]
|
||||
parts += [f"{counts[key]} {key}" for key in ("confirmed", "open") if counts[key]]
|
||||
parts.append(f"last run {counts['last']} on {page_host}")
|
||||
return " · ".join(parts) + "."
|
||||
|
||||
|
||||
def tested_section(name: str, items: list[dict]) -> list[str]:
|
||||
"""The Tested block of an emulator page, empty when nothing was recorded.
|
||||
|
||||
The answer first: one summary line, then one row per test with its
|
||||
result chip, a dot per variant and its date. What proves it (method,
|
||||
claim, computer, core build, record) waits in each row's details. Tests
|
||||
recorded before the boot oracle fold under one line, and benchmarks get
|
||||
their own table at the end.
|
||||
The answer first: one summary line, then the tests under the file each
|
||||
one varies, a row per test with its result chip, a dot per variant and
|
||||
its date. What proves it (method, claim, computer, core build, record)
|
||||
waits in each row's details. Tests recorded before the boot oracle fold
|
||||
under one line, and benchmarks get their own table at the end.
|
||||
"""
|
||||
if not items:
|
||||
return []
|
||||
counts = tested_counts(items)
|
||||
hosts = [i.get("host_short") or i["host"] for i in items]
|
||||
page_host = max(sorted(set(hosts)), key=hosts.count)
|
||||
cores = [core_label(i) for i in items]
|
||||
main_core = max(sorted(set(cores)), key=cores.count)
|
||||
tests = f"{counts['tests']} test{'s' if counts['tests'] != 1 else ''}"
|
||||
summary = (f"{tests} · {counts['confirmed']} confirmed · {counts['open']} open · "
|
||||
f"last run {counts['last']} on {page_host}.")
|
||||
titles = distinct_titles(items)
|
||||
links = (f'<a href="{markup.site_href(METHOD_PAGE.removesuffix(".md") + "/")}">How</a> · '
|
||||
f'<a href="{markup.site_href(EXPLORER_PAGE.removesuffix(".md") + "/")}">All tests</a> · '
|
||||
f'<a href="{markup.site_href(f"downloads/measurements/{name}.sqlite")}">SQLite</a>')
|
||||
@@ -778,18 +883,27 @@ def tested_section(name: str, items: list[dict]) -> list[str]:
|
||||
earlier = [i for i in ordered if i["kind"] != "bench" and i["status"] == "no verdict"]
|
||||
current = [i for i in ordered if i["kind"] != "bench" and i["status"] != "no verdict"]
|
||||
lines = ["## Tested", "", '<div class="rb-tested">',
|
||||
f'<p class="rb-tested-summary">{html.escape(summary)} {links}</p>']
|
||||
f'<p class="rb-tested-summary">{html.escape(summary_line(items, page_host))} {links}</p>']
|
||||
if current or earlier:
|
||||
lines.append('<div class="rb-tested-head" aria-hidden="true"><span>Test</span>'
|
||||
"<span>Result</span><span>Variants</span><span>Date</span></div>")
|
||||
for item in current:
|
||||
lines += _row(item, page_host, main_core)
|
||||
for file, group in by_file(current):
|
||||
label = f"<code>{html.escape(file)}</code>" if file else "No file varied"
|
||||
lines.append(f'<p class="rb-tested-file">{label}</p>')
|
||||
for item in group:
|
||||
lines += _row(item, titles[item["id"]], page_host, main_core)
|
||||
if current:
|
||||
lines.append('<p class="rb-tested-legend"><span class="rb-dot rb-dot-0"></span> A dot per file tried, '
|
||||
'the same colour for the same result. <span class="rb-dot rb-dot-0 rb-dot-empty">'
|
||||
"</span> No file staged.</p>")
|
||||
if earlier:
|
||||
lines += ['<details class="rb-tested-earlier">',
|
||||
f"<summary>{len(earlier)} earlier test{'s' if len(earlier) != 1 else ''}, "
|
||||
"before the boot oracle</summary>"]
|
||||
for item in earlier:
|
||||
lines += _row(item, page_host, main_core)
|
||||
file = Path(str(item.get("variable") or "")).name
|
||||
title = f"{file}, {titles[item['id']]}" if file else titles[item["id"]]
|
||||
lines += _row(item, title, page_host, main_core)
|
||||
lines.append("</details>")
|
||||
if benches:
|
||||
# One line: MDX reads a table split over lines as paragraphs.
|
||||
@@ -797,7 +911,7 @@ def tested_section(name: str, items: list[dict]) -> list[str]:
|
||||
"<th>fps</th><th>stdev</th><th>runs</th></tr></thead><tbody>"]
|
||||
for item in benches:
|
||||
timing = item.get("timing") or {}
|
||||
title = _cell(item.get("title") or short_title(item))
|
||||
title = _cell(titles[item["id"]])
|
||||
if core_label(item) != main_core:
|
||||
title += f' <small>{html.escape(core_label(item))}</small>'
|
||||
table.append(
|
||||
|
||||
+39
-10
@@ -221,7 +221,7 @@ class ReadAndIndex(Workspace):
|
||||
"claim", "method", "observable", "variable", "status", "statement", "content",
|
||||
"variants", "repetitions", "flags", "date", "host", "core", "frontend_commit", "id",
|
||||
"runs", "record_url", "sqlite", "verdicts", "title", "host_short", "groups",
|
||||
"proves", "timing"}
|
||||
"proves", "timing", "setup"}
|
||||
for item in index["items"]:
|
||||
self.assertEqual(set(item), keys)
|
||||
alpha = {i["subject"]: i for i in index["items"] if i["emulator"] == "alpha"}
|
||||
@@ -430,19 +430,23 @@ class SitePages(unittest.TestCase):
|
||||
self.assertEqual(sm.tested_section("beetle_saturn", []), [])
|
||||
text = "\n".join(sm.tested_section("beetle_saturn", [self.ITEM]))
|
||||
self.assertTrue(text.startswith("## Tested\n"))
|
||||
for expected in ("no difference", "<strong>Method:</strong> Twice per card. Every input fixed.",
|
||||
for expected in ("same with every file", "<strong>Method:</strong> Twice per card. Every input fixed.",
|
||||
"<strong>Ran on:</strong> Test CPU, 12 CPUs", self.ITEM["record_url"],
|
||||
"1 flag raised", "2 runs", "downloads/measurements/beetle_saturn.sqlite",
|
||||
"wiki/measurements/", "<image>", "1 test · 0 confirmed · 0 open"):
|
||||
"wiki/measurements/", "<image>", "1 test · last run 2026-10-27",
|
||||
'<p class="rb-tested-file"><code>mpeg_card.bin</code></p>',
|
||||
'class="rb-tested-legend"', "No file staged."):
|
||||
self.assertIn(expected, text)
|
||||
self.assertNotIn("confirmed", text)
|
||||
self.assertNotIn(" open", text)
|
||||
|
||||
def item(self, **values: object) -> dict:
|
||||
return {**self.ITEM, "host_short": "Test CPU, Linux", **values}
|
||||
|
||||
def test_summary_counts(self) -> None:
|
||||
items = [self.item(proves=["hle_fallback of a.bin"], open_findings=2),
|
||||
self.item(subject="b", date="2026-10-28T00:00:00+00:00", open_findings=2),
|
||||
self.item(kind="bench", status="measured", subject="c",
|
||||
self.item(id="b", subject="b", date="2026-10-28T00:00:00+00:00", open_findings=2),
|
||||
self.item(id="c", kind="bench", status="measured", subject="c",
|
||||
timing={"ms_per_frame": 1.5, "fps": 660.0, "stdev": 0.01})]
|
||||
self.assertEqual(sm.tested_counts(items), {"tests": 3, "confirmed": 1, "open": 2, "benchmarks": 1,
|
||||
"last": "2026-10-28"})
|
||||
@@ -463,7 +467,8 @@ class SitePages(unittest.TestCase):
|
||||
"content": "Redump: Lunar - Silver Star Story Complete (Japan), T-27904G V1.00"},
|
||||
"Lunar (issue 85 opening movie)"),
|
||||
({"subject": "with-command-auth-device-and-command-get-9a211715", "variable": "mpeg_card.bin",
|
||||
"content": "Redump: Lunar"}, "`mpeg_card.bin`, Lunar (with command auth device ...)"),
|
||||
"content": "Redump: Lunar"},
|
||||
"`mpeg_card.bin`, Lunar (with command auth device and get)"),
|
||||
]
|
||||
for item, title in cases:
|
||||
self.assertEqual(sm.short_title(item), title)
|
||||
@@ -471,6 +476,28 @@ class SitePages(unittest.TestCase):
|
||||
"os": "Linux-6.18.54.1-microsoft-standard-WSL2-x86_64"}),
|
||||
"Intel Core i9-13900K, Linux (WSL2)")
|
||||
|
||||
def test_rows_of_one_file_never_read_alike(self) -> None:
|
||||
def item(rid: str, build: str, files: int, observable: str, clock: bool = True) -> dict:
|
||||
record = {"core": {"build_command": build}, "subject": "mpeg-card-lunar", "kind": "check",
|
||||
"check": {"observable": observable}, "variants": [{}] * files,
|
||||
"inputs": {"options": {"beetle_saturn_mpeg_card": "enabled"},
|
||||
"env": {"FAKETIME": "x"} if clock else {}}}
|
||||
return self.item(id=rid, content="Redump: Lunar - Silver Star Story", setup=sm.setup_of(record))
|
||||
items = [item("a", "make -j8", 11, "video_sha1"),
|
||||
item("b", "make -j8 after editing cdb.c", 11, "video_sha1"),
|
||||
item("c", "make -j10, with the CR2 patch to cdb.c", 3, "state:MPEG.AuthState"),
|
||||
item("d", "make -j8", 11, "video_sha1", clock=False)]
|
||||
titles = sm.distinct_titles(items)
|
||||
self.assertEqual(titles, {
|
||||
"a": "Lunar",
|
||||
"b": "Lunar, patched build",
|
||||
"c": "Lunar, CR2 patch, 3 files, MPEG.AuthState",
|
||||
"d": "Lunar, running clock"})
|
||||
self.assertEqual(len(set(titles.values())), len(items))
|
||||
self.assertNotIn("...", " ".join(titles.values()))
|
||||
# Alone in its file, a test is named by its game.
|
||||
self.assertEqual(sm.distinct_titles(items[:1]), {"a": "Lunar"})
|
||||
|
||||
def test_no_empty_sentence(self) -> None:
|
||||
text = "\n".join(sm.tested_section("beetle_saturn", [self.item(claim=None, method=None, statement="",
|
||||
kind="run", status="ran")]))
|
||||
@@ -488,16 +515,18 @@ class SitePages(unittest.TestCase):
|
||||
for _ in group["variants"]][:1] + [1, 0])
|
||||
self.assertEqual([g["absent"] for g in groups], [False, False, True])
|
||||
text = "\n".join(sm.tested_section("alpha", [self.item(groups=groups)]))
|
||||
self.assertEqual(re.findall(r'rb-dot rb-dot-(\d)( rb-dot-empty)?"', text),
|
||||
strip = text.split('class="rb-dots"', 1)[1].split("</span></span>", 1)[0]
|
||||
self.assertEqual(re.findall(r'rb-dot rb-dot-(\d)( rb-dot-empty)?"', strip),
|
||||
[("0", ""), ("1", ""), ("0", " rb-dot-empty")])
|
||||
self.assertIn('aria-label="3 variants. v0: result A; v1: result B; v2: result A"', text)
|
||||
|
||||
def test_the_details_hold_the_method_and_the_host(self) -> None:
|
||||
text = "\n".join(sm.tested_section("beetle_saturn", [self.item()]))
|
||||
text = "\n".join(sm.tested_section("beetle_saturn", [self.item(runs=1)]))
|
||||
details = text.split('<div class="rb-tested-more">', 1)[1].split("</div>", 1)[0]
|
||||
self.assertIn("Twice per card", details)
|
||||
self.assertIn("Test CPU, 12 CPUs", details)
|
||||
self.assertIn(self.ITEM["record_url"], details)
|
||||
self.assertIn("<strong>Ran on:</strong> Test CPU, Linux.", details)
|
||||
self.assertIn(f'<a href="{self.ITEM["record_url"]}">Full record</a>.', details)
|
||||
self.assertNotIn("1 run", details)
|
||||
|
||||
def test_the_beetle_saturn_block_stays_short(self) -> None:
|
||||
stored = mr.read_records(REPO_ROOT)
|
||||
|
||||
@@ -913,7 +913,7 @@ starlight-tabs [role='tab'] { white-space: nowrap; }
|
||||
.sl-markdown-content .rb-tested-row > summary:hover { background: var(--rb-surface); }
|
||||
.sl-markdown-content .rb-tested-test { font-weight: 600; overflow-wrap: anywhere; }
|
||||
.sl-markdown-content :is(.rb-tested-test, .rb-tested-date) small { color: var(--rb-text-secondary); font-weight: 400; }
|
||||
.sl-markdown-content .rb-tested-date { color: var(--rb-text-secondary); font-variant-numeric: tabular-nums; }
|
||||
.sl-markdown-content .rb-tested-date { color: var(--rb-text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||||
.sl-markdown-content .rb-tested-more {
|
||||
margin: 0 0 0.75rem 1.75rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
@@ -938,12 +938,21 @@ starlight-tabs [role='tab'] { white-space: nowrap; }
|
||||
.sl-markdown-content .rb-dot-2 { --rb-dot: var(--rb-warn-fg); }
|
||||
.sl-markdown-content .rb-dot-3 { --rb-dot: var(--rb-err-fg); }
|
||||
.sl-markdown-content .rb-dot-4 { --rb-dot: var(--rb-muted-fg); }
|
||||
.sl-markdown-content p.rb-tested-file {
|
||||
margin: 0.9rem 0 0;
|
||||
padding-bottom: 0.2rem;
|
||||
border-bottom: 1px solid var(--rb-border-strong);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sl-markdown-content p.rb-tested-legend { margin-top: 0.5rem; color: var(--rb-text-secondary); font-size: 0.8rem; }
|
||||
.sl-markdown-content p.rb-tested-legend .rb-dot { display: inline-block; vertical-align: -1px; }
|
||||
.sl-markdown-content table.rb-tested-bench { margin-top: 1rem; font-size: 0.875rem; }
|
||||
.sl-markdown-content table.rb-tested-bench td:not(:first-child) { font-variant-numeric: tabular-nums; text-align: right; }
|
||||
@media (max-width: 50rem) {
|
||||
.sl-markdown-content div.rb-tested-head { display: none; }
|
||||
.sl-markdown-content details.rb-tested-row > summary {
|
||||
grid-template-columns: 1.25rem auto auto minmax(0, 1fr);
|
||||
grid-template-columns: 1.25rem auto minmax(0, 1fr) auto;
|
||||
row-gap: 0.2rem;
|
||||
}
|
||||
.sl-markdown-content .rb-tested-row > summary::before { grid-row: 1; }
|
||||
|
||||
Reference in new issue
Block a user