Michael Rabinovich Cursor commited on
Commit ·
a636039
1
Parent(s): 00f76c2
leaderboard: dedicated mobile card layout for the gallery
Browse filesA horizontal-scroll table is unusable on phones (one render at a time).
On narrow screens hide the table and render one card per model instead
(plus a ground-truth card), each showing the four renders in a labelled
2x2 grid, so the whole comparison reads top-to-bottom with a single
vertical scroll.
Co-authored-by: Cursor <cursoragent@cursor.com>
- gallery.py +90 -30
gallery.py
CHANGED
|
@@ -397,39 +397,51 @@ a.sub-name:hover { color: var(--accent); text-decoration: underline; }
|
|
| 397 |
.modal-close { margin-top: 20px; width: 100%; padding: 11px; border: 1px solid var(--line-strong); background: #fafbfc; border-radius: 10px; font-family: inherit; font-weight: 600; cursor: pointer; font-size: 14px; }
|
| 398 |
.modal-close:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
|
| 399 |
|
| 400 |
-
/* --- Mobile
|
| 401 |
-
The
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
reference while you scroll either way. */
|
| 407 |
@media (max-width: 760px) {
|
| 408 |
-
.wrap { padding: 0
|
| 409 |
-
.section-label { margin: 2px 0
|
| 410 |
-
.gallery {
|
| 411 |
-
.
|
| 412 |
-
|
|
|
|
|
|
|
|
|
|
| 413 |
}
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
.rank
|
| 418 |
-
|
| 419 |
-
.
|
| 420 |
-
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
}
|
| 423 |
-
.
|
| 424 |
-
.
|
| 425 |
-
.
|
| 426 |
-
.
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
.
|
| 431 |
-
.
|
| 432 |
-
.scroll-cue { right: 1px; }
|
| 433 |
}
|
| 434 |
"""
|
| 435 |
|
|
@@ -450,6 +462,7 @@ _BODY = """
|
|
| 450 |
</div>
|
| 451 |
<div class="scroll-cue" id="scrollCue" hidden><span>▾ scroll for more models</span></div>
|
| 452 |
</div>
|
|
|
|
| 453 |
</div>
|
| 454 |
<div class="modal-back" id="modalBack">
|
| 455 |
<div class="modal">
|
|
@@ -604,6 +617,52 @@ function wireGallery() {
|
|
| 604 |
});
|
| 605 |
}
|
| 606 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
function openModal(fxId, sub) {
|
| 608 |
const f = fixtureMeta(fxId);
|
| 609 |
const title = f
|
|
@@ -700,6 +759,7 @@ function fitIframe() {
|
|
| 700 |
}
|
| 701 |
|
| 702 |
buildGallery();
|
|
|
|
| 703 |
sizeGalleryBox();
|
| 704 |
fitIframe();
|
| 705 |
(function () {
|
|
|
|
| 397 |
.modal-close { margin-top: 20px; width: 100%; padding: 11px; border: 1px solid var(--line-strong); background: #fafbfc; border-radius: 10px; font-family: inherit; font-weight: 600; cursor: pointer; font-size: 14px; }
|
| 398 |
.modal-close:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
|
| 399 |
|
| 400 |
+
/* --- Mobile card layout (built by buildMobile) -----------------------------
|
| 401 |
+
The wide 4-sample matrix is unusable on a phone (one render at a time behind
|
| 402 |
+
a horizontal scroll). On narrow screens we hide the table entirely and show
|
| 403 |
+
one card per model instead, each with its four renders in a 2x2 grid, so the
|
| 404 |
+
whole comparison is readable with a single vertical scroll. */
|
| 405 |
+
.m-cards { display: none; }
|
|
|
|
| 406 |
@media (max-width: 760px) {
|
| 407 |
+
.wrap { padding: 0 10px; }
|
| 408 |
+
.section-label { margin: 2px 0 4px; font-size: 12px; }
|
| 409 |
+
.gallery-shell { display: none; } /* hide the desktop table */
|
| 410 |
+
.m-cards { display: block; }
|
| 411 |
+
.m-card {
|
| 412 |
+
background: var(--panel); border: 1px solid var(--line);
|
| 413 |
+
border-radius: var(--radius); box-shadow: var(--shadow);
|
| 414 |
+
padding: 13px 13px 14px; margin-bottom: 11px;
|
| 415 |
}
|
| 416 |
+
.m-card.m-gt { background: var(--gt-soft); border: 1px solid var(--gt); }
|
| 417 |
+
.m-head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 11px; }
|
| 418 |
+
.m-rank { font-family: var(--mono); font-weight: 700; font-size: 13px; color: var(--ink-faint); }
|
| 419 |
+
.m-rank.medal-1 { color: #b8860b; } .m-rank.medal-2 { color: #6b7280; } .m-rank.medal-3 { color: #a0522d; }
|
| 420 |
+
.m-id { flex: 1; min-width: 0; }
|
| 421 |
+
.m-name { font-weight: 600; font-size: 14.5px; line-height: 1.25; color: var(--ink); text-decoration: none; display: block; }
|
| 422 |
+
a.m-name:hover { color: var(--accent); }
|
| 423 |
+
.m-who { font-size: 11.5px; color: var(--ink-faint); font-family: var(--mono); }
|
| 424 |
+
.m-score { font-size: 21px; font-weight: 800; letter-spacing: -.01em; }
|
| 425 |
+
.m-val { font-size: 10.5px; font-family: var(--mono); font-weight: 700; color: var(--good); }
|
| 426 |
+
.m-val.imperfect { color: #b45309; }
|
| 427 |
+
.m-gt .m-name, .m-gt .m-score { color: var(--gt); }
|
| 428 |
+
.m-gt-sub { font-size: 11.5px; color: var(--gt); opacity: .8; }
|
| 429 |
+
.m-dl { font-size: 11.5px; font-weight: 600; color: var(--accent); text-decoration: none; }
|
| 430 |
+
.m-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
|
| 431 |
+
.m-cell { margin: 0; }
|
| 432 |
+
.m-thumb {
|
| 433 |
+
aspect-ratio: 16/10; border-radius: 8px; background: var(--thumb-bg);
|
| 434 |
+
border: 1px solid var(--line); overflow: hidden;
|
| 435 |
}
|
| 436 |
+
.m-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
|
| 437 |
+
.m-thumb.failed { background: var(--bad-soft); border: 1px dashed #e9b3ae; display: flex; align-items: center; justify-content: center; }
|
| 438 |
+
.m-thumb.failed span { font-family: var(--mono); font-size: 9.5px; font-weight: 700; color: var(--bad); text-transform: uppercase; text-align: center; line-height: 1.4; }
|
| 439 |
+
.m-cell figcaption {
|
| 440 |
+
font-size: 9.5px; text-transform: uppercase; letter-spacing: .04em;
|
| 441 |
+
color: var(--ink-faint); font-weight: 700; margin: 5px 0 1px; text-align: center;
|
| 442 |
+
}
|
| 443 |
+
.m-cell figcaption .mc-diff { color: var(--bad); }
|
| 444 |
+
.m-cell figcaption .mc-diff.mc-medium { color: #b45309; }
|
|
|
|
| 445 |
}
|
| 446 |
"""
|
| 447 |
|
|
|
|
| 462 |
</div>
|
| 463 |
<div class="scroll-cue" id="scrollCue" hidden><span>▾ scroll for more models</span></div>
|
| 464 |
</div>
|
| 465 |
+
<div class="m-cards" id="mGallery"></div>
|
| 466 |
</div>
|
| 467 |
<div class="modal-back" id="modalBack">
|
| 468 |
<div class="modal">
|
|
|
|
| 617 |
});
|
| 618 |
}
|
| 619 |
|
| 620 |
+
// --- Mobile card layout ---------------------------------------------------
|
| 621 |
+
// One card per model (plus a ground-truth card), each showing the four renders
|
| 622 |
+
// in a 2x2 grid with labels. Built once; CSS shows it only on narrow screens.
|
| 623 |
+
function mCaption(f) {
|
| 624 |
+
const diff = f.difficulty
|
| 625 |
+
? ' <span class="mc-diff mc-' + esc((f.difficulty || '').toLowerCase()) + '">' + esc(f.difficulty) + '</span>'
|
| 626 |
+
: '';
|
| 627 |
+
return esc(groupLabel(f.task)) + diff;
|
| 628 |
+
}
|
| 629 |
+
function mThumb(url, f) {
|
| 630 |
+
const inner = url
|
| 631 |
+
? '<img loading="lazy" decoding="async" src="' + url + '" alt="" onerror="mImgFail(this)">'
|
| 632 |
+
: '<span>invalid<br>generation</span>';
|
| 633 |
+
return '<figure class="m-cell"><div class="m-thumb' + (url ? '' : ' failed') + '">' + inner
|
| 634 |
+
+ '</div><figcaption>' + mCaption(f) + '</figcaption></figure>';
|
| 635 |
+
}
|
| 636 |
+
function mImgFail(img) {
|
| 637 |
+
const t = img.closest('.m-thumb');
|
| 638 |
+
if (t) { t.className = 'm-thumb failed'; t.innerHTML = '<span>invalid<br>generation</span>'; }
|
| 639 |
+
}
|
| 640 |
+
function buildMobile() {
|
| 641 |
+
const root = document.getElementById('mGallery');
|
| 642 |
+
if (!root) return;
|
| 643 |
+
if (!DATA.subs.length) { root.innerHTML = ''; return; }
|
| 644 |
+
let html = '<div class="m-card m-gt">'
|
| 645 |
+
+ '<div class="m-head"><div class="m-id"><span class="m-name">Ground truth</span>'
|
| 646 |
+
+ '<span class="m-gt-sub">reference geometry</span></div><span class="m-score">1.000</span></div>'
|
| 647 |
+
+ '<div class="m-grid">' + FIXTURES.map(f => mThumb(gtRenderFor(f.id), f)).join('') + '</div></div>';
|
| 648 |
+
DATA.subs.forEach((s, i) => {
|
| 649 |
+
const medal = i < 3 ? 'medal-' + (i + 1) : '';
|
| 650 |
+
const imperfect = (s.validity !== null && s.validity < 1) ? 'imperfect' : '';
|
| 651 |
+
const name = s.reportUrl
|
| 652 |
+
? '<a class="m-name" href="' + esc(s.reportUrl) + '" target="_blank" rel="noopener">' + esc(s.name) + '</a>'
|
| 653 |
+
: '<span class="m-name">' + esc(s.name) + '</span>';
|
| 654 |
+
html += '<div class="m-card"><div class="m-head">'
|
| 655 |
+
+ '<span class="m-rank ' + medal + '">' + (i + 1) + '</span>'
|
| 656 |
+
+ '<div class="m-id">' + name + '<span class="m-who">' + esc(s.who) + '</span></div>'
|
| 657 |
+
+ '<div style="text-align:right"><div class="m-score">' + fmt(s.score, 3) + '</div>'
|
| 658 |
+
+ '<span class="m-val ' + imperfect + '">' + pct(s.validity) + ' valid</span></div></div>'
|
| 659 |
+
+ '<div class="m-grid">' + FIXTURES.map(f => mThumb(gridRenderFor(s, f.id), f)).join('') + '</div>'
|
| 660 |
+
+ (s.blobUrl ? '<div style="margin-top:9px"><a class="m-dl" href="' + esc(s.blobUrl) + '" target="_blank" rel="noopener">⇣ Download ZIP</a></div>' : '')
|
| 661 |
+
+ '</div>';
|
| 662 |
+
});
|
| 663 |
+
root.innerHTML = html;
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
function openModal(fxId, sub) {
|
| 667 |
const f = fixtureMeta(fxId);
|
| 668 |
const title = f
|
|
|
|
| 759 |
}
|
| 760 |
|
| 761 |
buildGallery();
|
| 762 |
+
buildMobile();
|
| 763 |
sizeGalleryBox();
|
| 764 |
fitIframe();
|
| 765 |
(function () {
|