@ugfoundation/swaralipi-js 0.8.2 → 0.9.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.
Files changed (52) hide show
  1. package/README.md +1 -1
  2. package/dist/{batch-6FPBT5DP.js → batch-NEM7JXHV.js} +5 -5
  3. package/dist/{batch-6FPBT5DP.js.map → batch-NEM7JXHV.js.map} +1 -1
  4. package/dist/{chunk-UMBKIGVQ.js → chunk-DOEFSS66.js} +5 -4
  5. package/dist/chunk-DOEFSS66.js.map +1 -0
  6. package/dist/{chunk-4HNT6OAT.js → chunk-MYVIQU4O.js} +12 -3
  7. package/dist/chunk-MYVIQU4O.js.map +1 -0
  8. package/dist/{chunk-4IVUPMRK.js → chunk-SEN266BY.js} +28 -13
  9. package/dist/chunk-SEN266BY.js.map +1 -0
  10. package/dist/{chunk-FULC22MN.js → chunk-Z6A3CP5V.js} +28 -19
  11. package/dist/chunk-Z6A3CP5V.js.map +1 -0
  12. package/dist/cli.cjs +33 -19
  13. package/dist/cli.cjs.map +1 -1
  14. package/dist/cli.js +3 -3
  15. package/dist/convert/index.cjs +31 -17
  16. package/dist/convert/index.cjs.map +1 -1
  17. package/dist/convert/index.d.cts +1 -1
  18. package/dist/convert/index.d.ts +1 -1
  19. package/dist/convert/index.js +3 -3
  20. package/dist/index.cjs +38 -17
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +2 -2
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +2 -2
  25. package/dist/{lay-Csa4CD46.d.cts → lay-CZIzR9cM.d.ts} +16 -2
  26. package/dist/{lay-Ct9ky_EE.d.ts → lay-CfDlPhH2.d.cts} +16 -2
  27. package/dist/player/index.cjs +116 -71
  28. package/dist/player/index.cjs.map +1 -1
  29. package/dist/player/index.d.cts +11 -9
  30. package/dist/player/index.d.ts +11 -9
  31. package/dist/player/index.js +89 -66
  32. package/dist/player/index.js.map +1 -1
  33. package/dist/{schedule-B_UaluBi.d.cts → schedule-BaovCtmf.d.ts} +31 -12
  34. package/dist/{schedule-CdILTNsk.d.ts → schedule-CU-VECDH.d.cts} +31 -12
  35. package/dist/{schema-DGA_bNVZ.d.ts → schema-Cf_epUIC.d.cts} +333 -63
  36. package/dist/{schema-DGA_bNVZ.d.cts → schema-Cf_epUIC.d.ts} +333 -63
  37. package/dist/spec/index.cjs +38 -17
  38. package/dist/spec/index.cjs.map +1 -1
  39. package/dist/spec/index.d.cts +26 -8
  40. package/dist/spec/index.d.ts +26 -8
  41. package/dist/spec/index.js +2 -2
  42. package/dist/viewer/index.cjs +156 -73
  43. package/dist/viewer/index.cjs.map +1 -1
  44. package/dist/viewer/index.d.cts +25 -7
  45. package/dist/viewer/index.d.ts +25 -7
  46. package/dist/viewer/index.js +78 -27
  47. package/dist/viewer/index.js.map +1 -1
  48. package/package.json +1 -1
  49. package/dist/chunk-4HNT6OAT.js.map +0 -1
  50. package/dist/chunk-4IVUPMRK.js.map +0 -1
  51. package/dist/chunk-FULC22MN.js.map +0 -1
  52. package/dist/chunk-UMBKIGVQ.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { O as Octave, a as SvaraLetter, N as Note, M as Meta, S as Swaralipi, T as Taal } from '../schema-DGA_bNVZ.cjs';
2
- import { L as Lay } from '../lay-Csa4CD46.cjs';
3
- import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-B_UaluBi.cjs';
1
+ import { O as Octave, a as SvaraLetter, N as Note, M as Meta, S as Swaralipi, T as Taal } from '../schema-Cf_epUIC.cjs';
2
+ import { L as Lay } from '../lay-CfDlPhH2.cjs';
3
+ import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-CU-VECDH.cjs';
4
4
  import 'zod';
5
5
  import 'tone';
6
6
 
