summaryrefslogtreecommitdiff
path: root/lib/render/view.ml
Commit message (Collapse)AuthorAgeFilesLines
* fix(render): a month answers to both spellings of its own nameLukasz Kasprzak2026-08-201-1/+13
| | | | | | | | | | | | | | | | A week object carries month_name/month_num/month_abbr, because at that level a bare `name` would be ambiguous. An author who learned those names inside {{#weeks}} reaches for them one level up inside {{#months}} too -- where they resolved to nothing, and an unknown key renders as the empty string by design, so the result was a silently blank month heading rather than any error. Found by writing a template from scratch rather than copying a shipped one; every shipped template sidesteps it, which is why nothing caught it. The aliases are additive, so no existing template or golden changes. colitur-templates(5) now documents both spellings and says why they exist.
* feat(render): citations render through Sigla at both call sitesLukasz Kasprzak2026-08-201-7/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | View.citation_ref and readings' part_ref are the only two places a citation reaches output; every emitter goes through View. --raw passes Sigla.verbatim rather than a style built over Lang.raw: an identity name table would still reformat punctuation and renumber. Loosen reject_sigla_for: --sigla-style/--sigla-book/--sigla-tradition now actually render on readings/table/render/emit/publish (each builds its own Sigla.t via the new load_sigla), and stay refused only on commands that render no citation (day, easter, temporal, ...). names_of degrades a Lang.bible miss to Book.default_spelling rather than printing the lookup key itself ("luke.abbr"); pinned in cli.t against a language file with no [bible] section at all, independent of la.ini/en.ini's own eventual [bible] section. Regenerate the 11 golden templates for 2027: every changed line is one canonical citation replacing a stored variant, verified against the full readings 2027 diff (raw vs default) cell by cell -- 86 distinct (raw, rendered) pairs account for the entire diff across all 11 files, with zero unexplained residue. Two families: the seven duplicate book spellings collapsing onto one canonical form (Isa./Isa, 3 Kgs./3 Kings, ...), and citations reconstructed from parsed structure dropping stray punctuation the parser already treats as noise (a trailing period or semicolon, a comma chapter/verse separator, an elided inherited chapter) -- each of the latter already named and tested in test_citation.ml's parse_suite before this task.
* feat(render): Roman week numbers and date spans, as dataLukasz Kasprzak2026-08-201-7/+77
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The ordo booklet's week header repeated the month name on every week even though the heading above already established it -- pure noise. Replace it with a Roman week number plus the span of dates the week covers, e.g. "Hebdomada I (Ian 1-2)", following the project's own rule that a presentation choice is data, not code. lang/{la,en}.ini gain a [month_abbr] section (three-letter month abbreviations); Lang.month_abbr follows Lang.month's exact shape, including the out-of-range and miss-returns-the-key contracts. The coverage test now fails loudly if an abbreviation goes missing, the same as [month] already does. Every week object in the view gains num_roman (Roman numeral, num stays as the arabic original -- Roman is a presentation choice, not an engine change), first_dom/last_dom (the day-of-month of the week's first and last IN-MONTH days, padding excluded), month_abbr (resolved through Lang.month_abbr), and single_day (true when the week holds exactly one in-month day). single_day is a flag, not a preformatted span string: the engine is logic-less and cannot itself decide between "Ian 1" and "Ian 1-2", so a template makes that call from the flag instead -- the same "shape the data, not the template" discipline in_month and last already follow. Weeks are built per month with padding only at the two ends, so a week's in-month days never cross a month boundary -- verified, not assumed: every week always has at least one real day since no month is shorter than a single week. Covered by three new View tests, including a real single-day-week witness (January 2027's own trailing week is a lone Sunday, the 31st).
* feat(templates): the ordo booklet flows continuously, not one week per pageLukasz Kasprzak2026-08-201-0/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | One week per page filled roughly the top third of every A5 page and cost 65 pages for a year that needed far fewer -- the original requirement, reversed here: a full 7-day week already occupies most of an A5 page even at reduced type, so the real saving was always in the year's many SHORT boundary weeks (a month's first/last week is rarely a full seven days), not in packing two full weeks together. lib/render/view.ml gains a [first] field on every week object (true on a month's own first week, the same 'cheap flag beats invented template logic' call [last] already made for grid rows) -- there is still no {{../}} parent-path syntax for a template to test this any other way. ordo.tex: the per-week \clearpage is gone. Every week keeps its existing small header line; the month's own first week additionally prints a large, ruled banner. \needspace (a template-local LaTeX dependency, not one of the frozen OCaml deps) keeps a header from being stranded alone at a page's bottom -- and a real, separate bug surfaced building it: \label placed BEFORE \needspace captured the OLD page number when \needspace went on to force a break, producing a step-stale \pageref in the TOC (fixed by moving \label after both needspace checks). The day box itself is tightened (rank/colour folded onto the same line as the citations -- an A5 box is far wider than tall, so this fits even Holy Week's longest citations with room to spare) so a second week routinely shares a page with the first. Measured result: 65 -> 53 pages, two pdflatex passes still settle the TOC, zero orphaned headers, zero overfull boxes. ordo.typ: the per-week #pagebreak is gone; #show heading's own block gains sticky:true, Typst's built-in answer to what ordo.tex needs \needspace for. The month banner needed its OWN sticky:true, found live: without it a banner could sit alone at a page's bottom with the entire month pushed to the next page -- not caught by a check for an orphaned HEADING, since the banner is plain markup, not one. Measured result: 65 -> 27 pages (Typst's own page-fill packs tighter than ordo.tex's needspace-driven approach), one compile pass, zero orphans. The five non-paginated flavours (ms/html/md/adoc/txt) never had a forced page break to remove -- they mainly needed the same header treatment for structural consistency: a week-level header nested under each month's existing one, walking months>weeks>days (the week's own padded, in-month-guarded day list) in place of the old months>days flat loop. Verified against groff, asciidoctor and an HTML parser -- zero warnings. Goldens regenerated through the Test_view.view_of path test_render_golden.ml itself uses, not the CLI (whose --lang default would have pinned output the tests never produce).
* fix(naming): the rite prints its display name, not the internal id efLukasz Kasprzak2026-08-201-0/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The booklet's title page and running header read "Ordo 2027 . ef" -- an internal identifier reaching a reader, the same defect class the sanctoral/temporal slugs were already fixed for. lang/la.ini and lang/en.ini each gain a [rite] section mapping the rite id to a reader-facing name, sourced from the Missal's own title page (docs/research/LT.txt:6 "MISSALE ROMANUM", :15 "EDITIO TYPICA") plus the 1962 dating this project uses throughout (rules-register.md, CLAUDE.md) -- both cited in the ini comments, marked PATTERN since the phrase combines two title-page elements rather than quoting one verbatim heading. Lang.t gains a rite table/lookup (same total, miss-echoes-the-key contract as every other lookup here); View.of_days exposes rite_name alongside the existing rite field, which stays the stable key exactly as slug is kept beside name. Every shipped template that printed {{rite}} now prints {{rite_name}} instead. test_lang_coverage.ml gains a coverage assertion over every rite id the engine can emit -- one today -- so a second rite module (OF) landing without a matching [rite] entry fails loudly instead of printing its own bare id. Goldens regenerated through the Test_view.view_of path test_render_golden.ml itself uses (English with a Latin fallback), not the CLI (whose --lang default is plain Latin and would have pinned output the tests never produce). test/cli.t's own JSON prefix assertion updated to match: rite_name is a real new key in that generic view dump, sitting right after rite.
* feat(render): names reach the view and every emitterLukasz Kasprzak2026-08-191-36/+85
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The view's name is now the RESOLVED display string and slug is untouched, so machine formats carry both -- a script keeps the stable key, a human reads the name. name is a plain string, not a lang-keyed object. That removes the shadowing hazard outright: a dotted {{name.la}} used to fall back WHOLESALE to the enclosing month's name.la and print Ianuarius on every unnamed day, which is how a printed booklet came to show the month where the feast belonged. weekday, season, rank and colour all gain localised companions, because a calendar in a language needs more than feast names, and templates gain a term vocabulary so fixed strings need no template edit to translate. Asserted over a whole year: no day renders its slug as its name. Beyond the brief's own code sample: - comm_value's own name is now resolved through the same lang.celebration table too (not only the observed day's), because Task 8's own ordo template interpolates a plain {{name}} inside {{#comms}} -- an Obj there would render silently blank. A commemoration slug without Latin coverage still degrades to the slug, same as everywhere else in this system; that is a lang/la.ini DATA gap (113 of 327 sanctoral slugs, measured), not a regression this task introduced. - bin/main.ml's emit/table/publish call sites needed ~lang to compile at all, which is collateral from the of_days signature change, not this task's own file list. Rather than pass Lang.raw and ship the very slug-as-name defect this branch exists to fix, they load the shipped Latin table by the same probe order data_dir() already uses -- a deliberate, commented BRIDGE that Task 6 replaces wholesale with real --lang/--raw/config resolution. bin/dune gained colitur_naming accordingly. - test/cli.t needed two related fixes to stay green: the CSV header/row example, and a table/LaTeX escaping demonstration that relied on the kernel's own English name for Sts Peter & Paul -- gone from the view now that name resolves through lang tables only, and the Missal's own Latin spells the feast with et, never an ampersand. Escaping itself is still proved live on 2035 data in test_emit.ml. - Both schemas gained the new day/week/top-level keys (season_name, weekday, rank_name, colour_name, term, weekday_headings, month_num, month_name), not only the name shape change; schema/colitur-v1.xsd verified against real emitted XML via xmllint (make check-schema). Render/golden's 9 cases (the shipped ordo/grid templates, all six flavours) now fail as expected: their old {{name.la}} / {{#name}}... idiom finds nothing on a plain string. That is Tasks 8/9's own scope to rewrite, per the plan's own pre-flight conflict scan -- not fixed here, and not silently pinned by regenerating goldens off broken output. 495 tests run (490 + 5 new), 486 pass; the 9 failures are exactly Render/golden's ordo/grid cases.
* fix(render): remove rank_label -- it duplicated name verbatimLukasz Kasprzak2026-08-191-2/+6
| | | | | | | | | | | | | | | | | | view.ml's rank_label field was a byte-for-byte copy of the celebration's name (names_value cel.Celebration.names), not a localized rank label at all -- the kernel has no per-language rank names to draw one from, so there was no honest value to put there. Nothing consumed it: no template in the plan, no test, no other code referenced it. Removed from both day_value and padding_cell so the two key sets stay identical (23 keys each, verified). schema/day-v1.json already described 23 keys and needed no change -- it now matches the emitted output exactly. schema/day-v1.json is a published contract: once a phone subscribes or a site fetches this, removing a field is a breaking /v2/ change. The time to remove a field that lies about its own contents is before anyone can depend on it, not after.
* feat(render): the view modelLukasz Kasprzak2026-08-191-0/+145
Shapes a civil year of resolved days into the value a template renders against. This layer 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. Both weeks and days are offered at every level -- the booklet walks days, the grid walks weeks -- so the two artefacts cannot drift. Colours are six booleans, not hex: hex bakes a presentation policy into the engine, and LaTeX, groff and HTML each want a different colour expression. Asserted: exactly one of the six is true on every day of a whole year, so a template keying off them can never get none or two. Padding cells carry every field a real day carries, empty, so a template never hits a missing key mid-grid.