ereniko commited on
Commit
c8146b1
·
verified ·
1 Parent(s): 23f31cf

Upload 4 files

Browse files
Files changed (4) hide show
  1. background.js +60 -0
  2. index.html +3 -3
  3. script.js +60 -48
  4. style.css +18 -0
background.js CHANGED
@@ -27,5 +27,65 @@
27
  }
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  });
31
  })();
 
27
  }
28
  }
29
 
30
+ // ---- Grass strip ----
31
+ if (!document.querySelector(".grass")) {
32
+ const NS = "http://www.w3.org/2000/svg";
33
+ const W = isMobile ? 1200 : 2400;
34
+ const H = 70;
35
+ const svg = document.createElementNS(NS, "svg");
36
+ svg.setAttribute("class", "grass");
37
+ svg.setAttribute("viewBox", "0 0 " + W + " " + H);
38
+ svg.setAttribute("preserveAspectRatio", "xMidYMax slice");
39
+ svg.setAttribute("aria-hidden", "true");
40
+
41
+ svg.innerHTML =
42
+ '<defs><linearGradient id="grass-base" x1="0" y1="1" x2="0" y2="0">' +
43
+ '<stop offset="0" stop-color="#3e7a46" stop-opacity="0.55"/>' +
44
+ '<stop offset="1" stop-color="#3e7a46" stop-opacity="0"/>' +
45
+ "</linearGradient></defs>" +
46
+ '<rect x="0" y="' + (H - 16) + '" width="' + W + '" height="16" fill="url(#grass-base)"/>';
47
+
48
+ const layers = isMobile
49
+ ? [
50
+ { count: 50, hMin: 18, hMax: 32, colors: ["#4f9854"], opacity: 0.9 },
51
+ { count: 40, hMin: 24, hMax: 42, colors: ["#63ad64"], opacity: 1 },
52
+ ]
53
+ : [
54
+ { count: 190, hMin: 18, hMax: 36, colors: ["#3e7a46", "#457f4b", "#4a8a50"], opacity: 0.8 },
55
+ { count: 160, hMin: 26, hMax: 52, colors: ["#4f9854", "#57a05a", "#63ad64", "#6db36e"], opacity: 1 },
56
+ ];
57
+
58
+ layers.forEach((layer) => {
59
+ const g = document.createElementNS(NS, "g");
60
+ g.setAttribute("opacity", layer.opacity);
61
+ for (let i = 0; i < layer.count; i++) {
62
+ const x = Math.random() * W;
63
+ const h = layer.hMin + Math.random() * (layer.hMax - layer.hMin);
64
+ const lean = (Math.random() - 0.5) * 16;
65
+ const w = 3 + Math.random() * 3;
66
+ const color = layer.colors[(Math.random() * layer.colors.length) | 0];
67
+ const blade = document.createElementNS(NS, "path");
68
+ blade.setAttribute(
69
+ "d",
70
+ "M " + x + " " + H +
71
+ " Q " + (x + lean * 0.2) + " " + (H - h * 0.6) + " " + (x + lean) + " " + (H - h) +
72
+ " Q " + (x + lean * 0.6 + w * 0.5) + " " + (H - h * 0.55) + " " + (x + w) + " " + H + " Z"
73
+ );
74
+ blade.setAttribute("fill", color);
75
+ blade.style.transformBox = "fill-box";
76
+ blade.style.transformOrigin = "50% 100%";
77
+ if (!isMobile) {
78
+ blade.style.animation =
79
+ "blade-sway " + (2.6 + Math.random() * 2.4).toFixed(2) + "s ease-in-out " +
80
+ (-(x / W) * 3 - Math.random()).toFixed(2) + "s infinite alternate";
81
+ }
82
+ g.appendChild(blade);
83
+ }
84
+ svg.appendChild(g);
85
+ });
86
+
87
+ document.body.appendChild(svg);
88
+ }
89
+
90
  });
91
  })();
index.html CHANGED
@@ -51,12 +51,12 @@
51
  <div class="range-slider" id="params-range-slider">
52
  <div class="range-slider-track"></div>
53
  <div class="range-slider-fill" id="params-range-fill"></div>
