will 60da11a6bd
Deploy / deploy (push) Successful in 2m0s
Give every day of Passiontide its own real propers and label
Passion Sunday through Holy Saturday (14 days) all collapsed onto just
two ids (passion-sunday/palm-sunday) via resolveTemporalId's coarse
governing-Sunday bucketing, so the day-label header showed the same
generic "Weekday in Passiontide" phrase for all 14, and Maundy
Thursday/Good Friday/Holy Saturday silently rendered Palm Sunday's own
collect and antiphons.

Fixed via the same movable-feast override architecture already used for
Corpus Christi/Ascension (calendar/movable-feasts.ts's anchor-based
override), leaving resolveTemporalId itself untouched: each of the 12
non-Sunday days gets its own real winner id, its own authored
collect/Benedictus/Magnificat content, and its own temporal-feasts
display record, closing both the label bug and the underlying content
gap in one mechanism. day-label.ts's temporalLabel and
collectCommemorations also now recognize any named temporal identity
(not just Ember days), so a saint winning outright on one of these days
still surfaces the day's own real name as a commemoration.

A broader audit of every ALWAYS_OVERRIDE_TEMPORAL_IDS entry found the
same class of gap elsewhere: Corpus Christi and Sacred Heart were
completely unauthored across collect/Benedictus/Magnificat/Matins
reading, and Ascension/Trinity Sunday/Christ the King were missing
pieces too. All authored now, live-verified against the reference
engine. Added a general fallback in resolve-common.ts (missing
day-specific content degrades to the coarse Sunday's own instead of
rendering blank) so this class of gap degrades gracefully if it recurs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lu3sgMFpwud3CerW9r6LYc
2026-09-06 06:33:23 -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 18 MiB
Languages
TypeScript 99.1%
CSS 0.8%