rashisht commited on
Commit
e62e721
Β·
verified Β·
1 Parent(s): d6ce1e3

Toolbar: display-styled labels, bordered CSV glyph, hover on controls

Browse files
Files changed (1) hide show
  1. app.py +85 -28
app.py CHANGED
@@ -402,6 +402,7 @@ TABLE_CSS = """
402
  --lb-ink-3:#9ca3af; --lb-ink-4:#c3c8d0; --lb-empty:#cbd0d6; --lb-accent:#7b61c9;
403
  --lb-row-line:#f1f2f4; --lb-row-alt:#fafafa; --lb-tip-bg:#1f2328; --lb-tip-ink:#f5f6f7;
404
  --lb-tip-border:#1f2328; --lb-shadow:rgba(0,0,0,.18); --lb-ramp:@RAMP_LIGHT@;
 
405
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
406
  color:var(--lb-ink); max-width:100%; margin:0; color-scheme:light;
407
  background:var(--lb-surface); border:1px solid var(--lb-border); border-radius:12px; padding:16px 18px; }
@@ -409,6 +410,7 @@ TABLE_CSS = """
409
  --lb-ink-3:#9098a4; --lb-ink-4:#67707e; --lb-empty:#3f444d; --lb-accent:#9c83d6;
410
  --lb-row-line:#26292e; --lb-row-alt:#202327; --lb-tip-bg:#262e3a; --lb-tip-ink:#e7eaef;
411
  --lb-tip-border:#39424f; --lb-shadow:rgba(0,0,0,.55); --lb-ramp:@RAMP_DARK@;
 
412
  color-scheme:dark; }
413
  .ttslb .tbl-wrap { overflow-x:auto; }
414
  /* Natural table width + pinned widths keep the rank/provider/License columns the
@@ -430,7 +432,7 @@ TABLE_CSS = """
430
  sit in low rows, where a downward bubble would poke past the table). */
431
  .ttslb .info { position:relative; margin-left:5px; color:var(--lb-ink-4); cursor:help; font-weight:400; }
432
  .ttslb .info:hover { color:var(--lb-ink-2); }
433
- .ttslb .info::after, .ttslb .notemark[data-tip]::after, .ttslb .csv-btn[data-tip]::after {
434
  content:attr(data-tip); position:absolute; display:none; width:260px;
435
  background:var(--lb-tip-bg); color:var(--lb-tip-ink); font-size:11.5px;
436
  font-weight:400; letter-spacing:normal; line-height:1.45; text-align:left; white-space:normal;
@@ -438,9 +440,7 @@ TABLE_CSS = """
438
  box-shadow:0 4px 12px var(--lb-shadow); z-index:20; pointer-events:none; }
439
  .ttslb .info::after { top:calc(100% + 6px); right:-12px; }
440
  .ttslb .notemark[data-tip]::after { bottom:calc(100% + 8px); left:50%; transform:translateX(-50%); width:240px; }
441
- .ttslb .csv-btn[data-tip]::after { top:calc(100% + 6px); right:0; width:auto; white-space:nowrap; }
442
  .ttslb .info:hover::after { display:block; }
443
- .ttslb .csv-btn:hover::after { display:block; }
444
  .ttslb td.notecell { cursor:help; }
445
  .ttslb td.notecell:hover .notemark[data-tip]::after { display:block; }
446
  .ttslb table.lb th.rank { width:34px; text-align:right; }
@@ -455,6 +455,11 @@ TABLE_CSS = """
455
  font-variant-numeric:tabular-nums; font-weight:500;
456
  background:var(--hm-bg); color:var(--hm-fg); }
457
  .dark .ttslb .pill { background:var(--hm-bg-dark); color:var(--hm-fg-dark); }
 
 
 
 
 
458
  .ttslb .pill.primary { min-width:46px; font-weight:700; }
459
  .ttslb .cov-count { color:var(--lb-ink-2); font-size:12px; font-variant-numeric:tabular-nums; }
