cmpatino HF Staff commited on
Commit
2f2a29d
Β·
1 Parent(s): a608409

Change tooltip stickiness

Browse files
Files changed (1) hide show
  1. index.html +56 -3
index.html CHANGED
@@ -105,7 +105,7 @@
105
  padding: 12px 14px; min-width: 248px; max-width: 320px;
106
  border-radius: 4px;
107
  opacity: 0; visibility: hidden; transform: translateY(2px);
108
- transition: opacity 0.12s, transform 0.12s;
109
  pointer-events: none;
110
  }
111
  .point-card.visible { opacity: 1; visibility: visible; transform: none; pointer-events: auto; }
@@ -482,6 +482,50 @@ function renderChart() {
482
  const pointCard = document.getElementById('pointCard');
483
  let pcHideTimer = null;
484
  let pcSticky = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
 
486
  function buildPointCardHtml(e) {
487
  const info = AGENTS[e.agent] || null;
@@ -512,7 +556,11 @@ function buildPointCardHtml(e) {
512
 
513
  const linkBtns = [];
514
  if (e.filename) linkBtns.push(`<a href="${escapeHtml(submissionHref(e.filename))}" target="_blank" rel="noopener noreferrer">Submission β†—</a>`);
515
- (e.links || []).forEach(l => linkBtns.push(`<a href="${escapeHtml(l.href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(l.label)} β†—</a>`));
 
 
 
 
516
 
517
  return `${head}<div class="pc-score"><span class="big">${fmt2(e.score)}</span> tok/s ${verified}</div><div class="pc-rows">${rowsHtml}</div>${note}<div class="pc-links">${linkBtns.join('')}</div>`;
518
  }
@@ -532,6 +580,7 @@ function showPointCard(entry, c, element) {
532
  const rect = c.canvas.getBoundingClientRect();
533
  const px = rect.left + element.x;
534
  const py = rect.top + element.y;
 
535
  const w = pointCard.offsetWidth, h = pointCard.offsetHeight;
536
  let left = px + 14;
537
  if (left + w > window.innerWidth - 8) left = px - w - 14;
@@ -544,12 +593,16 @@ function showPointCard(entry, c, element) {
544
  }
545
  function hidePointCard() {
546
  pcSticky = false;
 
547
  pointCard.classList.remove('visible');
548
  pointCard.setAttribute('aria-hidden', 'true');
549
  }
550
- function scheduleHide() { if (!pcSticky) pcHideTimer = setTimeout(hidePointCard, 170); }
551
 
552
  function handleChartHover(evt, elements, c) {
 
 
 
553
  if (elements && elements.length) {
554
  const entry = entryFromElement(elements[0], c);
555
  if (entry) { pcSticky = false; showPointCard(entry, c, elements[0].element); return; }
 
105
  padding: 12px 14px; min-width: 248px; max-width: 320px;
106
  border-radius: 4px;
107
  opacity: 0; visibility: hidden; transform: translateY(2px);
108
+ transition: opacity 0.16s ease-out, transform 0.16s ease-out;
109
  pointer-events: none;
110
  }
111
  .point-card.visible { opacity: 1; visibility: visible; transform: none; pointer-events: auto; }
 
482
  const pointCard = document.getElementById('pointCard');
483
  let pcHideTimer = null;
484
  let pcSticky = false;
485
+ let cardAnchor = null; // screen coords of the dot the card points at
486
+ const cursor = { x: -1, y: -1 };
487
+ // Track the pointer everywhere (capture phase, so it updates before Chart's
488
+ // own hover handler runs) β€” the "safe corridor" test below needs to know
489
+ // whether the cursor is travelling from the dot toward the card.
490
+ document.addEventListener('mousemove', e => { cursor.x = e.clientX; cursor.y = e.clientY; }, true);
491
+
492
+ // Convex hull (monotone chain) + point-in-polygon. Together they form a
493
+ // forgiving hover corridor between the anchored dot and the card: while the
494
+ // cursor is inside it, the card stays put and other dots can't hijack it β€” so
495
+ // the user can actually reach the card and click its links.
496
+ function convexHull(pts) {
497
+ const p = pts.slice().sort((a, b) => a.x - b.x || a.y - b.y);
498
+ if (p.length < 3) return p;
499
+ const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
500
+ const lower = [];
501
+ for (const pt of p) { while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], pt) <= 0) lower.pop(); lower.push(pt); }
502
+ const upper = [];
503
+ for (let i = p.length - 1; i >= 0; i--) { const pt = p[i]; while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], pt) <= 0) upper.pop(); upper.push(pt); }
504
+ lower.pop(); upper.pop();
505
+ return lower.concat(upper);
506
+ }
507
+ function pointInPolygon(pt, poly) {
508
+ let inside = false;
509
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
510
+ const xi = poly[i].x, yi = poly[i].y, xj = poly[j].x, yj = poly[j].y;
511
+ if (((yi > pt.y) !== (yj > pt.y)) && (pt.x < (xj - xi) * (pt.y - yi) / (yj - yi) + xi)) inside = !inside;
512
+ }
513
+ return inside;
514
+ }
515
+ function cardVisible() { return pointCard.classList.contains('visible'); }
516
+ // True while the cursor is over the (padded) card or inside the hull spanning
517
+ // from the anchored dot to it β€” i.e. plausibly on its way to the card.
518
+ function inSafeCorridor() {
519
+ if (!cardVisible() || !cardAnchor) return false;
520
+ const r = pointCard.getBoundingClientRect();
521
+ const pad = 14;
522
+ if (cursor.x >= r.left - pad && cursor.x <= r.right + pad && cursor.y >= r.top - pad && cursor.y <= r.bottom + pad) return true;
523
+ const corners = [
524
+ { x: r.left - pad, y: r.top - pad }, { x: r.right + pad, y: r.top - pad },
525
+ { x: r.right + pad, y: r.bottom + pad }, { x: r.left - pad, y: r.bottom + pad },
526
+ ];
527
+ return pointInPolygon(cursor, convexHull([cardAnchor, ...corners]));
528
+ }
529
 
