calliope-ts 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +8 -0
- package/.opencode/skills/calliope-ts/SKILL.md +74 -0
- package/LICENSE +201 -0
- package/README.md +485 -0
- package/dist/depfix.d.ts +13 -0
- package/dist/depfix.d.ts.map +1 -0
- package/dist/depfix.js +84 -0
- package/dist/display.d.ts +38 -0
- package/dist/display.d.ts.map +1 -0
- package/dist/display.js +890 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +504 -0
- package/dist/parser.d.ts +10 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +688 -0
- package/dist/phonological.d.ts +41 -0
- package/dist/phonological.d.ts.map +1 -0
- package/dist/phonological.js +788 -0
- package/dist/rhyme.d.ts +26 -0
- package/dist/rhyme.d.ts.map +1 -0
- package/dist/rhyme.js +340 -0
- package/dist/scandroid.d.ts +17 -0
- package/dist/scandroid.d.ts.map +1 -0
- package/dist/scandroid.js +435 -0
- package/dist/scansion.d.ts +37 -0
- package/dist/scansion.d.ts.map +1 -0
- package/dist/scansion.js +1007 -0
- package/dist/scansion_debug.js +586 -0
- package/dist/stress.d.ts +32 -0
- package/dist/stress.d.ts.map +1 -0
- package/dist/stress.js +1372 -0
- package/dist/tagfix.d.ts +6 -0
- package/dist/tagfix.d.ts.map +1 -0
- package/dist/tagfix.js +101 -0
- package/dist/types.d.ts +173 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/package.json +62 -0
- package/src/depfix.ts +88 -0
- package/src/display.ts +954 -0
- package/src/index.ts +541 -0
- package/src/parser.ts +837 -0
- package/src/phonological.ts +849 -0
- package/src/rhyme.ts +328 -0
- package/src/scandroid.ts +434 -0
- package/src/scansion.ts +1053 -0
- package/src/stress.ts +1381 -0
- package/src/tagfix.ts +104 -0
- package/src/types.ts +230 -0
package/src/tagfix.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// tagfix.ts — Pre-parse POS-tag correction layer.
|
|
2
|
+
//
|
|
3
|
+
// FinNLP's en-pos tagger is structurally sound but carries a small tail of
|
|
4
|
+
// SYSTEMATIC tag errors that matter enormously for verse analysis, because a
|
|
5
|
+
// wrong tag flips a word's content/function status (→ its stress tier) and
|
|
6
|
+
// derails the en-parse dependency tree built from the tags. This pass runs
|
|
7
|
+
// BETWEEN en-pos and en-parse (see parseDocument in parser.ts), so corrected
|
|
8
|
+
// tags repair both the tagging AND the resulting dependency structure — a
|
|
9
|
+
// post-hoc fix of the parse could never do that.
|
|
10
|
+
//
|
|
11
|
+
// Every rule below targets an error class actually observed in this repo's
|
|
12
|
+
// trials; rules are deliberately narrow (anti-gaming: each must be justified
|
|
13
|
+
// by the error it fixes, not by benchmark deltas).
|
|
14
|
+
|
|
15
|
+
/** Zero-derived irregular past participles that en-pos tags NN/VBP after a
|
|
16
|
+
* have-auxiliary ("had quit", "has put", "have read"). Only forms whose
|
|
17
|
+
* participle is identical to the base/noun spelling — the -en/-ed forms tag
|
|
18
|
+
* fine on their own. */
|
|
19
|
+
const ZERO_PARTICIPLES = new Set([
|
|
20
|
+
'quit', 'put', 'set', 'cut', 'hit', 'let', 'shut', 'cast', 'cost', 'hurt',
|
|
21
|
+
'burst', 'split', 'spread', 'bet', 'wed', 'read', 'rid', 'shed', 'thrust',
|
|
22
|
+
'slit', 'bid', 'broadcast', 'upset', 'sunburst',
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const HAVE_FORMS = new Set(['have', 'has', 'had', 'having', "'ve", "'d"]);
|
|
26
|
+
|
|
27
|
+
/** Archaic / Early-Modern-English forms en-pos has no lexicon entries for —
|
|
28
|
+
* ubiquitous in the verse this toolkit exists to scan. */
|
|
29
|
+
const ARCHAIC_TAGS: Record<string, string> = {
|
|
30
|
+
thou: 'PRP', thee: 'PRP', ye: 'PRP',
|
|
31
|
+
thy: 'PRP$', thine: 'PRP$',
|
|
32
|
+
art: 'VBP', wert: 'VBD', wast: 'VBD',
|
|
33
|
+
doth: 'VBZ', hath: 'VBZ', dost: 'VBZ', hast: 'VBZ', saith: 'VBZ',
|
|
34
|
+
didst: 'VBD', hadst: 'VBD', wouldst: 'MD', couldst: 'MD', shouldst: 'MD',
|
|
35
|
+
shalt: 'MD', wilt: 'MD', canst: 'MD', mayst: 'MD', 'mightst': 'MD',
|
|
36
|
+
wherefore: 'WRB', whither: 'WRB', whence: 'WRB',
|
|
37
|
+
hither: 'RB', thither: 'RB', yon: 'JJ', yonder: 'RB',
|
|
38
|
+
ere: 'IN', oft: 'RB', anon: 'RB',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Correct a sentence's tags in place-safe fashion (returns a new array).
|
|
43
|
+
* `tokens` and `tags` are the en-pos outputs, index-aligned.
|
|
44
|
+
*/
|
|
45
|
+
export function correctTags(tokens: string[], tags: string[]): string[] {
|
|
46
|
+
const out = tags.slice();
|
|
47
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
48
|
+
const w = tokens[i].toLowerCase();
|
|
49
|
+
|
|
50
|
+
// 1. The pronoun "I". en-norm lowercases sentence-initial "I" → "i",
|
|
51
|
+
// which en-pos then reads as a foreign word / letter name (FW).
|
|
52
|
+
if (w === 'i' && out[i] === 'FW') out[i] = 'PRP';
|
|
53
|
+
|
|
54
|
+
// 2. Archaic forms (thou/thy/doth/shalt/wherefore…): en-pos guesses
|
|
55
|
+
// NN/JJ/FW for these, wrecking both stress class and the parse.
|
|
56
|
+
// Guard "art": only when a pronoun precedes ("thou art"), since the
|
|
57
|
+
// noun reading ("the art of…") is the modern default.
|
|
58
|
+
const archaic = ARCHAIC_TAGS[w];
|
|
59
|
+
if (archaic && !/^(NNP|NNPS)$/.test(out[i])) {
|
|
60
|
+
if (w === 'art') {
|
|
61
|
+
const prev = i > 0 ? tokens[i - 1].toLowerCase() : '';
|
|
62
|
+
if (prev === 'thou' || prev === 'ye' || prev === 'you') out[i] = 'VBP';
|
|
63
|
+
} else {
|
|
64
|
+
out[i] = archaic;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 3. Perfect-tense zero participles: have-form + ("quit"/"put"/"read"…)
|
|
69
|
+
// tagged as NN/VBP/VBD → VBN, so en-parse builds the verb chain
|
|
70
|
+
// instead of treating the participle as a direct-object noun
|
|
71
|
+
// ("I had quit the programming paradigm"). An intervening adverb
|
|
72
|
+
// ("had just quit") is allowed.
|
|
73
|
+
if (ZERO_PARTICIPLES.has(w) && /^(NN|NNS|VBP|VBD|VB)$/.test(out[i])) {
|
|
74
|
+
const prev1 = i > 0 ? tokens[i - 1].toLowerCase() : '';
|
|
75
|
+
const prev2 = i > 1 ? tokens[i - 2].toLowerCase() : '';
|
|
76
|
+
const prev1IsAdv = i > 0 && /^RB/.test(out[i - 1]);
|
|
77
|
+
if (HAVE_FORMS.has(prev1) || (prev1IsAdv && HAVE_FORMS.has(prev2))) {
|
|
78
|
+
out[i] = 'VBN';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 4. Impossible gerunds: a VBG tag on a token that does not end in
|
|
83
|
+
// -ing/-in' cannot be a gerund/present participle — it is an en-pos
|
|
84
|
+
// lexicon glitch. The right tag depends on context: before a noun it
|
|
85
|
+
// is a noun modifier ("wisdom"/VBG teeth → NN); after a subject
|
|
86
|
+
// pronoun it is a finite verb ("as they bicycle/VBG through" → VBP,
|
|
87
|
+
// which keeps "through" a phrasal particle in the parse). With no
|
|
88
|
+
// deciding context, leave the tag alone (en-parse treats VBG
|
|
89
|
+
// verb-ishly, the safer default).
|
|
90
|
+
if (out[i] === 'VBG' && !/in[g'’]?$/.test(w)) {
|
|
91
|
+
const prevTag = i > 0 ? out[i - 1] : '';
|
|
92
|
+
const nextTag = i + 1 < tokens.length ? out[i + 1] : '';
|
|
93
|
+
if (/^NNS?$/.test(nextTag)) out[i] = 'NN';
|
|
94
|
+
else if (prevTag === 'PRP') out[i] = 'VBP';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 5. Vocative "O" ("O wild West Wind"): en-pos gives NNP/JJ; it is an
|
|
98
|
+
// interjection (and must not become a content word with a beat by
|
|
99
|
+
// default). Only the bare capital O — "o'er" etc. are handled by the
|
|
100
|
+
// aphaeresis lexicon in stress.ts.
|
|
101
|
+
if (tokens[i] === 'O' && i + 1 < tokens.length && out[i] !== 'UH') out[i] = 'UH';
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// types.ts — Complete type declarations for Calliope_TS pipeline
|
|
2
|
+
// Reflects McAleese’s class diagrams (Figure 14) and additional phonological /
|
|
3
|
+
// metrical types required by stress, phonological hierarchy, scansion, and Scandroid modules.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Stress levels in McAleese’s relative system, in ascending order
|
|
7
|
+
* `x < w < n < m < s`. `x` is the "Zero Provision" tier (Hayes; Lerdahl &
|
|
8
|
+
* Jackendoff 1983): a level *weaker than a stressless overt syllable*, borne by
|
|
9
|
+
* maximally-reduced clitics (the/a/of/and…) and unfilled positions.
|
|
10
|
+
*/
|
|
11
|
+
export type StressLevel = 'x' | 'w' | 'n' | 'm' | 's';
|
|
12
|
+
|
|
13
|
+
/** A single syllable within a word, with phonetic and stress information */
|
|
14
|
+
export interface Syllable {
|
|
15
|
+
text: string; // the orthographic syllable (or entire word if unsplit)
|
|
16
|
+
phones: string; // CMU phonetic transcription (per-syllable ARPAbet)
|
|
17
|
+
weight?: 'H' | 'L'; // syllable weight (Heavy/Light)
|
|
18
|
+
stress: number; // numeric stress from CMU; modified by compound + nuclear rules
|
|
19
|
+
lexicalStress?: number; // stress before nuclear rule; used for relative mapping / meter detection
|
|
20
|
+
relativeStress?: StressLevel; // assigned after phrase‑stress rules and relative adjustment
|
|
21
|
+
extrametrical?: 'morphological' | 'light_noun' | 'derivational'; // extrametricality classification
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Represents a word in the dependency‑parse graph */
|
|
25
|
+
export interface ClsWord {
|
|
26
|
+
index: number; // 1‑based index in the sentence (matching Antelope’s XML)
|
|
27
|
+
lexicalClass: string; // POS tag, e.g., 'VBD', 'NN', 'PRP'
|
|
28
|
+
lexicalDetails: string; // additional morphological info (empty if none)
|
|
29
|
+
lexicalPlural: boolean; // true if plural
|
|
30
|
+
position: string; // textual position (not always used)
|
|
31
|
+
word: string; // the surface form of the word
|
|
32
|
+
absoluteIndex: number; // 0‑based index among all words in the text
|
|
33
|
+
isContent: boolean;
|
|
34
|
+
// extended properties
|
|
35
|
+
syllables: Syllable[]; // array of syllables for the word
|
|
36
|
+
morphSuffix?: string; // productive suffix split off for OOV stress (e.g. 'est'); guides display syllabification
|
|
37
|
+
phraseStress: number; // numeric phrase‑level stress after Nuclear Stress Rule
|
|
38
|
+
dependency?: ClsDependency; // back‑reference to dependency edge (if any)
|
|
39
|
+
node?: ClsNode; // back‑reference to the constituent node (if any)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A typed dependency edge between two words (as in Antelope’s XML) */
|
|
43
|
+
export interface ClsDependency {
|
|
44
|
+
index: number; // 1‑based dependency index
|
|
45
|
+
governorIndex: number; // word index of the governor (head)
|
|
46
|
+
dependentIndex: number; // word index of the dependent
|
|
47
|
+
dependentType: string; // type label: 'aux','nsubj','dobj','prep','det','poss','possessive','pobj',…
|
|
48
|
+
governorName: string; // surface form of the governor
|
|
49
|
+
dependentName: string; // surface form of the dependent
|
|
50
|
+
governor: ClsWord; // reference to the governor word object
|
|
51
|
+
dependent: ClsWord; // reference to the dependent word object
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A constituent node in the parse tree (mirrors Figure 14) */
|
|
55
|
+
export interface ClsNode {
|
|
56
|
+
index: string; // node identifier, e.g., '1', '1.2'
|
|
57
|
+
nodeName: string; // label (e.g., 'SQ', 'NP', 'VP', 'PP', or a word index)
|
|
58
|
+
parent: ClsNode | null; // parent node, null for root
|
|
59
|
+
contains: (ClsNode | ClsWord)[]; // children: either sub‑nodes or words
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A single parsed sentence */
|
|
63
|
+
export interface ClsSentence {
|
|
64
|
+
index: number; // sentence number (1‑based)
|
|
65
|
+
nodes: ClsNode | null; // root node of the parse tree
|
|
66
|
+
dependencies: ClsDependency[]; // all dependency edges in this sentence
|
|
67
|
+
words: ClsWord[]; // word objects in order
|
|
68
|
+
xml: string; // serialised XML representation (optional)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Top‑level document containing parsed sentences */
|
|
72
|
+
export interface ClsDocument {
|
|
73
|
+
sentences: ClsSentence[]; // list of parsed sentences
|
|
74
|
+
xml: string; // full XML document (optional)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Phonological Hierarchy (CP, PP, IU) ────────────────────────────
|
|
78
|
+
|
|
79
|
+
/** A clitic group: one content word plus its attached function words */
|
|
80
|
+
export interface CliticGroup {
|
|
81
|
+
tokens: ClsWord[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A phonological phrase: one or more clitic groups related syntactically */
|
|
85
|
+
export interface PhonologicalPhrase {
|
|
86
|
+
cliticGroups: CliticGroup[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** An intonational unit: one or more phonological phrases bounded by punctuation/line‑end */
|
|
90
|
+
export interface IntonationalUnit {
|
|
91
|
+
phonologicalPhrases: PhonologicalPhrase[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Metre‑ and scansion‑related types ─────────────────────────────
|
|
95
|
+
|
|
96
|
+
/** Recognised metre names */
|
|
97
|
+
export type MetreName =
|
|
98
|
+
| 'iambic'
|
|
99
|
+
| 'trochaic'
|
|
100
|
+
| 'spondaic'
|
|
101
|
+
| 'pyrrhic'
|
|
102
|
+
| 'anapestic'
|
|
103
|
+
| 'dactylic'
|
|
104
|
+
| 'amphibrachic'
|
|
105
|
+
| 'bacchic';
|
|
106
|
+
|
|
107
|
+
/** Definition of a metre candidate: its foot shape and syllable count per foot */
|
|
108
|
+
export interface MetreCandidate {
|
|
109
|
+
name: MetreName;
|
|
110
|
+
foot: string; // e.g., 'ws', 'sw', 'wws'
|
|
111
|
+
syllableCount: number; // number of syllables in one foot (2 or 3)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A key‑stress pattern extracted from one unit of the phonological hierarchy */
|
|
115
|
+
export interface KeyStress {
|
|
116
|
+
unitType: 'PW' | 'CP' | 'PP' | 'IU'; // type of prosodic unit
|
|
117
|
+
pattern: string; // stress pattern, e.g., 'ws', 'sw', 'wsw'
|
|
118
|
+
weight: number; // importance weight (1–3) as per McAleese’s scoring
|
|
119
|
+
positions: number[]; // global syllable indices involved in this key stress
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Result of scansion for a single line */
|
|
124
|
+
export interface ScansionResult {
|
|
125
|
+
meter: MetreName | 'free verse'; // identified metre, or free verse if below threshold
|
|
126
|
+
scansion: string; // the foot‑delimited scansion string, e.g., "ws|ws|ws|ws|ws"
|
|
127
|
+
certainty: number; // percentage of maximum possible weighted score
|
|
128
|
+
weightScore: number; // actual accumulated weight
|
|
129
|
+
maxPossibleWeight: number; // theoretical maximum weight for the line
|
|
130
|
+
algorithm?: string; // optional, to distinguish Phonological vs Scandroid results
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** One candidate meter's overall fit score (internal composite, not a probability) */
|
|
134
|
+
export interface MeterScore {
|
|
135
|
+
meter: MetreName | 'free verse';
|
|
136
|
+
score: number; // scoreMeters' finalScore — a relative fit score, higher = better
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Detailed phonological scansion for a single line */
|
|
140
|
+
export interface PhonologicalScansionDetail {
|
|
141
|
+
all: string; // hierarchical string, e.g. "<{[nm/ws\n]}mn/sw\]m]}>"
|
|
142
|
+
keyStresses: string; // key‑stress string, e.g. "<{[xx/ws\n]}xx/sw\]m]}>"
|
|
143
|
+
meter: string; // e.g. "iambic pentameter"
|
|
144
|
+
meterName: MetreName | 'free verse'; // enum value for tests
|
|
145
|
+
footCount: number; // e.g. 5
|
|
146
|
+
summary: string; // e.g. "IU=1 PP=1 PW=1" counts per metre direction
|
|
147
|
+
scansion: string; // foot‑separated, e.g. "xx/ws|nx/xs/wm"
|
|
148
|
+
certainty: number; // 0‑100
|
|
149
|
+
weightScore: number;
|
|
150
|
+
maxPossibleWeight: number;
|
|
151
|
+
ranking?: MeterScore[]; // candidate meters ranked by fit score (best first); optional
|
|
152
|
+
consensusMeter?: string; // set by applyStanzaConsensus when this line's standalone meter
|
|
153
|
+
// diverges from, yet closely fits, the stanza's dominant meter.
|
|
154
|
+
// The continuity-rename pass (index.ts) then normally CONVERTS
|
|
155
|
+
// this annotation: the line adopts the dominant meter as its BASE
|
|
156
|
+
// reading (meter/scansion/footCount/certainty re-fitted under it),
|
|
157
|
+
// consensusMeter is cleared, and standaloneMeter records the
|
|
158
|
+
// numerically-best standalone reading. consensusMeter survives
|
|
159
|
+
// only when the forced re-fit fails.
|
|
160
|
+
standaloneMeter?: string; // the line's numerically best standalone meter (e.g. "dactylic
|
|
161
|
+
// tetrameter"), kept when stanza/poem continuity renamed the base
|
|
162
|
+
// reading to the dominant meter.
|
|
163
|
+
rhythmNote?: string; // non-classical rhythm classification (Russian-metrics taxonomy):
|
|
164
|
+
// "4-ictus dolnik", "3-ictus taktovik", "4-beat accentual",
|
|
165
|
+
// "alternating 4·3-ictus accentual" — set at stanza level when
|
|
166
|
+
// beat counts are regular but syllable counts vary and no
|
|
167
|
+
// classical meter dominates; or per-line to refine a free-verse
|
|
168
|
+
// reading. NOT a form verdict: "ballad" etc. belong to the
|
|
169
|
+
// rhyme-aware form layer (rhyme.ts).
|
|
170
|
+
rhyme?: { // end-rhyme annotation (rhyme.ts), LYRICAL-compatible typology
|
|
171
|
+
endWord: string;
|
|
172
|
+
letter: string; // scheme letter 'A'/'B'/…, or '·' when unrhymed
|
|
173
|
+
type?: string; // perfect/rich/family/assonant/consonant/augmented/
|
|
174
|
+
// diminished/wrenched/eye/identical
|
|
175
|
+
matchedLine?: number; // 0-based stanza line this one rhymes with
|
|
176
|
+
};
|
|
177
|
+
formNote?: string; // stanza/poem FORM verdict (rhyme-aware): "ballad stanza
|
|
178
|
+
// (ABCB, 4·3)", "blank verse", "Shakespearean Sonnet",
|
|
179
|
+
// "terza rima (ABA BCB CDC…)", "limerick"…
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Complete per‑line result from the pipeline */
|
|
183
|
+
export interface LineResult {
|
|
184
|
+
sentence: ClsSentence; // parsed sentence with words, dependencies, nodes
|
|
185
|
+
phonologicalHierarchy: IntonationalUnit[]; // CP/PP/IU structure
|
|
186
|
+
keyStresses: KeyStress[]; // extracted key patterns with weights
|
|
187
|
+
phonologicalScansion: PhonologicalScansionDetail; // scansion via phonological scoring
|
|
188
|
+
scandroidCorral?: ScansionResult; // optional, scansion via Scandroid's 'Corral the Weird'
|
|
189
|
+
scandroidMaximise?: ScansionResult; // optional, scansion via Scandroid's 'Maximise the Normal'
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ─── Display formatting types ──────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/** Per-syllable information for display rendering */
|
|
195
|
+
export interface SyllableDisplayEntry {
|
|
196
|
+
wordText: string;
|
|
197
|
+
sylText: string; // orthographic text of this syllable
|
|
198
|
+
sylIndex: number; // 0‑based index within the word
|
|
199
|
+
sylCount: number; // total syllables in the word
|
|
200
|
+
relativeStress: StressLevel;
|
|
201
|
+
globalIndex: number;
|
|
202
|
+
wordIndex: number; // 0‑based index among non-punctuation words
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** A single foot in the display, mapping scansion pattern to its syllables */
|
|
206
|
+
export interface FootDisplayEntry {
|
|
207
|
+
footIndex: number;
|
|
208
|
+
footPattern: string; // raw foot pattern from scansion, e.g., 'ws', '-ms'
|
|
209
|
+
syllables: SyllableDisplayEntry[];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** All formatted display representations for a single line */
|
|
213
|
+
export interface FormattedDisplay {
|
|
214
|
+
originalText: string;
|
|
215
|
+
diacriticText: string;
|
|
216
|
+
uppercaseText: string;
|
|
217
|
+
ansiText: string;
|
|
218
|
+
sylColoredText: string;
|
|
219
|
+
footAligned: string;
|
|
220
|
+
syllableBreakdown: string;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Options controlling display formatting */
|
|
224
|
+
export interface DisplayOptions {
|
|
225
|
+
ansi: boolean;
|
|
226
|
+
diacritics: boolean;
|
|
227
|
+
footAligned: boolean;
|
|
228
|
+
verbose: boolean;
|
|
229
|
+
phrasal: boolean;
|
|
230
|
+
}
|