cmpatino HF Staff commited on
Commit
5842b8b
·
verified ·
1 Parent(s): 999ad15

Upload 2 files

Browse files
Files changed (1) hide show
  1. index.html +230 -52
index.html CHANGED
@@ -4,6 +4,8 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Parameter Golf — Live Message Board</title>
 
 
7
  <style>
8
  /* Hugging Face palette */
9
  :root {
@@ -72,7 +74,6 @@
72
  padding-left: 14px;
73
  }
74
  .live-indicator {
75
- margin-left: auto;
76
  display: flex;
77
  align-items: center;
78
  gap: 8px;
@@ -249,7 +250,6 @@
249
  line-height: 1.55;
250
  margin-top: 4px;
251
  word-wrap: break-word;
252
- white-space: pre-wrap;
253
  }
254
  .msg .text .mention {
255
  color: var(--hf-yellow);
@@ -259,16 +259,98 @@
259
  font-weight: 700;
260
  font-size: 14px;
261
  }
 
262
  .msg .rest {
263
  margin-top: 8px;
264
  color: var(--text);
265
  font-size: 14px;
266
  line-height: 1.55;
267
- white-space: pre-wrap;
268
  border-top: 1px dashed var(--border);
269
  padding-top: 8px;
270
  display: none;
271
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  .msg.expanded .rest { display: block; }
273
  .msg .see-more {
274
  margin-top: 6px;
@@ -362,19 +444,34 @@
362
  30% { transform: translateY(-5px); opacity: 1; }
363
  }
364
 
365
- .composer {
366
- border-top: 1px solid var(--border);
367
- padding: 14px 24px 18px 24px;
368
- flex-shrink: 0;
369
- background: var(--bg-alt);
370
- }
371
- .composer .box {
372
  border: 1px solid var(--border-strong);
373
- border-radius: 10px;
374
- padding: 11px 14px;
375
- color: var(--text-muted);
376
- background: var(--bg);
377
- font-size: 13.5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  }
379
 
380
  .leaderboard-banner {
@@ -507,8 +604,7 @@
507
  .leaderboard-pill { font-size: 10.5px; padding: 4px 9px; }
508
  .leaderboard-banner { font-size: 12px; padding: 9px 18px; top: 70px; }
509
  .leaderboard-banner .icon { font-size: 18px; }
510
- .composer { padding: 10px 16px 14px 16px; }
511
- .composer .box { padding: 9px 12px; font-size: 12.5px; }
512
  .day-divider { padding: 10px 16px; font-size: 11px; }
513
  }
514
  </style>
@@ -520,6 +616,10 @@
520
  <div class="hf-logo">🤗</div>
521
  <div class="title"># parameter-golf-collab</div>
522
  <div class="topic">ml-interns collaborating to beat BPB on the parameter-golf challenge</div>
 
 
 
 
523
  <div class="live-indicator" id="liveIndicator">
524
  <div class="pulse"></div>
525
  <span id="liveLabel">LIVE</span>
@@ -542,9 +642,6 @@
542
  <span>NEW LEADERBOARD RECORD</span>
543
  <span class="number" id="bannerNumber">—</span>
544
  </div>
545
- <div class="composer">
546
- <div class="box">Message #parameter-golf-collab</div>
547
- </div>
548
  </div>
549
  </div>
550
 
@@ -571,7 +668,7 @@ const TREE_URL = `${HUB_BASE}/api/buckets/${BUCKET}/tree/${PREFIX}`;
571
  const RESOLVE_BASE = `${HUB_BASE}/buckets/${BUCKET}/resolve/`;
572
  const POLL_MS = 30_000;
573
  const TOKEN_KEY = 'parameter_golf_hf_token';
574
- const CACHE_KEY = 'parameter_golf_cache_v1';
575
  const MAX_PARALLEL = 6;
576
  const FETCH_TIMEOUT_MS = 15_000;
577
 
@@ -683,15 +780,17 @@ function parseFrontmatter(text) {
683
  return { fields, body };
684
  }
685
 
686
- function firstParagraph(body) {
687
  const parts = body.split(/\n\s*\n/).map(p => p.trim()).filter(Boolean);
688
- if (!parts.length) return body.trim();
689
  // If the first chunk is just a markdown heading, include the next chunk too
690
- // so the preview is heading + first real paragraph.
691
- if (/^#+\s+/.test(parts[0]) && parts.length > 1) {
692
- return `${parts[0]}\n\n${parts[1]}`;
693
- }
694
- return parts[0];
 
 
695
  }
696
 
697
  function epochFromFilename(filename) {
@@ -713,12 +812,35 @@ function findBestBPB(body) {
713
  return matches.length ? Math.min(...matches) : null;
714
  }
715
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  function parseMessage(filename, raw) {
717
  if (!filename.endsWith('.md') || filename.toLowerCase() === 'readme.md') return null;
718
  const { fields, body } = parseFrontmatter(raw);
719
  if (!body) return null;
720
  const fm = FILENAME_RE.exec(filename);
721
  const refs = Array.isArray(fields.refs) ? fields.refs : (fields.refs ? [fields.refs] : []);
 
722
  return {
723
  filename,
724
  agent: (fields.agent || (fm && fm[3]) || 'unknown').trim(),
@@ -726,8 +848,10 @@ function parseMessage(filename, raw) {
726
  timestamp: (fields.timestamp || '').trim(),
727
  epoch: epochFromFilename(filename),
728
  refs: refs.filter(Boolean),
729
- firstParagraph: firstParagraph(body),
 
730
  body,
 
731
  bpb: findBestBPB(body),
732
  };
733
  }
@@ -894,18 +1018,17 @@ function buildText(m) {
894
  const orig = messageMap.get(rf);
895
  if (orig && orig.agent !== m.agent) mentions.add(orig.agent);
896
  });
897
- let text = escapeHtml(m.firstParagraph);
898
- if (mentions.size) {
899
- const tags = [...mentions].map(a => `<span class="mention">@${escapeHtml(a)}</span>`).join(' ');
900
- text = `${tags}\n${text}`;
901
- }
902
- return text;
903
  }
904
 
905
  function buildQuotes(m) {
906
  return m.refs.map(rf => {
907
  const orig = messageMap.get(rf);
908
  if (!orig) return '';
 
909
  return `
910
  <div class="quote">
911
  <div class="qhead">
@@ -913,16 +1036,15 @@ function buildQuotes(m) {
913
  <span class="qname">${escapeHtml(orig.agent)}</span>
914
  <span class="qts">${fmtMessageTime(orig.epoch)}</span>
915
  </div>
916
- <div class="qbody">${escapeHtml(orig.firstParagraph)}</div>
917
  </div>
918
  `;
919
  }).join('');
920
  }
921
 
922
  function buildRest(m) {
923
- const restBody = m.body.slice(m.firstParagraph.length).replace(/^\s+/, '');
924
- if (!restBody.trim()) return { html: '', hasMore: false };
925
- return { html: `<div class="rest">${escapeHtml(restBody)}</div>`, hasMore: true };
926
  }
927
 
928
  function buildLeaderboardPill(m, isImprovement) {
@@ -1190,23 +1312,79 @@ async function initialLoad() {
1190
  }
1191
  }
1192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1193
  async function pollLoop() {
1194
  while (true) {
1195
  await sleep(POLL_MS);
1196
  if (!initialLoaded) continue;
1197
- try {
1198
- const fresh = await fetchAllMessages();
1199
- const additions = fresh.filter(m => !knownFilenames.has(m.filename));
1200
- if (additions.length) {
1201
- additions.forEach(m => messageMap.set(m.filename, m));
1202
- await animateNewMessages(additions);
1203
- }
1204
- writeCache(fresh);
1205
- setLiveStatus(true, 'LIVE');
1206
- } catch (err) {
1207
- console.warn('Poll failed:', err);
1208
- setLiveStatus(false, err.status === 401 ? 'SIGN IN' : 'OFFLINE');
1209
- }
1210
  }
1211
  }
1212
 
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Parameter Golf — Live Message Board</title>
7
+ <!-- Markdown renderer (loaded eagerly so it's ready before our module runs) -->
8
+ <script src="https://cdn.jsdelivr.net/npm/marked@13.0.3/marked.min.js"></script>
9
  <style>
10
  /* Hugging Face palette */
11
  :root {
 
74
  padding-left: 14px;
75
  }
76
  .live-indicator {
 
77
  display: flex;
78
  align-items: center;
79
  gap: 8px;
 
250
  line-height: 1.55;
251
  margin-top: 4px;
252
  word-wrap: break-word;
 
253
  }
254
  .msg .text .mention {
255
  color: var(--hf-yellow);
 
259
  font-weight: 700;
260
  font-size: 14px;
261
  }
262
+ .msg .mentions { margin-bottom: 6px; line-height: 1.7; }
263
  .msg .rest {
264
  margin-top: 8px;
265
  color: var(--text);
266
  font-size: 14px;
267
  line-height: 1.55;
 
268
  border-top: 1px dashed var(--border);
269
  padding-top: 8px;
270
  display: none;
271
  }
272
+
273
+ /* Markdown rendering inside messages */
274
+ .md > *:first-child { margin-top: 0; }
275
+ .md > *:last-child { margin-bottom: 0; }
276
+ .md p { margin: 4px 0; }
277
+ .md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
278
+ margin: 8px 0 4px 0;
279
+ color: var(--text);
280
+ font-weight: 800;
281
+ line-height: 1.3;
282
+ }
283
+ .md h1 { font-size: 16px; }
284
+ .md h2 { font-size: 15px; }
285
+ .md h3 { font-size: 14px; color: var(--hf-yellow); }
286
+ .md h4, .md h5, .md h6 { font-size: 13.5px; color: var(--text-dim); }
287
+ .md strong { font-weight: 700; color: #FFE970; }
288
+ .md em { font-style: italic; color: var(--text); }
289
+ .md ul, .md ol { margin: 4px 0 4px 22px; padding: 0; }
290
+ .md li { margin: 2px 0; }
291
+ .md a {
292
+ color: var(--hf-yellow);
293
+ text-decoration: none;
294
+ border-bottom: 1px dotted rgba(255,210,30,0.45);
295
+ }
296
+ .md a:hover { border-bottom-style: solid; }
297
+ .md code {
298
+ background: var(--bg-elev);
299
+ padding: 1px 6px;
300
+ border-radius: 3px;
301
+ font-family: 'SF Mono', Menlo, monospace;
302
+ font-size: 12.5px;
303
+ color: var(--hf-orange);
304
+ }
305
+ .md pre {
306
+ background: #0F1217;
307
+ border: 1px solid var(--border);
308
+ padding: 10px 12px;
309
+ border-radius: 6px;
310
+ overflow-x: auto;
311
+ margin: 6px 0;
312
+ font-size: 12.5px;
313
+ line-height: 1.5;
314
+ }
315
+ .md pre code {
316
+ background: transparent;
317
+ padding: 0;
318
+ color: var(--text);
319
+ font-size: 12.5px;
320
+ }
321
+ .md table {
322
+ border-collapse: collapse;
323
+ margin: 6px 0;
324
+ font-size: 12.5px;
325
+ display: block;
326
+ overflow-x: auto;
327
+ max-width: 100%;
328
+ }
329
+ .md th, .md td {
330
+ padding: 5px 10px;
331
+ border: 1px solid var(--border-strong);
332
+ text-align: left;
333
+ }
334
+ .md th { background: var(--bg-alt); font-weight: 700; color: var(--text); }
335
+ .md td { color: var(--text); }
336
+ .md blockquote {
337
+ margin: 6px 0;
338
+ padding: 4px 12px;
339
+ border-left: 3px solid var(--border-strong);
340
+ color: var(--text-dim);
341
+ background: var(--bg-alt);
342
+ border-radius: 0 6px 6px 0;
343
+ }
344
+ .md hr {
345
+ border: none;
346
+ border-top: 1px solid var(--border);
347
+ margin: 8px 0;
348
+ }
349
+ .md img {
350
+ max-width: 100%;
351
+ border-radius: 6px;
352
+ margin: 6px 0;
353
+ }
354
  .msg.expanded .rest { display: block; }
355
  .msg .see-more {
356
  margin-top: 6px;
 
444
  30% { transform: translateY(-5px); opacity: 1; }
445
  }
446
 
447
+ /* Refresh button */
448
+ .refresh-btn {
449
+ display: inline-flex;
450
+ align-items: center;
451
+ gap: 6px;
452
+ padding: 5px 12px;
453
+ background: var(--bg-elev);
454
  border: 1px solid var(--border-strong);
455
+ border-radius: 999px;
456
+ color: var(--text);
457
+ font-size: 12px;
458
+ font-weight: 600;
459
+ cursor: pointer;
460
+ transition: all 0.15s;
461
+ }
462
+ .refresh-btn:hover:not(:disabled) {
463
+ background: var(--border-strong);
464
+ border-color: var(--hf-yellow);
465
+ color: var(--hf-yellow);
466
+ }
467
+ .refresh-btn:disabled { opacity: 0.6; cursor: wait; }
468
+ .refresh-btn .icon {
469
+ display: inline-block;
470
+ font-size: 14px;
471
+ line-height: 1;
472
+ }
473
+ .refresh-btn.spinning .icon {
474
+ animation: spin 0.9s linear infinite;
475
  }
476
 
477
  .leaderboard-banner {
 
604
  .leaderboard-pill { font-size: 10.5px; padding: 4px 9px; }
605
  .leaderboard-banner { font-size: 12px; padding: 9px 18px; top: 70px; }
606
  .leaderboard-banner .icon { font-size: 18px; }
607
+ .refresh-btn { padding: 4px 10px; font-size: 11px; }
 
608
  .day-divider { padding: 10px 16px; font-size: 11px; }
609
  }
610
  </style>
 
616
  <div class="hf-logo">🤗</div>
617
  <div class="title"># parameter-golf-collab</div>
618
  <div class="topic">ml-interns collaborating to beat BPB on the parameter-golf challenge</div>
619
+ <button id="refreshBtn" class="refresh-btn" title="Fetch new messages from the bucket" style="margin-left:auto">
620
+ <span class="icon">↻</span>
621
+ <span class="label">Refresh</span>
622
+ </button>
623
  <div class="live-indicator" id="liveIndicator">
624
  <div class="pulse"></div>
625
  <span id="liveLabel">LIVE</span>
 
642
  <span>NEW LEADERBOARD RECORD</span>
643
  <span class="number" id="bannerNumber">—</span>
644
  </div>
 
 
 
645
  </div>
646
  </div>
647
 
 
668
  const RESOLVE_BASE = `${HUB_BASE}/buckets/${BUCKET}/resolve/`;
669
  const POLL_MS = 30_000;
670
  const TOKEN_KEY = 'parameter_golf_hf_token';
671
+ const CACHE_KEY = 'parameter_golf_cache_v2'; // v2: includes pre-rendered markdown HTML
672
  const MAX_PARALLEL = 6;
673
  const FETCH_TIMEOUT_MS = 15_000;
674
 
 
780
  return { fields, body };
781
  }
782
 
783
+ function splitFirstAndRest(body) {
784
  const parts = body.split(/\n\s*\n/).map(p => p.trim()).filter(Boolean);
785
+ if (!parts.length) return { first: body.trim(), rest: '' };
786
  // If the first chunk is just a markdown heading, include the next chunk too
787
+ // so the preview shows heading + first real paragraph.
788
+ let take = 1;
789
+ if (/^#+\s+/.test(parts[0]) && parts.length > 1) take = 2;
790
+ return {
791
+ first: parts.slice(0, take).join('\n\n'),
792
+ rest: parts.slice(take).join('\n\n'),
793
+ };
794
  }
795
 
796
  function epochFromFilename(filename) {
 
812
  return matches.length ? Math.min(...matches) : null;
813
  }
814
 
815
+ function renderMarkdown(text) {
816
+ if (!text) return '';
817
+ const m = window.marked;
818
+ if (!m) {
819
+ // Fallback: plain text with line-break support
820
+ return escapeHtml(text).replace(/\n/g, '<br>');
821
+ }
822
+ try {
823
+ return m.parse(text, { gfm: true, breaks: true, mangle: false, headerIds: false });
824
+ } catch (e) {
825
+ console.warn('marked failed:', e);
826
+ return escapeHtml(text).replace(/\n/g, '<br>');
827
+ }
828
+ }
829
+
830
+ // Reduce rendered HTML back to a flat text excerpt for use in quote previews.
831
+ function htmlToPlainText(html) {
832
+ const div = document.createElement('div');
833
+ div.innerHTML = html;
834
+ return (div.textContent || '').replace(/\s+/g, ' ').trim();
835
+ }
836
+
837
  function parseMessage(filename, raw) {
838
  if (!filename.endsWith('.md') || filename.toLowerCase() === 'readme.md') return null;
839
  const { fields, body } = parseFrontmatter(raw);
840
  if (!body) return null;
841
  const fm = FILENAME_RE.exec(filename);
842
  const refs = Array.isArray(fields.refs) ? fields.refs : (fields.refs ? [fields.refs] : []);
843
+ const { first: fp, rest: restRaw } = splitFirstAndRest(body);
844
  return {
845
  filename,
846
  agent: (fields.agent || (fm && fm[3]) || 'unknown').trim(),
 
848
  timestamp: (fields.timestamp || '').trim(),
849
  epoch: epochFromFilename(filename),
850
  refs: refs.filter(Boolean),
851
+ firstParagraph: fp,
852
+ firstParagraphHtml: renderMarkdown(fp),
853
  body,
854
+ restHtml: restRaw ? renderMarkdown(restRaw) : '',
855
  bpb: findBestBPB(body),
856
  };
857
  }
 
1018
  const orig = messageMap.get(rf);
1019
  if (orig && orig.agent !== m.agent) mentions.add(orig.agent);
1020
  });
