diff --git a/src/commonlid/leaderboard/app.py b/src/commonlid/leaderboard/app.py index 7b35432..bbb5369 100644 --- a/src/commonlid/leaderboard/app.py +++ b/src/commonlid/leaderboard/app.py @@ -12,6 +12,7 @@ from __future__ import annotations +import base64 import json import logging from pathlib import Path @@ -42,8 +43,12 @@ ) PAPER_URL = "https://arxiv.org/abs/2601.18026" +GITHUB_URL = "https://github.com/commoncrawl/commonlid-eval/" + WEBSITE_URL = "https://commonlid.org/" +_IMAGES_DIR = Path(__file__).parent / "images" + NEW_MODEL_URL = ( "https://github.com/commoncrawl/commonlid-eval/blob/main/docs/contributing/adding_a_model.md" ) @@ -434,6 +439,53 @@ def _snapshot_root( ) +def _logo_data_uri(filename: str) -> str: + """Inline an SVG from the packaged ``images/`` dir as a base64 data URI. + + Inlining sidesteps Gradio's static-file allowlist so the logo works both + locally and in the HF Space without extra ``allowed_paths`` wiring. + """ + svg = (_IMAGES_DIR / filename).read_bytes() + return "data:image/svg+xml;base64," + base64.b64encode(svg).decode("ascii") + + +# Scoped CSS for the header block. Gradio wraps ``css_template`` as +# ``# { ... }`` (CSS nesting), so ``.dark &`` resolves to +# ``.dark #`` and picks up the ``dark`` class Gradio toggles on ````. +_HEADER_CSS = """ +.commonlid-header { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 4px 0 8px; } +.commonlid-header h1 { margin: 0; font-size: 1.75rem; font-weight: 600; } +.commonlid-logo { height: 44px; width: auto; display: block; } +.commonlid-logo--dark { display: none; } +.dark & .commonlid-logo--light { display: none; } +.dark & .commonlid-logo--dark { display: block; } +""" + + +def _header_html() -> str: + """Logo + title row; both logo variants are inlined and swapped by ``_HEADER_CSS``.""" + light = _logo_data_uri("commonlid-logo.svg") + dark = _logo_data_uri("commonlid-logo-dark.svg") + return ( + '
' + f'' + f'' + "

Leaderboard