460
  .ttslb .empty { color:var(--lb-empty); }
@@ -474,22 +479,38 @@ TABLE_CSS = """
474
  .ttslb .legend { display:flex; align-items:center; gap:7px; font-size:11.5px; color:var(--lb-ink-2); }
475
  .ttslb .legend-bar { width:160px; height:10px; border-radius:5px; background:var(--lb-ramp); }
476
  .ttslb .legend-end { color:var(--lb-ink-3); font-variant-numeric:tabular-nums; }
477
- /* icon-only download button: a data-URI SVG background (an inline <svg> in
478
- gr.HTML would risk the sanitizer; a CSS mask proved too fragile). The stroke
479
- colors are baked per state/theme below β€” url() can't read tokens β€” and track
480
- --lb-ink-2 (idle) / --lb-ink (hover). Hover shows a "Download CSV" bubble.
481
- NB: buttons inside gr.HTML get gradio's own button skin, so every contested
482
- property here (and on .smp-tab) is element-qualified and !important. */
483
- .ttslb button.csv-btn { display:inline-block; position:relative; width:26px; height:26px;
484
- margin:0; appearance:none; box-shadow:none; cursor:pointer;
485
- padding:0 !important; border:1px solid var(--lb-border) !important;
486
- border-radius:6px !important; background-color:transparent !important;
487
- background-image:@CSV_ICON_L@ !important; background-position:center !important;
488
- background-size:14px 14px !important; background-repeat:no-repeat !important; }
489
- .ttslb button.csv-btn:hover { border-color:var(--lb-ink-3) !important;
490
- background-image:@CSV_ICON_LH@ !important; }
491
- .dark .ttslb button.csv-btn { background-image:@CSV_ICON_D@ !important; }
492
- .dark .ttslb button.csv-btn:hover { background-image:@CSV_ICON_DH@ !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  /* per-tab Sample Generations section: factor category tabs + player rows */
494
  .ttslb .smp-h { margin:2px 2px 10px; font-size:15px; }
495
  .ttslb .smp-tabs { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:8px; }
@@ -624,13 +645,18 @@ TABLE_CSS = (TABLE_CSS.replace("@RAMP_LIGHT@", _ramp_css(_PALETTE))
624
 
625
 
626
  # Behaviour, injected into <head> (a <script> inside gr.HTML would not execute);
627
- # delegated listeners survive filter re-renders. Three concerns:
628
  # * click-to-sort β€” every row stays visible: rows with a value in the active
629
  # column are sorted and renumbered in the `#` column; rows without one sink
630
  # to the bottom, unranked ("β€”"). Striping is reapplied in display order. Idle
631
  # sortable headers keep a faint ↕ glyph; the active column shows β–²/β–Ό instead.
 
 
 
 
632
  # * CSV download β€” serialises the button's own table exactly as displayed
633
  # (current sort order, current license filter, raw data-v values).
 
634
  # * sample category tabs β€” one factor panel visible at a time per section.
635
  APP_JS = """
636
  <script>
@@ -673,6 +699,18 @@ APP_JS = """
673
  setTimeout(function () { URL.revokeObjectURL(a.href); }, 1000);
674
  return;
675
  }
 
 
 
 
 
 
 
 
 
 
 
 
676
  var stab = e.target.closest('.ttslb .smp-tab');
677
  if (stab) {
678
  var wrap = stab.closest('.smp');
@@ -731,13 +769,22 @@ APP_JS = """
731
  if (rk) rk.textContent = i < ranked.length ? i + 1 : 'β€”';
732
  tbody.appendChild(r);
733
  });
 
 
 
 
 
 
 
 
734
  });
735
  })();
736
  </script>
