Add Matins hymn override mechanism
Matins always rendered the plain ferial hymn, unlike Lauds/Vespers which already check a duplex-majus+ override then a seasonal default first. Surfaced by the Assumption's octave, which keeps the feast's own proper hymn all week rather than falling back to ferial. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L3wxvz3mPXiHxkPB5JpnmD
This commit is contained in:
+40
-3
@@ -60,7 +60,7 @@
|
|||||||
// Only a small, growable slice of content is authored so far (one clean
|
// Only a small, growable slice of content is authored so far (one clean
|
||||||
// ferial day, one clean Sunday) — this is the mechanism build, not the
|
// ferial day, one clean Sunday) — this is the mechanism build, not the
|
||||||
// full-calendar content pass. See TODO.md for what's deferred.
|
// full-calendar content pass. See TODO.md for what's deferred.
|
||||||
import type { ResolvedOrdo, ResolvedPart, ResolvedVerse } from './types';
|
import type { ResolvedOrdo, ResolvedPart, ResolvedText, ResolvedVerse } from './types';
|
||||||
import type { LiturgicalDay } from '../calendar/types';
|
import type { LiturgicalDay } from '../calendar/types';
|
||||||
import { resolveDay, resolveTemporalId, activeOctavesFor } from '../calendar';
|
import { resolveDay, resolveTemporalId, activeOctavesFor } from '../calendar';
|
||||||
import { isInTriduum } from '../calendar/temporal';
|
import { isInTriduum } from '../calendar/temporal';
|
||||||
@@ -70,7 +70,15 @@ import { getPsalmsFor, type PsalmRef } from '../psalter/distribution';
|
|||||||
import { getScriptureVerses } from '../scripture';
|
import { getScriptureVerses } from '../scripture';
|
||||||
import { getOpeningVersicleId } from './opening-versicle';
|
import { getOpeningVersicleId } from './opening-versicle';
|
||||||
import { isDoubleOrHigher } from './antiphon';
|
import { isDoubleOrHigher } from './antiphon';
|
||||||
import { resolveCommon, getDayCollect, resolveOfficeWinner, verifiedText, splitNamedAntiphon } from './resolve-common';
|
import {
|
||||||
|
resolveCommon,
|
||||||
|
getDayCollect,
|
||||||
|
getOfficeOverrideId,
|
||||||
|
resolveOfficeWinner,
|
||||||
|
seasonalOfficeSuffix,
|
||||||
|
verifiedText,
|
||||||
|
splitNamedAntiphon,
|
||||||
|
} from './resolve-common';
|
||||||
import { getBiblePlanReadings } from '../propers/bible-plan';
|
import { getBiblePlanReadings } from '../propers/bible-plan';
|
||||||
import { getNocturnReadings, type NocturnReading } from '../propers/nocturn-readings';
|
import { getNocturnReadings, type NocturnReading } from '../propers/nocturn-readings';
|
||||||
import { getOctaveReading } from '../propers/octave-readings';
|
import { getOctaveReading } from '../propers/octave-readings';
|
||||||
@@ -180,6 +188,35 @@ function sundayCanticleNocturn(group: SundayNocturn, day: LiturgicalDay): Resolv
|
|||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The Matins hymn — a duplex-majus+ saint's or eligible named temporal
|
||||||
|
* feast's own proper hymn (`matins-hymn-${overrideId}`, via the same
|
||||||
|
* getOfficeOverrideId eligibility Lauds/Vespers' own resolveOffice uses),
|
||||||
|
* when authored, else falls to a *seasonal* default (Advent/Lent/
|
||||||
|
* Passiontide/Paschaltide, none authored yet), else the plain year-round
|
||||||
|
* ferial hymn — same override > season > ferial precedence every other
|
||||||
|
* hour's own office bundle uses (see lauds.ts's resolveOffice), just never
|
||||||
|
* wired up here before. Concrete motivating case (user, 2026-08-21): the
|
||||||
|
* Assumption's octave (`Sancti/08-21bmv.txt`'s own `[Rule] ex Sancti/
|
||||||
|
* 08-15`) genuinely keeps the feast's own proper hymn all week, not the
|
||||||
|
* ferial one this always rendered previously. */
|
||||||
|
function resolveMatinsHymn(day: LiturgicalDay): ResolvedText {
|
||||||
|
const overrideId = getOfficeOverrideId(day);
|
||||||
|
if (overrideId) {
|
||||||
|
const proper = resolveCommon(`matins-hymn-${overrideId}`);
|
||||||
|
if (proper.status.la !== 'missing' || proper.status.en !== 'missing') {
|
||||||
|
return proper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const seasonSuffix = seasonalOfficeSuffix(day.season);
|
||||||
|
if (seasonSuffix) {
|
||||||
|
const seasonal = resolveCommon(`matins-hymn-${seasonSuffix}`);
|
||||||
|
if (seasonal.status.la !== 'missing' || seasonal.status.en !== 'missing') {
|
||||||
|
return seasonal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resolveCommon('matins-hymn-ferial');
|
||||||
|
}
|
||||||
|
|
||||||
function ferialPsalmody(day: LiturgicalDay): ResolvedPart[] {
|
function ferialPsalmody(day: LiturgicalDay): ResolvedPart[] {
|
||||||
return psalmRefParts(getPsalmsFor('matins', day.weekday));
|
return psalmRefParts(getPsalmsFor('matins', day.weekday));
|
||||||
}
|
}
|
||||||
@@ -317,7 +354,7 @@ export function resolveOrdo(date: string): ResolvedOrdo {
|
|||||||
plainPsalm(3),
|
plainPsalm(3),
|
||||||
{ kind: 'section-heading', label: 'Invitatory' },
|
{ kind: 'section-heading', label: 'Invitatory' },
|
||||||
...invitatoryParts(day),
|
...invitatoryParts(day),
|
||||||
{ kind: 'hymn', text: resolveCommon('matins-hymn-ferial') },
|
{ kind: 'hymn', text: resolveMatinsHymn(day) },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (threeNocturns && day.weekday === 'sunday') {
|
if (threeNocturns && day.weekday === 'sunday') {
|
||||||
|
|||||||
Reference in New Issue
Block a user