lvwerra HF Staff commited on
Commit
2c40511
verified
1 Parent(s): 93f1c09

Render baselines as horizontal reference lines, not timeline points

Browse files
Files changed (1) hide show
  1. static/index.html +81 -8
static/index.html CHANGED
@@ -1613,18 +1613,31 @@ const GRID_COLOR = 'rgba(0,0,0,0.05)';
1613
  function renderChart(entries) {
1614
  if (!window.Chart) return;
1615
  if (chart) { chart.destroy(); chart = null; }
1616
- const sorted = [...entries].sort((a, b) => new Date(a.date) - new Date(b.date));
 
 
 
 
 
 
 
 
 
1617
  let runningBest = Infinity;
1618
  sorted.forEach(e => { e.isRecord = e.score < runningBest; if (e.isRecord) runningBest = e.score; });
1619
  const bestEntries = sorted.filter(e => e.isRecord);
1620
  const nonBestEntries = sorted.filter(e => !e.isRecord);
1621
 
1622
- const allDates = sorted.map(e => new Date(e.date).getTime());
1623
- const minDate = Math.min(...allDates);
1624
- const latestDate = Math.max(...allDates);
 
 
 
1625
  const timeRange = latestDate - minDate || 3600000;
1626
  const datePadding = timeRange * 0.05;
1627
  const extendedEnd = latestDate + timeRange * 0.08;
 
1628
 
1629
  const bestLineData = bestEntries.map(e => ({ x: new Date(e.date).getTime(), y: e.score, agent: e.agent }));
1630
  if (bestLineData.length) {
@@ -1634,9 +1647,10 @@ function renderChart(entries) {
1634
  const bestScatter = bestEntries.map(e => ({ x: new Date(e.date).getTime(), y: e.score, agent: e.agent }));
1635
  const nonBestData = nonBestEntries.map(e => ({ x: new Date(e.date).getTime(), y: e.score, agent: e.agent }));
1636
 
1637
- const allScores = sorted.map(e => e.score);
1638
- const minScore = Math.min(...allScores);
1639
- const maxScore = Math.max(...allScores);
 
1640
  const scorePad = (maxScore - minScore) * 0.2 || 100;
1641
 
1642
  const bestLabels = {
@@ -1698,6 +1712,62 @@ function renderChart(entries) {
1698
  }
1699
  };
1700
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1701
  const ctx = document.getElementById('evolutionChart').getContext('2d');
1702
  chart = new Chart(ctx, {
1703
  type: 'line',
@@ -1706,6 +1776,7 @@ function renderChart(entries) {
1706
  { label: 'Running Best', data: bestLineData, borderColor: HF_ORANGE, backgroundColor: HF_ORANGE_DIM, borderWidth: 2.5, stepped: 'after', fill: true, pointRadius: 0, pointHoverRadius: 0, tension: 0, order: 2 },
1707
  { label: 'Records', data: bestScatter, type: 'scatter', backgroundColor: HF_ORANGE, borderColor: '#fff', borderWidth: 2, pointRadius: 7, pointHoverRadius: 9, pointStyle: 'circle', order: 1 },
1708
  { label: 'Non-Records', data: nonBestData, type: 'scatter', backgroundColor: NON_BEST_COLOR, borderColor: '#fff', borderWidth: 1.5, pointRadius: 5, pointHoverRadius: 7, pointStyle: 'circle', order: 0 },
 
1709
  ],
1710
  },
1711
  options: {
@@ -1720,6 +1791,8 @@ function renderChart(entries) {
1720
  titleFont: { family: "'Source Sans 3', sans-serif", size: 12, weight: '700' },
1721
  bodyFont: { family: "'JetBrains Mono', monospace", size: 11 },
1722
  titleColor: '#fff', bodyColor: '#d1d5db',
 
 
1723
  callbacks: {
1724
  title: items => items[0]?.raw?.agent || '',
1725
  label: it => { const d = new Date(it.raw.x); return [`Bytes: ${it.raw.y.toLocaleString()}`, `Date: ${d.toLocaleString()}`]; }
@@ -1752,7 +1825,7 @@ function renderChart(entries) {
1752
  },
1753
  interaction: { mode: 'nearest', intersect: true },
1754
  },
1755
- plugins: [bestLabels, nonBestLabels],
1756
  });
1757
  }
1758
 
 
1613
  function renderChart(entries) {
1614
  if (!window.Chart) return;
1615
  if (chart) { chart.destroy(); chart = null; }
1616
+
1617
+ // Baselines are fixed historical references, not events on this collab's
1618
+ // timeline. Render them as horizontal dashed lines, not as points that
1619
+ // contribute to the running-best curve.
1620
+ const runEntries = entries.filter(e => e.agent !== 'baseline');
1621
+ const baselineEntries = [...entries]
1622
+ .filter(e => e.agent === 'baseline')
1623
+ .sort((a, b) => a.score - b.score);
1624
+
1625
+ const sorted = [...runEntries].sort((a, b) => new Date(a.date) - new Date(b.date));
1626
  let runningBest = Infinity;
1627
  sorted.forEach(e => { e.isRecord = e.score < runningBest; if (e.isRecord) runningBest = e.score; });
1628
  const bestEntries = sorted.filter(e => e.isRecord);
1629
  const nonBestEntries = sorted.filter(e => !e.isRecord);
1630
 
1631
+ // X axis: based on agent runs only. Fallback to a window around "now" when
1632
+ // no runs exist yet, so baselines still have a sensible x-range to span.
1633
+ const now = Date.now();
1634
+ const runDates = sorted.map(e => new Date(e.date).getTime());
1635
+ const minDate = runDates.length ? Math.min(...runDates) : now - 30 * 60 * 1000;
1636
+ const latestDate = runDates.length ? Math.max(...runDates) : now;
1637
  const timeRange = latestDate - minDate || 3600000;
1638
  const datePadding = timeRange * 0.05;
1639
  const extendedEnd = latestDate + timeRange * 0.08;
1640
+ const xMin = minDate - datePadding;
1641
 
1642
  const bestLineData = bestEntries.map(e => ({ x: new Date(e.date).getTime(), y: e.score, agent: e.agent }));
1643
  if (bestLineData.length) {
 
1647
  const bestScatter = bestEntries.map(e => ({ x: new Date(e.date).getTime(), y: e.score, agent: e.agent }));
1648
  const nonBestData = nonBestEntries.map(e => ({ x: new Date(e.date).getTime(), y: e.score, agent: e.agent }));
1649
 
1650
+ // Y axis covers runs *and* baselines so baseline lines aren't clipped.
1651
+ const allScores = [...sorted.map(e => e.score), ...baselineEntries.map(e => e.score)];
1652
+ const minScore = allScores.length ? Math.min(...allScores) : 14_000_000;
1653
+ const maxScore = allScores.length ? Math.max(...allScores) : 25_000_000;
1654
  const scorePad = (maxScore - minScore) * 0.2 || 100;
1655
 
1656
  const bestLabels = {
 
1712
  }
1713
  };
1714
 
1715
+ // Horizontal dashed line per baseline, spanning the full visible x-range.
1716
+ // Added *after* the three core datasets so the bestLabels (idx 1) and
1717
+ // nonBestLabels (idx 2) plugins continue to find their dataset metas.
1718
+ const BASELINE_COLOR = 'rgba(107,114,128,0.55)';
1719
+ const baselineDatasets = baselineEntries.map(e => ({
1720
+ label: `baseline 路 ${e.method || ''}`,
1721
+ data: [{ x: xMin, y: e.score }, { x: extendedEnd, y: e.score }],
1722
+ type: 'line',
1723
+ borderColor: BASELINE_COLOR,
1724
+ backgroundColor: 'transparent',
1725
+ borderWidth: 1,
1726
+ borderDash: [5, 4],
1727
+ pointRadius: 0,
1728
+ pointHoverRadius: 0,
1729
+ fill: false,
1730
+ tension: 0,
1731
+ order: 100,
1732
+ }));
1733
+
1734
+ // Right-edge label per baseline. Stack vertically when y-values cluster.
1735
+ const baselineLabels = {
1736
+ id: 'baselineLabels',
1737
+ afterDatasetsDraw(c) {
1738
+ if (!baselineEntries.length) return;
1739
+ const ctx2 = c.ctx;
1740
+ const a = c.chartArea;
1741
+ const yScale = c.scales.y;
1742
+ ctx2.save();
1743
+ ctx2.font = '500 10px "JetBrains Mono", monospace';
1744
+ const placed = [];
1745
+ // Draw smallest-score (top of chart) first so stacking pushes downward.
1746
+ baselineEntries.forEach(e => {
1747
+ const text = `${e.method || 'baseline'} ${e.score.toLocaleString()}`;
1748
+ const tw = ctx2.measureText(text).width;
1749
+ const px = 6, boxH = 18, boxW = tw + px * 2;
1750
+ let yc = yScale.getPixelForValue(e.score);
1751
+ for (const p of placed) {
1752
+ if (Math.abs(yc - p) < boxH + 2) yc = p + boxH + 2;
1753
+ }
1754
+ placed.push(yc);
1755
+ let lx = a.right - boxW - 4;
1756
+ let ly = yc - boxH / 2;
1757
+ if (ly < a.top) ly = a.top + 1;
1758
+ if (ly + boxH > a.bottom) ly = a.bottom - boxH - 1;
1759
+ ctx2.fillStyle = 'rgba(243,244,246,0.95)';
1760
+ ctx2.strokeStyle = 'rgba(107,114,128,0.45)';
1761
+ ctx2.lineWidth = 1;
1762
+ ctx2.beginPath(); ctx2.roundRect(lx, ly, boxW, boxH, 5); ctx2.fill(); ctx2.stroke();
1763
+ ctx2.fillStyle = '#6b7280';
1764
+ ctx2.textBaseline = 'middle';
1765
+ ctx2.fillText(text, lx + px, ly + boxH / 2);
1766
+ });
1767
+ ctx2.restore();
1768
+ },
1769
+ };
1770
+
1771
  const ctx = document.getElementById('evolutionChart').getContext('2d');
1772
  chart = new Chart(ctx, {
1773
  type: 'line',
 
1776
  { label: 'Running Best', data: bestLineData, borderColor: HF_ORANGE, backgroundColor: HF_ORANGE_DIM, borderWidth: 2.5, stepped: 'after', fill: true, pointRadius: 0, pointHoverRadius: 0, tension: 0, order: 2 },
1777
  { label: 'Records', data: bestScatter, type: 'scatter', backgroundColor: HF_ORANGE, borderColor: '#fff', borderWidth: 2, pointRadius: 7, pointHoverRadius: 9, pointStyle: 'circle', order: 1 },
1778
  { label: 'Non-Records', data: nonBestData, type: 'scatter', backgroundColor: NON_BEST_COLOR, borderColor: '#fff', borderWidth: 1.5, pointRadius: 5, pointHoverRadius: 7, pointStyle: 'circle', order: 0 },
1779
+ ...baselineDatasets,
1780
  ],
1781
  },
1782
  options: {
 
1791
  titleFont: { family: "'Source Sans 3', sans-serif", size: 12, weight: '700' },
1792
  bodyFont: { family: "'JetBrains Mono', monospace", size: 11 },
1793
  titleColor: '#fff', bodyColor: '#d1d5db',
1794
+ // Skip tooltip points that aren't real run events (baselines, line extensions).
1795
+ filter: it => it.datasetIndex < 3 && it.raw && !it.raw._ext && it.raw.agent,
1796
  callbacks: {
1797
  title: items => items[0]?.raw?.agent || '',
1798
  label: it => { const d = new Date(it.raw.x); return [`Bytes: ${it.raw.y.toLocaleString()}`, `Date: ${d.toLocaleString()}`]; }
 
1825
  },
1826
  interaction: { mode: 'nearest', intersect: true },
1827
  },
1828
+ plugins: [bestLabels, nonBestLabels, baselineLabels],
1829
  });
1830
  }
1831