@ugfoundation/swaralipi-js 0.2.0 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/spec/schema.ts","../../src/spec/taals.ts","../../src/spec/index.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * swaralipi-js JSON spec — Zod schema + inferred types.\n\n/** Octave registers */\nexport const octaveSchema = z.enum([\n 'ati-mandra', // very low\n 'mandra', // low (dot below)\n 'madhyam', // middle\n 'taar', // high (ref/dot above)\n 'ati-taar', // very high\n]);\nexport type Octave = z.infer<typeof octaveSchema>;\n\n/**\n * The seven shuddha svara letters:\n * `d` = ধৈবত (dha), `n` = নিষাদ (ni). Komal/tivra are flags, not letters.\n */\nexport const svaraLetterSchema = z.enum(['s', 'r', 'g', 'm', 'p', 'd', 'n']);\nexport type SvaraLetter = z.infer<typeof svaraLetterSchema>;\n\n/** A pitch as `[octave, letter]` */\nexport const svaraSchema = z.tuple([octaveSchema, svaraLetterSchema]);\nexport type Svara = z.infer<typeof svaraSchema>;\n\n/** Microtone variants below komal (legend: অল্পকোমল / অতিকোমল). */\nexport const microtoneSchema = z.enum(['alpakomal', 'atikomal']);\nexport type Microtone = z.infer<typeof microtoneSchema>;\n\n/**\n * Barline weights. Swarabitan distinguishes the light দাঁড়ি (।) that\n * separates vibhags *within* a taal cycle from the দণ্ড (danda, I) strokes:\n * - `dari` — । vibhag separator inside the cycle\n * - `single` — I end of one taal cycle (ফেরা)\n * - `double` — II section (kali) boundary / return-to-asthayi marker\n * - `quad` — IIII end of the song\n */\nexport const barSchema = z.enum(['dari', 'single', 'double', 'quad']);\nexport type Bar = z.infer<typeof barSchema>;\n\n/**\n * A repeat-bracket edge. Brackets span a range of notes; the opening note\n * carries `{ ..., edge: 'open' }` and the closing note `{ ..., edge: 'close' }`.\n * kind: `repeat` = {} গুন্ডবন্ধনী, `skip` = () বক্রবন্ধনী (omit on repeat),\n * `alt` = [] সরল বন্ধনী (changed notes on repeat).\n */\nexport const bracketSchema = z.object({\n kind: z.enum(['repeat', 'skip', 'alt']),\n edge: z.enum(['open', 'close']),\n});\nexport type Bracket = z.infer<typeof bracketSchema>;\n\n/**\n * A single token in a row. A token is one of: a pitched note (`svara`),\n * a rest (`rest`), an আ-কার sustain (`sustain`), a barline (`bar`), or a\n * purely raw/undecoded cell (`raw`) — possibly carrying annotations\n * (`above`/`below`) or a lyric syllable on an otherwise empty column.\n * The `.refine` enforces that a note carries at least one of these so\n * truly empty tokens can't slip through.\n *\n * `above`/`below` make `Note` recursive, so the type is hand-written and\n * the schema is typed `z.ZodType<Note>` via `z.lazy` (all fields are\n * optional, hence input ≡ output and the annotation is sound).\n */\nexport interface Note {\n /** Pitch. Omitted for rests, sustains, and barlines. */\n svara?: Svara;\n /** কোমল (flat). */\n komal?: boolean;\n /** তীব্র / কড়ি (sharp — applies to ma). */\n tivra?: boolean;\n /** Sub-komal microtone (legend: অল্পকোমল / অতিকোমল). */\n microtone?: Microtone;\n /** Duration in মাত্রা (beats): 1 (default), 0.5, 0.25, 2, … */\n durationMatra?: number;\n /**\n * আ-কার: no new syllable attack. Alone (no `svara`) it holds the previous\n * note's pitch and vowel; combined with `svara` it is a melisma — the\n * pitch moves while the previous syllable's vowel continues (Swarabitan\n * writes these with a leading hyphen, e.g. `-sfa`).\n */\n sustain?: boolean;\n /** বিরাম: a rest of `durationMatra` beats. */\n rest?: boolean;\n /** কণ স্বর: a grace note attached before this note. */\n kan?: Svara;\n /** মীড় (glide/slur) endpoint. */\n meend?: 'start' | 'end';\n /** Barline. A bar token usually has no `svara`. */\n bar?: Bar;\n /** Repeat-bracket edges opening/closing on this token. */\n brackets?: Bracket[];\n /** Lyric syllable aligned under this note */\n lyric?: string;\n /** Lossless passthrough of the source cell (e.g. raw Swarabitan code). */\n raw?: string;\n /**\n * Annotation notes rendered above this column: alt-on-repeat passages\n * (legend: পরিবর্তিত স্বর in [ ] শিরোদেশে), stop marks, beat numbers.\n */\n above?: Note[];\n /** Annotation notes rendered below this column. */\n below?: Note[];\n}\n\nexport const noteSchema: z.ZodType<Note> = z.lazy(() =>\n z\n .object({\n svara: svaraSchema.optional(),\n komal: z.boolean().optional(),\n tivra: z.boolean().optional(),\n microtone: microtoneSchema.optional(),\n durationMatra: z.number().positive().optional(),\n sustain: z.boolean().optional(),\n rest: z.boolean().optional(),\n kan: svaraSchema.optional(),\n meend: z.enum(['start', 'end']).optional(),\n bar: barSchema.optional(),\n brackets: z.array(bracketSchema).optional(),\n lyric: z.string().optional(),\n raw: z.string().optional(),\n above: z.array(noteSchema).optional(),\n below: z.array(noteSchema).optional(),\n })\n .refine(\n (n) =>\n n.svara !== undefined ||\n n.rest === true ||\n n.sustain === true ||\n n.bar !== undefined ||\n n.raw !== undefined ||\n (n.brackets !== undefined && n.brackets.length > 0) ||\n (n.above !== undefined && n.above.length > 0) ||\n (n.below !== undefined && n.below.length > 0) ||\n n.lyric !== undefined,\n {\n message:\n 'note must carry one of: svara, rest, sustain, bar, raw, lyric, brackets, or a non-empty above/below',\n },\n ),\n);\n\n/** A written line of the song: a sequence of note tokens. */\nexport const rowSchema = z.object({\n /** Optional line label (e.g. a tuk/section marker). */\n label: z.string().optional(),\n notes: z.array(noteSchema),\n});\nexport type Row = z.infer<typeof rowSchema>;\n\n/** A structural section: আস্থায়ী / অন্তরা / সঞ্চারী / আভোগ, or free text. */\nexport const partSchema = z.object({\n name: z.string().optional(),\n rows: z.array(rowSchema),\n});\nexport type Part = z.infer<typeof partSchema>;\n\n/**\n * Taal definition. `numBeats` is the cycle length in মাত্রা; `bhaags` (if\n * present) must sum to `numBeats`; `sam`/`khaali` are 0-based beat indices.\n */\nexport const taalSchema = z\n .object({\n /** Player sample key, e.g. `dadra`, `teentaal`. */\n id: z.string(),\n /**\n * Which tabla loop variation to play, e.g. `pro` (CDN dir suffix). Optional\n * and additive — omitted means the taal's default variation, so existing\n * docs are unaffected. The player honors it but never exposes a variation\n * picker; selecting a non-default variation is the consumer's premium policy.\n */\n variation: z.string().optional(),\n numBeats: z.number().int().positive(),\n /** Vibhag groupings; if given, they sum to numBeats. */\n bhaags: z.array(z.number().int().positive()).optional(),\n /** Sam (cycle start) beat index, 0-based. */\n sam: z.number().int().nonnegative().optional(),\n /** Khaali (wave) beat indices, 0-based. */\n khaali: z.array(z.number().int().nonnegative()).optional(),\n })\n .refine((t) => t.bhaags === undefined || t.bhaags.reduce((a, b) => a + b, 0) === t.numBeats, {\n message: 'bhaags must sum to numBeats',\n });\nexport type Taal = z.infer<typeof taalSchema>;\n\n/** Document-level metadata. */\nexport const metaSchema = z.object({\n title: z.string(),\n /** Cross-reference to a gitabitan.io song content id (filename, immutable). */\n songId: z.string().optional(),\n /** Source NLTR node/song id. */\n nltrId: z.number().int().optional(),\n taal: taalSchema.optional(),\n /**\n * The taal name exactly as printed in the source (e.g. `তালমুক্ত`,\n * `ষষ্ঠী(২/৪)`). Always set by the NLTR converter; `taal` is set only\n * when the name resolves to a known definition.\n */\n taalName: z.string().optional(),\n /** Base pitch for playback, e.g. `C`, `C#`, or the player's `cs` form. */\n tonic: z.string().optional(),\n /** Default playback tempo in matra/min. Player-only — the viewer ignores it.\n * The player adopts it on `load()` (precedence: constructor `bpm` → `setBpm`\n * tweak → this → 90). `null` is the converter's blank placeholder = unset. */\n bpm: z.number().positive().nullable().optional(),\n raag: z.string().optional(),\n /** আবর্তন: number of taal cycles, when the source states it. */\n avartan: z.number().int().positive().optional(),\n /** স্বরলিপিকার (notation author). */\n notationBy: z.string().optional(),\n /** Provenance note or URL. */\n source: z.string().optional(),\n});\nexport type Meta = z.infer<typeof metaSchema>;\n\n/** A complete swaralipi document. */\nexport const swaralipiSchema = z.object({\n /** Spec version this document targets (see SPEC_VERSION in ./index). */\n version: z.string(),\n meta: metaSchema,\n parts: z.array(partSchema),\n});\nexport type Swaralipi = z.infer<typeof swaralipiSchema>;\n","import type { Taal } from './schema.js';\n\n/**\n * Reference definitions for the taals the player will sample:\n * adachautaal, dadra, ektaal, jhaptaal, kehrwa, rupak, shashthi, teentaal.\n *\n * `numBeats` and the `bhaags` given below are standard Hindustani taal theory.\n * `bhaags` is omitted where a single canonical division isn't well-agreed\n * (adachautaal) — we assert only what's uncontested. `sam` is beat 0 for all.\n *\n * Tabla loops live under `tabla/<id>/` by default; per-taal variation and\n * skip-bpm overrides (e.g. shashthi → `tabla/shashthi-pro/`) are in\n * instruments.ts's `TABLA_VARIATIONS`.\n */\nexport const TAALS = {\n dadra: { id: 'dadra', numBeats: 6, bhaags: [3, 3], sam: 0, khaali: [3] },\n kehrwa: { id: 'kehrwa', numBeats: 8, bhaags: [4, 4], sam: 0, khaali: [4] },\n rupak: { id: 'rupak', numBeats: 7, bhaags: [3, 2, 2], sam: 0, khaali: [0] },\n jhaptaal: { id: 'jhaptaal', numBeats: 10, bhaags: [2, 3, 2, 3], sam: 0, khaali: [5] },\n ektaal: { id: 'ektaal', numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },\n adachautaal: { id: 'adachautaal', numBeats: 14, sam: 0 },\n choutaal: { id: 'choutaal', numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] }, // চৌতাল\n shashthi: { id: 'shashthi', numBeats: 6, bhaags: [2, 4], sam: 0 }, // ষষ্ঠী\n teora: { id: 'teora', numBeats: 7, bhaags: [3, 2, 2], sam: 0 }, // টেওরা\n teentaal: { id: 'teentaal', numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },\n aratheka: { id: 'aratheka', numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] }, // আড়াঠেকা\n} as const satisfies Record<string, Taal>;\n\nexport type TaalId = keyof typeof TAALS;\n","/**\n * @ugfoundation/swaralipi-js/spec\n *\n * The swaralipi JSON spec: Zod schemas, inferred TS types, and parse helpers.\n */\nimport { swaralipiSchema, type Swaralipi } from './schema.js';\n\n/** Current spec version. Documents declare the version they target. */\nexport const SPEC_VERSION = '0.2.0';\n\nexport {\n octaveSchema,\n svaraLetterSchema,\n svaraSchema,\n microtoneSchema,\n barSchema,\n bracketSchema,\n noteSchema,\n rowSchema,\n partSchema,\n taalSchema,\n metaSchema,\n swaralipiSchema,\n} from './schema.js';\n\nexport type {\n Octave,\n SvaraLetter,\n Svara,\n Microtone,\n Bar,\n Bracket,\n Note,\n Row,\n Part,\n Taal,\n Meta,\n Swaralipi,\n} from './schema.js';\n\nexport { TAALS, type TaalId } from './taals.js';\n\n/** Parse + validate unknown data into a `Swaralipi`. Throws `ZodError` on failure. */\nexport function parseSwaralipi(data: unknown): Swaralipi {\n return swaralipiSchema.parse(data);\n}\n\n/** Non-throwing variant: returns Zod's `{ success, data | error }` result. */\nexport function safeParseSwaralipi(data: unknown) {\n return swaralipiSchema.safeParse(data);\n}\n"]}
1
+ {"version":3,"sources":["../../src/spec/schema.ts","../../src/spec/taals.ts","../../src/spec/index.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * swaralipi-js JSON spec — Zod schema + inferred types.\n\n/** Octave registers */\nexport const octaveSchema = z.enum([\n 'ati-mandra', // very low\n 'mandra', // low (dot below)\n 'madhyam', // middle\n 'taar', // high (ref/dot above)\n 'ati-taar', // very high\n]);\nexport type Octave = z.infer<typeof octaveSchema>;\n\n/**\n * The seven shuddha svara letters:\n * `d` = ধৈবত (dha), `n` = নিষাদ (ni). Komal/tivra are flags, not letters.\n */\nexport const svaraLetterSchema = z.enum(['s', 'r', 'g', 'm', 'p', 'd', 'n']);\nexport type SvaraLetter = z.infer<typeof svaraLetterSchema>;\n\n/** A pitch as `[octave, letter]` */\nexport const svaraSchema = z.tuple([octaveSchema, svaraLetterSchema]);\nexport type Svara = z.infer<typeof svaraSchema>;\n\n/** Microtone variants below komal (legend: অল্পকোমল / অতিকোমল). */\nexport const microtoneSchema = z.enum(['alpakomal', 'atikomal']);\nexport type Microtone = z.infer<typeof microtoneSchema>;\n\n/**\n * Barline weights. Swarabitan distinguishes the light দাঁড়ি (।) that\n * separates vibhags *within* a taal cycle from the দণ্ড (danda, I) strokes:\n * - `dari` — । vibhag separator inside the cycle\n * - `single` — I end of one taal cycle (ফেরা)\n * - `double` — II section (kali) boundary / return-to-asthayi marker\n * - `quad` — IIII end of the song\n */\nexport const barSchema = z.enum(['dari', 'single', 'double', 'quad']);\nexport type Bar = z.infer<typeof barSchema>;\n\n/**\n * A repeat-bracket edge. Brackets span a range of notes; the opening note\n * carries `{ ..., edge: 'open' }` and the closing note `{ ..., edge: 'close' }`.\n * kind: `repeat` = {} গুন্ডবন্ধনী, `skip` = () বক্রবন্ধনী (omit on repeat),\n * `alt` = [] সরল বন্ধনী (changed notes on repeat).\n */\nexport const bracketSchema = z.object({\n kind: z.enum(['repeat', 'skip', 'alt']),\n edge: z.enum(['open', 'close']),\n});\nexport type Bracket = z.infer<typeof bracketSchema>;\n\n/**\n * A single token in a row. A token is one of: a pitched note (`svara`),\n * a rest (`rest`), an আ-কার sustain (`sustain`), a barline (`bar`), or a\n * purely raw/undecoded cell (`raw`) — possibly carrying annotations\n * (`above`/`below`) or a lyric syllable on an otherwise empty column.\n * The `.refine` enforces that a note carries at least one of these so\n * truly empty tokens can't slip through.\n *\n * `above`/`below` make `Note` recursive, so the type is hand-written and\n * the schema is typed `z.ZodType<Note>` via `z.lazy` (all fields are\n * optional, hence input ≡ output and the annotation is sound).\n */\nexport interface Note {\n /** Pitch. Omitted for rests, sustains, and barlines. */\n svara?: Svara;\n /** কোমল (flat). */\n komal?: boolean;\n /** তীব্র / কড়ি (sharp — applies to ma). */\n tivra?: boolean;\n /** Sub-komal microtone (legend: অল্পকোমল / অতিকোমল). */\n microtone?: Microtone;\n /** Duration in মাত্রা (beats): 1 (default), 0.5, 0.25, 2, … */\n durationMatra?: number;\n /**\n * আ-কার: no new syllable attack. Alone (no `svara`) it holds the previous\n * note's pitch and vowel; combined with `svara` it is a melisma — the\n * pitch moves while the previous syllable's vowel continues (Swarabitan\n * writes these with a leading hyphen, e.g. `-sfa`).\n */\n sustain?: boolean;\n /** বিরাম: a rest of `durationMatra` beats. */\n rest?: boolean;\n /** কণ স্বর: a grace note attached before this note. */\n kan?: Svara;\n /** মীড় (glide/slur) endpoint. */\n meend?: 'start' | 'end';\n /** Barline. A bar token usually has no `svara`. */\n bar?: Bar;\n /** Repeat-bracket edges opening/closing on this token. */\n brackets?: Bracket[];\n /** Lyric syllable aligned under this note */\n lyric?: string;\n /** Lossless passthrough of the source cell (e.g. raw Swarabitan code). */\n raw?: string;\n /**\n * Annotation notes rendered above this column: alt-on-repeat passages\n * (legend: পরিবর্তিত স্বর in [ ] শিরোদেশে), stop marks, beat numbers.\n */\n above?: Note[];\n /** Annotation notes rendered below this column. */\n below?: Note[];\n}\n\nexport const noteSchema: z.ZodType<Note> = z.lazy(() =>\n z\n .object({\n svara: svaraSchema.optional(),\n komal: z.boolean().optional(),\n tivra: z.boolean().optional(),\n microtone: microtoneSchema.optional(),\n durationMatra: z.number().positive().optional(),\n sustain: z.boolean().optional(),\n rest: z.boolean().optional(),\n kan: svaraSchema.optional(),\n meend: z.enum(['start', 'end']).optional(),\n bar: barSchema.optional(),\n brackets: z.array(bracketSchema).optional(),\n lyric: z.string().optional(),\n raw: z.string().optional(),\n above: z.array(noteSchema).optional(),\n below: z.array(noteSchema).optional(),\n })\n .refine(\n (n) =>\n n.svara !== undefined ||\n n.rest === true ||\n n.sustain === true ||\n n.bar !== undefined ||\n n.raw !== undefined ||\n (n.brackets !== undefined && n.brackets.length > 0) ||\n (n.above !== undefined && n.above.length > 0) ||\n (n.below !== undefined && n.below.length > 0) ||\n n.lyric !== undefined,\n {\n message:\n 'note must carry one of: svara, rest, sustain, bar, raw, lyric, brackets, or a non-empty above/below',\n },\n ),\n);\n\n/** A written line of the song: a sequence of note tokens. */\nexport const rowSchema = z.object({\n /** Optional line label (e.g. a tuk/section marker). */\n label: z.string().optional(),\n notes: z.array(noteSchema),\n});\nexport type Row = z.infer<typeof rowSchema>;\n\n/** A structural section: আস্থায়ী / অন্তরা / সঞ্চারী / আভোগ, or free text. */\nexport const partSchema = z.object({\n name: z.string().optional(),\n rows: z.array(rowSchema),\n});\nexport type Part = z.infer<typeof partSchema>;\n\n/**\n * Taal definition. `numBeats` is the cycle length in মাত্রা; `bhaags` (if\n * present) must sum to `numBeats`; `sam`/`khaali` are 0-based beat indices.\n */\nexport const taalSchema = z\n .object({\n /** Player sample key, e.g. `dadra`, `teentaal`. */\n id: z.string(),\n /**\n * Which tabla loop variation to play, e.g. `pro` (CDN dir suffix). Optional\n * and additive — omitted means the taal's default variation, so existing\n * docs are unaffected. The player honors it but never exposes a variation\n * picker; selecting a non-default variation is the consumer's premium policy.\n */\n variation: z.string().optional(),\n numBeats: z.number().int().positive(),\n /** Vibhag groupings; if given, they sum to numBeats. */\n bhaags: z.array(z.number().int().positive()).optional(),\n /** Sam (cycle start) beat index, 0-based. */\n sam: z.number().int().nonnegative().optional(),\n /** Khaali (wave) beat indices, 0-based. */\n khaali: z.array(z.number().int().nonnegative()).optional(),\n })\n .refine((t) => t.bhaags === undefined || t.bhaags.reduce((a, b) => a + b, 0) === t.numBeats, {\n message: 'bhaags must sum to numBeats',\n });\nexport type Taal = z.infer<typeof taalSchema>;\n\n/** Document-level metadata. */\nexport const metaSchema = z.object({\n title: z.string(),\n /** Cross-reference to a gitabitan.io song content id (filename, immutable). */\n songId: z.string().optional(),\n /** Source NLTR node/song id. */\n nltrId: z.number().int().optional(),\n taal: taalSchema.optional(),\n /**\n * The taal name exactly as printed in the source (e.g. `তালমুক্ত`,\n * `ষষ্ঠী(২/৪)`). Always set by the NLTR converter; `taal` is set only\n * when the name resolves to a known definition.\n */\n taalName: z.string().optional(),\n /** Base pitch for playback, e.g. `C`, `C#`, or the player's `cs` form. */\n tonic: z.string().optional(),\n /** Default playback tempo in matra/min. Player-only — the viewer ignores it.\n * The player adopts it on `load()` (precedence: constructor `bpm` → `setBpm`\n * tweak → this → 90). `null` is the converter's blank placeholder = unset. */\n bpm: z.number().positive().nullable().optional(),\n raag: z.string().optional(),\n /** আবর্তন: number of taal cycles, when the source states it. */\n avartan: z.number().int().positive().optional(),\n /** স্বরলিপিকার (notation author). */\n notationBy: z.string().optional(),\n /** Provenance note or URL. */\n source: z.string().optional(),\n});\nexport type Meta = z.infer<typeof metaSchema>;\n\n/** A complete swaralipi document. */\nexport const swaralipiSchema = z.object({\n /** Spec version this document targets (see SPEC_VERSION in ./index). */\n version: z.string(),\n meta: metaSchema,\n parts: z.array(partSchema),\n});\nexport type Swaralipi = z.infer<typeof swaralipiSchema>;\n","import type { Taal } from './schema.js';\n\n/**\n * Reference definitions for the taals the player will sample:\n * adachautaal, dadra, ektaal, jhaptaal, kehrwa, rupak, shashthi, teentaal.\n *\n * `numBeats` and the `bhaags` given below are standard Hindustani taal theory.\n * `bhaags` is omitted where a single canonical division isn't well-agreed\n * (adachautaal) — we assert only what's uncontested. `sam` is beat 0 for all.\n *\n * Tabla loops live under `tabla/<id>/` by default; per-taal variation and\n * skip-bpm overrides (e.g. shashthi → `tabla/shashthi-pro/`) are in\n * instruments.ts's `TABLA_VARIATIONS`.\n */\nexport const TAALS = {\n dadra: { id: 'dadra', numBeats: 6, bhaags: [3, 3], sam: 0, khaali: [3] },\n kehrwa: { id: 'kehrwa', numBeats: 8, bhaags: [4, 4], sam: 0, khaali: [4] },\n rupak: { id: 'rupak', numBeats: 7, bhaags: [3, 2, 2], sam: 0, khaali: [0] },\n jhaptaal: { id: 'jhaptaal', numBeats: 10, bhaags: [2, 3, 2, 3], sam: 0, khaali: [5] },\n ektaal: { id: 'ektaal', numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] },\n adachautaal: { id: 'adachautaal', numBeats: 14, sam: 0 },\n choutaal: { id: 'choutaal', numBeats: 12, bhaags: [2, 2, 2, 2, 2, 2], sam: 0, khaali: [2, 6] }, // চৌতাল\n shashthi: { id: 'shashthi', numBeats: 6, bhaags: [2, 4], sam: 0 }, // ষষ্ঠী\n teora: { id: 'teora', numBeats: 7, bhaags: [3, 2, 2], sam: 0 }, // টেওরা\n teentaal: { id: 'teentaal', numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },\n aratheka: { id: 'aratheka', numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] }, // আড়াঠেকা\n} as const satisfies Record<string, Taal>;\n\nexport type TaalId = keyof typeof TAALS;\n","/**\n * @ugfoundation/swaralipi-js/spec\n *\n * The swaralipi JSON spec: Zod schemas, inferred TS types, and parse helpers.\n */\nimport { swaralipiSchema, type Swaralipi } from './schema.js';\n\n/** Current spec version. Documents declare the version they target. */\nexport const SPEC_VERSION = '0.2.1';\n\nexport {\n octaveSchema,\n svaraLetterSchema,\n svaraSchema,\n microtoneSchema,\n barSchema,\n bracketSchema,\n noteSchema,\n rowSchema,\n partSchema,\n taalSchema,\n metaSchema,\n swaralipiSchema,\n} from './schema.js';\n\nexport type {\n Octave,\n SvaraLetter,\n Svara,\n Microtone,\n Bar,\n Bracket,\n Note,\n Row,\n Part,\n Taal,\n Meta,\n Swaralipi,\n} from './schema.js';\n\nexport { TAALS, type TaalId } from './taals.js';\n\n/** Parse + validate unknown data into a `Swaralipi`. Throws `ZodError` on failure. */\nexport function parseSwaralipi(data: unknown): Swaralipi {\n return swaralipiSchema.parse(data);\n}\n\n/** Non-throwing variant: returns Zod's `{ success, data | error }` result. */\nexport function safeParseSwaralipi(data: unknown) {\n return swaralipiSchema.safeParse(data);\n}\n"]}
@@ -4,7 +4,7 @@ export { B as Bar, b as Bracket, M as Meta, c as Microtone, O as Octave, P as Pa
4
4
  export { a as TAALS, T as TaalId } from '../taals-FmEulsjY.cjs';
5
5
 
6
6
  /** Current spec version. Documents declare the version they target. */
7
- declare const SPEC_VERSION = "0.2.0";
7
+ declare const SPEC_VERSION = "0.2.1";
8
8
 
9
9
  /** Parse + validate unknown data into a `Swaralipi`. Throws `ZodError` on failure. */
10
10
  declare function parseSwaralipi(data: unknown): Swaralipi;
@@ -4,7 +4,7 @@ export { B as Bar, b as Bracket, M as Meta, c as Microtone, O as Octave, P as Pa
4
4
  export { a as TAALS, T as TaalId } from '../taals-FmEulsjY.js';
5
5
 
6
6
  /** Current spec version. Documents declare the version they target. */
7
- declare const SPEC_VERSION = "0.2.0";
7
+ declare const SPEC_VERSION = "0.2.1";
8
8
 
9
9
  /** Parse + validate unknown data into a `Swaralipi`. Throws `ZodError` on failure. */
10
10
  declare function parseSwaralipi(data: unknown): Swaralipi;
@@ -1,4 +1,4 @@
1
- export { SPEC_VERSION, barSchema, bracketSchema, metaSchema, microtoneSchema, noteSchema, octaveSchema, parseSwaralipi, partSchema, rowSchema, safeParseSwaralipi, svaraLetterSchema, svaraSchema, swaralipiSchema, taalSchema } from '../chunk-SH2INDZ2.js';
1
+ export { SPEC_VERSION, barSchema, bracketSchema, metaSchema, microtoneSchema, noteSchema, octaveSchema, parseSwaralipi, partSchema, rowSchema, safeParseSwaralipi, svaraLetterSchema, svaraSchema, swaralipiSchema, taalSchema } from '../chunk-KYNCQ7OF.js';
2
2
  export { TAALS } from '../chunk-H6HC44PZ.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -179,50 +179,46 @@ var init_spec = __esm({
179
179
  init_schema();
180
180
  init_schema();
181
181
  init_taals();
182
- SPEC_VERSION = "0.2.0";
182
+ SPEC_VERSION = "0.2.1";
183
183
  }
184
184
  });
185
185
 
186
186
  // src/viewer/glyphs.ts
187
- var LETTERS = {
188
- bn: {
189
- s: { shuddha: "\u09B8" },
190
- r: { shuddha: "\u09B0", komal: "\u098B" },
191
- g: { shuddha: "\u0997", komal: "\u099C\u09CD\u099E" },
192
- m: { shuddha: "\u09AE", tivra: "\u0995\u09CD\u09B7" },
193
- p: { shuddha: "\u09AA" },
194
- d: { shuddha: "\u09A7", komal: "\u09A6" },
195
- n: { shuddha: "\u09A8", komal: "\u09A3" }
196
- },
197
- hi: {
198
- s: { shuddha: "\u0938" },
199
- r: { shuddha: "\u0930", komal: "\u090B" },
200
- g: { shuddha: "\u0917", komal: "\u091C\u094D\u091E" },
201
- m: { shuddha: "\u092E", tivra: "\u0915\u094D\u0937" },
202
- p: { shuddha: "\u092A" },
203
- d: { shuddha: "\u0927", komal: "\u0926" },
204
- n: { shuddha: "\u0928", komal: "\u0923" }
205
- },
206
- en: {
207
- s: { shuddha: "S" },
208
- r: { shuddha: "R" },
209
- g: { shuddha: "G" },
210
- m: { shuddha: "M" },
211
- p: { shuddha: "P" },
212
- d: { shuddha: "D" },
213
- n: { shuddha: "N" }
214
- }
187
+ var SWARABITAN_SHUDDHA = {
188
+ s: "s",
189
+ r: "r",
190
+ g: "g",
191
+ m: "m",
192
+ p: "p",
193
+ d: "q",
194
+ n: "n"
195
+ // q = ধ
196
+ };
197
+ var SWARABITAN_KOMAL = {
198
+ r: "v",
199
+ g: "t",
200
+ d: "d",
201
+ n: "u"
202
+ // ঋ জ্ঞ দ ণ
203
+ };
204
+ var SWARABITAN_TIVRA = { m: "k" };
205
+ var BHATKHANDE_OCTAVE = {
206
+ madhyam: "",
207
+ taar: "u",
208
+ mandra: "l",
209
+ "ati-taar": "U",
210
+ "ati-mandra": "L"
215
211
  };
212
+ var BHATKHANDE_HAS_KOMAL = /* @__PURE__ */ new Set(["r", "g", "d", "n"]);
216
213
  var DIGITS = {
217
214
  bn: "\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED\u09EE\u09EF",
218
215
  hi: "\u0966\u0967\u0968\u0969\u096A\u096B\u096C\u096D\u096E\u096F",
219
216
  en: "0123456789"
220
217
  };
221
- var VOWEL_LETTER_GLYPHS = /* @__PURE__ */ new Set(["\u098B", "\u090B"]);
222
218
  var MARKS = {
223
- bn: { matra: "\u09BE", hyphen: "-", melisma: "\u09F9", cut: "\u0983", reph: "\u09B0\u09CD" },
224
- hi: { matra: "\u093E", hyphen: "-", melisma: "\u0970", cut: "\u0903", reph: "\u0930\u094D" },
225
- en: { matra: "", hyphen: "-", melisma: "\xB7", cut: ":", reph: "" }
219
+ bn: { hyphen: "-", melisma: "\u09F9", cut: "\u0983" },
220
+ hi: { hyphen: "-", melisma: "\u0970", cut: "\u0903" },
221
+ en: { hyphen: "-", melisma: "\xB7", cut: ":" }
226
222
  };
227
223
  var LABELS = {
228
224
  bn: { title: "\u09A8\u09BE\u09AE", taal: "\u09A4\u09BE\u09B2", raag: "\u09B0\u09BE\u0997", avartan: "\u0986\u09AC\u09B0\u09CD\u09A4\u09A8", notationBy: "\u09B8\u09CD\u09AC\u09B0\u09B2\u09BF\u09AA\u09BF" },
@@ -230,40 +226,20 @@ var LABELS = {
230
226
  en: { title: "Song", taal: "Taal", raag: "Raag", avartan: "Avartan", notationBy: "Notation" }
231
227
  };
232
228
  function composeCluster(letter, opts) {
233
- const forms = LETTERS[opts.lang][letter];
234
- const marks = MARKS[opts.lang];
235
229
  const classes = [];
236
- let base = forms.shuddha;
237
- if (opts.komal) {
238
- if (forms.komal !== void 0) base = forms.komal;
239
- else classes.push("sw-komal");
240
- }
241
- if (opts.tivra) {
242
- if (forms.tivra !== void 0) base = forms.tivra;
243
- else classes.push("sw-tivra");
244
- }
245
- const isVowelLetter = VOWEL_LETTER_GLYPHS.has(base);
246
- let text = base;
247
- switch (opts.octave) {
248
- case "taar":
249
- case "ati-taar":
250
- if (marks.reph === "") classes.push("sw-dot-above");
251
- else if (isVowelLetter) classes.push("sw-reph-css");
252
- else text = marks.reph + text;
253
- if (opts.octave === "ati-taar") classes.push("sw-ati-taar");
254
- break;
255
- case "mandra":
256
- case "ati-mandra":
257
- classes.push(marks.reph === "" ? "sw-dot-below" : "sw-mandra");
258
- if (opts.octave === "ati-mandra") classes.push("sw-ati-mandra");
259
- break;
230
+ if (opts.lang === "bn") {
231
+ let text = opts.tivra && SWARABITAN_TIVRA[letter] || opts.komal && SWARABITAN_KOMAL[letter] || SWARABITAN_SHUDDHA[letter];
232
+ if (opts.octave === "taar" || opts.octave === "ati-taar") text += "f";
233
+ else if (opts.octave === "mandra" || opts.octave === "ati-mandra") text += "h";
234
+ if (opts.octave === "ati-taar") classes.push("sw-ati-taar");
235
+ if (opts.octave === "ati-mandra") classes.push("sw-ati-mandra");
236
+ if (opts.withMatra) text += "a";
237
+ return { text, classes };
260
238
  }
261
- let matraSeparate = false;
262
- if (opts.withMatra && marks.matra !== "") {
263
- if (isVowelLetter) matraSeparate = true;
264
- else text += marks.matra;
265
- }
266
- return { text, classes, matraSeparate };
239
+ let base = letter;
240
+ if (opts.tivra && letter === "m") base = "M";
241
+ else if (opts.komal && BHATKHANDE_HAS_KOMAL.has(letter)) base = base.toUpperCase();
242
+ return { text: base + BHATKHANDE_OCTAVE[opts.octave], classes };
267
243
  }
268
244
  function localizeDigits(s, lang) {
269
245
  const digits = DIGITS[lang];
@@ -386,11 +362,8 @@ function bracketGlyphs(note, edge) {
386
362
  if (!note.brackets) return "";
387
363
  return note.brackets.filter((b) => b.edge === edge).map((b) => `<span class="sw-br">${BRACKET_GLYPHS[edge][b.kind]}</span>`).join("");
388
364
  }
389
- function isoMatra(ctx) {
390
- return ctx.marks.matra === "" ? "" : `<span class="sw-iso">${NBSP}${ctx.marks.matra}</span>`;
391
- }
392
- function restGlyph(ctx) {
393
- return ctx.marks.matra === "" ? '<span class="sw-iso">\u2013</span>' : isoMatra(ctx);
365
+ function matraGlyph(ctx) {
366
+ return ctx.lang === "bn" ? '<span class="sw-cl">a</span>' : '<span class="sw-iso">\u2013</span>';
394
367
  }
395
368
  function renderKan(note, ctx) {
396
369
  if (!note.kan) return "";
@@ -412,26 +385,24 @@ function renderPitchedInner(note, opts, ctx) {
412
385
  lang: ctx.lang
413
386
  });
414
387
  inner += `<span class="${["sw-cl", ...c.classes].join(" ")}">${c.text}</span>`;
415
- if (c.matraSeparate) inner += isoMatra(ctx);
416
388
  } else if (note.rest === true) {
417
- inner += restGlyph(ctx);
389
+ inner += matraGlyph(ctx);
418
390
  } else if (note.sustain === true && opts.withMatra) {
419
- inner += isoMatra(ctx);
391
+ inner += matraGlyph(ctx);
420
392
  }
421
393
  if (note.microtone !== void 0) {
422
- const mark = note.microtone === "atikomal" ? "\u21CA" : "\u2193";
394
+ const mark = localizeDigits(note.microtone === "atikomal" ? "2" : "1", ctx.lang);
423
395
  inner += `<span class="sw-micro" title="${note.microtone}">${mark}</span>`;
424
396
  }
425
397
  const d = note.durationMatra ?? 1;
426
- if (isIntegral(d) && d > 1 + EPS) inner += isoMatra(ctx).repeat(Math.round(d) - 1);
398
+ if (isIntegral(d) && d > 1 + EPS) inner += matraGlyph(ctx).repeat(Math.round(d) - 1);
427
399
  if (opts.cuts > 0) inner += `<span class="sw-cut">${NBSP}${ctx.marks.cut}</span>`.repeat(opts.cuts);
428
400
  inner += bracketGlyphs(note, "close");
429
401
  return inner;
430
402
  }
431
- var BAR_STROKES = { dari: 1, single: 1, double: 2, quad: 4 };
403
+ var BAR_GLYPHS = { dari: "A", single: "l", double: "L", quad: "LL" };
432
404
  function renderBarInner(note) {
433
- const strokes = "<i></i>".repeat(BAR_STROKES[note.bar]);
434
- return `${bracketGlyphs(note, "open")}${strokes}${bracketGlyphs(note, "close")}`;
405
+ return `${bracketGlyphs(note, "open")}${BAR_GLYPHS[note.bar]}${bracketGlyphs(note, "close")}`;
435
406
  }
436
407
  function renderAnnotation(notes, ctx) {
437
408
  const parts = [];
@@ -510,7 +481,7 @@ function renderRow(row, partIdx, rowIdx, ctx) {
510
481
  }
511
482
  if (note.lyric !== void 0) {
512
483
  lyric.push(
513
- `<span class="sw-lyr" style="grid-column:${col}">${escapeHtml(lyricText(note.lyric, ctx))}</span>`
484
+ `<span class="sw-lyr${j === 0 ? " sw-cs" : ""}" style="grid-column:${col}">${escapeHtml(lyricText(note.lyric, ctx))}</span>`
514
485
  );
515
486
  }
516
487
  if (note.above !== void 0 && note.above.length > 0) {
@@ -566,13 +537,29 @@ function renderToHtml(doc, opts = {}) {
566
537
  }
567
538
 
568
539
  // src/viewer/styles.ts
540
+ var NOTATION_FONT_URLS = {
541
+ Swarabitan: "https://cdn.daserve.in/webfonts/swarabitan/Swarabitan.ttf",
542
+ ome_bhatkhande_hindi: "https://cdn.daserve.in/webfonts/ome-bhatkhande/OmeBhatkhandeHindi.ttf",
543
+ ome_bhatkhande_english: "https://cdn.daserve.in/webfonts/ome-bhatkhande/OmeBhatkhandeEnglish.ttf"
544
+ };
545
+ NOTATION_FONT_URLS.Swarabitan;
546
+ var FONT_FACES = Object.entries(NOTATION_FONT_URLS).map(
547
+ ([family, url]) => `@font-face { font-family: ${family}; src: url('${url}') format('truetype'); font-display: block; }`
548
+ ).join("\n");
569
549
  var SWARALIPI_CSS = `
550
+ ${FONT_FACES}
551
+
570
552
  .sw-sheet {
571
553
  font-family: var(--sw-font, 'Noto Sans Bengali', 'Kohinoor Bangla', 'Bangla Sangam MN', 'Nirmala UI', 'Noto Sans Devanagari', sans-serif);
572
554
  font-size: var(--sw-size, 1.125rem);
573
555
  line-height: 1.5;
574
556
  }
575
557
 
558
+ /* ---- notation font, per language (the note clusters + grace notes) ---- */
559
+ .sw-sheet[lang="bn"] .sw-cl, .sw-sheet[lang="bn"] .sw-kan { font-family: Swarabitan, var(--sw-font, sans-serif); }
560
+ .sw-sheet[lang="hi"] .sw-cl, .sw-sheet[lang="hi"] .sw-kan { font-family: ome_bhatkhande_hindi, var(--sw-font, sans-serif); }
561
+ .sw-sheet[lang="en"] .sw-cl, .sw-sheet[lang="en"] .sw-kan { font-family: ome_bhatkhande_english, var(--sw-font, sans-serif); }
562
+
576
563
  /* ---- header ---- */
577
564
  .sw-header {
578
565
  margin: 0 0 0.75em;
@@ -601,10 +588,10 @@ var SWARALIPI_CSS = `
601
588
  align-items: end;
602
589
  }
603
590
  .sw-above { grid-row: 1; font-size: 0.62em; justify-self: start; white-space: nowrap; padding-left: 0.2em; }
604
- .sw-n { grid-row: 2; white-space: nowrap; }
591
+ .sw-n { grid-row: 2; justify-self: center; white-space: nowrap; }
605
592
  .sw-arc,
606
593
  .sw-below { grid-row: 3; }
607
- .sw-lyr { grid-row: 4; white-space: nowrap; padding-top: 0.3em; }
594
+ .sw-lyr { grid-row: 4; justify-self: center; white-space: nowrap; padding-top: 0.3em; }
608
595
  .sw-below { font-size: 0.62em; justify-self: start; white-space: nowrap; padding-left: 0.2em; }
609
596
 
610
597
  /* inter-cell gap as padding on each cell's first notation item, so arcs
@@ -614,71 +601,21 @@ var SWARALIPI_CSS = `
614
601
  /* ---- notation clusters & marks ---- */
615
602
  .sw-cl { position: relative; display: inline-block; }
616
603
 
617
- /* mandra: hasanta-shaped understroke (a real \u09CD before \u09BE is invalid Unicode) */
618
- .sw-mandra::after {
619
- content: '';
620
- position: absolute;
621
- left: 0.1em;
622
- bottom: -0.08em;
623
- width: 0.45em;
624
- height: 0.09em;
625
- background: currentColor;
626
- transform: rotate(-32deg);
627
- }
628
- /* ati-mandra: an extra dot below */
604
+ /* bn ati-octave: Swarabitan's f/h give a single reph/hasanta, so ati adds
605
+ * one extra dot above (ati-taar) / below (ati-mandra). bn-only \u2014 hi/en get
606
+ * the two-dot glyph natively from the U/L octave suffix. */
607
+ .sw-ati-taar::before,
629
608
  .sw-ati-mandra::before {
630
609
  content: '';
631
610
  position: absolute;
632
611
  left: 0.25em;
633
- bottom: -0.35em;
634
612
  width: 0.16em;
635
613
  height: 0.16em;
636
614
  border-radius: 50%;
637
615
  background: currentColor;
638
616
  }
639
- /* ati-taar (reph already in the text): an extra dot above */
640
- .sw-ati-taar::before {
641
- content: '';
642
- position: absolute;
643
- left: 0.25em;
644
- top: -0.3em;
645
- width: 0.16em;
646
- height: 0.16em;
647
- border-radius: 50%;
648
- background: currentColor;
649
- }
650
- /* CSS reph fallback for vowel letters \u098B/\u090B that reject a real reph */
651
- .sw-reph-css::before {
652
- content: '';
653
- position: absolute;
654
- top: -0.12em;
655
- left: 0.12em;
656
- width: 0.3em;
657
- height: 0.3em;
658
- border: 0.09em solid currentColor;
659
- border-right: none;
660
- border-bottom: none;
661
- border-top-left-radius: 0.3em;
662
- }
663
- /* en octave dots (Bhatkhande-style) */
664
- .sw-dot-above::before,
665
- .sw-dot-below::before {
666
- content: '';
667
- position: absolute;
668
- left: 50%;
669
- margin-left: -0.07em;
670
- width: 0.14em;
671
- height: 0.14em;
672
- border-radius: 50%;
673
- background: currentColor;
674
- }
675
- .sw-dot-above::before { top: -0.25em; }
676
- .sw-dot-below::before { bottom: -0.2em; }
677
- .sw-ati-taar.sw-dot-above::before,
678
- .sw-ati-mandra.sw-dot-below::before { box-shadow: 0.24em 0 0 currentColor; margin-left: -0.19em; }
679
- /* en accidentals (bn/hi use distinct letters instead) */
680
- .sw-komal { text-decoration: underline; text-underline-offset: 0.18em; }
681
- .sw-tivra::after { content: '\\2032'; font-size: 0.7em; vertical-align: super; }
617
+ .sw-ati-taar::before { top: -0.3em; }
618
+ .sw-ati-mandra::before { bottom: -0.35em; }
682
619
 
683
620
  /* kan grace: small raised cluster left of the main note */
684
621
  .sw-kan {
@@ -695,29 +632,25 @@ var SWARALIPI_CSS = `
695
632
  .sw-br { opacity: 0.8; padding: 0 0.06em; }
696
633
  .sw-micro { font-size: 0.6em; vertical-align: super; opacity: 0.8; }
697
634
 
698
- /* raw fallback: never dropped, visibly non-notation */
635
+ /* raw fallback: never dropped, visibly non-notation. Swarabitan first \u2014
636
+ * raw IS that font's encoding, so undecoded cells show NLTR's own glyphs
637
+ * (dimmed: semantics unverified); monospace ASCII when the font is out. */
699
638
  .sw-raw {
700
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
639
+ font-family: Swarabitan, ui-monospace, SFMono-Regular, Menlo, monospace;
701
640
  font-size: 0.72em;
702
641
  opacity: 0.55;
703
642
  }
704
643
 
705
- /* ---- bars: dari (thin) and danda strokes, notation-row height ---- */
644
+ /* ---- bars: dari (A) and danda (l/L) Swarabitan glyphs ---- */
706
645
  /* horizontal gap before a bar comes from its own .sw-cs padding-left \u2014
707
646
  * never reset it with a padding shorthand here */
708
647
  .sw-bar {
709
648
  grid-row: 2;
710
- align-self: stretch;
711
- display: inline-flex;
712
- align-items: stretch;
713
- gap: 0.16em;
649
+ font-family: Swarabitan, var(--sw-font, sans-serif);
650
+ white-space: nowrap;
714
651
  padding-right: 0.1em;
715
652
  }
716
- .sw-bar i { display: inline-block; width: 0.14em; min-height: 1.25em; background: currentColor; }
717
- .sw-bar--dari i { width: 0.05em; opacity: 0.9; }
718
- .sw-bar--quad i:nth-child(3) { margin-left: 0.18em; }
719
- .sw-above .sw-bar, .sw-below .sw-bar { display: inline-flex; vertical-align: baseline; }
720
- .sw-above .sw-bar i, .sw-below .sw-bar i { min-height: 1em; }
653
+ .sw-bar--quad { letter-spacing: 0.15em; }
721
654
 
722
655
  /* ---- under-arcs (slurs decoded from w/x segments and meend) ---- */
723
656
  .sw-arc {
@@ -826,7 +759,10 @@ function printSwaralipi(doc, opts = {}) {
826
759
  }
827
760
  const cleanup = () => setTimeout(() => iframe.remove(), 1e3);
828
761
  win.addEventListener("afterprint", cleanup, { once: true });
762
+ let printed = false;
829
763
  const go = () => {
764
+ if (printed) return;
765
+ printed = true;
830
766
  try {
831
767
  win.focus();
832
768
  win.print();
@@ -834,8 +770,14 @@ function printSwaralipi(doc, opts = {}) {
834
770
  }
835
771
  };
836
772
  const fonts = iframe.contentDocument?.fonts;
837
- if (fonts?.ready) void fonts.ready.then(go).catch(go);
838
- else setTimeout(go, 250);
773
+ if (fonts && typeof fonts.forEach === "function") {
774
+ const loads = [Promise.resolve(fonts.ready)];
775
+ fonts.forEach((face) => loads.push(face.load().catch(() => void 0)));
776
+ void Promise.all(loads).then(go);
777
+ setTimeout(go, 3e3);
778
+ } else {
779
+ setTimeout(go, 250);
780
+ }
839
781
  };
840
782
  iframe.srcdoc = html;
841
783
  document.body.appendChild(iframe);
@@ -856,6 +798,15 @@ async function loadLyricTransliterator(lang) {
856
798
 
857
799
  // src/viewer/element.ts
858
800
  var LANGS = /* @__PURE__ */ new Set(["bn", "hi", "en"]);
801
+ function ensureNotationFonts() {
802
+ if (typeof FontFace === "undefined" || typeof document === "undefined") return;
803
+ const present = /* @__PURE__ */ new Set();
804
+ for (const f of document.fonts) present.add(f.family.replace(/['"]/g, ""));
805
+ for (const [family, url] of Object.entries(NOTATION_FONT_URLS)) {
806
+ if (present.has(family)) continue;
807
+ document.fonts.add(new FontFace(family, `url(${url})`, { display: "block" }));
808
+ }
809
+ }
859
810
  var sharedSheet;
860
811
  function createElementClass() {
861
812
  return class SwaralipiSheet extends HTMLElement {
@@ -988,6 +939,7 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
988
939
  }
989
940
  const existing = customElements.get(tagName);
990
941
  if (existing) return existing;
942
+ ensureNotationFonts();
991
943
  const ctor = registered.get(tagName) ?? createElementClass();
992
944
  registered.set(tagName, ctor);
993
945
  customElements.define(tagName, ctor);
@@ -996,8 +948,8 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
996
948
 
997
949
  exports.DIGITS = DIGITS;
998
950
  exports.LABELS = LABELS;
999
- exports.LETTERS = LETTERS;
1000
951
  exports.MARKS = MARKS;
952
+ exports.NOTATION_FONT_URLS = NOTATION_FONT_URLS;
1001
953
  exports.SWARALIPI_CSS = SWARALIPI_CSS;
1002
954
  exports.SWARALIPI_PRINT_CSS = SWARALIPI_PRINT_CSS;
1003
955
  exports.composeCluster = composeCluster;