lvwerra HF Staff commited on
Commit
bb10dc1
·
verified ·
1 Parent(s): f013877

feature: book view (#/book) — whole wiki as one printable document (title page, contents, parts, articles, sources appendix); Save-as-PDF + pre-rendered downloadable PDF; top-bar + landing entry points

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. app.js +98 -1
  3. index.html +1 -0
  4. rl-llm-wiki-book.pdf +3 -0
  5. styles.css +91 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ rl-llm-wiki-book.pdf filter=lfs diff=lfs merge=lfs -text
app.js CHANGED
@@ -384,6 +384,7 @@ function parseHash() {
384
  if (kind === 'topic') return { view: 'topic', path: arg, section };
385
  if (kind === 'source') return { view: 'source', id: arg, section };
386
  if (kind === 'sources') return { view: 'sources' };
 
387
  return { view: 'home' };
388
  }
389
  function go(hash) { location.hash = hash; }
@@ -393,6 +394,7 @@ let _currentKey = null;
393
  async function route() {
394
  const r = parseHash();
395
  closeNav();
 
396
  // Same doc, only the #section changed → just scroll; don't re-fetch/re-render.
397
  if ((r.view === 'topic' || r.view === 'source') && docKey(r) === _currentKey) {
398
  scrollToSection(r.section);
@@ -404,6 +406,7 @@ async function route() {
404
  if (r.view === 'topic') await renderTopic(r.path);
405
  else if (r.view === 'source') await renderSource(r.id);
406
  else if (r.view === 'sources') renderSourcesBrowser();
 
407
  else renderHome();
408
  _currentKey = docKey(r);
409
  if (r.section) scrollToSection(r.section);
@@ -479,7 +482,8 @@ function renderHome() {
479
  <a href="#/sources"><b>${nS}</b> sources</a>
480
  ${state.contributors ? `<span class="sep">·</span><span><b>${state.contributors}</b> agent contributors</span>` : ''}
481
  <span class="sep">·</span><a href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">reviewed in the open ↗</a>
482
- </div>`;
 
483
 
484
  // Start here — curated cornerstone articles that have been written.
485
  const featured = [];
@@ -586,6 +590,99 @@ function toggleTreeCat(cat) {
586
  if (head) { head.classList.toggle('collapsed'); head.nextElementSibling?.classList.toggle('collapsed'); }
587
  }
588
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  /* ── TOPIC ARTICLE ─────────────────────────────────────── */
590
  async function renderTopic(path) {
591
  loadingView('loading article…');
 
384
  if (kind === 'topic') return { view: 'topic', path: arg, section };
385
  if (kind === 'source') return { view: 'source', id: arg, section };
386
  if (kind === 'sources') return { view: 'sources' };
387
+ if (kind === 'book') return { view: 'book' };
388
  return { view: 'home' };
389
  }
390
  function go(hash) { location.hash = hash; }
 
394
  async function route() {
395
  const r = parseHash();
396
  closeNav();
397
+ document.body.classList.toggle('book-mode', r.view === 'book'); // full-width, chrome hidden
398
  // Same doc, only the #section changed → just scroll; don't re-fetch/re-render.
399
  if ((r.view === 'topic' || r.view === 'source') && docKey(r) === _currentKey) {
400
  scrollToSection(r.section);
 
406
  if (r.view === 'topic') await renderTopic(r.path);
407
  else if (r.view === 'source') await renderSource(r.id);
408
  else if (r.view === 'sources') renderSourcesBrowser();
409
+ else if (r.view === 'book') await renderBook();
410
  else renderHome();
411
  _currentKey = docKey(r);
412
  if (r.section) scrollToSection(r.section);
 
482
  <a href="#/sources"><b>${nS}</b> sources</a>
483
  ${state.contributors ? `<span class="sep">·</span><span><b>${state.contributors}</b> agent contributors</span>` : ''}
484
  <span class="sep">·</span><a href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">reviewed in the open ↗</a>
485
+ </div>
486
+ <a class="book-cta" href="#/book">📖 &nbsp;Read the whole wiki as one book — print it or save the full PDF <span class="arr">→</span></a>`;
487
 
488
  // Start here — curated cornerstone articles that have been written.
489
  const featured = [];
 
590
  if (head) { head.classList.toggle('collapsed'); head.nextElementSibling?.classList.toggle('collapsed'); }
591
  }
592
 
593
+ /* ── BOOK VIEW (#/book) — the whole wiki as one printable document ─────────
594
+ Browser "Save as PDF" (or the pre-rendered download) turns this into a book:
595
+ title page → contents → parts (categories) → articles → sources appendix. */
596
+ const BOOK_PDF_URL = 'rl-llm-wiki-book.pdf'; // pre-rendered snapshot hosted alongside the app
597
+ async function renderBook() {
598
+ layoutAside(false);
599
+ setView('<div class="loading"><div class="spinner"></div><span id="bkProg">assembling the book…</span></div>');
600
+ const parts = state.tax.filter(c => c.nodes.some(n => n.page));
601
+ // fetch every article body (cached; instant if the citation map already ran)
602
+ const contentByPath = new Map();
603
+ let done = 0; const tot = state.pages.length;
604
+ await Promise.all(state.pages.map(p => getPageContent(p.path)
605
+ .then(c => { if (c) contentByPath.set(p.path, c); }).catch(() => {})
606
+ .finally(() => { done++; const e = document.getElementById('bkProg'); if (e) e.textContent = `assembling the book… ${done}/${tot} articles`; })));
607
+
608
+ const bibIds = new Set();
609
+ let totalWords = 0, totalArticles = 0;
610
+ const stripH1 = (b) => b.replace(/^\s*#\s+.*(\r?\n)+/, '');
611
+ const gatherBib = (b) => { const re = /\[source:([^\]\s]+)\]/g; let m; while ((m = re.exec(b))) bibIds.add(m[1]); };
612
+
613
+ // Pre-scan for totals
614
+ parts.forEach(c => c.nodes.forEach(n => { if (n.page && contentByPath.has(n.page.path)) { const b = parseFrontmatter(contentByPath.get(n.page.path)).body; totalWords += countWords(b); totalArticles++; } }));
615
+
616
+ const dateStr = (() => { try { return new Date().toISOString().slice(0, 10); } catch (e) { return ''; } })();
617
+
618
+ let toc = '', bodyHtml = '';
619
+ let partNo = 0;
620
+ parts.forEach(c => {
621
+ partNo++;
622
+ const arts = c.nodes.filter(n => n.page && contentByPath.has(n.page.path));
623
+ if (!arts.length) return;
624
+ const partId = `bk-part-${slugify(c.cat)}`;
625
+ toc += `<div class="bk-toc-part"><a data-bk="${partId}"><span class="bk-toc-num">${partNo}</span> ${esc(catDisplay(c.cat))}</a></div>`;
626
+ bodyHtml += `<section class="book-part" id="${partId}">
627
+ <div class="bk-part-kicker">Part ${partNo}</div>
628
+ <h1 class="bk-part-title">${esc(catDisplay(c.cat))}</h1>
629
+ ${c.description ? `<p class="bk-part-desc">${esc(c.description)}</p>` : ''}
630
+ </section>`;
631
+ arts.forEach(n => {
632
+ const { fm, body } = parseFrontmatter(contentByPath.get(n.page.path));
633
+ gatherBib(body);
634
+ const title = fm.title || n.page.title || n.slug;
635
+ const artId = `bk-${slugify(n.page.path)}`;
636
+ toc += `<div class="bk-toc-art"><a data-bk="${artId}">${esc(title)}</a></div>`;
637
+ bodyHtml += `<article class="book-article" id="${artId}">
638
+ <div class="bk-art-cat">${esc(catDisplay(c.cat))}</div>
639
+ <h1 class="bk-art-title">${esc(title)}</h1>
640
+ <div class="prose book-prose">${renderBody(stripH1(body))}</div>
641
+ </article>`;
642
+ });
643
+ });
644
+
645
+ // Sources appendix (bibliography)
646
+ const bib = [...bibIds].sort().map(id => {
647
+ const s = state.srcById.get(id); const url = (s && s.original_url) || externalUrl(id);
648
+ return `<div class="bk-ref"><span class="bk-ref-id">${esc(id)}</span><span class="bk-ref-title">${esc(s ? (s.title || '') : '')}</span>${url ? `<span class="bk-ref-url">${esc(prettyUrl(url))}</span>` : ''}</div>`;
649
+ }).join('');
650
+
651
+ const html = `
652
+ <div class="book-toolbar" role="toolbar">
653
+ <a class="btn" href="#/">← back to the wiki</a>
654
+ <span class="spacer"></span>
655
+ <span class="bk-hint">Tip: “Save as PDF” in the print dialog for the full book.</span>
656
+ <a class="btn" href="${esc(BOOK_PDF_URL)}" target="_blank" rel="noopener" id="bkDownload">download PDF ↓</a>
657
+ <button class="btn btn-sources" id="bkPrint">Save as PDF / Print</button>
658
+ </div>
659
+ <div class="book" id="book">
660
+ <div class="book-titlepage">
661
+ <div class="bk-tp-kicker">A living reference · reinforcement learning for language models</div>
662
+ <h1 class="bk-tp-title">The RL<span class="dot">·</span>for<span class="dot">·</span>LLMs Wiki</h1>
663
+ <div class="bk-tp-sub">From RLHF and preference optimization to reward modeling, verifiable rewards, reasoning, training systems, and the failure modes — synthesized from the primary literature, every claim cited.</div>
664
+ <div class="bk-tp-meta">
665
+ <div><b>${totalArticles}</b> articles · <b>${fmtNum(totalWords)}</b> words · <b>${bibIds.size}</b> sources</div>
666
+ <div>Built and reviewed in the open by a collaboration of AI agents${dateStr ? ` · compiled ${dateStr}` : ''}</div>
667
+ <div class="bk-tp-src">huggingface.co/datasets/${DATASET}</div>
668
+ </div>
669
+ </div>
670
+ <section class="book-toc" id="bk-contents">
671
+ <h1 class="bk-h">Contents</h1>
672
+ ${toc}
673
+ </section>
674
+ ${bodyHtml}
675
+ ${bib ? `<section class="book-appendix" id="bk-sources"><h1 class="bk-h">Sources</h1>
676
+ <p class="bk-appendix-lead">The ${bibIds.size} sources cited across this book. Each resolves in the wiki to a faithful summary and to the original.</p>
677
+ <div class="bk-refs">${bib}</div></section>` : ''}
678
+ <div class="book-colophon">The RL·for·LLMs Wiki — a public, continuously reviewed dataset. This book is a snapshot; the living version is at huggingface.co/spaces/rl-llm-wiki/rl-wiki.</div>
679
+ </div>`;
680
+ setView(html);
681
+ $('#bkPrint')?.addEventListener('click', () => window.print());
682
+ $$('#view [data-bk]').forEach(a => a.addEventListener('click', () => document.getElementById(a.getAttribute('data-bk'))?.scrollIntoView({ behavior: 'smooth' })));
683
+ }
684
+ const slugify = (s) => String(s || '').toLowerCase().replace(/[^\w]+/g, '-').replace(/^-|-$/g, '');
685
+
686
  /* ── TOPIC ARTICLE ─────────────────────────────────────── */
687
  async function renderTopic(path) {
688
  loadingView('loading article…');
index.html CHANGED
@@ -50,6 +50,7 @@
50
  <div class="spacer"></div>
51
  <div class="toolbar">
52
  <a class="btn btn-sources" id="lnkSources" href="#/sources">sources</a>
 
53
  <a class="btn" id="lnkDataset" href="https://huggingface.co/datasets/rl-llm-wiki/knowledge-base" target="_blank" rel="noopener">dataset ↗</a>
54
  <a class="btn" id="lnkDash" href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">dashboard ↗</a>
55
  <button class="btn icon-btn" id="themeToggle" title="Toggle theme" aria-label="Toggle theme">◐</button>
 
50
  <div class="spacer"></div>
51
  <div class="toolbar">
52
  <a class="btn btn-sources" id="lnkSources" href="#/sources">sources</a>
53
+ <a class="btn" id="lnkBook" href="#/book" title="Read the whole wiki as a book / export PDF">book ↓</a>
54
  <a class="btn" id="lnkDataset" href="https://huggingface.co/datasets/rl-llm-wiki/knowledge-base" target="_blank" rel="noopener">dataset ↗</a>
55
  <a class="btn" id="lnkDash" href="https://rl-llm-wiki-rl-dashboard.hf.space" target="_blank" rel="noopener">dashboard ↗</a>
56
  <button class="btn icon-btn" id="themeToggle" title="Toggle theme" aria-label="Toggle theme">◐</button>
rl-llm-wiki-book.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b7e68748637a27a58e3299855cdfe0481c91a2c3f769a372ffb4a35fb0b1141f
3
+ size 5074238
styles.css CHANGED
@@ -700,3 +700,94 @@ a.tree-leaf:hover { background: var(--bg-soft); color: var(--accent-deep); }
700
  .citedby-row .cb-title { font-size: 12.5px; color: var(--ink-2); line-height: 1.35; border-bottom: none; }
701
  .citedby-row:hover .cb-title { color: var(--accent-deep); }
702
  .citedby-row .cb-cat { font-family: var(--mono); font-size: 9px; color: var(--muted-4); text-transform: uppercase; letter-spacing: 0.3px; white-space: nowrap; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
  .citedby-row .cb-title { font-size: 12.5px; color: var(--ink-2); line-height: 1.35; border-bottom: none; }
701
  .citedby-row:hover .cb-title { color: var(--accent-deep); }
702
  .citedby-row .cb-cat { font-family: var(--mono); font-size: 9px; color: var(--muted-4); text-transform: uppercase; letter-spacing: 0.3px; white-space: nowrap; }
703
+
704
+ /* ============================================================
705
+ BOOK VIEW (#/book) — the whole wiki as one printable document
706
+ ============================================================ */
707
+ .book-cta {
708
+ display: flex; align-items: center; gap: 4px; margin: 10px 0 2px; padding: 12px 16px;
709
+ border: 1px solid var(--border); border-radius: 10px; background: var(--bg-card);
710
+ font-size: 13.5px; color: var(--ink-2); text-decoration: none; transition: all .15s; box-shadow: var(--shadow);
711
+ }
712
+ .book-cta:hover { border-color: var(--accent); color: var(--accent-deep); transform: translateY(-1px); }
713
+ .book-cta .arr { margin-left: auto; color: var(--accent); font-weight: 600; }
714
+
715
+ body.book-mode .sidebar, body.book-mode .aside { display: none !important; }
716
+ body.book-mode .layout { grid-template-columns: 1fr; }
717
+ body.book-mode .main { padding: 0; }
718
+
719
+ .book-toolbar {
720
+ position: sticky; top: 53px; z-index: 40; display: flex; align-items: center; gap: 8px;
721
+ padding: 10px 22px; background: color-mix(in srgb, var(--bg) 90%, transparent);
722
+ backdrop-filter: blur(8px); border-bottom: 1px solid var(--border);
723
+ }
724
+ .book-toolbar .spacer { flex: 1; }
725
+ .book-toolbar .bk-hint { font-size: 11.5px; color: var(--muted-3); }
726
+ @media (max-width: 640px) { .book-toolbar .bk-hint { display: none; } }
727
+
728
+ .book { max-width: 760px; margin: 0 auto; padding: 40px 40px 90px; color: var(--ink-2); }
729
+ .book .dot { color: var(--accent); }
730
+
731
+ /* title page */
732
+ .book-titlepage { text-align: center; padding: 56px 0 72px; border-bottom: 1px solid var(--border); margin-bottom: 44px; }
733
+ .bk-tp-kicker { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 1.6px; color: var(--accent); margin-bottom: 22px; }
734
+ .bk-tp-title { font-family: var(--mono); font-weight: 600; font-size: 42px; line-height: 1.08; color: var(--ink); margin-bottom: 22px; letter-spacing: -0.5px; }
735
+ .bk-tp-sub { font-size: 15px; line-height: 1.62; color: var(--muted); max-width: 540px; margin: 0 auto 34px; font-weight: 300; }
736
+ .bk-tp-meta { font-family: var(--mono); font-size: 12px; color: var(--muted-2); line-height: 2; }
737
+ .bk-tp-meta b { color: var(--accent); }
738
+ .bk-tp-src { color: var(--muted-4); font-size: 11px; }
739
+
740
+ /* contents */
741
+ .book-toc { margin-bottom: 20px; }
742
+ .bk-h { font-family: var(--mono); font-size: 24px; font-weight: 600; color: var(--ink); margin-bottom: 18px; }
743
+ .bk-toc-part { margin: 13px 0 3px; }
744
+ .bk-toc-part a { font-family: var(--mono); font-weight: 600; font-size: 14px; color: var(--ink); cursor: pointer; text-decoration: none; }
745
+ .bk-toc-num { color: var(--accent); margin-right: 8px; }
746
+ .bk-toc-art { padding-left: 28px; margin: 2px 0; }
747
+ .bk-toc-art a { font-size: 13px; color: var(--muted); cursor: pointer; text-decoration: none; }
748
+ .bk-toc-art a:hover, .bk-toc-part a:hover { color: var(--accent); }
749
+
750
+ /* parts */
751
+ .book-part { padding: 30px 0 10px; border-top: 2px solid var(--ink); margin-top: 44px; }
752
+ .bk-part-kicker { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 2px; color: var(--accent); }
753
+ .bk-part-title { font-family: var(--mono); font-size: 30px; font-weight: 600; color: var(--ink); margin: 8px 0 12px; letter-spacing: -0.3px; }
754
+ .bk-part-desc { font-size: 14px; color: var(--muted); line-height: 1.6; max-width: 560px; }
755
+
756
+ /* articles */
757
+ .book-article { padding: 30px 0; border-top: 1px solid var(--border); }
758
+ .bk-art-cat { font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: var(--muted-3); }
759
+ .bk-art-title { font-family: var(--mono); font-size: 22px; font-weight: 600; color: var(--ink); margin: 4px 0 14px; letter-spacing: -0.2px; }
760
+ .book-prose h1 { display: block; font-size: 20px; }
761
+
762
+ /* appendix + colophon */
763
+ .book-appendix { padding: 30px 0; border-top: 2px solid var(--ink); margin-top: 44px; }
764
+ .bk-appendix-lead { font-size: 13px; color: var(--muted); margin-bottom: 16px; max-width: 560px; }
765
+ .bk-refs { display: block; }
766
+ .bk-ref { display: grid; grid-template-columns: 150px 1fr; gap: 2px 12px; padding: 7px 0; border-bottom: 1px solid var(--border-soft); font-size: 12px; }
767
+ .bk-ref-id { font-family: var(--mono); color: var(--accent); }
768
+ .bk-ref-title { color: var(--ink-3); }
769
+ .bk-ref-url { grid-column: 2; font-family: var(--mono); font-size: 10.5px; color: var(--muted-4); }
770
+ .book-colophon { margin-top: 44px; padding-top: 20px; border-top: 1px solid var(--border); font-size: 11px; color: var(--muted-3); text-align: center; line-height: 1.6; }
771
+
772
+ /* ── Print (browser "Save as PDF") ─────────────────────── */
773
+ @media print {
774
+ .topbar, .sidebar, .aside, .nav-scrim, .book-toolbar, .palette-scrim, #hovercard, #toast, .foot, .menu-toggle { display: none !important; }
775
+ html, body { background: #fff; color: #000; }
776
+ .layout, .main, body.book-mode .main { display: block; padding: 0; margin: 0; }
777
+ .book { max-width: none; margin: 0; padding: 0; color: #111; font-size: 10.5pt; }
778
+ @page { size: A4; margin: 18mm 16mm; }
779
+ .book-titlepage { break-after: page; border: none; padding: 30mm 0; margin: 0; }
780
+ .book-toc { break-after: page; }
781
+ .book-part { break-before: page; border-top: none; margin: 0; padding: 0; }
782
+ .book-appendix { break-before: page; border-top: none; margin: 0; }
783
+ .book-article { break-before: page; border-top: none; padding: 0; margin: 0; } /* each topic starts a fresh page */
784
+ .bk-art-title, .bk-part-title, .prose h2, .prose h3, .bk-art-cat { break-after: avoid; }
785
+ .prose table, .prose pre, .prose blockquote, .katex-display, .bk-ref, .prose li { break-inside: avoid; }
786
+ .book-prose { line-height: 1.5; }
787
+ .book-colophon { break-before: avoid; }
788
+ .prose a, .book a { color: #111; text-decoration: none; }
789
+ .cite { background: none !important; border: none !important; color: #444 !important; font-size: 0.78em; padding: 0 1px; }
790
+ .cite .cite-mark { display: none; }
791
+ a.secref { color: #111; border: none; }
792
+ .prose abbr.ac { border: none; }
793
+ }