Milestone 0/1: shell, calendar/psalter/hours scaffold, Prime
Deploy / deploy (push) Failing after 1m8s

Client-side-first PWA (Vite/TS, no backend) per the approved plan: day-
navigable shell listing all 8 hours, Prime fully resolves via the
calendar -> psalter -> ordo pipeline, the other 7 hours are registered
but flagged not-implemented. Sanctoral/temporal calendar data uses a
day -> id indirection layer (saints, easter-offsets, fixed-date-calendar)
so reassigning a feast to a different day is a data edit, not a code
change. Docker (Caddy-serving-static) + Gitea CI workflow scaffolded to
match the eec/drip/bookshop operational pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 15:20:06 -04:00
commit a5dc6502d7
53 changed files with 8634 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
import type { HourId } from '../hours/types';
import type { Weekday } from '../calendar/types';
import distributionData from '../data/psalter-distribution.yml';
type DistributionTable = Partial<Record<HourId, Partial<Record<Weekday, number[]>>>>;
const distribution = distributionData as DistributionTable;
/** Placeholder distribution — see data/psalter-distribution.yml for caveats. */
export function getPsalmNumbersFor(hourId: HourId, weekday: Weekday): number[] {
return distribution[hourId]?.[weekday] ?? [];
}
+22
View File
@@ -0,0 +1,22 @@
import type { Psalm } from './types';
// @rollup/plugin-yaml parses each .yml into a plain object at build time —
// no YAML parser shipped to the client.
const modules = import.meta.glob<{ default: Psalm }>('../data/psalms/*.yml', {
eager: true,
});
const psalms = new Map<number, Psalm>();
for (const mod of Object.values(modules)) {
psalms.set(mod.default.number, mod.default);
}
export function getPsalm(number: number): Psalm {
const psalm = psalms.get(number);
if (!psalm) {
throw new Error(`no data for psalm ${number}`);
}
return psalm;
}
export type { Psalm, PsalmVerse, LanguageCode, TranslationStatus } from './types';
+16
View File
@@ -0,0 +1,16 @@
/** e.g. "la", "en" — open-ended so a third language is a data change, not a schema change. */
export type LanguageCode = string;
export type TranslationStatus = 'verified' | 'draft' | 'missing';
export interface PsalmVerse {
n: number;
text: Partial<Record<LanguageCode, string>>;
status: Partial<Record<LanguageCode, TranslationStatus>>;
}
/** Keyed by Vulgate/Gallican numbering, not Hebrew/modern numbering. */
export interface Psalm {
number: number;
verses: PsalmVerse[];
}