Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 97 additions & 23 deletions _includes/pubs.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,101 @@
<p>This list is not guaranteed to be up to date, but the lab's complete publications can be found <a href="https://scholar.google.com/citations?user=4whjDosAAAAJ&hl=en">here.</a> </p>
{%- comment -%}
Publications list rendered from _data/publications.yaml (auto-populated
weekly by scholar_scraper.py from Google Scholar).

Features:
- Each title links to the paper's URL when one is present in the data.
- Lab member authors are bold. The set of "lab last names" is built
from _data/people.yml (PI + all roles, current and former), so
adding/removing a member there propagates here automatically.
- Auto-derived type badge (journal / preprint / conference) based
on the venue string — no extra data entry required.
- Year-filter chips at the top let visitors narrow to a single year.
{%- endcomment -%}

{%- assign all_lab_people = "" | split: "" -%}
{%- if site.data.people.pi -%}{%- assign all_lab_people = all_lab_people | push: site.data.people.pi.name -%}{%- endif -%}
{%- for p in site.data.people.postdocs -%}{%- assign all_lab_people = all_lab_people | push: p.name -%}{%- endfor -%}
{%- for p in site.data.people.graduate_students -%}{%- assign all_lab_people = all_lab_people | push: p.name -%}{%- endfor -%}
{%- for p in site.data.people.research_associates -%}{%- assign all_lab_people = all_lab_people | push: p.name -%}{%- endfor -%}
{%- for p in site.data.people.undergraduates -%}{%- assign all_lab_people = all_lab_people | push: p.name -%}{%- endfor -%}

{%- comment -%}
Build "first-initial|last-name" keys (e.g. "J|Pearson") so matching is
tighter than last-name-only — keeps Amanda Li from being bolded just
because Thomas Li is a former undergrad.
{%- endcomment -%}
{%- assign lab_keys = "" | split: "" -%}
{%- for full_name in all_lab_people -%}
{%- assign parts = full_name | split: " " -%}
{%- assign first = parts | first -%}
{%- assign last = parts | last -%}
{%- assign initial = first | slice: 0, 1 -%}
{%- assign key = initial | append: "|" | append: last -%}
{%- assign lab_keys = lab_keys | push: key -%}
{%- endfor -%}

<p>This list is not guaranteed to be up to date, but the lab's complete publications can be found <a href="https://scholar.google.com/citations?user=4whjDosAAAAJ&hl=en">here</a>.</p>

{% assign sorted_pubs = site.data.publications | group_by_exp:'item', 'item.issued.first.year' | sort: 'name' | reverse %}

<div class="year-filter" role="toolbar" aria-label="Filter publications by year">
<button type="button" class="year-chip active" data-year="all">All</button>
{% for year in sorted_pubs %}
<button type="button" class="year-chip" data-year="{{ year.name }}">{{ year.name }}</button>
{% endfor %}
</div>