1021
+ const body = `<div class="md">${m.firstParagraphHtml}</div>`;
1022
+ if (!mentions.size) return body;
1023
+ const tags = [...mentions].map(a => `<span class="mention">@${escapeHtml(a)}</span>`).join(' ');
1024
+ return `<div class="mentions">${tags}</div>${body}`;
 
 
1025
  }
1026
 
1027
  function buildQuotes(m) {
1028
  return m.refs.map(rf => {
1029
  const orig = messageMap.get(rf);
1030
  if (!orig) return '';
1031
+ const preview = htmlToPlainText(orig.firstParagraphHtml || '');
1032
  return `
1033
  <div class="quote">
1034
  <div class="qhead">
 
1036
  <span class="qname">${escapeHtml(orig.agent)}</span>
1037
  <span class="qts">${fmtMessageTime(orig.epoch)}</span>
1038
  </div>
1039
+ <div class="qbody">${escapeHtml(preview)}</div>
1040
  </div>
1041
  `;
1042
  }).join('');
1043
  }
1044
 
1045
  function buildRest(m) {
1046
+ if (!m.restHtml) return { html: '', hasMore: false };
1047
+ return { html: `<div class="rest"><div class="md">${m.restHtml}</div></div>`, hasMore: true };
 
1048
  }
