@ugfoundation/swaralipi-js 0.6.0 → 0.6.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.
@@ -162,9 +162,22 @@ function resolveArcs(cells, noteCount) {
162
162
  }
163
163
  return arcs;
164
164
  }
165
- function cutMarkCount(raw) {
166
- if (raw === void 0) return 0;
167
- return (raw.match(/[i:]/g) ?? []).length;
165
+ var SVARA_CHAR = /[srgmpqndtkuv]/;
166
+ function rawMarks(raw, count) {
167
+ if (raw === void 0) return void 0;
168
+ const marks = { aakar: false, pre: "", post: new Array(count).fill("") };
169
+ const leads = SVARA_CHAR.test(raw);
170
+ let seen = 0;
171
+ for (const ch of raw) {
172
+ if (SVARA_CHAR.test(ch)) seen = Math.min(seen + 1, count);
173
+ else if (ch === "a") marks.aakar = true;
174
+ else if (ch === "i" || ch === ":") {
175
+ if (seen > 0) marks.post[seen - 1] += ch;
176
+ else if (leads) marks.pre += ch;
177
+ else marks.post[0] += ch;
178
+ }
179
+ }
180
+ return marks;
168
181
  }
169
182
  function parseBeatLabel(raw) {
170
183
  const m = raw.match(/^([0-9]{1,2})([fF])?:?$/);
@@ -188,16 +201,31 @@ function renderGrace(svara, cls, ctx) {
188
201
  const c = composeCluster(letter, { octave, withMatra: false, lang: ctx.lang });
189
202
  return `<span class="${[cls, ...c.classes].join(" ")}">${c.text}</span>`;
190
203
  }
204
+ function cutGlyphs(marks, ctx) {
205
+ if (marks === "") return "";
206
+ return ctx.lang === "bn" ? `<span class="sw-cl" title="${ctx.marks.cut}">${marks}</span>` : `<span class="sw-cut" title="${ctx.marks.cut}"></span>`.repeat(marks.length);
207
+ }
191
208
  function cellMatra(cell) {
192
209
  const total = cell.notes.reduce((a, n) => a + (n.durationMatra ?? 1), 0);
193
210
  const half = cell.notes.some((n) => n.halfMatra === true);
194
211
  const whole = Math.floor(total + EPS);
195
212
  return { matras: half ? whole : Math.max(1, whole), half };
196
213
  }
214
+ function cellPitchedOpts(cell) {
215
+ const { matras, half } = cellMatra(cell);
216
+ const marks = rawMarks(cell.notes[0].raw, cell.notes.length);
217
+ const aakars = marks !== void 0 && !marks.aakar ? 0 : matras;
218
+ return cell.notes.map((_, j) => ({
219
+ matras: j === cell.notes.length - 1 ? aakars : 0,
220
+ pre: j === 0 ? marks?.pre ?? "" : "",
221
+ post: marks !== void 0 ? marks.post[j] : half && j === cell.notes.length - 1 ? "i" : ""
222
+ }));
223
+ }
197
224
  function renderPitchedInner(note, opts, ctx) {
198
225
  let inner = bracketGlyphs(note, "open");
199
226
  inner += renderGrace(note.kan, "sw-kan", ctx);
200
227
  if (note.sustain === true) inner += `<span class="sw-hy">${ctx.marks.hyphen}</span>`;
228
+ inner += cutGlyphs(opts.pre, ctx);
201
229
  if (note.svara !== void 0) {
202
230
  const [octave, letter] = note.svara;
203
231
  const c = composeCluster(letter, {
@@ -223,8 +251,7 @@ function renderPitchedInner(note, opts, ctx) {
223
251
  if (note.svara !== void 0 && opts.matras > 1) {
224
252
  inner += matraGlyph(ctx).repeat(opts.matras - 1);
225
253
  }
226
- if (opts.half) inner += `<span class="sw-half" title="${ctx.marks.cut}"></span>`;
227
- if (opts.cuts > 0) inner += `<span class="sw-cut" title="${ctx.marks.cut}"></span>`.repeat(opts.cuts);
254
+ inner += cutGlyphs(opts.post, ctx);
228
255
  inner += bracketGlyphs(note, "close");
229
256
  return inner;
230
257
  }
@@ -241,12 +268,9 @@ function renderAnnotation(notes, ctx) {
241
268
  continue;
242
269
  }
243
270
  if (cell.kind === "group") {
244
- const { matras, half } = cellMatra(cell);
245
- const cuts = half ? 0 : cutMarkCount(cell.notes[0].raw);
271
+ const opts = cellPitchedOpts(cell);
246
272
  cell.notes.forEach((note2, j) => {
247
- const last = j === cell.notes.length - 1;
248
- const opts = last ? { matras, half, cuts } : { matras: 0, half: false, cuts: 0 };
249
- parts.push(`<span class="sw-n">${renderPitchedInner(note2, opts, ctx)}</span>`);
273
+ parts.push(`<span class="sw-n">${renderPitchedInner(note2, opts[j], ctx)}</span>`);
250
274
  });
251
275
  continue;
252
276
  }
@@ -284,8 +308,7 @@ function renderRow(row, partIdx, rowIdx, ctx) {
284
308
  const firstCol = cell.start + 1;
285
309
  const lastCol = cell.start + cell.notes.length;
286
310
  const cellSpan = lastCol > firstCol ? `grid-column:${firstCol} / ${lastCol + 1}` : `grid-column:${firstCol}`;
287
- const { matras, half } = cellMatra(cell);
288
- const cuts = cell.kind === "group" && !half ? cutMarkCount(cell.notes[0].raw) : 0;
311
+ const opts = cell.kind === "group" ? cellPitchedOpts(cell) : [];
289
312
  cell.notes.forEach((note, j) => {
290
313
  const col = cell.start + j + 1;
291
314
  const path = `${partIdx}.${rowIdx}.${cell.start + j}`;
@@ -295,10 +318,8 @@ function renderRow(row, partIdx, rowIdx, ctx) {
295
318
  `<span class="sw-bar sw-bar--${note.bar}${cellEdges}" data-note="${path}" style="grid-column:${col}">${renderBarInner(note)}</span>`
296
319
  );
297
320
  } else if (cell.kind === "group") {
298
- const last = j === cell.notes.length - 1;
299
- const pOpts = last ? { matras, half, cuts } : { matras: 0, half: false, cuts: 0 };
300
321
  notation.push(
301
- `<span class="sw-n${cellEdges}" data-note="${path}" style="grid-column:${col}">${renderPitchedInner(note, pOpts, ctx)}</span>`
322
+ `<span class="sw-n${cellEdges}" data-note="${path}" style="grid-column:${col}">${renderPitchedInner(note, opts[j], ctx)}</span>`
302
323
  );
303
324
  } else {
304
325
  const raw = rawDisplay(note);
@@ -463,21 +484,19 @@ ${FONT_FACES}
463
484
 
464
485
  .sw-hy { padding-right: 0.02em; }
465
486
  .sw-iso { display: inline-block; margin-left: -0.18em; }
466
- /* The \u0985\u09B0\u09CD\u09A7\u09AE\u09BE\u09A4\u09CD\u09B0\u09BE / \u099B\u09C7\u09A6 mark: two stacked rings (legend p. \u09E7\u09E9\u09EB), drawn rather
467
- * than typed \u2014 an isolated \u0983 has no base, so every Bengali font shapes it with
468
- * a dotted circle (an NBSP carrier does not help). sw-half = semantic
469
- * (halfMatra), sw-cut = decoded from a source cell's raw: one look, two
470
- * provenances. */
471
- .sw-cut,
472
- .sw-half {
487
+ /* The \u0985\u09B0\u09CD\u09A7\u09AE\u09BE\u09A4\u09CD\u09B0\u09BE / \u099B\u09C7\u09A6 mark: two stacked rings (legend p. \u09E7\u09E9\u09EB). bn types
488
+ * Swarabitan's own glyph (the ASCII i key) \u2014 an isolated Unicode \u0983 has no base and
489
+ * every Bengali text font shapes it with a dotted circle (an NBSP carrier
490
+ * does not help), but the notation font has the mark itself. hi/en fall back
491
+ * to these drawn rings: Bhatkhande has no such sign. */
492
+ .sw-cut {
473
493
  position: relative;
474
494
  display: inline-block;
475
495
  width: 0.3em;
476
496
  height: 1em;
477
497
  vertical-align: baseline;
478
498
  }
479
- .sw-cut::before, .sw-cut::after,
480
- .sw-half::before, .sw-half::after {
499
+ .sw-cut::before, .sw-cut::after {
481
500
  content: '';
482
501
  position: absolute;
483
502
  left: 0.08em;
@@ -486,8 +505,8 @@ ${FONT_FACES}
486
505
  border-radius: 50%;
487
506
  background: currentColor;
488
507
  }
489
- .sw-cut::before, .sw-half::before { top: 0.2em; }
490
- .sw-cut::after, .sw-half::after { top: 0.55em; }
508
+ .sw-cut::before { top: 0.2em; }
509
+ .sw-cut::after { top: 0.55em; }
491
510
  .sw-br { opacity: 0.8; padding: 0 0.06em; }
492
511
  .sw-micro { font-size: 0.6em; vertical-align: super; opacity: 0.8; }
493
512