54
- <input type="range" id="params-range-min" class="range-thumb range-thumb-min" min="0" max="100" step="0.1" value="0" aria-label="Minimum parameters in billions">
55
- <input type="range" id="params-range-max" class="range-thumb range-thumb-max" min="0" max="100" step="0.1" value="100" aria-label="Maximum parameters in billions">
56
  </div>
57
  <div class="range-slider-values">
58
  <span id="params-range-min-label">0B</span>
59
- <span id="params-range-max-label">100B+</span>
60
  </div>
61
  <button type="button" class="filter-reset" id="params-filter-reset">Reset</button>
62
  </div>
 
51
  <div class="range-slider" id="params-range-slider">
52
  <div class="range-slider-track"></div>
53
  <div class="range-slider-fill" id="params-range-fill"></div>
54
+ <input type="range" id="params-range-min" class="range-thumb range-thumb-min" min="0" max="1000" step="1" value="0" aria-label="Minimum parameters in billions">
55
+ <input type="range" id="params-range-max" class="range-thumb range-thumb-max" min="0" max="1000" step="1" value="1000" aria-label="Maximum parameters in billions">
56
  </div>
57
  <div class="range-slider-values">
58
  <span id="params-range-min-label">0B</span>
59
+ <span id="params-range-max-label">1000B+</span>
60
  </div>
61
  <button type="button" class="filter-reset" id="params-filter-reset">Reset</button>
62
  </div>
script.js CHANGED
@@ -6,8 +6,7 @@
6
  activeBenchmark: null,
7
  search: "",
8
  sort: "score-desc",
9
- paramsBounds: { min: 0, max: 100 }, // full data range, computed on load
10
- paramsFilter: { min: 0, max: 100 }, // current filter selection
11
  };
12
 
13
  const els = {};
@@ -154,31 +153,39 @@
154
 
155
  // -------------------- PARAMS FILTER --------------------
156
 
157
- // Derive slider bounds from the actual dataset so the range always matches
158
- // whatever models are in models.json (no hardcoded cap).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  function initParamsBounds() {
160
- const values = state.data.models
161
- .map((m) => m.params_b)
162
- .filter((v) => typeof v === "number");
163
-
164
- let min = 0;
165
- let max = values.length ? Math.max(...values) : 100;
166
- // round the ceiling up a bit so the top model's dot isn't pinned to the
167
- // very edge of the track, and so the slider still makes sense with 1 model
168
- max = Math.max(1, Math.ceil(max * 1.1 * 10) / 10);
169
-
170
- state.paramsBounds = { min, max };
171
- state.paramsFilter = { min, max };
172
-
173
- els.rangeMin.min = String(min);
174
- els.rangeMin.max = String(max);
175
- els.rangeMin.step = "0.1";
176
- els.rangeMin.value = String(min);
177
-
178
- els.rangeMax.min = String(min);
179
- els.rangeMax.max = String(max);
180
- els.rangeMax.step = "0.1";
181
- els.rangeMax.value = String(max);
182
  }
183
 
184
  function bindParamsFilter() {
@@ -201,12 +208,16 @@
201
  });
202
 
203
  const onSlide = () => {
204
- let lo = parseFloat(els.rangeMin.value);
205
- let hi = parseFloat(els.rangeMax.value);
206
  // keep the two thumbs from crossing
207
- if (lo > hi) {
208
- [lo, hi] = [hi, lo];
209
  }
 
 
 
 
210
  state.paramsFilter = { min: lo, max: hi };
211
  updateRangeVisuals();
212
  render();
@@ -216,10 +227,9 @@
216
  els.rangeMax.addEventListener("input", onSlide);
217
 
218
  els.filterReset.addEventListener("click", () => {
219
- const { min, max } = state.paramsBounds;
220
- state.paramsFilter = { min, max };
221
- els.rangeMin.value = String(min);
222
- els.rangeMax.value = String(max);
223
  updateRangeVisuals();
224
  render();
225
  });
@@ -233,26 +243,29 @@
233
  }
234
 
235
  function isParamsFilterActive() {
236
- const { min, max } = state.paramsBounds;
237
- return state.paramsFilter.min > min || state.paramsFilter.max < max;
238
  }
239
 