<div class="pubs-list">
{% for year in sorted_pubs %}
<h3>{{ year.name }}</h3>
<ol>
{% for pub in year.items %}
<li>
<b>{{ pub.title }}</b>
<br>
{% for aa in pub.author %}
{% if forloop.last == true %}and {% endif %} {{ aa.given }} {{ aa.family }}{% if forloop.last == false %}, {% endif %}
{% endfor %}
({{ year.name }}).
<br>
<em>{{ pub.container-title }}</em>
{% if pub.volume != blank %}
{{ pub.volume}}{%if pub.issue and pub.issue != "" %}({{ pub.issue }}){% endif %}
{% endif %}
<br>
{% if pub.note != blank %}
<b>{{ pub.note}}</b>
{% endif %}
</li>
{% endfor %}
</ol>
<section class="pubs-year" data-year="{{ year.name }}">
<h3>{{ year.name }}</h3>
<ol>
{% for pub in year.items %}
{%- assign venue_lc = pub.container-title | downcase -%}
{%- if venue_lc contains "biorxiv" or venue_lc contains "arxiv" or venue_lc contains "medrxiv" or venue_lc contains "chemrxiv" or venue_lc contains "psyarxiv" or venue_lc contains "osf preprints" -%}
{%- assign pub_type = "preprint" -%}
{%- elsif venue_lc contains "proceedings" or venue_lc contains "advances in neural" or venue_lc contains "neurips" or venue_lc contains "cosyne" or venue_lc contains "conference" -%}
{%- assign pub_type = "conference" -%}
{%- else -%}
{%- assign pub_type = "journal" -%}
{%- endif -%}
<li>
<span class="pub-badge pub-badge-{{ pub_type }}">{{ pub_type }}</span>
{% if pub.URL and pub.URL != "" %}<a href="{{ pub.URL }}"><b>{{ pub.title }}</b></a>{% else %}<b>{{ pub.title }}</b>{% endif %}
<br>
{% for aa in pub.author -%}
{%- if forloop.last and forloop.first == false %}and {% endif -%}
{%- assign author_initial = aa.given | slice: 0, 1 -%}
{%- assign author_key = author_initial | append: "|" | append: aa.family -%}
{%- if lab_keys contains author_key -%}<b>{{ aa.given }} {{ aa.family }}</b>{%- else -%}{{ aa.given }} {{ aa.family }}{%- endif -%}
{%- if forloop.last == false %}, {% endif -%}
{%- endfor %}
({{ year.name }}).
<br>
<em>{{ pub.container-title }}</em>
{%- if pub.volume != blank %} {{ pub.volume }}{% if pub.issue and pub.issue != "" %}({{ pub.issue }}){% endif %}{% endif %}
{%- if pub.note != blank %}
<br><b>{{ pub.note }}</b>
{% endif %}
</li>
{% endfor %}
</ol>
</section>
{% endfor %}
</div>

<script>
(function () {
var chips = document.querySelectorAll('.year-filter .year-chip');
var sections = document.querySelectorAll('.pubs-year');
chips.forEach(function (btn) {
btn.addEventListener('click', function () {
var target = btn.dataset.year;
chips.forEach(function (b) { b.classList.toggle('active', b === btn); });
sections.forEach(function (s) {
s.style.display = (target === 'all' || s.dataset.year === target) ? '' : 'none';
});
});
});
})();
</script>
47 changes: 47 additions & 0 deletions css/publications.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,53 @@
max-width: 900px;
}

/* Year filter chips */
.year-filter {
margin: 0 0 1.5em 0;
display: flex;
flex-wrap: wrap;
gap: 0.4em;
}

.year-filter .year-chip {
padding: 0.25em 0.7em;
border: 1px solid #ccc;
background: #fff;
border-radius: 12px;
font-size: 0.9em;
cursor: pointer;
color: #444;
line-height: 1.4;
}

.year-filter .year-chip:hover {
background: #f5f5f5;
}

.year-filter .year-chip.active {
background: #337ab7;
color: #fff;
border-color: #337ab7;
}

/* Per-paper venue-type badge (auto-derived from container-title) */
.pub-badge {
display: inline-block;
padding: 0.1em 0.5em;
margin-right: 0.5em;
border-radius: 3px;
font-size: 0.7em;
font-weight: 600;
text-transform: uppercase;
vertical-align: middle;
letter-spacing: 0.04em;
}

.pub-badge-journal { background: #e8f0fe; color: #1a73e8; }
.pub-badge-preprint { background: #fef3c7; color: #92400e; }
.pub-badge-conference { background: #f0e6ff; color: #6b21a8; }

/* Preserve the existing tighter list spacing inside year sections. */
h3 li:not(:last-child) {
margin-bottom: 0.75em;
}
4 changes: 3 additions & 1 deletion lychee.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ user_agent = "Mozilla/5.0 (compatible; lychee-link-checker; +https://lychee.cli.
# Status codes to treat as success. We're trying to catch dead links
# (404) and broken servers, not paywalls or anti-bot measures.
# - 200/204/206: success
# - 202: escholarship.org and some other deferred-fetch repos return
# this on a request that will succeed if you retry or wait
# - 401: paywalled but exists
# - 403/405/429: bot-protection / rate-limit / method-not-allowed
# - 999: LinkedIn's bot-detection response
accept = [200, 204, 206, 401, 403, 405, 429, 999]
accept = [200, 202, 204, 206, 401, 403, 405, 429, 999]

# Hosts to skip:
# - pearsonlab.github.io: this is the site we're building. The sitemap
Expand Down
Loading