clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

main
forge / site / index.njk
index.njk 4.6K
1---2permalink: /index.html3---4{#- The home page: the motd an admin sets (bin/forge motd), then who is5 here (profiles), the shelves across them (topics) and what happened6 lately on public repos (recent, lib/recent.ts). Every build redoes it,7 so it is as fresh as the last push; ui.ts keeps its times relative, and8 live.ts swaps it in when a build finishes. Each owner's page has their9 repos on shelves. -#}10{% extends "base.njk" %}11{% import "macros.njk" as m with context %}12{% import "ui.njk" as ui %}13{% block content %}14{% css %}15main.wrap > p.motd { margin: calc(-1 * var(--space-5)) 0 0; padding: var(--space-3) 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-sm); color: var(--muted); }16/* Two columns: profiles and topics, then what happened; on phones one,17 recent after profiles. */18.home { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); grid-template-areas: "profiles recent" "topics recent"; grid-template-rows: auto 1fr; gap: var(--space-4); align-items: start; }19.home > .panel { margin: 0; }20.home .profiles { grid-area: profiles; }21.home .topics { grid-area: topics; }22.home .recent { grid-area: recent; }23@media (max-width: 720px) {24 .home { grid-template-columns: minmax(0, 1fr); grid-template-areas: "profiles" "recent" "topics"; grid-template-rows: none; }25}26.home .panel-head .end a { font-weight: var(--fw-medium); }27/* A profile: their avatar, the name, how many repos and when. */28.profile { position: relative; display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3); }29.profile:hover, .profile:focus-within { background: var(--hover); }30.profile-text { display: flex; flex-direction: column; min-width: 0; }31.profile-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-prose); font-weight: var(--fw-strong); color: var(--fg); }32.profile-name:hover { color: var(--fg); }33.profile-name::after { content: ''; position: absolute; inset: 0; }34.profile-meta { font-size: var(--text-sm); color: var(--muted); }35.profile-meta time { font: inherit; }36/* Topics: tiles with hairlines between them; the panel's edge is the rule37 round them, so the last column's and row's own rules are cut off. */38.topics { overflow: hidden; }39.topic-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0 calc(-1 * var(--hairline)) calc(-1 * var(--hairline)) 0; padding: 0; list-style: none; }40.topic-grid > li { position: relative; border-right: var(--hairline) solid var(--border); border-bottom: var(--hairline) solid var(--border); }41.topic-grid > li:hover, .topic-grid > li:focus-within { background: var(--hover); }42.topic-grid a { display: flex; flex-direction: column; padding: var(--space-2) var(--space-3); color: var(--fg); font-weight: var(--fw-medium); overflow-wrap: anywhere; }43.topic-grid a:hover { color: var(--fg); }44.topic-grid .n { font: var(--fw-regular) var(--text-xs) var(--mono); color: var(--faint); }45@media (max-width: 720px) {46 .topic-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }47}48{% endcss %}49{% if forge.motd %}<p class="motd">{{ forge.motd }}</p>50{% endif %}51<div class="home">52{% set head %}<h2>profiles</h2><span class="n">{{ forge.profiles.length }}</span>{% endset %}53{% call ui.panel(head, 'profiles') %}54{% if forge.profiles.length %}55<ul class="rows">56{% for pr in forge.profiles %}57<li class="profile">{{ pr.owner | avatar(36) }}<span class="profile-text"><a class="profile-name" href="/{{ pr.owner }}/">{{ pr.owner }}</a><span class="profile-meta">{{ pr.repos | plural("repo") }}{% if pr.active %}, active {{ pr.active | ago }}{% endif %}</span></span></li>58{% endfor %}59</ul>60{% else %}<p class="empty">No public repos here yet.</p>61{% endif %}62{% endcall %}63{% set head %}<h2>recent</h2><span class="end"><a href="/insights/">insights</a></span>{% endset %}64{% call ui.panel(head, 'recent') %}65{% if forge.recent.length %}66<ul class="rows ev-rows">67{% for e in forge.recent %}68<li class="ev"><span class="ev-kind">{{ e.kind }}</span><a class="ev-what" href="{{ e.href }}"><b>{{ e.repo.name }}</b> {% if e.kind == 'release' %}<span class="tag">{{ e.ref }}</span>{% else %}{{ (e.ref + ' ') if e.ref }}{{ e.text }}{% endif %}</a>{{ e.at | date }}</li>69{% endfor %}70</ul>71{% else %}<p class="empty">Nothing has happened here yet.</p>72{% endif %}73{% endcall %}74{% if forge.topics.length %}75{% set head %}<h2>topics</h2>{% endset %}76{% call ui.panel(head, 'topics') %}77<ul class="topic-grid">78{% for t in forge.topics %}79<li><a href="{{ t.href }}">{{ t.title }}<span class="n">{{ t.repos | plural('repo') }}</span></a></li>80{% endfor %}81</ul>82{% endcall %}83{% endif %}84</div>85{% endblock %}