530
  function buildPointCardHtml(e) {
531
  const info = AGENTS[e.agent] || null;
 
556
 
557
  const linkBtns = [];
558
  if (e.filename) linkBtns.push(`<a href="${escapeHtml(submissionHref(e.filename))}" target="_blank" rel="noopener noreferrer">Submission β†—</a>`);
559
+ // The artifacts dir holds the run's code, so surface it as "Code" for clarity.
560
+ (e.links || []).forEach(l => {
561
+ const label = l.label.replace(/^Artifacts/i, 'Code');
562
+ linkBtns.push(`<a href="${escapeHtml(l.href)}" target="_blank" rel="noopener noreferrer">${escapeHtml(label)} β†—</a>`);
563
+ });
564
 
565
  return `${head}<div class="pc-score"><span class="big">${fmt2(e.score)}</span> tok/s ${verified}</div><div class="pc-rows">${rowsHtml}</div>${note}<div class="pc-links">${linkBtns.join('')}</div>`;
566
  }
 
580
  const rect = c.canvas.getBoundingClientRect();
581
  const px = rect.left + element.x;
582
  const py = rect.top + element.y;
583
+ cardAnchor = { x: px, y: py };
584
  const w = pointCard.offsetWidth, h = pointCard.offsetHeight;
585
  let left = px + 14;
586
  if (left + w > window.innerWidth - 8) left = px - w - 14;
 
593
  }
594
  function hidePointCard() {
595
  pcSticky = false;
596
+ cardAnchor = null;
597
  pointCard.classList.remove('visible');
598
  pointCard.setAttribute('aria-hidden', 'true');
599
  }
600
+ function scheduleHide() { if (pcSticky) return; clearTimeout(pcHideTimer); pcHideTimer = setTimeout(hidePointCard, 300); }
601
 
602
  function handleChartHover(evt, elements, c) {
603
+ // Cursor is over the card or travelling toward it β†’ keep the current card,
604
+ // and don't let an intervening dot swap it out from under the user.
605
+ if (inSafeCorridor()) { clearTimeout(pcHideTimer); return; }
606
  if (elements && elements.length) {
607
  const entry = entryFromElement(elements[0], c);
608
  if (entry) { pcSticky = false; showPointCard(entry, c, elements[0].element); return; }