will 128dcb1b7a Author All Saints' missing Matins/Vespers content, fix wrong hymn recension
Nov 1's own content had several real gaps and one transcription bug,
found live-verifying against Divinum Officium (Monastic Tridentinum
1617, 2026-11-01, clean year):

- Matins Nocturn 3 had no Gospel (Matt 5:1-12, the Beatitudes) or St.
  Augustine's homily on it -- a 2026-09-05 pass had wrongly assumed a
  duplex-1-classis feast like this one shouldn't carry its own
  Gospel/homily pairing. Authored with the full canonical text (chased
  down via the Mass propers' own Evangelium, since Matins' own lesson
  gives only a bare "Et réliqua" incipit) rather than just the incipit.
- Vespers had no Magnificat antiphon at all ("O quam gloriósum est
  regnum...").
- Vespers had no proper psalmody override, so it fell back to the plain
  4-psalm Sunday default; the real office uses 5 psalms (109/110/111/
  112/115), each with its own proper antiphon.
- Both the Lauds and Vespers hymn files had the right English translation
  paired with the WRONG Latin recension (the 1631 Urban VIII revision,
  "Jesu Salvátor sæculi"/"Christe Redémptor ómnium") -- the live Monastic
  engine actually renders the older pre-Urban VIII text ("Salútis ætérnæ
  dator" for Lauds, "Placáre, Christe, sérvulis" for Vespers/Matins,
  confirmed identical between those two hours). Corrected both, and
  authored Matins' own hymn file (previously missing entirely, silently
  falling back to the plain ferial hymn).
- St. Raphael the Archangel's own Nocturn 3 Gospel (John 5:1-4) was
  previously left as homily-only because Matins' own lesson gives only a
  bare incipit -- same fix as All Saints, full text recovered from the
  Mass propers instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LGsATZvfnpQ81HQuoF5JuL
2026-09-05 20:08:44 -04:00
2026-08-16 06:30:00 -04:00

vu

A static, offline-first PWA for praying the Divine Office — Matins, Lauds, Prime, Terce, Sext, None, Vespers, and Compline — in bilingual Latin/English, for any given date.

This isn't a reconstruction of any single historical breviary. It's a personal design that draws on several historical sources without reproducing any one of them exactly: hour structure chiefly from the Monastic Tridentinum (1617), precedence/rubrics from Divino Afflatu, and a calendar merged from both the Tridentine 1906 and Divino Afflatu calendars, with more generous handling of commemorations and octaves than any one historical rite used. See CLAUDE.md (gitignored, not shipped in this repo) or the project's own notes for the full rationale if you're extending it.

There's no backend and no database — everything is a pre-built static bundle, and the app is designed to keep working fully offline once it's been loaded once.

Development

Requires Node.js (developed against Node 20+).

npm install
npm run dev        # start the Vite dev server
npm run build       # type-check (tsc --noEmit) and produce a production build in dist/
npm run preview     # serve the production build locally
npm test            # run the test suite (vitest)

Run a single test file or a subset by name:

npx vitest run tests/hours/vespers.test.ts
npx vitest run -t "some test name"

Running in production

The app is entirely static once built, so "running it" just means serving dist/ — any static file host works, as long as it falls back to index.html for unknown paths (the app does its own client-side routing for URLs like /2026-08-09/prime).

Docker

A Dockerfile builds the static bundle with Node and serves it with Caddy:

docker build -t vu .
docker run -p 8080:80 vu

Or with Docker Compose (docker-compose.yml is included):

docker compose up -d --build

This serves the app on http://localhost:8080. Put a reverse proxy/TLS terminator in front of it for a real deployment; the container itself just listens on port 80 internally and has no configuration, secrets, or external dependencies.

Without Docker

Any static file server works — build the app, then serve dist/ with SPA-style fallback routing. For example, with Caddy directly:

npm run build
caddy file-server --root dist --listen :8080

(For a real deployment you'll want a fallback rule so unknown paths resolve to index.html — see the Caddyfile in this repo for the pattern.)

S
Description
Divine Office / Liturgy of the Hours app (pre-1955-leaning calendar, modified Benedictine psalter, whole-Bible reading plan)
Readme 17 MiB
Languages
TypeScript 99%
CSS 0.9%