" + "
" + ) + + +def _ext_link(label: str, url: str) -> str: + """Render an external link as raw HTML that opens in a new tab. + + Plain Markdown links render as same-tab navigations; when the leaderboard + is embedded in the Hugging Face Space iframe the browser blocks those, so + every outbound link needs ``target="_blank"``. Gradio's Markdown sanitizer + keeps ``target`` and ``rel`` on anchors. + """ + return f'{label}' + + def _tab_label(dataset_id: str) -> str: """Human-friendly tab title; falls back to the raw dataset_id.""" try: @@ -466,7 +518,7 @@ def _dataset_metadata_markdown(dataset_id: str) -> str: parts.append(cls.description) meta_bits: list[str] = [] if cls.reference_url: - meta_bits.append(f"[Reference]({cls.reference_url})") + meta_bits.append(_ext_link("Reference", cls.reference_url)) license_bit = _format_license(cls.license_name, cls.license_url) if license_bit: meta_bits.append(license_bit) @@ -480,7 +532,7 @@ def _format_license(license_name: str, license_url: str | None) -> str: if not license_name: return "" if license_url: - return f"License: [`{license_name}`]({license_url})" + return f"License: {_ext_link(f'{license_name}', license_url)}" return f"License: `{license_name}`" @@ -556,19 +608,25 @@ def build_app( revision_label = revision[:12] if revision else "HEAD" header = ( - f"# CommonLID Leaderboard\n" - f"Results for the **CommonLID** and **CommonLID-nano** benchmarks. " - f"Headline metric: **macro F1**. Models are ranked by macro F1 " - f"within each tab; click a row to see per-language metrics.\n" - f"\n" - f"🌐 [Website]({WEBSITE_URL}) • 📝 [Blog post]({BLOG_URL}) • 📄 [Paper]({PAPER_URL}) • 🆕 [Add a model]({NEW_MODEL_URL})" + "Results for the **CommonLID** and **CommonLID-nano** benchmarks. " + "Headline metric: **macro F1**. Models are ranked by macro F1 " + "within each tab; click a row to see per-language metrics.\n" + "\n" + + " • ".join([ + "🌐 " + _ext_link("Website", WEBSITE_URL), + "📝 " + _ext_link("Blog post", BLOG_URL), + "📄 " + _ext_link("Paper", PAPER_URL), + "🐙 " + _ext_link("GitHub", GITHUB_URL), + "🆕 " + _ext_link("Add a model", NEW_MODEL_URL), + ]) ) repo_url = f"https://huggingface.co/datasets/{repo_id}" if revision: repo_url += f"/tree/{revision}" - footer = f"_Source: [`{repo_id}`]({repo_url}) @ `{revision_label}`._" + footer = f"_Source: {_ext_link(f'{repo_id}', repo_url)} @ `{revision_label}`._" with gr.Blocks(title="CommonLID Leaderboard") as demo: + gr.HTML(_header_html(), css_template=_HEADER_CSS) gr.Markdown(header) with gr.Tabs(): for dataset_id in VISIBLE_DATASETS: diff --git a/src/commonlid/leaderboard/images/commonlid-logo-dark.svg b/src/commonlid/leaderboard/images/commonlid-logo-dark.svg new file mode 100644 index 0000000..535faa7 --- /dev/null +++ b/src/commonlid/leaderboard/images/commonlid-logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/commonlid/leaderboard/images/commonlid-logo.svg b/src/commonlid/leaderboard/images/commonlid-logo.svg new file mode 100644 index 0000000..07c64f9 --- /dev/null +++ b/src/commonlid/leaderboard/images/commonlid-logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/unit/test_leaderboard_data.py b/tests/unit/test_leaderboard_data.py index be74b6a..1e875b7 100644 --- a/tests/unit/test_leaderboard_data.py +++ b/tests/unit/test_leaderboard_data.py @@ -195,9 +195,22 @@ def test_header_links_to_blog_and_paper(tmp_path: Path) -> None: text = "\n".join(markdown_values) assert app_module.BLOG_URL in text assert app_module.PAPER_URL in text + assert app_module.GITHUB_URL in text + # External links must open in a new tab: same-tab navigation is blocked + # when the app is embedded in the HF Space iframe. + for url in ( + app_module.WEBSITE_URL, + app_module.BLOG_URL, + app_module.PAPER_URL, + app_module.GITHUB_URL, + app_module.NEW_MODEL_URL, + ): + assert f'' in text # Footer links to the HF dataset page (lives in its own Markdown block). assert any( - "huggingface.co/datasets/some-org/some-results" in v and "Source:" in v + "huggingface.co/datasets/some-org/some-results" in v + and "Source:" in v + and 'target="_blank"' in v for v in markdown_values ) # The constants point at the real docs. @@ -205,6 +218,22 @@ def test_header_links_to_blog_and_paper(tmp_path: Path) -> None: assert "arxiv.org/abs/2601.18026" in app_module.PAPER_URL +def test_header_html_inlines_light_and_dark_logo() -> None: + """The header embeds both logo variants as data URIs and swaps them via ``.dark``.""" + pytest.importorskip("gradio") + from commonlid.leaderboard.app import _HEADER_CSS, _header_html + + html = _header_html() + assert html.count("data:image/svg+xml;base64,") == 2 + assert 'class="commonlid-logo commonlid-logo--light"' in html + assert 'class="commonlid-logo commonlid-logo--dark"' in html + # Gradio nests css_template under the component id, so the body-level + # ``.dark`` class must be referenced via ``.dark &``. + assert ".commonlid-logo--dark { display: none; }" in _HEADER_CSS + assert ".dark & .commonlid-logo--light { display: none; }" in _HEADER_CSS + assert ".dark & .commonlid-logo--dark { display: block; }" in _HEADER_CSS + + def test_dataset_metadata_markdown_uses_registry() -> None: pytest.importorskip("gradio") from commonlid.leaderboard.app import _dataset_metadata_markdown, _tab_label @@ -212,7 +241,10 @@ def test_dataset_metadata_markdown_uses_registry() -> None: md = _dataset_metadata_markdown("commonlid") assert "CommonLID" in md # License is rendered as ``License: ``` and linked to license_url. - assert "License: [`common-crawl-tou`](https://commoncrawl.org/terms-of-use)" in md + assert ( + 'License: common-crawl-tou' + ) in md assert "Reference" in md assert "Main score: `macro_f1`" in md assert _tab_label("commonlid") == "CommonLID"