737
  """
738
 
739
 
740
- def _row_html(p, heat_cols, primary_col, cov_col, rank_label, color_fn, marker_of=None) -> str:
 
741
  m = p.get("metrics", {}) or {}
742
  model_raw = str(p.get("model", ""))
743
  lic_raw = (p.get("license") or "").strip() or "β€”"
@@ -765,7 +812,11 @@ def _row_html(p, heat_cols, primary_col, cov_col, rank_label, color_fn, marker_o
765
  else:
766
  (bg, fg), (bgd, fgd) = color_fn(c, v)
767
  cls = "pill primary" if c is primary_col else "pill"
768
- td_cls = " class='notecell'" if mark else ""
 
 
 
 
769
  # unit suffix ("%", "x") rides along in the pill; data-v stays the
770
  # bare number so sorting and CSV export keep working
771
  txt = f"{v:.2f}{html.escape(c.get('unit') or '')}"
@@ -857,7 +908,7 @@ def render_board_html(board: dict, lic: str = "all", fname: str = "board") -> st
857
  for i, p in enumerate(ranked + unranked):
858
  alt = "alt" if (i + 1) % 2 == 0 else ""
859
  label = i + 1 if i < len(ranked) else "β€”"
860
- body.append(f"<tr class='{alt}'>{_row_html(p, heat_cols, primary_col, cov_col, label, color_fn, marker_of)}</tr>")
861
 
862
  if not body:
863
  return "<div class='ttslb'><p class='nomatch'>No providers match this filter.</p></div>"
@@ -868,9 +919,10 @@ def render_board_html(board: dict, lic: str = "all", fname: str = "board") -> st
868
  f"<div><span class='notemark'>{mark}</span> {html.escape(note)}</div>"
869
  for note, mark in marker_of.items()) + "</div>")
870
 
871
- # Toolbar: the score-scale legend and an icon button downloading the table as
872
- # CSV, exactly as displayed (handled in APP_JS). Both sit inside .tbl-box, so
873
- # they align with the table's edges. The legend follows the heatmap mode:
 
874
  # 1–5 rater scale by default, the board's own value anchors for "absolute"
875
  # boards (ends flipped for asc columns so red stays on the worst end), and a
876
  # generic worstβ†’best for "normalized" boards.
@@ -895,8 +947,13 @@ def render_board_html(board: dict, lic: str = "all", fname: str = "board") -> st
895
  "<span class='legend-bar'></span><span class='legend-end'>5</span>"
896
  "<span>mean human rating</span></div>")
897
  toolbar = (f"<div class='lb-toolbar'>{legend}"
898
- f"<button class='csv-btn' data-fname='{html.escape(fname, quote=True)}'"
899
- " data-tip='Download CSV' aria-label='Download CSV'></button></div>")
 
 
 
 
 
900
 
901
  return (
902
  "<div class='ttslb'><div class='tbl-box'>"
 
402
  --lb-ink-3:#9ca3af; --lb-ink-4:#c3c8d0; --lb-empty:#cbd0d6; --lb-accent:#7b61c9;
403
  --lb-row-line:#f1f2f4; --lb-row-alt:#fafafa; --lb-tip-bg:#1f2328; --lb-tip-ink:#f5f6f7;
404
  --lb-tip-border:#1f2328; --lb-shadow:rgba(0,0,0,.18); --lb-ramp:@RAMP_LIGHT@;
405
+ --lb-dim-bg:#eef0f2; --lb-dim-ink:#9aa1ab;
406
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
407
  color:var(--lb-ink); max-width:100%; margin:0; color-scheme:light;
408
  background:var(--lb-surface); border:1px solid var(--lb-border); border-radius:12px; padding:16px 18px; }
 
410
  --lb-ink-3:#9098a4; --lb-ink-4:#67707e; --lb-empty:#3f444d; --lb-accent:#9c83d6;
411
  --lb-row-line:#26292e; --lb-row-alt:#202327; --lb-tip-bg:#262e3a; --lb-tip-ink:#e7eaef;
412
  --lb-tip-border:#39424f; --lb-shadow:rgba(0,0,0,.55); --lb-ramp:@RAMP_DARK@;
413
+ --lb-dim-bg:#22262d; --lb-dim-ink:#79818d;
414
  color-scheme:dark; }
415
  .ttslb .tbl-wrap { overflow-x:auto; }
416
  /* Natural table width + pinned widths keep the rank/provider/License columns the
 
432
  sit in low rows, where a downward bubble would poke past the table). */
433
  .ttslb .info { position:relative; margin-left:5px; color:var(--lb-ink-4); cursor:help; font-weight:400; }
434
  .ttslb .info:hover { color:var(--lb-ink-2); }
435
+ .ttslb .info::after, .ttslb .notemark[data-tip]::after {
436
  content:attr(data-tip); position:absolute; display:none; width:260px;
437
  background:var(--lb-tip-bg); color:var(--lb-tip-ink); font-size:11.5px;
438
  font-weight:400; letter-spacing:normal; line-height:1.45; text-align:left; white-space:normal;
 
440
  box-shadow:0 4px 12px var(--lb-shadow); z-index:20; pointer-events:none; }
441
  .ttslb .info::after { top:calc(100% + 6px); right:-12px; }
442
  .ttslb .notemark[data-tip]::after { bottom:calc(100% + 8px); left:50%; transform:translateX(-50%); width:240px; }
 
443
  .ttslb .info:hover::after { display:block; }
 
444
  .ttslb td.notecell { cursor:help; }
445
  .ttslb td.notecell:hover .notemark[data-tip]::after { display:block; }
446
  .ttslb table.lb th.rank { width:34px; text-align:right; }
 
455
  font-variant-numeric:tabular-nums; font-weight:500;
456
  background:var(--hm-bg); color:var(--hm-fg); }
457
  .dark .ttslb .pill { background:var(--hm-bg-dark); color:var(--hm-fg-dark); }
458
+ /* focus mode (default): only the active sort column keeps its heatmap β€” pills
459
+ in other columns grey out with dimmer numbers. The sort JS moves td.dim as
460
+ the active column changes; the toggle-all button puts .allcolors on the
461
+ table, which disables this rule and restores every column's colors. */
462
+ .ttslb table.lb:not(.allcolors) td.dim .pill { background:var(--lb-dim-bg); color:var(--lb-dim-ink); }
463
  .ttslb .pill.primary { min-width:46px; font-weight:700; }
464
  .ttslb .cov-count { color:var(--lb-ink-2); font-size:12px; font-variant-numeric:tabular-nums; }
465
  .ttslb .empty { color:var(--lb-empty); }
 
479
  .ttslb .legend { display:flex; align-items:center; gap:7px; font-size:11.5px; color:var(--lb-ink-2); }
480
  .ttslb .legend-bar { width:160px; height:10px; border-radius:5px; background:var(--lb-ramp); }
481
  .ttslb .legend-end { color:var(--lb-ink-3); font-variant-numeric:tabular-nums; }
482
+ /* toolbar controls (right side): a vertical stack β€” the "Toggle colors"
483
+ switch on top, "Download CSV" + bordered glyph button below, labels left /
484
+ controls right-aligned (track and box are both 26px wide, so their right
485
+ edges line up). The labels are display-styled β€” muted ink, no hover cue β€”
486
+ but stay inside the buttons, so they still enlarge the hit target. Buttons
487
+ inside gr.HTML get gradio's own button skin, so every contested property
488
+ here (and on .smp-tab) is element-qualified and !important. */
489
+ .ttslb .lb-toolbar .btns { display:flex; flex-direction:column; align-items:flex-end; gap:7px; }
490
+ .ttslb button.hm-btn, .ttslb button.csv-btn { display:inline-flex; align-items:center; gap:7px;
491
+ margin:0; appearance:none; cursor:pointer; font-family:inherit; line-height:1;
492
+ border:0 !important; box-shadow:none !important; background:transparent !important;
493
+ padding:0 !important; color:var(--lb-ink-2) !important;
494
+ font-size:11.5px !important; font-weight:600 !important; }
495
+ /* the switch: off = focus mode (only the sorted column colored); on (.on,
496
+ accent track, knob right) = every column's heatmap shown */
497
+ .ttslb .hm-track { flex:none; position:relative; width:26px; height:14px;
498
+ border-radius:999px; background:var(--lb-ink-4); transition:background .15s; }
499
+ .ttslb button.hm-btn:hover .hm-track { background:var(--lb-ink-3); }
500
+ .ttslb .hm-knob { position:absolute; top:2px; left:2px; width:10px; height:10px;
501
+ border-radius:50%; background:#ffffff; transition:left .15s; }
502
+ .ttslb button.hm-btn.on .hm-track, .ttslb button.hm-btn.on:hover .hm-track { background:var(--lb-accent); }
503
+ .ttslb button.hm-btn.on .hm-knob { left:14px; }
504
+ /* the download glyph in its bordered box (the affordance lives on the box,
505
+ not the label): a data-URI SVG background (an inline <svg> in gr.HTML would
506
+ risk the sanitizer). Stroke colors are baked per state/theme below β€” url()
507
+ can't read tokens β€” tracking --lb-ink-2 (idle) / --lb-ink (hover). */
508
+ .ttslb .csv-ico { flex:none; width:26px; height:26px; border:1px solid var(--lb-border);
509
+ border-radius:6px; background-image:@CSV_ICON_L@;
510
+ background-position:center; background-size:14px 14px; background-repeat:no-repeat; }
511
+ .ttslb button.csv-btn:hover .csv-ico { border-color:var(--lb-ink-3); background-image:@CSV_ICON_LH@; }
512
+ .dark .ttslb .csv-ico { background-image:@CSV_ICON_D@; }
513
+ .dark .ttslb button.csv-btn:hover .csv-ico { background-image:@CSV_ICON_DH@; }
514
  /* per-tab Sample Generations section: factor category tabs + player rows */
515
  .ttslb .smp-h { margin:2px 2px 10px; font-size:15px; }
516
  .ttslb .smp-tabs { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:8px; }
 
645
 
646
 
647
  # Behaviour, injected into <head> (a <script> inside gr.HTML would not execute);
648
+ # delegated listeners survive filter re-renders. Concerns:
649
  # * click-to-sort β€” every row stays visible: rows with a value in the active
650
  # column are sorted and renumbered in the `#` column; rows without one sink
651
  # to the bottom, unranked ("β€”"). Striping is reapplied in display order. Idle
652
  # sortable headers keep a faint ↕ glyph; the active column shows β–²/β–Ό instead.
653
+ # Focus mode follows: the active column's pills keep their heatmap, all
654
+ # other pill cells are marked .dim (greyed unless .allcolors is on).
655
+ # * toggle-all-colors β€” the toolbar's grid button flips .allcolors on the
656
+ # table, restoring (or re-hiding) every column's heatmap at once.
657
  # * CSV download β€” serialises the button's own table exactly as displayed
658
  # (current sort order, current license filter, raw data-v values).
659
+ # * About-tab panel cards β€” click (or Enter/Space) jumps to that tab.
660
  # * sample category tabs β€” one factor panel visible at a time per section.
661
  APP_JS = """
