thibaud frere commited on
Commit
03cd478
·
1 Parent(s): cb92d91
app/src/components/ThemeToggle.astro CHANGED
@@ -1,18 +1,16 @@
1
  <button id="theme-toggle" aria-label="Toggle color theme">
2
- <svg class="icon light" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">
3
- <circle cx="12" cy="12" r="5"/>
4
- <g stroke="currentColor" stroke-width="2" stroke-linecap="round">
5
- <line x1="12" y1="1" x2="12" y2="4"/>
6
- <line x1="12" y1="20" x2="12" y2="23"/>
7
- <line x1="1" y1="12" x2="4" y2="12"/>
8
- <line x1="20" y1="12" x2="23" y2="12"/>
9
- <line x1="4.22" y1="4.22" x2="6.34" y2="6.34"/>
10
- <line x1="17.66" y1="17.66" x2="19.78" y2="19.78"/>
11
- <line x1="4.22" y1="19.78" x2="6.34" y2="17.66"/>
12
- <line x1="17.66" y1="6.34" x2="19.78" y2="4.22"/>
13
- </g>
14
  </svg>
15
- <svg class="icon dark" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="currentColor">
16
  <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
17
  </svg>
18
  <script>
@@ -35,11 +33,13 @@
35
  else if (media.addListener) media.addListener(syncWithSystem);
36
  }
37
 
38
- btn.addEventListener('click', () => {
39
- const next = (document.documentElement.dataset.theme === 'dark') ? 'light' : 'dark';
40
- localStorage.setItem('theme', next);
41
- apply(next);
42
- });
 
 
43
  </script>
44
  </button>
45
 
 
1
  <button id="theme-toggle" aria-label="Toggle color theme">
2
+ <svg class="icon light" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
3
+ <circle cx="12" cy="12" r="5"></circle>
4
+ <line x1="12" y1="1" x2="12" y2="4"></line>
5
+ <line x1="12" y1="20" x2="12" y2="23"></line>
6
+ <line x1="1" y1="12" x2="4" y2="12"></line>
7
+ <line x1="20" y1="12" x2="23" y2="12"></line>
8
+ <line x1="4.22" y1="4.22" x2="6.34" y2="6.34"></line>
9
+ <line x1="17.66" y1="17.66" x2="19.78" y2="19.78"></line>
10
+ <line x1="4.22" y1="19.78" x2="6.34" y2="17.66"></line>
11
+ <line x1="17.66" y1="6.34" x2="19.78" y2="4.22"></line>
 
 
12
  </svg>
13
+ <svg class="icon dark" width="20" height="20" viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
14
  <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
15
  </svg>
16
  <script>
 
33
  else if (media.addListener) media.addListener(syncWithSystem);
34
  }
35
 
36
+ if (btn) {
37
+ btn.addEventListener('click', () => {
38
+ const next = (document.documentElement.dataset.theme === 'dark') ? 'light' : 'dark';
39
+ localStorage.setItem('theme', next);
40
+ apply(next);
41
+ });
42
+ }
43
  </script>
44
  </button>
45
 
app/src/content/assets/data/against_baselines.csv CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d5db6c112739ccf6b5f98dfdd480f9748c5b78c66452b02e0168eeed6acea875
3
- size 50100
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:85f900011d0ea9cd2e0326e2d5bf40b74e90e696901b8fd9283dd802aa0bdbe1
3
+ size 37017
app/src/content/embeds/d3-line.html CHANGED
@@ -156,6 +156,7 @@
156
  const gGrid = gRoot.append('g').attr('class', 'grid');
157
  const gAxes = gRoot.append('g').attr('class', 'axes');
158
  const gLines = gRoot.append('g').attr('class', 'lines');
 
159
  const gHover = gRoot.append('g').attr('class', 'hover');
160
  const gLegend = gRoot.append('foreignObject').attr('class', 'legend');
161
 
@@ -197,11 +198,15 @@
197
  let xScale = d3.scaleLinear();
198
  let yScale = d3.scaleLinear();
199
 
200
- // Line generator
201
- const lineGen = d3.line()
202
  .curve(d3.curveCatmullRom.alpha(0.05))
203
  .x((d) => xScale(d.step))
204
  .y((d) => yScale(d.value));
 
 
 
 
205
 
206
  // Hover elements
207
  const hoverLine = gHover.append('line').attr('stroke-width', 1);
@@ -327,15 +332,33 @@
327
  // Bind lines
328
  const series = runs.map((r, i) => ({ run: r, color: pool[i % pool.length], values: (map[r]||[]).slice().sort((a,b)=>a.step-b.step) }));
329
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
 
330
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
331
  .attr('stroke', d=>d.color).attr('opacity',0.9)
332
- .attr('d', d=>lineGen(d.values))
333
  .merge(paths)
334
  .transition().duration(200)
335
  .attr('stroke', d=>d.color)
336
- .attr('d', d=>lineGen(d.values));
337
  paths.exit().remove();
338
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  // Inline legend content (row, right side) compact
340
  legendInline.innerHTML = series.map(s => `<span style="display:inline-flex;align-items:center;gap:6px;white-space:nowrap;"><span style="width:18px;height:10px;background:${s.color};border-radius:3px;display:inline-block"></span><span>${s.run}</span></span>`).join('');
