Michael Rabinovich Cursor commited on
Commit ·
5eaf6b8
1
Parent(s): a0462d9
leaderboard: make the gallery the single scroller
Browse filesSizing 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>
- app.py +7 -8
- 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`)
|
| 797 |
-
#
|
| 798 |
-
#
|
| 799 |
-
#
|
| 800 |
-
#
|
| 801 |
-
#
|
| 802 |
-
# browser viewport). The inline height is the pre-script fallback.
|
| 803 |
return (
|
| 804 |
f'<iframe srcdoc="{escaped}" '
|
| 805 |
-
'style="width:100%; height:80vh;
|
| 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 |
-
//
|
| 578 |
-
//
|
| 579 |
-
//
|
| 580 |
-
//
|
| 581 |
-
//
|
| 582 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
function fitIframe() {
|
| 584 |
try {
|
| 585 |
const fe = window.frameElement;
|
| 586 |
-
if (fe)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
"""
|