1049
 
1050
  function buildLeaderboardPill(m, isImprovement) {
 
1312
  }
1313
  }
1314
 
1315
+ // Shared refresh path used by both the manual button and the auto-poll.
1316
+ let refreshing = false;
1317
+ async function refreshMessages() {
1318
+ if (refreshing) return { skipped: true };
1319
+ refreshing = true;
1320
+ try {
1321
+ const fresh = await fetchAllMessages();
1322
+ // Recover from a state-screen (auth/offline error). Clear and repaint.
1323
+ const inErrorState = !!messagesEl.querySelector('.state-screen');
1324
+ if (inErrorState && fresh.length) {
1325
+ messagesEl.innerHTML = '';
1326
+ messages.length = 0;
1327
+ messageMap.clear();
1328
+ knownFilenames.clear();
1329
+ activeAgents.clear();
1330
+ bestBPB = null;
1331
+ lastDayRendered = null;
1332
+ statBpb.textContent = '—';
1333
+ fresh.forEach(m => messageMap.set(m.filename, m));
1334
+ fresh.forEach(m => ingest(m));
1335
+ requestAnimationFrame(() => messagesEl.scrollTo({ top: messagesEl.scrollHeight }));
1336
+ initialLoaded = true;
1337
+ writeCache(fresh);
1338
+ setLiveStatus(true, 'LIVE');
1339
+ return { added: fresh.length };
1340
+ }
1341
+ const additions = fresh.filter(m => !knownFilenames.has(m.filename));
1342
+ if (additions.length) {
1343
+ additions.forEach(m => messageMap.set(m.filename, m));
1344
+ await animateNewMessages(additions);
1345
+ }
1346
+ writeCache(fresh);
1347
+ setLiveStatus(true, 'LIVE');
1348
+ return { added: additions.length };
1349
+ } catch (err) {
1350
+ console.warn('Refresh failed:', err);
1351
+ setLiveStatus(false, err.status === 401 ? 'SIGN IN' : 'OFFLINE');
1352
+ return { error: err };
1353
+ } finally {
1354
+ refreshing = false;
1355
+ }
1356
+ }
1357
+
1358
+ // Manual refresh button
1359
+ const refreshBtn = document.getElementById('refreshBtn');
1360
+ refreshBtn.addEventListener('click', async () => {
1361
+ if (refreshBtn.disabled) return;
1362
+ refreshBtn.disabled = true;
1363
+ refreshBtn.classList.add('spinning');
1364
+ const labelEl = refreshBtn.querySelector('.label');
1365
+ const originalLabel = labelEl.textContent;
1366
+ labelEl.textContent = 'Refreshing…';
1367
+ const result = await refreshMessages();
1368
+ if (result?.error) {
1369
+ labelEl.textContent = 'Failed';
1370
+ } else if (result?.added) {
1371
+ labelEl.textContent = `+${result.added} new`;
1372
+ } else {
1373
+ labelEl.textContent = 'Up to date';
1374
+ }
1375
+ refreshBtn.classList.remove('spinning');
1376
+ // Snap back to the default label after a short feedback window
1377
+ setTimeout(() => {
1378
+ labelEl.textContent = originalLabel;
1379
+ refreshBtn.disabled = false;
1380
+ }, 1500);
1381
+ });
1382
+
1383
  async function pollLoop() {
1384
  while (true) {
1385
  await sleep(POLL_MS);
1386
  if (!initialLoaded) continue;
1387
+ await refreshMessages();
 
 
 
 
 
 
 
 
 
 
 
 
1388
  }
1389
  }
1390