662
  <script>
 
699
  setTimeout(function () { URL.revokeObjectURL(a.href); }, 1000);
700
  return;
701
  }
702
+ var hmb = e.target.closest('.ttslb .hm-btn');
703
+ if (hmb) { // "Toggle colors" switch: all-colors mode for this card's table
704
+ var hcard = hmb.closest('.ttslb');
705
+ var htable = hcard && hcard.querySelector('table.lb');
706
+ if (htable) {
707
+ htable.classList.toggle('allcolors');
708
+ var on = htable.classList.contains('allcolors');
709
+ hmb.classList.toggle('on', on);
710
+ hmb.setAttribute('aria-checked', on ? 'true' : 'false');
711
+ }
712
+ return;
713
+ }
714
  var stab = e.target.closest('.ttslb .smp-tab');
715
  if (stab) {
716
  var wrap = stab.closest('.smp');
 
769
  if (rk) rk.textContent = i < ranked.length ? i + 1 : 'β€”';
770
  tbody.appendChild(r);
771
  });
772
+ // focus mode follows the sort: only the active column's pills stay
773
+ // colored; every other pill cell is marked dim (a no-op visually while
774
+ // the table is in .allcolors mode)
775
+ Array.prototype.forEach.call(tbody.rows, function (r) {
776
+ Array.prototype.forEach.call(r.cells, function (td, i) {
777
+ if (td.querySelector('.pill')) td.classList.toggle('dim', i !== ci);
778
+ });
779
+ });
780
  });