@@ -31,6 +31,9 @@ declare const LABELS: Record<GridLang, {
31
31
  * term; en names the tempo first (a reader of the English sheet may not know
32
32
  * the word — the same call `LABELS.en` makes with "Cycles" over "Avartan"). */
33
33
  declare const LAY_LABELS: Record<GridLang, Record<Lay, string>>;
34
+ /** Printed where a row switches to free rhythm (`row.taal: null`) with no
35
+ * `taalName` of its own. bn is the converter's canonical মুক্তছন্দ. */
36
+ declare const FREE_TAAL_LABELS: Record<GridLang, string>;
34
37
  /** A composed notation cluster: the font's ASCII text plus any residual CSS
35
38
  * classes the font itself cannot express (only the bn ati-octave dot). */
36
39
  interface Cluster {
@@ -214,7 +217,7 @@ declare const ZOOM_STEP = 1.2;
214
217
  * --sw-matra minimum width of one matra column
215
218
  * --sw-header-min floor for the নাম/তাল/আবর্তন header (it must stay legible
216
219
  * when the notation is scaled down to fit a phone)
217
- * --sw-lay-min same floor for the লয় (tempo tier) marker band
220
+ * --sw-lay-min same floor for the তাল / লয় change band
218
221
  * --sw-hl player-highlight background (P2.3)
219
222
  * --sw-rule header rule / muted line color
220
223
  *
@@ -242,7 +245,7 @@ declare const SWARALIPI_CSS: string;
242
245
  * - rows are un-clipped (no scrollbar on paper) and kept whole across page
243
246
  * breaks; part headings stay with their first row.
244
247
  */
245
- declare const SWARALIPI_PRINT_CSS = "\n@page { margin: 16mm; }\n@media print {\n .sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }\n .sw-scroll { overflow: visible !important; }\n .sw-row { break-inside: avoid; page-break-inside: avoid; }\n .sw-part-name, .sw-header, .sw-lay { break-after: avoid-page; page-break-after: avoid; }\n .sw-hl { background: none !important; }\n /* Keep each engraved staff system whole across a page break (the abcjs line\n is a <div> wrapping the <svg>, so the break rule must sit on the div). */\n .sw-staff-lines > div { break-inside: avoid; page-break-inside: avoid; }\n .sw-staff-meta { break-after: avoid-page; page-break-after: avoid; }\n}\n";
248
+ declare const SWARALIPI_PRINT_CSS = "\n@page { margin: 16mm; }\n@media print {\n .sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }\n .sw-scroll { overflow: visible !important; }\n .sw-row { break-inside: avoid; page-break-inside: avoid; }\n .sw-part-name, .sw-header, .sw-change { break-after: avoid-page; page-break-after: avoid; }\n .sw-hl { background: none !important; }\n /* Keep each engraved staff system whole across a page break (the abcjs line\n is a <div> wrapping the <svg>, so the break rule must sit on the div). */\n .sw-staff-lines > div { break-inside: avoid; page-break-inside: avoid; }\n .sw-staff-meta { break-after: avoid-page; page-break-after: avoid; }\n}\n";
246
249
  /** Staff-notation (abcjs) wrapper chrome: instrument/tempo header + a fluid
247
250
  * SVG. The engraving itself is drawn by abcjs; this only frames it. */
248
251
  declare const SWARALIPI_STAFF_CSS = "\n.sw-staff { color: currentColor; }\n.sw-staff-meta { display: flex; gap: 1.25em; align-items: baseline; margin: 0 0 .5em; font-size: .95em; opacity: .85; }\n.sw-staff-instrument { font-weight: 600; }\n.sw-staff-drums { opacity: .75; }\n.sw-staff-tempo { font-variant-numeric: tabular-nums; }\n/* abcjs (oneSvgPerLine) wraps the music into one <div><svg> per system; stack\n them as short, swaralipi-style lines. abcjs sets a fixed pixel height +\n overflow:hidden on each wrapper div \u2014 neutralise it so the viewBox'd SVG can\n scale to the container (and to the print page) instead of being clipped. */\n.sw-staff-lines > div { margin: 0 0 .35em; height: auto !important; overflow: visible !important; }\n/* Fill up to the engraved (staffwidth) size \u2014 shrink to fit a narrow container,\n but never stretch past natural width (abcToSvg pins per-SVG max-width in px),\n else a short last system balloons. */\n.sw-staff-lines svg { display: block; width: 100%; height: auto; }\n.sw-staff-missing { padding: 1em; border: 1px dashed currentColor; border-radius: 6px; opacity: .8; font-size: .9em; }\n.sw-staff-title { font-size: 1.15em; font-weight: 700; }\n.sw-staff-subtitle { opacity: .8; font-size: .95em; margin-top: .15em; }\n";
@@ -311,6 +314,15 @@ declare function midiToAbc(midi: number): string;
311
314
  * nearest 1/4-matra count of `/4` units so odd durations still sum correctly.
312
315
  */
313
316
  declare function abcDuration(beats: number): string;
317
+ /** One তাল stretch of the score: its meter, and drum voices when drums are on. */
318
+ interface AbcMeter {
319
+ /** Start, in schedule beats. */
320
+ beat: number;
321
+ /** Matra per bar (one তাল cycle) from here on. */
322
+ beatsPerBar: number;
323
+ /** Per-matra drum voices from here on; `undefined` rests (মুক্তছন্দ). */
324
+ drums?: DrumVoice[];
325
+ }
314
326
  /**
315
327
  * Build a complete ABC tune string from a schedule. Pure and dependency-free
316
328
  * (the testable seam). Rests advance time as `z`; melody notes carry duration;
@@ -326,15 +338,21 @@ declare function abcDuration(beats: number): string;
326
338
  * pattern one quarter-note per matra with `%%percmap` GM drum sounds (see
327
339
  * `buildDrumVoice`). Both voices share the same bar grid, so they wrap and
328
340
  * align together.
341
+ *
342
+ * `meters`, if given, is one entry per তাল stretch (তালফেরতা): at each start
343
+ * the running bar is closed and the new meter restated inline as `[M:n/4]` —
344
+ * the bracketed form, because a bare `M:` line forces a new system.
329
345
  */
330
346
  declare function buildAbc(schedule: Schedule, lyricOf: (ev: PlayEvent) => string | undefined, opts: {
331
347
  bpm: number;
332
348
  lyrics: boolean;
333
349
  beatsPerBar: number;
334
350
  drums?: DrumVoice[];
351
+ meters?: AbcMeter[];
335
352
  }): string;
336
353
  /** Build the ABC tune string for a whole document (schedule + lyrics + tempo).
337
- * The measure length is one তাল cycle (`meta.taal.numBeats`), falling back to 4.
354
+ * The measure length is one তাল cycle — per stretch, so a তালফেরতা changes the
355
+ * time signature where the taal changes — falling back to 4.
338
356
  * `opts.drums` adds the percussion staff (see `buildAbc`). */
339
357
  declare function docToAbc(doc: Swaralipi, opts?: StaffOptions): string;
340
358
  /**
@@ -533,4 +551,4 @@ type Transliterator = (bengaliText: string) => string;
533
551
  */
534
552
  declare function loadLyricTransliterator(lang: ViewerLang): Promise<Transliterator | undefined>;
535
553
 
536
- export { type Arc, type Cell, type Cluster, type ComposeOptions, DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, type GridLang, LABELS, LAY_LABELS, type Layout, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, type PrintOptions, type RenderOptions, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, type StaffOptions, type SwaralipiSheetElement, type TaalStaffOptions, type Transliterator, type ViewerLang, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
554
+ export { type AbcMeter, type Arc, type Cell, type Cluster, type ComposeOptions, DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, FREE_TAAL_LABELS, type GridLang, LABELS, LAY_LABELS, type Layout, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, type PrintOptions, type RenderOptions, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, type StaffOptions, type SwaralipiSheetElement, type TaalStaffOptions, type Transliterator, type ViewerLang, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
@@ -1,6 +1,6 @@
1
- import { O as Octave, a as SvaraLetter, N as Note, M as Meta, S as Swaralipi, T as Taal } from '../schema-DGA_bNVZ.js';
2
- import { L as Lay } from '../lay-Ct9ky_EE.js';
3
- import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-CdILTNsk.js';
1
+ import { O as Octave, a as SvaraLetter, N as Note, M as Meta, S as Swaralipi, T as Taal } from '../schema-Cf_epUIC.js';
2
+ import { L as Lay } from '../lay-CZIzR9cM.js';
3
+ import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-BaovCtmf.js';
4
4
  import 'zod';
5
5
  import 'tone';
6
6
 
@@ -31,6 +31,9 @@ declare const LABELS: Record<GridLang, {
31
31
  * term; en names the tempo first (a reader of the English sheet may not know
32
32
  * the word — the same call `LABELS.en` makes with "Cycles" over "Avartan"). */
33
33
  declare const LAY_LABELS: Record<GridLang, Record<Lay, string>>;
34
+ /** Printed where a row switches to free rhythm (`row.taal: null`) with no
35
+ * `taalName` of its own. bn is the converter's canonical মুক্তছন্দ. */
36
+ declare const FREE_TAAL_LABELS: Record<GridLang, string>;
34
37
  /** A composed notation cluster: the font's ASCII text plus any residual CSS
35
38
  * classes the font itself cannot express (only the bn ati-octave dot). */
36
39
  interface Cluster {
@@ -214,7 +217,7 @@ declare const ZOOM_STEP = 1.2;
214
217
  * --sw-matra minimum width of one matra column
215
218
  * --sw-header-min floor for the নাম/তাল/আবর্তন header (it must stay legible
216
219
  * when the notation is scaled down to fit a phone)
217
- * --sw-lay-min same floor for the লয় (tempo tier) marker band
220
+ * --sw-lay-min same floor for the তাল / লয় change band
218
221
  * --sw-hl player-highlight background (P2.3)
219
222
  * --sw-rule header rule / muted line color
220
223
  *
@@ -242,7 +245,7 @@ declare const SWARALIPI_CSS: string;
242
245
  * - rows are un-clipped (no scrollbar on paper) and kept whole across page
243
246
  * breaks; part headings stay with their first row.
244
247
  */
245
- declare const SWARALIPI_PRINT_CSS = "\n@page { margin: 16mm; }\n@media print {\n .sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }\n .sw-scroll { overflow: visible !important; }\n .sw-row { break-inside: avoid; page-break-inside: avoid; }\n .sw-part-name, .sw-header, .sw-lay { break-after: avoid-page; page-break-after: avoid; }\n .sw-hl { background: none !important; }\n /* Keep each engraved staff system whole across a page break (the abcjs line\n is a <div> wrapping the <svg>, so the break rule must sit on the div). */\n .sw-staff-lines > div { break-inside: avoid; page-break-inside: avoid; }\n .sw-staff-meta { break-after: avoid-page; page-break-after: avoid; }\n}\n";
248
+ declare const SWARALIPI_PRINT_CSS = "\n@page { margin: 16mm; }\n@media print {\n .sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }\n .sw-scroll { overflow: visible !important; }\n .sw-row { break-inside: avoid; page-break-inside: avoid; }\n .sw-part-name, .sw-header, .sw-change { break-after: avoid-page; page-break-after: avoid; }\n .sw-hl { background: none !important; }\n /* Keep each engraved staff system whole across a page break (the abcjs line\n is a <div> wrapping the <svg>, so the break rule must sit on the div). */\n .sw-staff-lines > div { break-inside: avoid; page-break-inside: avoid; }\n .sw-staff-meta { break-after: avoid-page; page-break-after: avoid; }\n}\n";
246
249
  /** Staff-notation (abcjs) wrapper chrome: instrument/tempo header + a fluid
247
250
  * SVG. The engraving itself is drawn by abcjs; this only frames it. */
248
251
  declare const SWARALIPI_STAFF_CSS = "\n.sw-staff { color: currentColor; }\n.sw-staff-meta { display: flex; gap: 1.25em; align-items: baseline; margin: 0 0 .5em; font-size: .95em; opacity: .85; }\n.sw-staff-instrument { font-weight: 600; }\n.sw-staff-drums { opacity: .75; }\n.sw-staff-tempo { font-variant-numeric: tabular-nums; }\n/* abcjs (oneSvgPerLine) wraps the music into one <div><svg> per system; stack\n them as short, swaralipi-style lines. abcjs sets a fixed pixel height +\n overflow:hidden on each wrapper div \u2014 neutralise it so the viewBox'd SVG can\n scale to the container (and to the print page) instead of being clipped. */\n.sw-staff-lines > div { margin: 0 0 .35em; height: auto !important; overflow: visible !important; }\n/* Fill up to the engraved (staffwidth) size \u2014 shrink to fit a narrow container,\n but never stretch past natural width (abcToSvg pins per-SVG max-width in px),\n else a short last system balloons. */\n.sw-staff-lines svg { display: block; width: 100%; height: auto; }\n.sw-staff-missing { padding: 1em; border: 1px dashed currentColor; border-radius: 6px; opacity: .8; font-size: .9em; }\n.sw-staff-title { font-size: 1.15em; font-weight: 700; }\n.sw-staff-subtitle { opacity: .8; font-size: .95em; margin-top: .15em; }\n";
@@ -311,6 +314,15 @@ declare function midiToAbc(midi: number): string;
311
314
  * nearest 1/4-matra count of `/4` units so odd durations still sum correctly.
312
315
  */
313
316
  declare function abcDuration(beats: number): string;
317
+ /** One তাল stretch of the score: its meter, and drum voices when drums are on. */
318
+ interface AbcMeter {
319
+ /** Start, in schedule beats. */
320
+ beat: number;
321
+ /** Matra per bar (one তাল cycle) from here on. */
322
+ beatsPerBar: number;
323
+ /** Per-matra drum voices from here on; `undefined` rests (মুক্তছন্দ). */
324
+ drums?: DrumVoice[];
325
+ }
314
326
  /**
315
327
  * Build a complete ABC tune string from a schedule. Pure and dependency-free
316
328
  * (the testable seam). Rests advance time as `z`; melody notes carry duration;
@@ -326,15 +338,21 @@ declare function abcDuration(beats: number): string;
326
338
  * pattern one quarter-note per matra with `%%percmap` GM drum sounds (see
327
339
  * `buildDrumVoice`). Both voices share the same bar grid, so they wrap and
328
340
  * align together.
341
+ *
342
+ * `meters`, if given, is one entry per তাল stretch (তালফেরতা): at each start
343
+ * the running bar is closed and the new meter restated inline as `[M:n/4]` —
344
+ * the bracketed form, because a bare `M:` line forces a new system.
329
345
  */
330
346
  declare function buildAbc(schedule: Schedule, lyricOf: (ev: PlayEvent) => string | undefined, opts: {
331
347
  bpm: number;
332
348
  lyrics: boolean;
333
349
  beatsPerBar: number;
334
350
  drums?: DrumVoice[];
351
+ meters?: AbcMeter[];
335
352
  }): string;
336
353
  /** Build the ABC tune string for a whole document (schedule + lyrics + tempo).
337
- * The measure length is one তাল cycle (`meta.taal.numBeats`), falling back to 4.
354
+ * The measure length is one তাল cycle — per stretch, so a তালফেরতা changes the
355
+ * time signature where the taal changes — falling back to 4.
338
356
  * `opts.drums` adds the percussion staff (see `buildAbc`). */
339
357
  declare function docToAbc(doc: Swaralipi, opts?: StaffOptions): string;
340
358
  /**
@@ -533,4 +551,4 @@ type Transliterator = (bengaliText: string) => string;
533
551
  */
534
552
  declare function loadLyricTransliterator(lang: ViewerLang): Promise<Transliterator | undefined>;
535
553
 
536
- export { type Arc, type Cell, type Cluster, type ComposeOptions, DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, type GridLang, LABELS, LAY_LABELS, type Layout, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, type PrintOptions, type RenderOptions, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, type StaffOptions, type SwaralipiSheetElement, type TaalStaffOptions, type Transliterator, type ViewerLang, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
554
+ export { type AbcMeter, type Arc, type Cell, type Cluster, type ComposeOptions, DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, FREE_TAAL_LABELS, type GridLang, LABELS, LAY_LABELS, type Layout, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, type PrintOptions, type RenderOptions, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, type StaffOptions, type SwaralipiSheetElement, type TaalStaffOptions, type Transliterator, type ViewerLang, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
@@ -1,5 +1,5 @@
1
- import { isArcRaw, glideTargets, buildSchedule, drumPattern } from '../chunk-4IVUPMRK.js';
2
- import { hasKomal, TAALS, graceNotes } from '../chunk-4HNT6OAT.js';
1
+ import { isArcRaw, glideTargets, buildSchedule, drumPattern } from '../chunk-SEN266BY.js';
2
+ import { hasKomal, isTaalFerta, sameTaal, TAALS, graceNotes } from '../chunk-MYVIQU4O.js';
3
3
 
4
4
  // src/viewer/glyphs.ts
5
5
  var SWARABITAN_SHUDDHA = {
@@ -49,6 +49,11 @@ var LAY_LABELS = {
49
49
  hi: { drut: "\u0926\u094D\u0930\u0941\u0924 \u0932\u092F", madhya: "\u092E\u0927\u094D\u092F \u0932\u092F", vilambit: "\u0935\u093F\u0932\u0902\u092C\u093F\u0924 \u0932\u092F" },
50
50
  en: { drut: "Fast (drut)", madhya: "Medium (madhya)", vilambit: "Slow (vilambit)" }
51
51
  };
52
+ var FREE_TAAL_LABELS = {
53
+ bn: "\u09AE\u09C1\u0995\u09CD\u09A4\u099B\u09A8\u09CD\u09A6",
54
+ hi: "\u092E\u0941\u0915\u094D\u0924\u091B\u0902\u0926",
55
+ en: "Free rhythm"
56
+ };
52
57
  function composeCluster(letter, opts) {
53
58
  const classes = [];
54
59
  if (opts.lang === "bn") {
@@ -521,15 +526,25 @@ function renderToHtml(doc, opts = {}) {
521
526
  out.push(`<div class="sw-scroll"><div class="sw-block" style="${tracks}">`);
522
527
  let n = 0;
523
528
  let lay;
529
+ let taal = doc.meta.taal;
530
+ let named = !isTaalFerta(doc.meta.taalName);
524
531
  doc.parts.forEach((part, p) => {
525
532
  out.push('<section class="sw-part">');
526
533
  if (part.name !== void 0)
527
534
  out.push(`<h3 class="sw-part-name">${escapeHtml(ctx.tr(part.name))}</h3>`);
528
535
  part.rows.forEach((row, r) => {
536
+ const marks = [];
537
+ if (row.taal !== void 0 && (!named || !sameTaal(row.taal, taal))) {
538
+ taal = row.taal;
539
+ named = true;
540
+ const name = row.taalName ?? (taal === null ? FREE_TAAL_LABELS[ctx.lang] : taal.id);
541
+ marks.push(`<span class="sw-taal" data-taal="${escapeHtml(taal?.id ?? "free")}">${escapeHtml(ctx.tr(name))}</span>`);
542
+ }
529
543
  if (row.lay !== void 0 && row.lay !== lay) {
530
544
  lay = row.lay;
531
- out.push(`<div class="sw-lay" data-lay="${lay}">${escapeHtml(LAY_LABELS[ctx.lang][lay])}</div>`);
545
+ marks.push(`<span class="sw-lay" data-lay="${lay}">${escapeHtml(LAY_LABELS[ctx.lang][lay])}</span>`);
532
546
  }
547
+ if (marks.length > 0) out.push(`<div class="sw-change">${marks.join("")}</div>`);
533
548
  out.push(renderRow(row, p, r, layout.offsets[n++], ctx));
534
549
  });
535
550
  out.push("</section>");
@@ -623,10 +638,12 @@ ${FONT_FACES}
623
638
 
624
639
  .sw-part-name { font-size: 0.85em; font-weight: 600; margin: 1em 0 0.2em; opacity: 0.85; }
625
640
 
626
- /* \u09B2\u09AF\u09BC marker: the line where the tempo tier turns over. A full-width rule
627
- rather than a row label \u2014 the label track is what the notation columns are
628
- measured against, and a six-em word in it would shove the whole sheet right. */
629
- .sw-lay {
641
+ /* \u09A4\u09BE\u09B2 / \u09B2\u09AF\u09BC change: the line where the \u09A4\u09BE\u09B2\u09AB\u09C7\u09B0\u09A4\u09BE or the tempo tier turns over,
642
+ centred between two systems the way \u09B8\u09CD\u09AC\u09B0\u09AC\u09BF\u09A4\u09BE\u09A8 prints "\u099D\u09BE\u0981\u09AA\u09A4\u09BE\u09B2" or
643
+ "\u09A6\u09CD\u09B0\u09C1\u09A4 \u09B2\u09AF\u09BC". A full-width band rather than a row label \u2014 the label track is
644
+ what the notation columns are measured against, and a six-em word in it
645
+ would shove the whole sheet right. */
646
+ .sw-change {
630
647
  grid-column: 1 / -1;
631
648
  display: flex;
632
649
  align-items: center;
@@ -640,12 +657,14 @@ ${FONT_FACES}
640
657
  opacity: 0.75;
641
658
  break-after: avoid;
642
659
  }
643
- .sw-lay::after {
660
+ .sw-change::before,
661
+ .sw-change::after {
644
662
  content: '';
645
663
  flex: 1;
646
664
  height: 1px;
647
665
  background: var(--sw-rule, rgba(128, 128, 128, 0.35));
648
666
  }
667
+ .sw-taal + .sw-lay::before { content: '\xB7'; margin-right: 0.6em; }
649
668
 
650
669
  /* ---- block grid: ONE column set for the whole sheet ----
651
670
  * NLTR emits a song as a single html table, which is why its lines align
@@ -863,7 +882,7 @@ var SWARALIPI_PRINT_CSS = `
863
882
  .sw-sheet, .sw-staff { -webkit-print-color-adjust: exact; print-color-adjust: exact; color: #000; }
864
883
  .sw-scroll { overflow: visible !important; }
865
884
  .sw-row { break-inside: avoid; page-break-inside: avoid; }
866
- .sw-part-name, .sw-header, .sw-lay { break-after: avoid-page; page-break-after: avoid; }
885
+ .sw-part-name, .sw-header, .sw-change { break-after: avoid-page; page-break-after: avoid; }
867
886
  .sw-hl { background: none !important; }
868
887
  /* Keep each engraved staff system whole across a page break (the abcjs line
869
888
  is a <div> wrapping the <svg>, so the break rule must sit on the div). */
@@ -920,11 +939,15 @@ function eventPaths(schedule) {
920
939
  }
921
940
  return paths;
922
941
  }
942
+ var barLen = (m) => m.beatsPerBar > 0 ? m.beatsPerBar : 4;
923
943
  function abcText(s) {
924
944
  return s.replace(/[\r\n]+/g, " ").trim();
925
945
  }
926
946
  function buildAbc(schedule, lyricOf, opts) {
927
- const beatsPerBar = opts.beatsPerBar > 0 ? opts.beatsPerBar : 4;
947
+ const meters = opts.meters?.length ? opts.meters : [{ beat: 0, beatsPerBar: opts.beatsPerBar, drums: opts.drums }];
948
+ const withDrums = opts.drums !== void 0 || meters.some((m2) => m2.drums !== void 0);
949
+ let m = 0;
950
+ let beatsPerBar = barLen(meters[0]);
928
951
  const tokens = [];
929
952
  const words = [];
930
953
  let barPos = 0;
@@ -938,6 +961,17 @@ function buildAbc(schedule, lyricOf, opts) {
938
961
  }
939
962
  };
940
963
  schedule.events.forEach((ev, i) => {
964
+ while (m + 1 < meters.length && ev.beat >= meters[m + 1].beat - EPS2) {
965
+ m++;
966
+ if (barPos > EPS2) {
967
+ tokens.push("|");
968
+ words.push("|");
969
+ }
970
+ barPos = 0;
971
+ const next = barLen(meters[m]);
972
+ if (next !== beatsPerBar) tokens.push(`[M:${ratioMeter(next)}]`);
973
+ beatsPerBar = next;
974
+ }
941
975
  if (ev.midi === null) {
942
976
  if (ev.kind === "rest") {
943
977
  tokens.push(`z${abcDuration(ev.durationBeats)}`);
@@ -956,16 +990,16 @@ function buildAbc(schedule, lyricOf, opts) {
956
990
  });
957
991
  if (tokens.length && tokens[tokens.length - 1] !== "|") tokens.push("|]");
958
992
  else if (tokens.length) tokens[tokens.length - 1] = "|]";
959
- const meter = ratioMeter(beatsPerBar);
993
+ const meter = ratioMeter(barLen(meters[0]));
960
994
  const head = ["X:1", `M:${meter}`, "L:1/4", `Q:1/4=${Math.round(opts.bpm)}`];
961
- if (opts.drums) head.push(...percMapLines(opts.drums));
995
+ if (withDrums) head.push(...percMapLines(meters.flatMap((x) => x.drums ?? [])));
962
996
  head.push("K:C treble");
963
997
  const melodyBody = tokens.length ? tokens.join(" ") : "z |]";
964
998
  const lines = [...head];
965
- if (opts.drums) {
999
+ if (withDrums) {
966
1000
  lines.push("V:1", melodyBody);
967
1001
  if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
968
- lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats, beatsPerBar, opts.drums));
1002
+ lines.push("V:2 clef=perc stem=up", buildDrumVoice(schedule.totalBeats, meters));
969
1003
  } else {
970
1004
  lines.push(melodyBody);
971
1005
  if (opts.lyrics && words.some((w) => w !== "*" && w !== "|")) lines.push(`w: ${words.join(" ")}`);
@@ -994,13 +1028,21 @@ function percMapLines(voices) {
994
1028
  }
995
1029
  return lines;
996
1030
  }
997
- function buildDrumVoice(totalBeats, beatsPerBar, pattern) {
998
- const n = Math.max(1, Math.ceil(totalBeats));
1031
+ function buildDrumVoice(totalBeats, meters) {
999
1032
  const tokens = [];
1000
- for (let beat = 0; beat < n; beat++) {
1001
- tokens.push(DRUM_ABC[pattern[beat % pattern.length]].note);
1002
- if ((beat + 1) % beatsPerBar === 0) tokens.push("|");
1003
- }
1033
+ meters.forEach((m, i) => {
1034
+ const bpb = barLen(m);
1035
+ if (i > 0) {
1036
+ if (tokens.length > 0 && tokens[tokens.length - 1] !== "|") tokens.push("|");
1037
+ if (bpb !== barLen(meters[i - 1])) tokens.push(`[M:${ratioMeter(bpb)}]`);
1038
+ }
1039
+ const end = meters[i + 1]?.beat ?? Math.max(1, Math.ceil(totalBeats));
1040
+ const n = Math.ceil(end - m.beat - 1e-6);
1041
+ for (let beat = 0; beat < n; beat++) {
1042
+ tokens.push(m.drums?.length ? DRUM_ABC[m.drums[beat % m.drums.length]].note : "z");
1043
+ if ((beat + 1) % bpb === 0) tokens.push("|");
1044
+ }
1045
+ });
1004
1046
  if (tokens.length === 0 || tokens[tokens.length - 1] !== "|") tokens.push("|]");
1005
1047
  else tokens[tokens.length - 1] = "|]";
1006
1048
  return tokens.join(" ");
@@ -1011,16 +1053,25 @@ function lyricLookup(doc) {
1011
1053
  return doc.parts[p]?.rows[r]?.notes[i]?.lyric;
1012
1054
  };
1013
1055
  }
1014
- function resolveDrumTaal(doc) {
1015
- const id = doc.meta.taal?.id;
1056
+ function resolveDrumTaal(taal) {
1057
+ const id = taal?.id;
1016
1058
  return id !== void 0 && id in TAALS ? TAALS[id] : { numBeats: 4, sam: 0 };
1017
1059
  }
1018
1060
  function docToAbc(doc, opts = {}) {
1019
1061
  const schedule = buildSchedule(doc, { tonic: opts.tonic, baseOctave: opts.baseOctave, lay: false });
1020
1062
  const bpm = opts.bpm ?? doc.meta.bpm ?? 90;
1021
- const beatsPerBar = opts.beatsPerBar ?? doc.meta.taal?.numBeats ?? 4;
1022
- const drums = opts.drums ? drumPattern(resolveDrumTaal(doc)) : void 0;
1023
- return buildAbc(schedule, lyricLookup(doc), { bpm, lyrics: opts.lyrics !== false, beatsPerBar, drums });
1063
+ const meters = schedule.tempo.map((seg) => ({
1064
+ beat: seg.beat,
1065
+ beatsPerBar: opts.beatsPerBar ?? seg.taal?.numBeats ?? 4,
1066
+ drums: opts.drums && seg.taal !== null ? drumPattern(resolveDrumTaal(seg.taal)) : void 0
1067
+ }));
1068
+ return buildAbc(schedule, lyricLookup(doc), {
1069
+ bpm,
1070
+ lyrics: opts.lyrics !== false,
1071
+ beatsPerBar: meters[0].beatsPerBar,
1072
+ drums: opts.drums ? meters[0].drums ?? [] : void 0,
1073
+ meters
1074
+ });
1024
1075
  }
1025
1076
  async function abcToSvg(abc, opts = {}, notePaths) {
1026
1077
  if (typeof document === "undefined") {
@@ -1099,7 +1150,7 @@ function buildTaalAbc(taal, opts = {}) {
1099
1150
  const voices = Array.from({ length: n }, (_, i) => src[i % src.length]);
1100
1151
  const bpm = Math.round(opts.bpm ?? 90);
1101
1152
  const head = ["X:1", `M:${ratioMeter(n)}`, "L:1/4", `Q:1/4=${bpm}`, ...percMapLines(voices), "K:C"];
1102
- const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, n, voices)];
1153
+ const lines = [...head, "V:1 clef=perc stem=up", buildDrumVoice(n, [{ beat: 0, beatsPerBar: n, drums: voices }])];
1103
1154
  if (opts.bols?.length) {
1104
1155
  const w = opts.bols.slice(0, n).map((b) => b.trim() ? abcText(b).replace(/\s+/g, "~") : "*");
1105
1156
  while (w.length < n) w.push("*");
@@ -1487,6 +1538,6 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
1487
1538
  return ctor;
1488
1539
  }
1489
1540
 
1490
- export { DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, LABELS, LAY_LABELS, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
1541
+ export { DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, FREE_TAAL_LABELS, LABELS, LAY_LABELS, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, SWARALIPI_CSS, SWARALIPI_PRINT_CSS, SWARALIPI_STAFF_CSS, ZOOM_STEP, abcDuration, abcToSvg, alignRows, buildAbc, buildTaalAbc, cellMatra, composeCluster, defineSwaralipiSheet, docToAbc, fitSwaralipi, groupRowIntoCells, loadLyricTransliterator, localizeDigits, midiToAbc, printSwaralipi, renderMetaHeader, renderPrintDocument, renderStaffHtml, renderStaffPrintDocument, renderTaalStaffHtml, renderToHtml, resolveArcs };
1491
1542
  //# sourceMappingURL=index.js.map
1492
1543
  //# sourceMappingURL=index.js.map