341
 
@@ -353,7 +376,10 @@
353
  series.forEach(s=>{
354
  const m = new Map(s.values.map(v=>[v.step, v.value]));
355
  const val = m.has(nearest) ? m.get(nearest) : null;
356
- if (val != null) html += `<div><span style="display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;"></span><strong>${s.run}</strong> ${(+val).toFixed(4)}</div>`;
 
 
 
357
  });
358
  tipInner.innerHTML = html;
359
  const offsetX = 12, offsetY = 12;
 
156
  const gGrid = gRoot.append('g').attr('class', 'grid');
157
  const gAxes = gRoot.append('g').attr('class', 'axes');
158
  const gLines = gRoot.append('g').attr('class', 'lines');
159
+ const gPoints = gRoot.append('g').attr('class', 'points');
160
  const gHover = gRoot.append('g').attr('class', 'hover');
161
  const gLegend = gRoot.append('foreignObject').attr('class', 'legend');
162
 
 
198
  let xScale = d3.scaleLinear();
199
  let yScale = d3.scaleLinear();
200
 
201
+ // Line generators
202
+ const lineGenSmooth = d3.line()
203
  .curve(d3.curveCatmullRom.alpha(0.05))
204
  .x((d) => xScale(d.step))
205
  .y((d) => yScale(d.value));
206
+ const lineGenStep = d3.line()
207
+ .curve(d3.curveStepAfter)
208
+ .x((d) => xScale(d.step))
209
+ .y((d) => yScale(d.value));
210
 
211
  // Hover elements
212
  const hoverLine = gHover.append('line').attr('stroke-width', 1);
 
332
  // Bind lines
333
  const series = runs.map((r, i) => ({ run: r, color: pool[i % pool.length], values: (map[r]||[]).slice().sort((a,b)=>a.step-b.step) }));
334
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
335
+ const gen = isRank ? lineGenStep : lineGenSmooth;
336
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
337
  .attr('stroke', d=>d.color).attr('opacity',0.9)
338
+ .attr('d', d=>gen(d.values))
339
  .merge(paths)
340
  .transition().duration(200)
341
  .attr('stroke', d=>d.color)
342
+ .attr('d', d=>gen(d.values));
343
  paths.exit().remove();
344
 
345
+ // Points for rank metrics (discrete steps)
346
+ if (isRank) {
347
+ const pointData = series.flatMap(s => s.values.map(v => ({ run: s.run, color: s.color, step: v.step, value: v.value })));
348
+ const circles = gPoints.selectAll('circle.point').data(pointData, d => d.run + '-' + d.step);
349
+ circles.enter().append('circle').attr('class', 'point')
350
+ .attr('r', 2.5)
351
+ .attr('fill', d => d.color)
352
+ .attr('cx', d => xScale(d.step))
353
+ .attr('cy', d => yScale(d.value))
354
+ .merge(circles)
355
+ .attr('cx', d => xScale(d.step))
356
+ .attr('cy', d => yScale(d.value));
357
+ circles.exit().remove();
358
+ } else {
359
+ gPoints.selectAll('*').remove();
360
+ }
361
+
362
  // Inline legend content (row, right side) compact
363
  legendInline.innerHTML = series.map(s => `<span style="display:inline-flex;align-items:center;gap:6px;white-space:nowrap;"><span style="width:18px;height:10px;background:${s.color};border-radius:3px;display:inline-block"></span><span>${s.run}</span></span>`).join('');
364
 
 
376
  series.forEach(s=>{
377
  const m = new Map(s.values.map(v=>[v.step, v.value]));
378
  const val = m.has(nearest) ? m.get(nearest) : null;
379
+ if (val != null) {
380
+ const formatVal = (vv) => (isRank ? d3.format('d')(vv) : (+vv).toFixed(4));
381
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
382
+ }
383
  });
384
  tipInner.innerHTML = html;
385
  const offsetX = 12, offsetY = 12;
app/src/content/embeds/d3-pie.html CHANGED
@@ -6,7 +6,7 @@
6
  .d3-pie .legend .swatch { width:14px; height:14px; border-radius:3px; display:inline-block; border: 1px solid var(--border-color); }
7
  .d3-pie .caption { font-size: 14px; font-weight: 800; fill: var(--text-color); }
8
  .d3-pie .nodata { font-size: 12px; fill: var(--muted-color); }
9
- .d3-pie .slice-label { font-size: 11px; font-weight: 700; fill: var(--text-color); paint-order: stroke; stroke: rgba(255,255,255,0.6); stroke-width: 3px; }
10
  </style>
11
  <script>
12
  (() => {
 
6
  .d3-pie .legend .swatch { width:14px; height:14px; border-radius:3px; display:inline-block; border: 1px solid var(--border-color); }
7
  .d3-pie .caption { font-size: 14px; font-weight: 800; fill: var(--text-color); }
8
  .d3-pie .nodata { font-size: 12px; fill: var(--muted-color); }
9
+ .d3-pie .slice-label { font-size: 11px; font-weight: 700; fill: var(--text-color); paint-order: stroke; stroke: rgba(255,255,255,0.2); stroke-width: 3px; }
10
  </style>
11
  <script>
12
  (() => {