781
  })();
782
  </script>
783
  """
784
 
785
 
786
+ def _row_html(p, heat_cols, primary_col, cov_col, rank_label, color_fn, marker_of=None,
787
+ active_col=None) -> str:
788
  m = p.get("metrics", {}) or {}
789
  model_raw = str(p.get("model", ""))
790
  lic_raw = (p.get("license") or "").strip() or "β€”"
 
812
  else:
813
  (bg, fg), (bgd, fgd) = color_fn(c, v)
814
  cls = "pill primary" if c is primary_col else "pill"
815
+ # dim = outside the initially-sorted column (focus mode); the sort
816
+ # JS re-marks these as the active column changes
817
+ td_classes = ((["notecell"] if mark else [])
818
+ + (["dim"] if active_col is not None and c is not active_col else []))
819
+ td_cls = f" class='{' '.join(td_classes)}'" if td_classes else ""
820
  # unit suffix ("%", "x") rides along in the pill; data-v stays the
821
  # bare number so sorting and CSV export keep working
822
  txt = f"{v:.2f}{html.escape(c.get('unit') or '')}"
 
908
  for i, p in enumerate(ranked + unranked):
909
  alt = "alt" if (i + 1) % 2 == 0 else ""
910
  label = i + 1 if i < len(ranked) else "β€”"
911
+ body.append(f"<tr class='{alt}'>{_row_html(p, heat_cols, primary_col, cov_col, label, color_fn, marker_of, first_col)}</tr>")
912
 
913
  if not body:
914
  return "<div class='ttslb'><p class='nomatch'>No providers match this filter.</p></div>"
 
919
  f"<div><span class='notemark'>{mark}</span> {html.escape(note)}</div>"
920
  for note, mark in marker_of.items()) + "</div>")
921
 
922
+ # Toolbar: the score-scale legend (left) and a vertical stack of labelled
923
+ # controls (right): the "Toggle colors" switch over "Download CSV", both
924
+ # handled in APP_JS. All sit inside .tbl-box, so they align with the
925
+ # table's edges. The legend follows the heatmap mode:
926
  # 1–5 rater scale by default, the board's own value anchors for "absolute"
927
  # boards (ends flipped for asc columns so red stays on the worst end), and a
928
  # generic worstβ†’best for "normalized" boards.
 
947
  "<span class='legend-bar'></span><span class='legend-end'>5</span>"
948
  "<span>mean human rating</span></div>")
949
  toolbar = (f"<div class='lb-toolbar'>{legend}"
950
+ "<div class='btns'>"
951
+ "<button class='hm-btn' role='switch' aria-checked='false'>"
952
+ "<span class='hm-lbl'>Toggle colors</span>"
953
+ "<span class='hm-track'><span class='hm-knob'></span></span></button>"
954
+ f"<button class='csv-btn' data-fname='{html.escape(fname, quote=True)}'>"
955
+ "<span class='csv-lbl'>Download CSV</span><span class='csv-ico'></span></button>"
956
+ "</div></div>")
957
 
958
  return (
959
  "<div class='ttslb'><div class='tbl-box'>"