diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-19 11:48:30 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-19 11:48:30 +0200 |
| commit | 6762ce46af3cb12bc6ae37cda762c5d95add7903 (patch) | |
| tree | bc1d8c86050d0149ff961a9a4ff838f9c474ac2a /templates/ef | |
| parent | 897c274fd28402159ca6d45eedc1257b1ce98696 (diff) | |
| parent | 390bc6ac5196a946c473d0dbe7760fa41837c428 (diff) | |
| download | colitur-6762ce46af3cb12bc6ae37cda762c5d95add7903.tar.gz colitur-6762ce46af3cb12bc6ae37cda762c5d95add7903.zip | |
feat: output, rendering and publishing
Gives colitur a publishable exit. Until now its only output was terminal
rows; it can now print an ordo booklet and a wall calendar, publish an
iCalendar feed people subscribe to, and serve a static JSON/XML API.
lib/render escaping (six flavours + RFC 5545 folding), a deliberately
logic-less template engine, the view model, and five
emitters (CSV, JSON, XML, iCalendar, S-expression)
CLI emit, table, render, publish -- all accepting --overlay
templates ordo booklet in six flavours, wall grid in three
schema day-v1.json and colitur-v1.xsd, the published contract
man colitur-templates.5, plus colitur.1 updates
The view model is why the engine can stay logic-less: a month grid needs
leading blank cells, week bucketing and an in-month test, and a
logic-less template can compute none of it. Shaping the data in OCaml
keeps the engine safe for untrusted templates and makes the grid
trivial.
Formats split by whether correctness is mechanical. Presentation goes
through templates; iCalendar and XML get dedicated emitters, because
folding, exclusive DTEND, stable UIDs and schema fidelity are rules a
template cannot enforce and each fails silently in a subscriber's
client rather than loudly at generation.
publish is deterministic and non-destructive: two runs produce a
byte-identical tree, and --prune removes only files a previous run
created, refusing any manifest entry that escapes the output directory.
No new dependencies. The kernel and rite modules are untouched, and
colitur day and colitur readings remain byte-identical.
Diffstat (limited to 'templates/ef')
| -rw-r--r-- | templates/ef/grid.html | 35 | ||||
| -rw-r--r-- | templates/ef/grid.ms | 95 | ||||
| -rw-r--r-- | templates/ef/grid.tex | 35 | ||||
| -rw-r--r-- | templates/ef/ordo.adoc | 25 | ||||
| -rw-r--r-- | templates/ef/ordo.html | 28 | ||||
| -rw-r--r-- | templates/ef/ordo.md | 23 | ||||
| -rw-r--r-- | templates/ef/ordo.ms | 27 | ||||
| -rw-r--r-- | templates/ef/ordo.tex | 33 | ||||
| -rw-r--r-- | templates/ef/ordo.txt | 10 |
9 files changed, 311 insertions, 0 deletions
diff --git a/templates/ef/grid.html b/templates/ef/grid.html new file mode 100644 index 0000000..b92e14a --- /dev/null +++ b/templates/ef/grid.html @@ -0,0 +1,35 @@ +<!-- colitur wall calendar -- HTML. flavour: html --> +<!-- Day label falls back to the slug when the day carries no Latin name + (most temporal days, and most sanctoral entries, which are Latin-less + in the shipped data) -- see ordo.tex's own comment for why the fallback + is written as a name-section wrapping a plain var and its inverse, + rather than a single dotted lookup and its inverse: the enclosing month + object also carries its own name.la, and a dotted lookup that misses + climbs the WHOLE path to that outer scope instead of falling back. --> +<!doctype html> +<html lang="la"><head><meta charset="utf-8"> +<title>Calendarium {{year}}</title> +<style> + body{font-family:sans-serif;margin:1em} + table{border-collapse:collapse;width:100%;margin-bottom:1.5em;page-break-inside:avoid} + caption{font-size:1.2em;font-weight:bold;padding:.4em} + th{font-size:.8em;padding:.2em;border-bottom:1px solid #999} + td{border:1px solid #ddd;vertical-align:top;height:5em;width:14.28%;padding:.2em;font-size:.75em} + td.pad{background:#fafafa;border-color:#eee} + .dom{font-weight:bold;display:block} + .white{background:#fff} .red{background:#fdd} .green{background:#dfd} + .violet{background:#eae} .rose{background:#fde} .black{background:#ddd} + @media print{@page{size:landscape}body{margin:0}} +</style></head><body> +<h1>Calendarium {{year}} · {{rite}}</h1> +{{#months}} +<table> + <caption>{{name.la}}</caption> + <tr><th>Dom</th><th>Lun</th><th>Mar</th><th>Mer</th><th>Iov</th><th>Ven</th><th>Sab</th></tr> + {{#weeks}}<tr> + {{#days}}{{#in_month}}<td class="{{colour}}"><span class="dom">{{dom}}</span>{{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}}</td>{{/in_month}}{{^in_month}}<td class="pad"></td>{{/in_month}}{{/days}} + </tr> + {{/weeks}} +</table> +{{/months}} +</body></html> diff --git a/templates/ef/grid.ms b/templates/ef/grid.ms new file mode 100644 index 0000000..703f46b --- /dev/null +++ b/templates/ef/grid.ms @@ -0,0 +1,95 @@ +.\" colitur wall calendar -- groff ms with tbl. flavour: groff +.\" Build: colitur table --year 2027 --template grid.ms | groff -ms -t -Tpdf -P-pa4l > grid.pdf +.\" +.\" LANDSCAPE, not the ms default portrait: with allbox cells and Latin-less +.\" days falling back to the raw slug (some fallback slugs run past 40 +.\" characters, e.g. "commemoration-of-the-baptism-of-the-lord"), seven +.\" columns of real content do not fit a portrait line length at any +.\" reasonable point size. "-P-pa4l" is gropdf's own landscape papersize +.\" (groff_font(5): a trailing "l" on a papersize name swaps width/height at +.\" the OUTPUT DEVICE level) -- it must be a command-line flag, not a request +.\" inside this file: an in-document "\X'papersize=a4l'" escape was tried and +.\" rejected (it does not actually rotate gropdf's page in this groff, and +.\" throws diagnostics doing it). ".pl"/".po"/".ll" below independently widen +.\" TROFF's OWN idea of the page to match -- ".pl"/".ll" are requests, unlike +.\" the device's own physical page size, and are not implied by "-P-pa4l" -- +.\" so both halves are needed, or one side clips the other. +.\" +.\" ".TL" (the ms title macro, s.tmac) ITSELF narrows the line length to +.\" 5/6 of register "LL" for the title text, and never widens it back +.\" afterward -- there is no third place in s.tmac that ever does. Setting +.\" only the primitive ".ll" before ".TL" is therefore not enough: ".TL" +.\" overwrites it and every table for the rest of the document inherits +.\" the NARROWED value, silently, which is a second, independent way the +.\" original template lost its width (found by reading s.tmac itself, not +.\" guessed). Fixed by setting register "LL" (what ".TL" actually reads) +.\" and re-asserting ".ll" once more, explicitly, right after the title. +.\" +.\" Every column is a FIXED-WIDTH, WRAPPING tbl "w()" block, not a plain "l": +.\" without "w()" tbl sizes a column to its single widest entry and does NOT +.\" wrap, so a 40-character slug forces every column that wide -- seven of +.\" them together run far past any line length, tbl warns "table wider than +.\" line length", and whatever falls past the physical page edge is not a +.\" rendering nicety missing, it is GONE: `pdftotext -layout` (and a printed +.\" page) show only however many columns fit, not all seven. "w()" makes +.\" each column wrap its own text block instead, the same fix grid.tex +.\" already uses via LaTeX's "p{2.6cm}" (see that file's own comment) -- +.\" this is the tbl-native equivalent, not a different design. +.\" +.\" "w()" alone is NOT sufficient, and this is the part that actually cost +.\" the time: tbl only wraps a cell as a text block when its content is +.\" delimited by "T{"/"T}" on their own lines. A PLAIN cell entry in a +.\" "w()" column (what the first attempt at this fix used) is never +.\" wrapped at all -- tbl just widens the column past the requested "w()" +.\" to fit the single longest unbroken entry, exactly reproducing the +.\" original bug. Confirmed directly against tbl's own generated troff +.\" code (`tbl grid.ms`, grep "3w0 .*>?"): every column width register is +.\" `max(w(), \w'cell text')`, so an un-delimited 40-character cell always +.\" wins that max. Every day cell below is therefore its own "T{"/"T}" +.\" block; the closing "T}" and the following column's opening "T{" share +.\" one line with the tab between them (the standard tbl idiom: "T}<TAB> +.\" T{"). The template writes that as "{{#last}}T}{{/last}}{{^last}}T} +.\" <TAB>{{/last}}" rather than "T}{{^last}}<TAB>{{/last}}T{" so that no +.\" literal "T{" ever sits directly against a "{{"/"}}" delimiter in the +.\" source -- three braces in a row there parses as a malformed tag, not +.\" as literal text followed by a tag. +.\" +.\" ".na" (no-adjust, ragged right) is required alongside the wrap, not +.\" cosmetic: a hyphenated slug like "commemoration-of-the-baptism-of-the- +.\" lord" DOES break at its own hyphens once wrapping is active (verified: +.\" troff's filler treats an ASCII hyphen as a break point), but adjusted +.\" (justified) fill in a column this narrow cannot always stretch such a +.\" line to the full measure and troff warns "cannot adjust line" for +.\" every such row -- another warning `make check-templates` must fail on. +.\" Ragged-right removes the stretching, not the wrapping. +.\" +.\" Day label falls back to the slug when the day carries no Latin name (most +.\" temporal days, and most sanctoral entries, which are Latin-less in the +.\" shipped data) -- see ordo.tex's own comment for why the fallback is +.\" written as a name-section wrapping a plain var and its inverse, rather +.\" than a single dotted lookup and its inverse. +.\" +.\" Every cell also carries a last flag, true on the seventh of its row: tbl +.\" needs the tab separator BETWEEN columns, not after the last one, and the +.\" engine has no unless-last construct, so the flag comes from data. +.pl 8.27i +.po 0.3i +.nr LL 10.9i +.ll \n[LL]u +.TL +Calendarium {{year}} \(bu {{rite}} +.ll \n[LL]u +.na +{{#months}} +.SH +{{name.la}} +.TS +allbox; +cw(1.3i) cw(1.3i) cw(1.3i) cw(1.3i) cw(1.3i) cw(1.3i) cw(1.3i) +lw(1.3i) lw(1.3i) lw(1.3i) lw(1.3i) lw(1.3i) lw(1.3i) lw(1.3i). +Dom Lun Mar Mer Iov Ven Sab +{{#weeks}}{{#days}}T{ +{{#in_month}}\fB{{dom}}\fP \s-2{{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}}\s+2{{/in_month}} +{{#last}}T}{{/last}}{{^last}}T} {{/last}}{{/days}} +{{/weeks}}.TE +{{/months}} diff --git a/templates/ef/grid.tex b/templates/ef/grid.tex new file mode 100644 index 0000000..51432e9 --- /dev/null +++ b/templates/ef/grid.tex @@ -0,0 +1,35 @@ +% colitur wall calendar -- LaTeX. flavour: latex +% Build: colitur table --year 2027 --template grid.tex > grid.tex && pdflatex grid.tex +% +% Day label falls back to the slug when the day carries no Latin name (most +% temporal days, and most sanctoral entries, which are Latin-less in the +% shipped data). The fallback is written as a name-section wrapping a plain +% var and its inverse, deliberately NOT as a single dotted-path lookup +% followed by its own inverse: a dotted lookup that misses climbs to the +% enclosing scope for the WHOLE path, and the month object also carries a +% same-named key one level up, so the naive form would render the month's +% own Latin name on every day lacking one, and never fall back at all. +% +% Every cell also carries a last flag, true on the seventh of its row: the +% engine has no unless-last construct, so the separator BETWEEN cells comes +% from data, not the template. A trailing separator on every cell would give +% eight columns for seven and pdflatex would reject the file outright. +\documentclass[10pt,landscape]{article} +\usepackage[a4paper,margin=10mm]{geometry} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[table]{xcolor} +\definecolor{lwhite}{HTML}{FFFFFF}\definecolor{lred}{HTML}{FFDDDD} +\definecolor{lgreen}{HTML}{DDFFDD}\definecolor{lviolet}{HTML}{EEAAEE} +\definecolor{lrose}{HTML}{FFDDEE}\definecolor{lblack}{HTML}{DDDDDD} +\begin{document} +{{#months}} +\section*{ {{name.la}} {{year}} } +\begin{tabular}{|p{2.6cm}|p{2.6cm}|p{2.6cm}|p{2.6cm}|p{2.6cm}|p{2.6cm}|p{2.6cm}|} +\hline Dom & Lun & Mar & Mer & Iov & Ven & Sab \\ \hline +{{#weeks}}{{#days}}{{#in_month}}\cellcolor{l{{colour}}}\textbf{ {{dom}} } \footnotesize {{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}}{{/in_month}}{{^last}} & {{/last}}{{/days}} \\ \hline +{{/weeks}} +\end{tabular} +\newpage +{{/months}} +\end{document} diff --git a/templates/ef/ordo.adoc b/templates/ef/ordo.adoc new file mode 100644 index 0000000..13d09ec --- /dev/null +++ b/templates/ef/ordo.adoc @@ -0,0 +1,25 @@ +// colitur ordo booklet -- AsciiDoc. flavour: none (see man colitur-overlay) +// AsciiDoc's own metacharacters (*, _, +, `, ...) are context-dependent, so +// this template's flavour deliberately does NOT escape interpolated values. +// A feast name containing `*` or `_` will render as emphasis. That is a +// documented limitation, not a bug to fix. +// +// Day label falls back to the slug when the day carries no Latin name (most +// temporal days, and most sanctoral entries, which are Latin-less in the +// shipped data) -- see ordo.tex's own comment for why the fallback is +// written as a name-section wrapping a plain var and its inverse, rather +// than a single dotted lookup and its inverse. += Ordo {{year}} · {{rite}} +:toc: + +{{#months}} +== {{name.la}} + +{{#days}} +*{{dom}}* {{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}} + +{{rank}} · {{colour}} + +{{#first}}Ep. {{first}} {{/first}}{{#gospel}}Ev. {{gospel}}{{/gospel}} +{{#comms}} + +Com. {{slug}}{{/comms}} + +{{/days}}{{/months}} diff --git a/templates/ef/ordo.html b/templates/ef/ordo.html new file mode 100644 index 0000000..e516816 --- /dev/null +++ b/templates/ef/ordo.html @@ -0,0 +1,28 @@ +<!-- colitur ordo booklet -- HTML. flavour: html --> +<!-- Day label falls back to the slug when the day carries no Latin name + (most temporal days, and most sanctoral entries, which are Latin-less + in the shipped data) -- see ordo.tex's own comment for why the fallback + is written as a name-section wrapping a plain var and its inverse, + rather than a single dotted lookup and its inverse. --> +<!doctype html> +<html lang="la"><head><meta charset="utf-8"> +<title>Ordo {{year}}</title> +<style> + body{font-family:serif;max-width:40em;margin:2em auto;line-height:1.4} + .day{margin:.6em 0;padding-left:2.5em;text-indent:-2.5em} + .dom{display:inline-block;width:2em;font-weight:bold} + .meta{font-size:.85em;color:#555} + .white{border-left:4px solid #ccc} .red{border-left:4px solid #b00} + .green{border-left:4px solid #070} .violet{border-left:4px solid #609} + .rose{border-left:4px solid #e7a} .black{border-left:4px solid #000} + @media print{body{margin:0}} +</style></head><body> +<h1>Ordo {{year}} · {{rite}}</h1> +{{#months}}<h2>{{name.la}}</h2> +{{#days}}<div class="day {{colour}}"> + <span class="dom">{{dom}}</span>{{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}} + <div class="meta">{{rank}} · {{colour}}{{#first}} · Ep. {{first}}{{/first}}{{#gospel}} · Ev. {{gospel}}{{/gospel}}</div> + {{#comms}}<div class="meta">Com. {{slug}}</div>{{/comms}} +</div> +{{/days}}{{/months}} +</body></html> diff --git a/templates/ef/ordo.md b/templates/ef/ordo.md new file mode 100644 index 0000000..606ea01 --- /dev/null +++ b/templates/ef/ordo.md @@ -0,0 +1,23 @@ +<!-- colitur ordo booklet -- Markdown. flavour: none --> +<!-- Markdown's own metacharacters (*, _, `, [, ...) are context-dependent, + so this template's flavour deliberately does NOT escape interpolated + values. A feast name containing `*` or `_` will render as emphasis. + That is a documented limitation, not a bug to fix. --> +<!-- Day label falls back to the slug when the day carries no Latin name + (most temporal days, and most sanctoral entries, which are Latin-less + in the shipped data) -- see ordo.tex's own comment for why the fallback + is written as a name-section wrapping a plain var and its inverse, + rather than a single dotted lookup and its inverse. --> +# Ordo {{year}} · {{rite}} + +{{#months}} +## {{name.la}} + +{{#days}} +**{{dom}}** {{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}} +`{{rank}}` · {{colour}}{{#first}} · Ep. {{first}}{{/first}}{{#gospel}} · Ev. {{gospel}}{{/gospel}} +{{#comms}} +- Com. {{slug}} +{{/comms}} + +{{/days}}{{/months}} diff --git a/templates/ef/ordo.ms b/templates/ef/ordo.ms new file mode 100644 index 0000000..db9b9fc --- /dev/null +++ b/templates/ef/ordo.ms @@ -0,0 +1,27 @@ +.\" colitur ordo booklet -- groff ms. flavour: groff +.\" Build: colitur table --year 2027 --template ordo.ms | groff -ms -Tpdf > ordo.pdf +.\" +.\" Day label falls back to the slug when the day carries no Latin name (most +.\" temporal days, and most sanctoral entries, which are Latin-less in the +.\" shipped data) -- see ordo.tex's own comment for why the fallback is +.\" written as a name-section wrapping a plain var and its inverse, rather +.\" than a single dotted lookup and its inverse. +.TL +ORDO {{year}} \(bu {{rite}} +{{#months}} +.SH +{{name.la}} +.LP +{{#days}} +.IP "{{dom}}" 4 +{{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}} +.br +\s-2{{rank}} \(bu {{colour}}\s+2 +{{#first}}.br +\s-2Ep. {{first}}\s+2 +{{/first}}{{#gospel}}.br +\s-2Ev. {{gospel}}\s+2 +{{/gospel}}{{#comms}}.br +\s-2Com. {{slug}}\s+2 +{{/comms}}{{/days}} +{{/months}} diff --git a/templates/ef/ordo.tex b/templates/ef/ordo.tex new file mode 100644 index 0000000..6012753 --- /dev/null +++ b/templates/ef/ordo.tex @@ -0,0 +1,33 @@ +% colitur ordo booklet -- LaTeX. flavour: latex +% Build: colitur table --year 2027 --template ordo.tex > ordo.tex && pdflatex ordo.tex +% +% Day label falls back to the slug when the day carries no Latin name (most +% temporal days, and most sanctoral entries, which are Latin-less in the +% shipped data). The fallback is written as a name-section wrapping a plain +% var and its inverse, deliberately NOT as a single dotted-path lookup +% followed by its own inverse: a dotted lookup that misses climbs to the +% enclosing scope for the WHOLE path, and the month object also carries a +% same-named key one level up, so the naive form would render the month's +% own Latin name on every day lacking one, and never fall back at all. +\documentclass[10pt,twoside]{article} +\usepackage[a5paper,margin=15mm]{geometry} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage{fancyhdr} +\pagestyle{fancy} +\fancyhead[C]{ORDO {{year}} \textperiodcentered\ {{rite}}} +\setlength{\parindent}{0pt} +\begin{document} +{{#months}} +\section*{ {{name.la}} } +{{#days}} +\textbf{ {{dom}} } \quad {{#name}}{{la}}{{^la}}{{slug}}{{/la}}{{/name}}\\ +\small {{rank}} \textperiodcentered\ {{colour}}\\ +{{#first}}\small Ep. {{first}}\quad{{/first}}{{#gospel}}\small Ev. {{gospel}}{{/gospel}}\\ +{{#comms}}\small Com. {{slug}}\\ +{{/comms}} +\medskip + +{{/days}} +{{/months}} +\end{document} diff --git a/templates/ef/ordo.txt b/templates/ef/ordo.txt new file mode 100644 index 0000000..aba121a --- /dev/null +++ b/templates/ef/ordo.txt @@ -0,0 +1,10 @@ +{{#months}} +{{name.la}} {{year}} +{{#days}} +{{dom}} {{slug}} + {{rank}} · {{colour}} +{{#first}} Ep. {{first}} +{{/first}}{{#gospel}} Ev. {{gospel}} +{{/gospel}}{{#comms}} Com. {{slug}} +{{/comms}}{{/days}} +{{/months}} |
