feat: emit capsule entries as lists, with no separator after the date

This commit is contained in:
randogoth 2026-10-11 16:10:07 +03:00
parent e025f32f61
commit a873ef23ea

View file

@ -32,23 +32,18 @@ setting, and every setting has a default, so a site can switch it on with just
every site using this plugin has been
checked for footnotes
Why link-only paragraphs rather than lists: gemtext has one kind of link, a line
beginning `=>`, so a renderer has to lift links out of the prose. itsybitsy's
gemtext renderer emits a bare `=>` line for a paragraph that holds nothing but
links, so a run of links comes out as a clean run of link lines. A Markdown list
would instead print a bullet *and* a link line for every entry, listing
everything twice.
Every run of links here is a **Markdown list**, which is what each format needs:
Mews Profile reads a dated link as a feed entry only when it starts a list item
(mews.page/spec 6.1), and the `text` renderer used for Nex and Gopher keeps one
item per line instead of reflowing a paragraph into a run-on line. gemtext has
one kind of link, a line beginning `=>`, and itsybitsy prints only those lines
for a list item that holds nothing but links, so no entry is listed twice.
That last part is a floor on the daemon version: an itsybitsy before the fix in
its gemtext renderer prints a bullet *and* a link line for every entry here.
Gemini clients number link lines themselves, which means a menu of areas is
dialled by number without this plugin writing a single bracket.
The links are separated by **hard breaks**, which is two trailing spaces. gemtext
lifts each link out either way, but the inline formats lay a paragraph out as
flowing text, so with soft breaks a six-item menu renders as one run-on line in a
browser and on a handset. A hard break becomes `<br/>` there and costs gemtext
nothing. The `text` renderer used for Nex and Gopher flattens both break kinds to
a space and still reflows, which is the one format this shape does not serve; a
site whose main face is Gopher should emit lists instead.
"""
import os
@ -350,6 +345,11 @@ def link(label, href):
return "[%s](%s)" % (label.replace("]", ""), href)
def bullets(items):
"""A Markdown list, one item per line. See the module header."""
return "\n".join("- %s" % item for item in items)
def gate(block, formats):
"""Wrap a block in an itsybitsy format gate.
@ -490,7 +490,7 @@ class CapsuleGenerator(Generator):
index = ["# %s" % s.get("SITENAME", "index"), ""]
if sections:
index += ["## %s" % labels["sections"], ""]
index.append(" \n".join(
index.append(bullets(
link("%s · %d" % (category, len(items)),
"/%s/%s" % (section_dir, category.slug))
for category, items in sections
@ -502,7 +502,7 @@ class CapsuleGenerator(Generator):
link(labels["archive"], "/archive"), ""]
if pages:
index += ["## %s" % labels["pages"], "",
" \n".join(link(p.title, "/%s" % p.slug) for p in pages), ""]
bullets(link(p.title, "/%s" % p.slug) for p in pages), ""]
tagline = s.get("SMOLNET_TAGLINE")
if tagline:
index += ["---", "", tagline, ""]
@ -514,10 +514,7 @@ class CapsuleGenerator(Generator):
self._copy_assets(out)
def _links(self, docs):
"""One link per line in a single paragraph.
Deliberately not a Markdown list: a list item renders as a bullet *and*
a link line, so every entry would appear twice.
"""A list of dated links, one entry per item.
Targets are root-relative. A section index sits one directory down, so
relative targets would need `../` and would break again at any further
@ -525,29 +522,27 @@ class CapsuleGenerator(Generator):
against the site root.
The date is ISO 8601 and it leads the label, which is protocol surface
rather than a style choice. Gemini's subscription convention takes a
link line for a feed entry when the first ten characters of its label
are a `YYYY-MM-DD` date, reads the rest as the title, and ignores every
line where they are not, so this one format is what makes each index a
feed: the front page, the archive and all six areas, subscribable in a
client and submittable to an aggregator. A board's `09 Oct 26` parses
as nothing and registers zero entries.
rather than a style choice. Both subscription conventions this capsule
is read through take a link as a feed entry when its label begins with
a `YYYY-MM-DD` date and read the rest as the title: Gemini's over the
`=>` lines, Mews Profile's over the list items (6.1). That one format is
what makes each index a feed: the front page, the archive and all six
areas, subscribable in a client and submittable to an aggregator. A
board's `09 Oct 26` parses as nothing and registers zero entries.
The separator is the one the convention's own examples use. Clients are
asked to sanitise whatever sits between the date and the title, but not
all of them do, and a `·` that survives arrives as part of the title.
Nothing separates the date from the title. Gemini's examples put a dash
there and ask clients to strip whatever they find, but Mews reads the
label from the date to the end of the line, so a separator arrives as
part of the title -- as does a `·` in a Gemini client that skips the
sanitising step.
"""
rows = []
for doc in docs:
label = doc.title
if getattr(doc, "date", None):
label = "%s - %s" % (doc.date.strftime("%Y-%m-%d"), doc.title)
label = "%s %s" % (doc.date.strftime("%Y-%m-%d"), doc.title)
rows.append(link(label, "/%s" % doc.slug))
# Hard breaks, which is two trailing spaces in CommonMark. gemtext lifts
# each link to its own => line either way, but the inline formats lay a
# paragraph out as flowing text, so without these the whole menu renders
# as one run-on line in a browser and on a handset.
return " \n".join(rows)
return bullets(rows)
def _read(self, doc):
with open(doc.source_path, encoding="utf-8") as fh: