SPB Git

Repository Public MIT Selected

research.njk 9.3 KB 134 lines jinja

1{#2  ─────────────────────────────────────────────3   SPB Git — Personal Git Platform4  ─────────────────────────────────────────────5   Author  : Simon-Pierre Boucher6   Contact : contact@spboucher.ai7   File    : src/web/views/research.njk8   Purpose : Research Mode v2.0 — manuscript, LaTeX sources, figures, tables, reproducibility checklist, outputs, versions9   License : MIT © Simon-Pierre Boucher10  ─────────────────────────────────────────────11#}{% extends "layout.njk" %}12{% import "components/ui.njk" as ui %}13{% block content %}14{% include "partials/repo-header.njk" %}1516{% if not overview.research %}17<p class="notice">{{ ui.icon('beaker', 14) }}<span><strong>Preview.</strong> Research Mode is not enabled for this repository; this page is built from detected signals ({{ profile.counts.tex }} LaTeX, {{ profile.counts.pdfs }} PDF, {{ profile.counts.bib }} BibTeX, {{ profile.counts.figures }} figures). Enable it with <code>PATCH /api/v1/repos/{{ overview.name }} {"research": true}</code>.</span></p>18{% endif %}1920{{ ui.statStrip([21  { value: profile.counts.pdfs | num, label: 'manuscripts (PDF)', href: '#manuscripts' },22  { value: profile.counts.tex | num, label: 'LaTeX sources', href: '#sources' },23  { value: profile.counts.figures | num, label: 'figures', href: '#figures' },24  { value: profile.counts.tables | num, label: 'tables', href: '#tables' },25  { value: profile.counts.outputs | num, label: 'experiment outputs', href: '#outputs' },26  { value: profile.versions.length | num, label: 'versions', href: '#versions' }27], 'stat-strip-repo') }}2829<div class="research-grid">30<section class="section" id="manuscripts" aria-labelledby="ms-h">31  {{ ui.sectionHead('ms-h', '01', 'Manuscript', 'The paper', "Rendered by your browser from the repository's own PDF — nothing is compiled on the server.") }}32  {% if profile.primaryPdf %}33  <div class="card pdf-card">34    <div class="card-head"><code>{{ profile.primaryPdf.path }}</code><span class="card-head-note num">{{ profile.primaryPdf.size | bytes }}</span>35      <a class="btn btn-sm card-head-push" href="/raw/{{ overview.name }}/{{ currentRef }}/{{ profile.primaryPdf.path }}" download>{{ ui.icon('download', 14) }} Download</a>36      <a class="btn btn-sm" href="/{{ overview.name }}/blob/{{ currentRef }}/{{ profile.primaryPdf.path }}">Open</a></div>37    <div class="pdf-viewer hide-mobile"><object class="pdf-frame" data="/raw/{{ overview.name }}/{{ currentRef }}/{{ profile.primaryPdf.path }}" type="application/pdf" aria-label="Manuscript PDF"><div class="pdf-fallback"><p>Your browser cannot display PDFs inline.</p></div></object></div>38  </div>39  {% if profile.pdfs.length > 1 %}40  <ul class="ref-list ref-list-gap">41    {% for pdf in profile.pdfs %}{% if not loop.first %}<li><span class="ref-name"><a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ pdf.path }}">{{ pdf.path }}</a></span><span class="ref-size num">{{ pdf.size | bytes }}</span></li>{% endif %}{% endfor %}42  </ul>43  {% endif %}44  {% else %}{{ ui.emptyState('No PDF manuscript found', 'Commit the compiled paper (e.g. paper/main.pdf) to preview it here.', '', 'book') }}{% endif %}45</section>4647<section class="section" id="sources" aria-labelledby="src-h">48  {{ ui.sectionHead('src-h', '02', 'Sources', 'LaTeX sources', 'Main documents are detected by their document class; metadata is parsed, never compiled.') }}49  {% if profile.manuscripts.length %}50  <div class="doc-grid">51    {% for m in profile.manuscripts %}52    <article class="card card-pad doc-card">53      <h3 class="doc-card-title"><a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ m.path }}">{{ m.title or m.path }}</a></h3>54      {% if m.authors %}<p class="doc-card-authors">{{ m.authors }}</p>{% endif %}55      <p class="doc-card-path"><code>{{ m.path }}</code>{% if m.documentclass %} · {{ m.documentclass }}{% endif %}</p>56      <div class="chip-row">57        {{ ui.chip(m.figures + ' figure env.', 'neutral') }}{{ ui.chip(m.tables + ' table env.', 'neutral') }}{{ ui.chip(m.citations + ' citations', 'neutral') }}58        {% if m.bibliography %}{{ ui.chip('bib: ' + m.bibliography, 'info') }}{% endif %}59      </div>60      {% if m.inputs.length %}<p class="doc-card-path">includes: {{ m.inputs | join(', ') }}</p>{% endif %}61    </article>62    {% endfor %}63  </div>64  {% endif %}65  {% if profile.tex.length or profile.bib.length %}66  <ul class="ref-list ref-list-gap">67    {% for t in profile.tex %}<li><span class="ref-name">{{ ui.icon('file-text', 14) }}<a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ t.path }}">{{ t.path }}</a></span><span class="ref-size num">{{ t.size | bytes }}</span></li>{% endfor %}68    {% for b in profile.bib %}<li><span class="ref-name">{{ ui.icon('book', 14) }}<a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ b.path }}">{{ b.path }}</a></span><span class="ref-size num">{{ b.size | bytes }}</span></li>{% endfor %}69  </ul>70  {% else %}{{ ui.emptyState('No LaTeX or BibTeX sources', '', '', 'code') }}{% endif %}71</section>7273<section class="section" id="figures" aria-labelledby="fig-h">74  {{ ui.sectionHead('fig-h', '03', 'Figures', 'Figures', '', profile.counts.figures) }}75  {% if profile.figures.length %}76  <div class="figure-grid">77    {% for f in profile.figures %}78    <a class="figure-card" href="/{{ overview.name }}/blob/{{ currentRef }}/{{ f.path }}" title="{{ f.path }}">79      {% set isPdf = (f.path | lower).endsWith('.pdf') %}80      {% if isPdf %}<div class="figure-pdf">{{ ui.icon('file-pdf', 22) }}<span>PDF figure</span></div>{% else %}<img src="/raw/{{ overview.name }}/{{ currentRef }}/{{ f.path }}" alt="{{ f.path }}" loading="lazy">{% endif %}81      <span class="figure-cap">{{ f.path.split('/').pop() }}</span>82    </a>83    {% endfor %}84  </div>85  {% else %}{{ ui.emptyState('No figures directory', 'Images under figures/, fig/, plots/ or img/ appear here.', '', 'image') }}{% endif %}86</section>8788<section class="section" id="tables" aria-labelledby="tab-h">89  {{ ui.sectionHead('tab-h', '04', 'Tables', 'Tables', '', profile.counts.tables) }}90  {% if profile.tables.length %}91  <ul class="ref-list">{% for t in profile.tables %}<li><span class="ref-name"><a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ t.path }}">{{ t.path }}</a></span><span class="ref-size num">{{ t.size | bytes }}</span></li>{% endfor %}</ul>92  {% else %}<p class="result-none">No tables/ directory. Table environments inside LaTeX sources are counted above.</p>{% endif %}93</section>9495<section class="section" id="reproducibility" aria-labelledby="rep-h">96  {{ ui.sectionHead('rep-h', '05', 'Reproducibility', 'Reproducibility checklist', 'Static checks on the file tree at ' + currentRef + '. Nothing is executed.') }}97  <ul class="ref-list checklist">98    {% for row in profile.checklist %}99    <li>100      <span class="ref-name"><span class="check-mark {{ 'is-ok' if row.ok else 'is-no' }}">{{ ui.icon('check' if row.ok else 'close', 13) }}<span class="sr-only">{{ 'yes' if row.ok else 'no' }}: </span></span>{{ row.label }}</span>101      <span class="ref-sub">{% if row.path %}<a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ row.path }}">{{ row.path }}</a>{% elif row.note %}{{ row.note }}{% endif %}</span>102    </li>103    {% endfor %}104  </ul>105</section>106107<section class="section" id="outputs" aria-labelledby="out-h">108  {{ ui.sectionHead('out-h', '06', 'Outputs', 'Experiment outputs', '', profile.counts.outputs) }}109  {% if profile.outputs.length %}110  <ul class="ref-list">{% for o in profile.outputs %}<li><span class="ref-name"><a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ o.path }}">{{ o.path }}</a></span><span class="ref-size num">{{ o.size | bytes }}</span></li>{% endfor %}</ul>111  {% else %}<p class="result-none">No results/, outputs/ or experiments/ directory with data files.</p>{% endif %}112  {% if profile.notebooks.length %}113  <h3 class="label-head label-head-gap">Notebooks</h3>114  <ul class="ref-list">{% for n in profile.notebooks %}<li><span class="ref-name"><a href="/{{ overview.name }}/blob/{{ currentRef }}/{{ n.path }}">{{ n.path }}</a></span><span class="ref-size num">{{ n.size | bytes }}</span></li>{% endfor %}</ul>115  {% endif %}116</section>117118<section class="section" id="versions" aria-labelledby="ver-h">119  {{ ui.sectionHead('ver-h', '07', 'Versions', 'Manuscript versions', 'Tags link the paper to the exact code and data snapshot.') }}120  {% if profile.versions.length %}121  <ul class="ref-list">122    {% for v in profile.versions %}123    <li>124      <span class="ref-name"><a href="/{{ overview.name }}/tree/{{ v.name }}">{{ v.name }}</a>{% if loop.first %}{{ ui.chip('latest', 'accent') }}{% endif %}</span>125      <span class="ref-sub">{{ v.subject }} · <time datetime="{{ v.date }}">{{ v.date | reltime }}</time></span>126      <span class="btn-row">{% if profile.primaryPdf %}<a class="btn btn-sm" href="/raw/{{ overview.name }}/{{ v.name }}/{{ profile.primaryPdf.path }}">PDF @ {{ v.name }}</a>{% endif %}<a class="btn btn-sm" href="/archive/{{ overview.name }}/{{ v.name }}.zip">{{ ui.icon('download', 14) }} zip</a></span>127    </li>128    {% endfor %}129  </ul>130  {% else %}{{ ui.emptyState('No tagged versions yet', 'Tag each submitted version: git tag v1-submitted && git push --tags', '', 'tag') }}{% endif %}131</section>132</div>133{% endblock %}134
Files main
Menu

Theme