@ugfoundation/swaralipi-js 0.1.0

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.
Files changed (51) hide show
  1. package/LICENSE +347 -0
  2. package/README.md +257 -0
  3. package/dist/batch-NF2Q2CFS.js +47 -0
  4. package/dist/batch-NF2Q2CFS.js.map +1 -0
  5. package/dist/chunk-C6O7DYU5.js +115 -0
  6. package/dist/chunk-C6O7DYU5.js.map +1 -0
  7. package/dist/chunk-GWXI2HJA.js +14 -0
  8. package/dist/chunk-GWXI2HJA.js.map +1 -0
  9. package/dist/chunk-N3NNWAOW.js +593 -0
  10. package/dist/chunk-N3NNWAOW.js.map +1 -0
  11. package/dist/cli.cjs +913 -0
  12. package/dist/cli.cjs.map +1 -0
  13. package/dist/cli.d.cts +1 -0
  14. package/dist/cli.d.ts +1 -0
  15. package/dist/cli.js +91 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/convert/index.cjs +720 -0
  18. package/dist/convert/index.cjs.map +1 -0
  19. package/dist/convert/index.d.cts +270 -0
  20. package/dist/convert/index.d.ts +270 -0
  21. package/dist/convert/index.js +5 -0
  22. package/dist/convert/index.js.map +1 -0
  23. package/dist/index.cjs +143 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +4 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.js +4 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/player/index.cjs +775 -0
  30. package/dist/player/index.cjs.map +1 -0
  31. package/dist/player/index.d.cts +285 -0
  32. package/dist/player/index.d.ts +285 -0
  33. package/dist/player/index.js +743 -0
  34. package/dist/player/index.js.map +1 -0
  35. package/dist/schema-gxhG45OK.d.cts +467 -0
  36. package/dist/schema-gxhG45OK.d.ts +467 -0
  37. package/dist/spec/index.cjs +143 -0
  38. package/dist/spec/index.cjs.map +1 -0
  39. package/dist/spec/index.d.cts +70 -0
  40. package/dist/spec/index.d.ts +70 -0
  41. package/dist/spec/index.js +4 -0
  42. package/dist/spec/index.js.map +1 -0
  43. package/dist/taals-DguYW0wf.d.cts +60 -0
  44. package/dist/taals-DguYW0wf.d.ts +60 -0
  45. package/dist/viewer/index.cjs +998 -0
  46. package/dist/viewer/index.cjs.map +1 -0
  47. package/dist/viewer/index.d.cts +285 -0
  48. package/dist/viewer/index.d.ts +285 -0
  49. package/dist/viewer/index.js +814 -0
  50. package/dist/viewer/index.js.map +1 -0
  51. package/package.json +93 -0
