Michael Rabinovich Cursor commited on
Commit
5eaf6b8
·
1 Parent(s): a0462d9

leaderboard: make the gallery the single scroller

Browse files

Sizing the iframe to a fixed 80vh left two scrollbars (the iframe body
and the outer Gradio page), which was confusing. Now fitIframe measures
the available height from the iframe's own top down to the bottom of the
viewport (via the same-origin parent) and sizes the iframe to
min(content, available), reserving room for the Refresh button. Short
content shrinks the iframe (nothing scrolls); tall content fills the
viewport so only the iframe body scrolls and the sticky header + GT row
stay locked. The outer page no longer needs its own scrollbar.

Co-authored-by: Cursor <cursoragent@cursor.com>

Files changed (2) hide show
  1. app.py +7 -8
  2. gallery.py +23 -7
app.py CHANGED
@@ -793,16 +793,15 @@ def _gallery_iframe_html() -> str:
793
  rows, _render_proxy_url, _gt_proxy_url, _render_diff_proxy_url,
794
  )
795
  escaped = html.escape(doc, quote=True)
796
- # The gallery JS (`fitIframe`) resizes this iframe to its own content so it
797
- # is compact when there are few rows. `max-height` caps it: once the content
798
- # is taller than the cap the iframe stops growing and its own body scrolls,
799
- # which is what keeps the gallery's sticky column header + ground-truth row
800
- # locked at the top. The cap is in `vh` here (reliable in the Gradio doc)
801
- # rather than computed from JS (the nested-iframe parent height is not the
802
- # browser viewport). The inline height is the pre-script fallback.
803
  return (
804
  f'<iframe srcdoc="{escaped}" '
805
- 'style="width:100%; height:80vh; max-height:80vh; border:0; display:block;" '
806
  'title="CADGenBench gallery"></iframe>'
807
  )
808
 
 
793
  rows, _render_proxy_url, _gt_proxy_url, _render_diff_proxy_url,
794
  )
795
  escaped = html.escape(doc, quote=True)
796
+ # The gallery JS (`fitIframe`) sizes this iframe to be the single scroller:
797
+ # it shrinks to the content for few rows, otherwise fills down to the bottom
798
+ # of the viewport so only the iframe's own body scrolls (keeping the sticky
799
+ # header + ground-truth row locked) and the outer Gradio page does not also
800
+ # scroll. The inline `height` is just the pre-script fallback; JS overrides
801
+ # it, so no `max-height` here (it would clamp the measured fill height).
 
802
  return (
803
  f'<iframe srcdoc="{escaped}" '
804
+ 'style="width:100%; height:80vh; border:0; display:block;" '
805
  'title="CADGenBench gallery"></iframe>'
806
  )
807
 
gallery.py CHANGED
@@ -574,16 +574,29 @@ function syncHeadHeight() {
574
  if (head) document.documentElement.style.setProperty('--head-h', head.offsetHeight + 'px');
575
  }
576
 
577
- // Size the iframe to its content, but only up to the `max-height` the host sets
578
- // on the iframe element (see app.py). With few rows the iframe shrinks to the
579
- // content (compact, no empty box); once the content is taller than the cap the
580
- // iframe stays at the cap and its own body scrolls -- which is what lets the
581
- // sticky column header + ground-truth row stay locked at the top while the
582
- // submission rows scroll under them. No-ops if frameElement is unreadable.
 
 
 
 
583
  function fitIframe() {
584
  try {
585
  const fe = window.frameElement;
586
- if (fe) fe.style.height = Math.ceil(document.body.scrollHeight) + 'px';
 
 
 
 
 
 
 
 
 
587
  } catch (e) { /* sandboxed -> keep fallback height */ }
588
  }
589
 
@@ -591,6 +604,9 @@ buildGallery();
591
  fitIframe();
592
  function relayout() { syncHeadHeight(); fitIframe(); }
593
  window.addEventListener('resize', relayout);
 
 
 
594
  if (window.ResizeObserver) new ResizeObserver(fitIframe).observe(document.body);
595
  if (document.fonts && document.fonts.ready) document.fonts.ready.then(relayout);
596
  """
 
574
  if (head) document.documentElement.style.setProperty('--head-h', head.offsetHeight + 'px');
575
  }
576
 
577
+ // Make the gallery the ONLY scroller -- no nested inner+outer scrollbars.
578
+ // When the rows are short the iframe shrinks to the content (compact, nothing
579
+ // scrolls). Otherwise it fills exactly from its own top down to the bottom of
580
+ // the viewport (leaving a little room for the Refresh button below it), so the
581
+ // outer Gradio page does not need to scroll and only the iframe's own body
582
+ // scrolls -- which is what keeps the sticky column header + ground-truth row
583
+ // locked at the top. The available height is measured from the parent (the
584
+ // srcdoc iframe is same-origin); if that read is blocked it falls back to the
585
+ // content height.
586
+ var BOTTOM_RESERVE = 72; // px kept clear below the iframe for the Refresh button
587
  function fitIframe() {
588
  try {
589
  const fe = window.frameElement;
590
+ if (!fe) return;
591
+ const content = Math.ceil(document.body.scrollHeight);
592
+ let target = content;
593
+ const pv = window.parent;
594
+ if (pv && pv.innerHeight) {
595
+ const top = fe.getBoundingClientRect().top; // iframe top in viewport
596
+ const avail = Math.floor(pv.innerHeight - top - BOTTOM_RESERVE);
597
+ if (avail > 240) target = Math.min(content, avail);
598
+ }
599
+ fe.style.height = target + 'px';
600
  } catch (e) { /* sandboxed -> keep fallback height */ }
601
  }
602
 
 
604
  fitIframe();
605
  function relayout() { syncHeadHeight(); fitIframe(); }
606
  window.addEventListener('resize', relayout);
607
+ // The viewport height can change without the iframe's own width changing
608
+ // (e.g. browser window resized shorter), so also listen on the parent.
609
+ try { window.parent.addEventListener('resize', relayout); } catch (e) { /* ignore */ }
610
  if (window.ResizeObserver) new ResizeObserver(fitIframe).observe(document.body);
611
  if (document.fonts && document.fonts.ready) document.fonts.ready.then(relayout);
612
  """