240
  function updateRangeVisuals() {
241
- const { min, max } = state.paramsBounds;
242
- const span = Math.max(0.0001, max - min);
243
- const loPct = ((state.paramsFilter.min - min) / span) * 100;
244
- const hiPct = ((state.paramsFilter.max - min) / span) * 100;
 
245
 
246
  els.rangeFill.style.left = loPct.toFixed(2) + "%";
247
  els.rangeFill.style.right = (100 - hiPct).toFixed(2) + "%";
248
 
249
- const fmt = (v) => {
250
- const isMax = v >= max;
251
- const rounded = Math.round(v * 10) / 10;
252
- return (Number.isInteger(rounded) ? rounded.toFixed(0) : rounded.toFixed(1)) + "B" + (isMax ? "+" : "");
 
 
 
253
  };
254
- els.rangeMinLabel.textContent = fmt(state.paramsFilter.min);
255
- els.rangeMaxLabel.textContent = fmt(state.paramsFilter.max);
256
 
257
  const active = isParamsFilterActive();
258
  els.filterBtn.classList.toggle("has-active-filter", active);
@@ -346,11 +359,10 @@
346
 
347
  if (isParamsFilterActive()) {
348
  const { min, max } = state.paramsFilter;
349
- const { max: dataMax } = state.paramsBounds;
350
  rows = rows.filter((r) => {
351
  const p = r.model.params_b;
352
  if (p == null) return false; // unknown size excluded once a filter is set
353
- if (max >= dataMax) return p >= min; // top thumb at the ceiling = open-ended "+"
354
  return p >= min && p <= max;
355
  });
356
  }
 
6
  activeBenchmark: null,
7
  search: "",
8
  sort: "score-desc",
9
+ paramsFilter: { min: 0, max: 1000 }, // current filter selection, in billions of params
 
10
  };
11
 
12
  const els = {};
 
153
 
154
  // -------------------- PARAMS FILTER --------------------
155
 
156
+ // Absolute ceiling the slider will ever offer, regardless of what's in the
157
+ // data today 1000B (1T) parameters, so future large-model submissions
158
+ // don't require touching this code.
159
+ const PARAMS_ABSOLUTE_MAX = 1000;
160
+
161
+ // The slider works in log-space (0.1B .. 1000B) so both a 0.5B and a
162
+ // 400B model are draggable to precisely — a linear 0..1000 slider would
163
+ // make anything under ~10B unreachable with a mouse.
164
+ const LOG_MIN = -1; // 10^-1 = 0.1B
165
+ const LOG_MAX = Math.log10(PARAMS_ABSOLUTE_MAX); // 10^3 = 1000B
166
+
167
+ function paramsToSlider(b) {
168
+ const clamped = Math.max(0.1, Math.min(PARAMS_ABSOLUTE_MAX, b));
169
+ return (Math.log10(clamped) - LOG_MIN) / (LOG_MAX - LOG_MIN);
170
+ }
171
+
172
+ function sliderToParams(t) {
173
+ return Math.pow(10, LOG_MIN + t * (LOG_MAX - LOG_MIN));
174
+ }
175
+
176
+ // Derive slider bounds from the actual dataset, but always offer the full
177
+ // 0.1B–1000B range so filtering isn't capped to whatever's in models.json
178
+ // today.
179
  function initParamsBounds() {
180
+ state.paramsFilter = { min: 0, max: PARAMS_ABSOLUTE_MAX };
181
+
182
+ [els.rangeMin, els.rangeMax].forEach((el) => {
183
+ el.min = "0";
184
+ el.max = "1000";
185
+ el.step = "1";
186
+ });
187
+ els.rangeMin.value = "0";
188
+ els.rangeMax.value = "1000";
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  }
190
 
191
  function bindParamsFilter() {
 
208
  });
209
 