@@ -0,0 +1,285 @@
1
+ import { O as Octave, a as SvaraLetter, N as Note, S as Swaralipi } from '../schema-gxhG45OK.js';
2
+ import 'zod';
3
+
4
+ /**
5
+ * Per-language glyph tables + Unicode cluster composition for the viewer.
6
+ *
7
+ * The akarmatrik system is script-portable: Bengali and Devanagari use the
8
+ * same letters-with-distinct-komal-forms convention (legend: কোমল র—ঋ,
9
+ * কোমল গ—জ্ঞ, কড়ি ম—ক্ষ, কোমল ধ—দ, কোমল ন—ণ), the same আ-কার matra carrier,
10
+ * the same reph-above for taar and understroke for mandra. `en` has no
11
+ * native convention, so it uses S R G M P D N with CSS marks (komal
12
+ * underline, tivra prime, octave dots) and no matra carrier.
13
+ *
14
+ * Unicode hazards owned here (see composeCluster):
15
+ * - mandra can NEVER be a real hasanta: letter + ্ + া is an invalid
16
+ * cluster (virama before vowel sign → dotted circle). It is always a
17
+ * CSS understroke (`sw-mandra`).
18
+ * - ঋ/ऋ (komal re) is an independent VOWEL letter: both র্+ঋ (reph over a
19
+ * vowel) and ঋ+া (vowel sign on a vowel) are invalid clusters. taar-ঋ
20
+ * falls back to a CSS reph (`sw-reph-css`); a matra following ঋ must be
21
+ * emitted as a separate isolated-matra span (`matraSeparate`).
22
+ * - Isolated dependent signs (া, ঃ) need a base: U+00A0 NBSP is the
23
+ * Unicode-sanctioned placeholder base and avoids dotted circles.
24
+ */
25
+
26
+ type ViewerLang = 'bn' | 'hi' | 'en';
27
+ interface LetterForms {
28
+ shuddha: string;
29
+ /** Distinct komal glyph (bn/hi); absent for en (CSS underline instead). */
30
+ komal?: string;
31
+ /** Distinct tivra glyph (bn/hi ক্ষ/क्ष); absent for en (CSS prime). */
32
+ tivra?: string;
33
+ }
34
+ declare const LETTERS: Record<ViewerLang, Record<SvaraLetter, LetterForms>>;
35
+ /** Digits 0-9 in the target script (beat labels, avartan). */
36
+ declare const DIGITS: Record<ViewerLang, string>;
37
+ interface LangMarks {
38
+ /** আ-কার matra carrier appended to a group-final cluster ('' for en). */
39
+ matra: string;
40
+ /** Melisma / no-new-attack dash before a sustained cluster. */
41
+ hyphen: string;
42
+ /** Lyric continuation mark shown under sustains (the source uses ৹). */
43
+ melisma: string;
44
+ /** ছেদ cut/articulation mark (two stacked rings, ঃ-shaped). */
45
+ cut: string;
46
+ /** Real-text reph prefix for taar ('' for en → CSS dot instead). */
47
+ reph: string;
48
+ }
49
+ declare const MARKS: Record<ViewerLang, LangMarks>;
50
+ /** Header labels (NLTR prints নাম / তাল / আবর্তন …). */
51
+ declare const LABELS: Record<ViewerLang, {
52
+ title: string;
53
+ taal: string;
54
+ raag: string;
55
+ avartan: string;
56
+ notationBy: string;
57
+ }>;
58
+ /** A composed notation cluster: text plus the CSS classes for marks the
59
+ * text itself cannot carry (mandra understroke, ati dots, en komal/tivra,
60
+ * CSS-reph fallback). */
61
+ interface Cluster {
62
+ text: string;
63
+ classes: string[];
64
+ /** Set when the matra could not join the cluster (ঋ/ऋ, or en where the
65
+ * carrier doesn't exist) and must be emitted as an isolated-matra span. */
66
+ matraSeparate: boolean;
67
+ }
68
+ interface ComposeOptions {
69
+ komal?: boolean;
70
+ tivra?: boolean;
71
+ octave: Octave;
72
+ /** Append the আ-কার matra carrier (group-final notes only). */
73
+ withMatra: boolean;
74
+ lang: ViewerLang;
75
+ }
76
+ /**
77
+ * Compose one svara cluster. The single place that knows Unicode
78
+ * composition order: reph? + base + matra?, with the ঋ/mandra/en fallbacks
79
+ * documented in the file header.
80
+ */
81
+ declare function composeCluster(letter: SvaraLetter, opts: ComposeOptions): Cluster;
82
+ /** Map ASCII digits in `s` to the target script's digits. */
83
+ declare function localizeDigits(s: string, lang: ViewerLang): string;
84
+
85
+ /**
86
+ * Pure spec-JSON → HTML renderer. No DOM, no side effects: usable for SSR
87
+ * (Astro/static sites emit `renderToHtml(doc)` next to a
88
+ * `<style>${SWARALIPI_CSS}</style>`) and unit-testable in plain Node.
89
+ *
90
+ * Layout model (mirrors NLTR's 4-row systems): each spec `Row` becomes one
91
+ * CSS grid with **one column per top-level note** and four grid rows —
92
+ * above-annotations / notation / below (slur arcs, marks) / lyric. Source
93
+ * cells (groups of sub-notes sharing one matra) become column *spans*, so
94
+ * per-sub-note lyric alignment and P2.3's per-note highlight targets fall
95
+ * out of the column model for free.
96
+ *
97
+ * Rendering is spec-driven (semantics, not `raw`): grouping derives from
98
+ * `durationMatra` accumulation so hand-authored documents without `raw`
99
+ * render identically. `raw` is consulted only for (a) raw-only fallback
100
+ * notes (never dropped — dimmed monospace), (b) the ছেদ cut mark and beat
101
+ * labels, whose grammar lives in raw (see src/convert/codec.ts).
102
+ */
103
+
104
+ interface RenderOptions {
105
+ /** Notation + UI language. Default `bn`. */
106
+ lang?: ViewerLang;
107
+ /** Render the NLTR-style নাম/তাল/আবর্তন header block. Default true. */
108
+ showHeader?: boolean;
109
+ /**
110
+ * Lyric/title transliterator for `hi`/`en` (see loadLyricTransliterator).
111
+ * Without it, non-bn renders keep Bengali lyrics (graceful interim).
112
+ */
113
+ transliterate?: (bengaliText: string) => string;
114
+ }
115
+ interface Cell {
116
+ kind: 'bar' | 'group' | 'raw' | 'carrier';
117
+ /** Index of the cell's first note within the row's notes array. */
118
+ start: number;
119
+ notes: Note[];
120
+ }
121
+ /**
122
+ * Group a row's notes into rendered cells using a fractional-duration
123
+ * accumulator (raw-independent — works for hand-authored docs):
124
+ * - bar / raw-only / carrier notes force-close and form singleton cells;
125
+ * - full-matra notes are singleton groups;
126
+ * - fractional notes accumulate until the sum reaches an integer matra
127
+ * boundary (epsilon for 1/3, 0.2 chains), then the group closes.
128
+ * The converter emits k notes × 1/k per source cell, so groups coincide
129
+ * with source cells; an incomplete trailing group still closes at row end
130
+ * (the matra carrier lands on its last note).
131
+ */
132
+ declare function groupRowIntoCells(notes: Note[]): Cell[];
133
+ interface Arc {
134
+ /** 1-based grid column of the arc's first / last note column. */
135
+ startCol: number;
136
+ endCol: number;
137
+ /** True when the matching end lies on a previous / following system. */
138
+ openLeft: boolean;
139
+ openRight: boolean;
140
+ /** Fill-segments-only row: render a straight underline, no curled ends. */
141
+ line?: boolean;
142
+ }
143
+ /**
144
+ * Resolve under-arcs for one row. Sources, scanned left→right:
145
+ * - below-annotation raws: `w` opens at its cell's first column, `x`
146
+ * closes at its cell's last column ("xw" in one cell = close-then-open);
147
+ * - spec `meend: 'start' | 'end'` fields feed the same scan as synthetic
148
+ * w/x at the note's own column.
149
+ * Unmatched `x` → arc from column 1 with a squared-off (open) left end;
150
+ * unclosed `w` → arc to the last column with an open right end — both
151
+ * match the print's behavior when an arc continues across a system wrap.
152
+ * A row with fill segments but no ends at all renders one straight line.
153
+ */
154
+ declare function resolveArcs(cells: Cell[], noteCount: number): Arc[];
155
+ /** Render a complete swaralipi document to an HTML string. Pair with
156
+ * SWARALIPI_CSS (shadow DOM does this automatically; SSR consumers emit a
157
+ * `<style>` themselves). All source text is HTML-escaped. */
158
+ declare function renderToHtml(doc: Swaralipi, opts?: RenderOptions): string;
159
+
160
+ /**
161
+ * Encapsulated stylesheet for the viewer. Injected into the
162
+ * `<swaralipi-sheet>` shadow root automatically; SSR consumers emit it
163
+ * themselves: `<style>${SWARALIPI_CSS}</style>` next to `renderToHtml()`.
164
+ *
165
+ * Theming hooks (CSS custom properties, all optional):
166
+ * --sw-font font stack (default: Noto Sans Bengali + system Bengali)
167
+ * --sw-size base font size
168
+ * --sw-cell-gap horizontal gap between source cells
169
+ * --sw-hl player-highlight background (P2.3)
170
+ * --sw-rule header rule / muted line color
171
+ *
172
+ * All ink uses `currentColor`, so the sheet follows the host's text color
173
+ * (dark mode included). Octave marks that Unicode cannot express in a valid
174
+ * cluster (mandra understroke, ati dots, ঋ-reph, en octave dots) are drawn
175
+ * with positioned pseudo-elements; offsets are em-based and tuned against
176
+ * the NLTR reference rendering of song 4270.
177
+ */
178
+ declare const SWARALIPI_CSS = "\n.sw-sheet {\n font-family: var(--sw-font, 'Noto Sans Bengali', 'Kohinoor Bangla', 'Bangla Sangam MN', 'Nirmala UI', 'Noto Sans Devanagari', sans-serif);\n font-size: var(--sw-size, 1.125rem);\n line-height: 1.5;\n}\n\n/* ---- header ---- */\n.sw-header {\n margin: 0 0 0.75em;\n padding: 0 0 0.5em;\n border-bottom: 1px solid var(--sw-rule, rgba(128, 128, 128, 0.35));\n font-size: 0.95em;\n}\n.sw-meta-row { display: flex; gap: 0.3em; }\n.sw-meta-label { font-weight: 600; min-width: 4.5em; }\n.sw-meta-label::after { content: ':'; font-weight: 400; margin-left: 0.15em; }\n\n.sw-part-name { font-size: 0.85em; font-weight: 600; margin: 1em 0 0.2em; opacity: 0.85; }\n\n/* ---- row grid: above / notation / below(arcs) / lyric ---- */\n.sw-row {\n display: flex;\n align-items: flex-start;\n overflow-x: auto;\n padding: 0.4em 0;\n}\n.sw-label { flex: none; margin-right: 0.8em; font-weight: 600; }\n.sw-grid {\n display: grid;\n grid-template-rows: auto auto auto auto;\n grid-auto-columns: max-content;\n align-items: end;\n}\n.sw-above { grid-row: 1; font-size: 0.62em; justify-self: start; white-space: nowrap; padding-left: 0.2em; }\n.sw-n { grid-row: 2; white-space: nowrap; }\n.sw-arc,\n.sw-below { grid-row: 3; }\n.sw-lyr { grid-row: 4; white-space: nowrap; padding-top: 0.3em; }\n.sw-below { font-size: 0.62em; justify-self: start; white-space: nowrap; padding-left: 0.2em; }\n\n/* inter-cell gap as padding on each cell's first notation item, so arcs\n * and above-spans crossing cells stay continuous */\n.sw-cs { padding-left: var(--sw-cell-gap, 0.55em); }\n\n/* ---- notation clusters & marks ---- */\n.sw-cl { position: relative; display: inline-block; }\n\n/* mandra: hasanta-shaped understroke (a real \u09CD before \u09BE is invalid Unicode) */\n.sw-mandra::after {\n content: '';\n position: absolute;\n left: 0.1em;\n bottom: -0.08em;\n width: 0.45em;\n height: 0.09em;\n background: currentColor;\n transform: rotate(-32deg);\n}\n/* ati-mandra: an extra dot below */\n.sw-ati-mandra::before {\n content: '';\n position: absolute;\n left: 0.25em;\n bottom: -0.35em;\n width: 0.16em;\n height: 0.16em;\n border-radius: 50%;\n background: currentColor;\n}\n/* ati-taar (reph already in the text): an extra dot above */\n.sw-ati-taar::before {\n content: '';\n position: absolute;\n left: 0.25em;\n top: -0.3em;\n width: 0.16em;\n height: 0.16em;\n border-radius: 50%;\n background: currentColor;\n}\n/* CSS reph fallback for vowel letters \u098B/\u090B that reject a real reph */\n.sw-reph-css::before {\n content: '';\n position: absolute;\n top: -0.12em;\n left: 0.12em;\n width: 0.3em;\n height: 0.3em;\n border: 0.09em solid currentColor;\n border-right: none;\n border-bottom: none;\n border-top-left-radius: 0.3em;\n}\n/* en octave dots (Bhatkhande-style) */\n.sw-dot-above::before,\n.sw-dot-below::before {\n content: '';\n position: absolute;\n left: 50%;\n margin-left: -0.07em;\n width: 0.14em;\n height: 0.14em;\n border-radius: 50%;\n background: currentColor;\n}\n.sw-dot-above::before { top: -0.25em; }\n.sw-dot-below::before { bottom: -0.2em; }\n.sw-ati-taar.sw-dot-above::before,\n.sw-ati-mandra.sw-dot-below::before { box-shadow: 0.24em 0 0 currentColor; margin-left: -0.19em; }\n/* en accidentals (bn/hi use distinct letters instead) */\n.sw-komal { text-decoration: underline; text-underline-offset: 0.18em; }\n.sw-tivra::after { content: '\\2032'; font-size: 0.7em; vertical-align: super; }\n\n/* kan grace: small raised cluster left of the main note */\n.sw-kan {\n position: relative;\n display: inline-block;\n font-size: 0.58em;\n vertical-align: 0.75em;\n margin-right: 0.08em;\n}\n\n.sw-hy { padding-right: 0.02em; }\n.sw-iso { display: inline-block; margin-left: -0.18em; }\n.sw-cut { font-size: 0.8em; display: inline-block; margin-left: -0.15em; }\n.sw-br { opacity: 0.8; padding: 0 0.06em; }\n.sw-micro { font-size: 0.6em; vertical-align: super; opacity: 0.8; }\n\n/* raw fallback: never dropped, visibly non-notation */\n.sw-raw {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.72em;\n opacity: 0.55;\n}\n\n/* ---- bars: dari (thin) and danda strokes, notation-row height ---- */\n/* horizontal gap before a bar comes from its own .sw-cs padding-left \u2014\n * never reset it with a padding shorthand here */\n.sw-bar {\n grid-row: 2;\n align-self: stretch;\n display: inline-flex;\n align-items: stretch;\n gap: 0.16em;\n padding-right: 0.1em;\n}\n.sw-bar i { display: inline-block; width: 0.14em; min-height: 1.25em; background: currentColor; }\n.sw-bar--dari i { width: 0.05em; opacity: 0.9; }\n.sw-bar--quad i:nth-child(3) { margin-left: 0.18em; }\n.sw-above .sw-bar, .sw-below .sw-bar { display: inline-flex; vertical-align: baseline; }\n.sw-above .sw-bar i, .sw-below .sw-bar i { min-height: 1em; }\n\n/* ---- under-arcs (slurs decoded from w/x segments and meend) ---- */\n.sw-arc {\n align-self: start;\n height: 0.4em;\n margin: 0.05em 0.25em 0;\n border: 0.09em solid currentColor;\n border-top: none;\n border-bottom-left-radius: 0.6em;\n border-bottom-right-radius: 0.6em;\n opacity: 0.85;\n}\n.sw-arc--openl { border-left: none; border-bottom-left-radius: 0; }\n.sw-arc--openr { border-right: none; border-bottom-right-radius: 0; }\n.sw-arc--line { border-left: none; border-right: none; border-radius: 0; height: 0.22em; }\n\n/* ---- annotation content ---- */\n.sw-beat { position: relative; display: inline-block; padding: 0 0.1em; }\n.sw-sam::after {\n content: '';\n position: absolute;\n top: -0.45em;\n left: 50%;\n width: 0.1em;\n height: 0.4em;\n background: currentColor;\n transform: rotate(14deg);\n}\n.sw-stop i {\n display: inline-block;\n width: 0.07em;\n height: 1em;\n margin: 0 0.07em;\n background: currentColor;\n vertical-align: text-bottom;\n}\n\n/* ---- player sync (P2.3) ---- */\n.sw-hl { background: var(--sw-hl, rgba(255, 196, 64, 0.45)); border-radius: 0.15em; }\n\n/* ---- element error box ---- */\n.sw-error {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.85em;\n padding: 0.6em 0.8em;\n border: 1px solid rgba(200, 64, 64, 0.6);\n border-radius: 0.3em;\n color: #b33;\n background: rgba(200, 64, 64, 0.08);\n}\n";
179
+ /**
180
+ * Print stylesheet — pair with `SWARALIPI_CSS` (renderPrintDocument injects
181
+ * both). Gated on `@media print`, so it also works if a consumer drops it into
182
+ * a page and prints that page directly.
183
+ *
184
+ * - `print-color-adjust: exact` forces the marks that are drawn as tiny
185
+ * `background: currentColor` pseudo-elements (octave dots, arcs, danda
186
+ * strokes, sam tick) to actually print — browsers drop backgrounds by
187
+ * default, which would silently strip the notation's diacritics.
188
+ * - rows are un-clipped (no scrollbar on paper) and kept whole across page
189
+ * breaks; part headings stay with their first row.
190
+ */
191
+ declare const SWARALIPI_PRINT_CSS = "\n@page { margin: 16mm; }\n@media print {\n .sw-sheet { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }\n .sw-row { overflow: visible !important; break-inside: avoid; page-break-inside: avoid; }\n .sw-part { break-inside: auto; }\n .sw-part-name, .sw-header { break-after: avoid-page; page-break-after: avoid; }\n .sw-hl { background: none !important; }\n}\n";
192
+
193
+ /**
194
+ * Print / "Save as PDF" support.
195
+ *
196
+ * High-fidelity PDF of আকারমাত্রিক notation can only come from the browser's
197
+ * own print engine — it reuses the exact HarfBuzz shaping (reph, conjuncts,
198
+ * matra) and the CSS-drawn marks (octave dots, slur arcs, danda strokes) that
199
+ * the viewer renders on screen. Rasterizers (html2canvas) and PDF-primitive
200
+ * libraries (jsPDF/pdfmake) have no Indic shaping and drop pseudo-element
201
+ * marks, so we deliberately avoid them.
202
+ *
203
+ * `renderPrintDocument` builds a self-contained HTML page from the pure
204
+ * `renderToHtml` + `SWARALIPI_CSS` (so it sidesteps the shadow-DOM "print drops
205
+ * encapsulated styles" gotcha); `printSwaralipi` prints it via a hidden iframe.
206
+ * Both reuse the same renderer as the viewer, so the PDF matches the screen.
207
+ */
208
+
209
+ interface PrintOptions {
210
+ /** Notation + UI language. Default `bn`. */
211
+ lang?: ViewerLang;
212
+ /** Render the নাম/তাল header block. Default true. */
213
+ showHeader?: boolean;
214
+ /** Lyric/title transliterator for hi/en (see loadLyricTransliterator). */
215
+ transliterate?: (bengaliText: string) => string;
216
+ /** Document `<title>` — the browser's default "Save as PDF" filename.
217
+ * Defaults to the document's `meta.title`. */
218
+ title?: string;
219
+ }
220
+ /**
221
+ * Build a complete, standalone HTML document for printing a swaralipi sheet:
222
+ * the rendered notation plus `SWARALIPI_CSS` and the print stylesheet, on a
223
+ * white page. Usable for a hidden-iframe print, a new browser tab, or
224
+ * server-side headless-Chrome PDF generation.
225
+ */
226
+ declare function renderPrintDocument(doc: Swaralipi, opts?: PrintOptions): string;
227
+ /**
228
+ * Open the browser print dialog for a swaralipi document (the user picks a
229
+ * printer or "Save as PDF"). Renders into an off-screen iframe so it prints
230
+ * only the sheet — independent of the host page's chrome and CSS — then cleans
231
+ * up. Browser-only; throws in a non-DOM environment (use renderPrintDocument
232
+ * for SSR / headless-Chrome instead).
233
+ */
234
+ declare function printSwaralipi(doc: Swaralipi, opts?: PrintOptions): void;
235
+
236
+ /**
237
+ * The `<swaralipi-sheet>` custom element — a thin shadow-DOM shell around
238
+ * the pure renderer.
239
+ *
240
+ * The element class is created lazily inside defineSwaralipiSheet():
241
+ * a top-level `extends HTMLElement` would throw on import in Node, and this
242
+ * module must stay importable for SSR (where only renderToHtml is used).
243
+ * Nothing registers automatically — call defineSwaralipiSheet() once.
244
+ *
245
+ * Validation is off by default (converter output is pre-validated); setting
246
+ * `validate = true` parses documents against the Zod schema via a dynamic
247
+ * import, so the viewer bundle stays free of bare runtime imports (it can
248
+ * be loaded directly from dist/ in a browser without a bundler).
249
+ */
250
+
251
+ interface SwaralipiSheetElement extends HTMLElement {
252
+ /** The document to render (property only; renders on assignment). */
253
+ doc: Swaralipi | undefined;
254
+ /** Parse documents against the spec schema before rendering. */
255
+ validate: boolean;
256
+ /** Add the player-highlight class to the note at "part.row.note". */
257
+ highlight(path: string): HTMLElement | null;
258
+ clearHighlights(): void;
259
+ /** Open the print dialog (→ "Save as PDF") for the current document. */
260
+ print(options?: PrintOptions): void;
261
+ }
262
+ /**
263
+ * Register `<swaralipi-sheet>` (or a custom tag name). Idempotent. Throws
264
+ * outside a DOM environment — use renderToHtml() for SSR.
265
+ */
266
+ declare function defineSwaralipiSheet(tagName?: string): CustomElementConstructor;
267
+
268
+ /**
269
+ * Lazy lyric transliteration for `hi` / `en` renders.
270
+ *
271
+ * Swara glyphs come from the sync tables in glyphs.ts; only *linguistic*
272
+ * text (lyrics, title, names) needs transliteration, so sanscript loads via
273
+ * dynamic import — `bn`-only consumers (and their bundles) never touch it.
274
+ */
275
+
276
+ type Transliterator = (bengaliText: string) => string;
277
+ /**
278
+ * Load a Bengali→target transliterator for the given viewer language.
279
+ * Returns undefined for `bn` (no work to do). Pass the result as
280
+ * `RenderOptions.transliterate`; the `<swaralipi-sheet>` element wires this
281
+ * automatically when its `lang` attribute changes.
282
+ */
283
+ declare function loadLyricTransliterator(lang: ViewerLang): Promise<Transliterator | undefined>;
284
+
285
+ export { type Arc, type Cell, type Cluster, type ComposeOptions, DIGITS, LABELS, LETTERS, MARKS, type PrintOptions, type RenderOptions, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, type SwaralipiSheetElement, type Transliterator, type ViewerLang, composeCluster, defineSwaralipiSheet, groupRowIntoCells, loadLyricTransliterator, localizeDigits, printSwaralipi, renderPrintDocument, renderToHtml, resolveArcs };