Spaces:
Running
Running
About tab: JSON-driven panel cards
Browse files
app.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
| 1 |
"""Real World VoiceEQ Benchmark β Gradio Space (Text-to-Speech / Speech-to-Speech /
|
| 2 |
Voice Controllability / Speech Understanding / Speech Recognition / SLM Judge).
|
| 3 |
|
| 4 |
-
One Space
|
| 5 |
-
|
|
|
|
|
|
|
| 6 |
table from a self-describing JSON file that holds a *list* of boards (an Overall
|
| 7 |
board + one per factor). The boards are pivoted into one wide table: each factor's
|
| 8 |
`score` becomes a heatmapped column. Rows are ranked by the first factor column by
|
|
@@ -314,6 +316,44 @@ def merge_boards(boards: list) -> dict:
|
|
| 314 |
}
|
| 315 |
|
| 316 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
def _logo_data_uri(suffix: str = "") -> str:
|
| 318 |
mimes = {".avif": "image/avif", ".svg": "image/svg+xml",
|
| 319 |
".png": "image/png", ".webp": "image/webp"}
|
|
@@ -523,6 +563,20 @@ TABLE_CSS = """
|
|
| 523 |
.ttslb .smp-text { flex:1; min-width:220px; text-align:left; font-size:12.5px;
|
| 524 |
line-height:1.45; color:var(--lb-ink-2); }
|
| 525 |
.ttslb .smp-pending { color:var(--lb-ink-3); font-size:12px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
/* Header logo swap: each img bakes its default visibility inline as
|
| 527 |
display:var(--lb-logo-*, ...) β gradio strips class-based styling from
|
| 528 |
gr.HTML imgs, but inline styles and inherited custom properties survive.
|
|
@@ -580,6 +634,15 @@ APP_JS = """
|
|
| 580 |
}
|
| 581 |
document.addEventListener('click', function (e) {
|
| 582 |
if (!e.target.closest) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
var btn = e.target.closest('.ttslb .csv-btn');
|
| 584 |
if (btn) {
|
| 585 |
var card = btn.closest('.ttslb');
|
|
@@ -619,6 +682,13 @@ APP_JS = """
|
|
| 619 |
if (numeric) { var f = parseFloat(v); return isNaN(f) ? { empty: true } : { n: f }; }
|
| 620 |
return { s: v };
|
| 621 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
document.addEventListener('click', function (e) {
|
| 623 |
if (!e.target.closest) return;
|
| 624 |
if (e.target.closest('.info')) return; // β tooltip icon: not a sort click
|
|
@@ -973,10 +1043,13 @@ def build_demo() -> gr.Blocks:
|
|
| 973 |
gr.Markdown(f"# {title}")
|
| 974 |
|
| 975 |
samples = load_samples()
|
|
|
|
| 976 |
with gr.Tabs():
|
|
|
|
|
|
|
|
|
|
| 977 |
for label, key, data_name in MODALITIES:
|
| 978 |
-
|
| 979 |
-
board = merge_boards(boards)
|
| 980 |
factor_labels = {c["field"]: c["label"] for c in board["metricColumns"]}
|
| 981 |
# CSV filenames follow the visible tab label, e.g.
|
| 982 |
# rw-voice-eq-speech-understanding.csv
|
|
|
|
| 1 |
"""Real World VoiceEQ Benchmark β Gradio Space (Text-to-Speech / Speech-to-Speech /
|
| 2 |
Voice Controllability / Speech Understanding / Speech Recognition / SLM Judge).
|
| 3 |
|
| 4 |
+
One Space: an About tab (about.json prose + one clickable card per panel that
|
| 5 |
+
jumps to its tab), then one tab per modality (plus the SLM Judge
|
| 6 |
+
meta-leaderboard of judge-vs-human agreement). Each leaderboard tab renders a
|
| 7 |
+
heatmap-style ranking
|
| 8 |
table from a self-describing JSON file that holds a *list* of boards (an Overall
|
| 9 |
board + one per factor). The boards are pivoted into one wide table: each factor's
|
| 10 |
`score` becomes a heatmapped column. Rows are ranked by the first factor column by
|
|
|
|
| 316 |
}
|
| 317 |
|
| 318 |
|
| 319 |
+
# ββ About tab βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 320 |
+
# Content lives in about.json next to the board JSONs ({intro: [...],
|
| 321 |
+
# panels: [{tab, title, paragraphs}], outro: [...]}). Each panel renders as
|
| 322 |
+
# one clickable card that jumps to that leaderboard tab (wired in APP_JS);
|
| 323 |
+
# "tab" must match the MODALITIES label exactly.
|
| 324 |
+
ABOUT_NAME = "about.json"
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
def load_about() -> dict:
|
| 328 |
+
"""about.json from the dataset (or ./data), {} when absent β the About
|
| 329 |
+
tab is simply skipped without it."""
|
| 330 |
+
try:
|
| 331 |
+
return json.loads(_data_path(ABOUT_NAME).read_text())
|
| 332 |
+
except Exception:
|
| 333 |
+
return {}
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def render_about(about: dict) -> str:
|
| 337 |
+
"""The About tab: intro paragraphs, one clickable card per leaderboard
|
| 338 |
+
panel (title + description, click = jump to that tab), outro."""
|
| 339 |
+
parts = ["<div class='ttslb abt'>"]
|
| 340 |
+
for p in about.get("intro", []):
|
| 341 |
+
parts.append(f"<p class='abt-p'>{html.escape(p)}</p>")
|
| 342 |
+
cards = []
|
| 343 |
+
for panel in about.get("panels", []):
|
| 344 |
+
body = "".join(f"<p>{html.escape(q)}</p>" for q in panel.get("paragraphs", []))
|
| 345 |
+
cards.append(
|
| 346 |
+
f"<div class='abt-card' role='button' tabindex='0' "
|
| 347 |
+
f"data-tab='{html.escape(panel['tab'], quote=True)}'>"
|
| 348 |
+
f"<div class='abt-card-t'>{html.escape(panel.get('title') or panel['tab'])}"
|
| 349 |
+
f"<span class='abt-go'>β</span></div>{body}</div>")
|
| 350 |
+
parts.append("<div class='abt-cards'>" + "".join(cards) + "</div>")
|
| 351 |
+
for p in about.get("outro", []):
|
| 352 |
+
parts.append(f"<p class='abt-p'>{html.escape(p)}</p>")
|
| 353 |
+
parts.append("</div>")
|
| 354 |
+
return "".join(parts)
|
| 355 |
+
|
| 356 |
+
|
| 357 |
def _logo_data_uri(suffix: str = "") -> str:
|
| 358 |
mimes = {".avif": "image/avif", ".svg": "image/svg+xml",
|
| 359 |
".png": "image/png", ".webp": "image/webp"}
|
|
|
|
| 563 |
.ttslb .smp-text { flex:1; min-width:220px; text-align:left; font-size:12.5px;
|
| 564 |
line-height:1.45; color:var(--lb-ink-2); }
|
| 565 |
.ttslb .smp-pending { color:var(--lb-ink-3); font-size:12px; }
|
| 566 |
+
/* About tab: intro/outro prose + one clickable card per leaderboard panel
|
| 567 |
+
(title + description; the whole card jumps to that tab via APP_JS). */
|
| 568 |
+
.ttslb.abt { max-width:920px; }
|
| 569 |
+
.ttslb .abt-p { margin:8px 2px; font-size:13px; line-height:1.55; color:var(--lb-ink-2); text-align:left; }
|
| 570 |
+
.ttslb .abt-cards { display:grid; grid-template-columns:repeat(auto-fill, minmax(360px, 1fr));
|
| 571 |
+
gap:12px; margin:14px 0 12px; }
|
| 572 |
+
.ttslb .abt-card { border:1px solid var(--lb-border); border-radius:10px; padding:13px 16px;
|
| 573 |
+
cursor:pointer; text-align:left; }
|
| 574 |
+
.ttslb .abt-card:hover { border-color:var(--lb-accent); box-shadow:inset 0 0 0 1px var(--lb-accent); }
|
| 575 |
+
.ttslb .abt-card-t { display:flex; align-items:baseline; justify-content:space-between; gap:10px;
|
| 576 |
+
font-size:13.5px; font-weight:700; }
|
| 577 |
+
.ttslb .abt-go { color:var(--lb-ink-4); font-weight:400; }
|
| 578 |
+
.ttslb .abt-card:hover .abt-go { color:var(--lb-accent); }
|
| 579 |
+
.ttslb .abt-card p { margin:7px 0 0; font-size:12.5px; line-height:1.5; color:var(--lb-ink-2); }
|
| 580 |
/* Header logo swap: each img bakes its default visibility inline as
|
| 581 |
display:var(--lb-logo-*, ...) β gradio strips class-based styling from
|
| 582 |
gr.HTML imgs, but inline styles and inherited custom properties survive.
|
|
|
|
| 634 |
}
|
| 635 |
document.addEventListener('click', function (e) {
|
| 636 |
if (!e.target.closest) return;
|
| 637 |
+
var chip = e.target.closest('.ttslb .abt-card');
|
| 638 |
+
if (chip) { // About-tab panel card: jump to that panel's tab
|
| 639 |
+
var want = chip.getAttribute('data-tab');
|
| 640 |
+
var tabs = document.querySelectorAll("button[role='tab']");
|
| 641 |
+
for (var i = 0; i < tabs.length; i++) {
|
| 642 |
+
if (tabs[i].textContent.trim() === want) { tabs[i].click(); window.scrollTo(0, 0); break; }
|
| 643 |
+
}
|
| 644 |
+
return;
|
| 645 |
+
}
|
| 646 |
var btn = e.target.closest('.ttslb .csv-btn');
|
| 647 |
if (btn) {
|
| 648 |
var card = btn.closest('.ttslb');
|
|
|
|
| 682 |
if (numeric) { var f = parseFloat(v); return isNaN(f) ? { empty: true } : { n: f }; }
|
| 683 |
return { s: v };
|
| 684 |
}
|
| 685 |
+
document.addEventListener('keydown', function (e) {
|
| 686 |
+
// About-tab panel cards are div[role=button]: Enter/Space activates
|
| 687 |
+
if ((e.key === 'Enter' || e.key === ' ') && e.target.closest) {
|
| 688 |
+
var card = e.target.closest('.ttslb .abt-card');
|
| 689 |
+
if (card) { e.preventDefault(); card.click(); }
|
| 690 |
+
}
|
| 691 |
+
});
|
| 692 |
document.addEventListener('click', function (e) {
|
| 693 |
if (!e.target.closest) return;
|
| 694 |
if (e.target.closest('.info')) return; // β tooltip icon: not a sort click
|
|
|
|
| 1043 |
gr.Markdown(f"# {title}")
|
| 1044 |
|
| 1045 |
samples = load_samples()
|
| 1046 |
+
about = load_about()
|
| 1047 |
with gr.Tabs():
|
| 1048 |
+
if about:
|
| 1049 |
+
with gr.Tab("About"):
|
| 1050 |
+
gr.HTML(render_about(about))
|
| 1051 |
for label, key, data_name in MODALITIES:
|
| 1052 |
+
board = merge_boards(load_boards(data_name))
|
|
|
|
| 1053 |
factor_labels = {c["field"]: c["label"] for c in board["metricColumns"]}
|
| 1054 |
# CSV filenames follow the visible tab label, e.g.
|
| 1055 |
# rw-voice-eq-speech-understanding.csv
|