210
  const onSlide = () => {
211
+ let loPos = parseFloat(els.rangeMin.value);
212
+ let hiPos = parseFloat(els.rangeMax.value);
213
  // keep the two thumbs from crossing
214
+ if (loPos > hiPos) {
215
+ [loPos, hiPos] = [hiPos, loPos];
216
  }
217
+ // slider position 0 is a true "no lower bound" (0B); anything above
218
+ // maps through the log curve so small models stay draggable.
219
+ const lo = loPos <= 0 ? 0 : sliderToParams(loPos / 1000);
220
+ const hi = hiPos >= 1000 ? PARAMS_ABSOLUTE_MAX : sliderToParams(hiPos / 1000);
221
  state.paramsFilter = { min: lo, max: hi };
222
  updateRangeVisuals();
223
  render();
 
227
  els.rangeMax.addEventListener("input", onSlide);
228
 
229
  els.filterReset.addEventListener("click", () => {
230
+ state.paramsFilter = { min: 0, max: PARAMS_ABSOLUTE_MAX };
231
+ els.rangeMin.value = "0";
232
+ els.rangeMax.value = "1000";
 
233
  updateRangeVisuals();
234
  render();
235
  });
 
243
  }
244
 
245
  function isParamsFilterActive() {
246
+ return state.paramsFilter.min > 0 || state.paramsFilter.max < PARAMS_ABSOLUTE_MAX;
 
247
  }
248
 
249
  function updateRangeVisuals() {
250
+ // thumbs are already in slider-position units (0-1000); just read them
251
+ // back for the fill bar rather than re-deriving from log space, so the
252
+ // fill always matches exactly where the thumbs are.
253
+ const loPct = (parseFloat(els.rangeMin.value) / 1000) * 100;
254
+ const hiPct = (parseFloat(els.rangeMax.value) / 1000) * 100;
255
 
256
  els.rangeFill.style.left = loPct.toFixed(2) + "%";
257
  els.rangeFill.style.right = (100 - hiPct).toFixed(2) + "%";
258
 
259
+ const fmt = (b, isMax) => {
260
+ if (isMax && b >= PARAMS_ABSOLUTE_MAX) return "1000B+";
261
+ if (b <= 0) return "0B";
262
+ if (b >= 1000) return (b / 1000).toFixed(1).replace(/\.0$/, "") + "T";
263
+ if (b < 1) return b.toFixed(2) + "B";
264
+ if (b < 10) return b.toFixed(1) + "B";
265
+ return Math.round(b) + "B";
266
  };
267
+ els.rangeMinLabel.textContent = fmt(state.paramsFilter.min, false);
268
+ els.rangeMaxLabel.textContent = fmt(state.paramsFilter.max, true);
269
 
270
  const active = isParamsFilterActive();
271
  els.filterBtn.classList.toggle("has-active-filter", active);
 
359
 
360
  if (isParamsFilterActive()) {
361
  const { min, max } = state.paramsFilter;
 
362
  rows = rows.filter((r) => {
363
  const p = r.model.params_b;
364
  if (p == null) return false; // unknown size excluded once a filter is set
365
+ if (max >= PARAMS_ABSOLUTE_MAX) return p >= min; // top thumb at the ceiling = open-ended "+"
366
  return p >= min && p <= max;
367
  });
368
  }
style.css CHANGED
@@ -116,6 +116,23 @@ a:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-
116
  to { transform: translateX(120vw) scale(var(--cloud-scale, 1)); }
117
  }
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  /* -------------------- LAYOUT -------------------- */
120
 
121
  .container {
@@ -222,6 +239,7 @@ a:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-
222
  /* -------------------- SUBMIT PANEL -------------------- */
223
 
224
  .submit-panel {
 
225
  margin-bottom: 26px;
226
  border-radius: var(--radius);
227
  background: var(--surface);
 
116
  to { transform: translateX(120vw) scale(var(--cloud-scale, 1)); }
117
  }
118
 
119
+ .grass {
120
+ position: fixed;
121
+ bottom: -2px; left: 0;
122
+ width: 100%; height: 70px;
123
+ z-index: 50;
124
+ pointer-events: none;
125
+ }
126
+
127
+ @keyframes blade-sway {
128
+ from { transform: rotate(-2.4deg); }
129
+ to { transform: rotate(3.2deg); }
130
+ }
131
+
132
+ @media (prefers-reduced-motion: reduce) {
133
+ .grass path { animation: none !important; }
134
+ }
135
+
136
  /* -------------------- LAYOUT -------------------- */
137
 
138
  .container {
 
239
  /* -------------------- SUBMIT PANEL -------------------- */
240
 
241
  .submit-panel {
242
+ margin-top: 32px;
243
  margin-bottom: 26px;
244
  border-radius: var(--radius);
245
  background: var(--surface);