@ugfoundation/swaralipi-js 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{batch-NDL6CW7Q.js → batch-6FPBT5DP.js} +4 -4
- package/dist/{batch-NDL6CW7Q.js.map → batch-6FPBT5DP.js.map} +1 -1
- package/dist/{chunk-LPCKL3AM.js → chunk-FULC22MN.js} +3 -3
- package/dist/{chunk-LPCKL3AM.js.map → chunk-FULC22MN.js.map} +1 -1
- package/dist/{chunk-Z42M5JAA.js → chunk-UMBKIGVQ.js} +3 -3
- package/dist/{chunk-Z42M5JAA.js.map → chunk-UMBKIGVQ.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/convert/index.cjs +1 -1
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/spec/index.cjs +1 -1
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +1 -1
- package/dist/spec/index.d.ts +1 -1
- package/dist/spec/index.js +1 -1
- package/dist/viewer/index.cjs +157 -109
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +11 -5
- package/dist/viewer/index.d.ts +11 -5
- package/dist/viewer/index.js +156 -108
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
package/dist/viewer/index.d.cts
CHANGED
|
@@ -116,6 +116,9 @@ interface Arc {
|
|
|
116
116
|
/** 0-based index of the arc's first / last **cell** within the row. */
|
|
117
117
|
start: number;
|
|
118
118
|
end: number;
|
|
119
|
+
/** Index, within that cell's notes, of the note the arc hangs from / into. */
|
|
120
|
+
startNote: number;
|
|
121
|
+
endNote: number;
|
|
119
122
|
/** True when the matching end lies on a previous / following system. */
|
|
120
123
|
openLeft: boolean;
|
|
121
124
|
openRight: boolean;
|
|
@@ -123,11 +126,14 @@ interface Arc {
|
|
|
123
126
|
line?: boolean;
|
|
124
127
|
}
|
|
125
128
|
/**
|
|
126
|
-
* Resolve under-arcs for one row
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* -
|
|
129
|
+
* Resolve under-arcs for one row down to the **note** each end hangs from, so a
|
|
130
|
+
* slur inside one half-matra cell (মজ্ঞা) still runs centre to centre. Sources,
|
|
131
|
+
* scanned left→right:
|
|
132
|
+
* - spec `meend: 'start' | 'end'` — exact notes;
|
|
133
|
+
* - below-annotation raws, which belong to the whole cell (NLTR types them
|
|
134
|
+
* centred under the column): `w` opens on the cell's first sounded note,
|
|
135
|
+
* `x` closes on its last, `$` (Swarabitan's short in-cell arc) runs first to
|
|
136
|
+
* last, and `xw` — close, then re-open — meets in the middle instead.
|
|
131
137
|
* Unmatched `x` → arc from cell 0 with a squared-off (open) left end;
|
|
132
138
|
* unclosed `w` → arc to the last cell with an open right end — both match
|
|
133
139
|
* the print's behavior when an arc continues across a system wrap.
|
package/dist/viewer/index.d.ts
CHANGED
|
@@ -116,6 +116,9 @@ interface Arc {
|
|
|
116
116
|
/** 0-based index of the arc's first / last **cell** within the row. */
|
|
117
117
|
start: number;
|
|
118
118
|
end: number;
|
|
119
|
+
/** Index, within that cell's notes, of the note the arc hangs from / into. */
|
|
120
|
+
startNote: number;
|
|
121
|
+
endNote: number;
|
|
119
122
|
/** True when the matching end lies on a previous / following system. */
|
|
120
123
|
openLeft: boolean;
|
|
121
124
|
openRight: boolean;
|
|
@@ -123,11 +126,14 @@ interface Arc {
|
|
|
123
126
|
line?: boolean;
|
|
124
127
|
}
|
|
125
128
|
/**
|
|
126
|
-
* Resolve under-arcs for one row
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* -
|
|
129
|
+
* Resolve under-arcs for one row down to the **note** each end hangs from, so a
|
|
130
|
+
* slur inside one half-matra cell (মজ্ঞা) still runs centre to centre. Sources,
|
|
131
|
+
* scanned left→right:
|
|
132
|
+
* - spec `meend: 'start' | 'end'` — exact notes;
|
|
133
|
+
* - below-annotation raws, which belong to the whole cell (NLTR types them
|
|
134
|
+
* centred under the column): `w` opens on the cell's first sounded note,
|
|
135
|
+
* `x` closes on its last, `$` (Swarabitan's short in-cell arc) runs first to
|
|
136
|
+
* last, and `xw` — close, then re-open — meets in the middle instead.
|
|
131
137
|
* Unmatched `x` → arc from cell 0 with a squared-off (open) left end;
|
|
132
138
|
* unclosed `w` → arc to the last cell with an open right end — both match
|
|
133
139
|
* the print's behavior when an arc continues across a system wrap.
|
package/dist/viewer/index.js
CHANGED
|
@@ -194,42 +194,64 @@ function alignRows(rows) {
|
|
|
194
194
|
}
|
|
195
195
|
function resolveArcs(cells) {
|
|
196
196
|
const arcs = [];
|
|
197
|
-
let
|
|
197
|
+
let open;
|
|
198
198
|
let sawEnds = false;
|
|
199
199
|
const fillCells = [];
|
|
200
|
-
const openAt = (
|
|
200
|
+
const openAt = (cell, note) => {
|
|
201
201
|
sawEnds = true;
|
|
202
|
-
|
|
202
|
+
open ??= { cell, note };
|
|
203
203
|
};
|
|
204
|
-
const closeAt = (
|
|
204
|
+
const closeAt = (cell, note) => {
|
|
205
205
|
sawEnds = true;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
const from = open ?? { cell: 0, note: 0 };
|
|
207
|
+
arcs.push({
|
|
208
|
+
start: from.cell,
|
|
209
|
+
startNote: from.note,
|
|
210
|
+
end: cell,
|
|
211
|
+
endNote: note,
|
|
212
|
+
openLeft: open === void 0,
|
|
213
|
+
openRight: false
|
|
214
|
+
});
|
|
215
|
+
open = void 0;
|
|
212
216
|
};
|
|
213
217
|
cells.forEach((cell, i) => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
218
|
+
const pitched = cell.notes.flatMap((n, j) => isPitched(n) ? [j] : []);
|
|
219
|
+
const first = pitched[0] ?? 0;
|
|
220
|
+
const last = pitched[pitched.length - 1] ?? cell.notes.length - 1;
|
|
221
|
+
let marks = "";
|
|
222
|
+
cell.notes.forEach((note, j) => {
|
|
223
|
+
if (note.meend === "start") openAt(i, j);
|
|
224
|
+
else if (note.meend === "end") closeAt(i, j);
|
|
225
|
+
for (const b of note.below ?? []) if (isArcRaw(b.raw)) marks += b.raw;
|
|
226
|
+
});
|
|
227
|
+
const reopens = /[xX].*[wW]/.test(marks);
|
|
228
|
+
for (const ch of marks) {
|
|
229
|
+
if (ch === "w" || ch === "W") openAt(i, reopens ? last : first);
|
|
230
|
+
else if (ch === "x" || ch === "X") closeAt(i, reopens ? first : last);
|
|
231
|
+
else if (ch === "$") {
|
|
232
|
+
openAt(i, first);
|
|
233
|
+
closeAt(i, last);
|
|
234
|
+
} else fillCells.push(i);
|
|
225
235
|
}
|
|
226
236
|
});
|
|
227
|
-
|
|
228
|
-
|
|
237
|
+
const lastCell = Math.max(cells.length - 1, 0);
|
|
238
|
+
const lastNote = (c) => Math.max((cells[c]?.notes.length ?? 1) - 1, 0);
|
|
239
|
+
if (open !== void 0) {
|
|
240
|
+
arcs.push({
|
|
241
|
+
start: open.cell,
|
|
242
|
+
startNote: open.note,
|
|
243
|
+
end: lastCell,
|
|
244
|
+
endNote: lastNote(lastCell),
|
|
245
|
+
openLeft: false,
|
|
246
|
+
openRight: true
|
|
247
|
+
});
|
|
229
248
|
} else if (!sawEnds && fillCells.length > 0) {
|
|
249
|
+
const end = Math.max(...fillCells);
|
|
230
250
|
arcs.push({
|
|
231
251
|
start: Math.min(...fillCells),
|
|
232
|
-
|
|
252
|
+
startNote: 0,
|
|
253
|
+
end,
|
|
254
|
+
endNote: lastNote(end),
|
|
233
255
|
openLeft: true,
|
|
234
256
|
openRight: true,
|
|
235
257
|
line: true
|
|
@@ -237,6 +259,35 @@ function resolveArcs(cells) {
|
|
|
237
259
|
}
|
|
238
260
|
return arcs;
|
|
239
261
|
}
|
|
262
|
+
function arcInk(cells, arcs) {
|
|
263
|
+
const ink = { cells: cells.map(() => ""), notes: cells.map((c) => c.notes.map(() => "")) };
|
|
264
|
+
const mark = (c, j, cls, clip = false) => {
|
|
265
|
+
ink.notes[c][j] += ` ${cls}`;
|
|
266
|
+
if (clip && !ink.cells[c].includes(" sw-mk")) ink.cells[c] += " sw-mk";
|
|
267
|
+
};
|
|
268
|
+
for (const arc of arcs) {
|
|
269
|
+
const { start: s, end: e, startNote: j, endNote: k } = arc;
|
|
270
|
+
const hookL = arc.line !== true && !arc.openLeft;
|
|
271
|
+
const hookR = arc.line !== true && !arc.openRight;
|
|
272
|
+
if (s === e && hookL && hookR) {
|
|
273
|
+
if (j === k) {
|
|
274
|
+
mark(s, j, "sw-mos");
|
|
275
|
+
mark(s, j, "sw-mcs");
|
|
276
|
+
} else {
|
|
277
|
+
mark(s, j, "sw-mob");
|
|
278
|
+
for (let n = j + 1; n < k; n++) mark(s, n, "sw-mf");
|
|
279
|
+
mark(s, k, "sw-mcb");
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
for (let c = s; c <= e; c++) {
|
|
284
|
+
if (c === s && hookL) mark(c, j, "sw-mo", true);
|
|
285
|
+
else if (c === e && hookR) mark(c, k, "sw-mc", true);
|
|
286
|
+
else mark(c, 0, "sw-mm", true);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return ink;
|
|
290
|
+
}
|
|
240
291
|
var SVARA_CHAR = /[srgmpqndtkuv]/;
|
|
241
292
|
function rawMarks(raw, count) {
|
|
242
293
|
if (raw === void 0) return void 0;
|
|
@@ -412,25 +463,9 @@ function renderNote(note, kind, opts, ctx) {
|
|
|
412
463
|
const raw = rawDisplay(note);
|
|
413
464
|
return bracketGlyphs(note, "open") + (raw !== void 0 ? `<span class="sw-raw" title="${escapeHtml(raw)}">${escapeHtml(raw)}</span>` : "") + bracketGlyphs(note, "close");
|
|
414
465
|
}
|
|
415
|
-
function renderArc(arc, offset) {
|
|
416
|
-
const open = arc.line === true;
|
|
417
|
-
const openL = arc.openLeft || open;
|
|
418
|
-
const openR = arc.openRight || open;
|
|
419
|
-
const fill = '<i class="sw-arc-m"></i>';
|
|
420
|
-
const hookL = openL ? "" : '<i class="sw-arc-l">w</i>';
|
|
421
|
-
const hookR = openR ? "" : '<i class="sw-arc-r">x</i>';
|
|
422
|
-
const piece = (col, cls, inner) => `<span class="sw-arc${cls}" style="grid-column:${col}">${inner}</span>`;
|
|
423
|
-
const f = offset + arc.start + COL_BASE;
|
|
424
|
-
const t = offset + arc.end + COL_BASE;
|
|
425
|
-
const openCls = (openL ? " sw-arc--openl" : "") + (openR ? " sw-arc--openr" : "");
|
|
426
|
-
if (t === f) return piece(`${f}`, openCls, hookL + fill + hookR);
|
|
427
|
-
const head = piece(`${f}`, openL ? " sw-arc--openl" : " sw-arc--head", hookL + fill);
|
|
428
|
-
const tail = piece(`${t}`, openR ? " sw-arc--openr" : " sw-arc--tail", fill + hookR);
|
|
429
|
-
const mid = t > f + 1 ? piece(`${f + 1} / ${t}`, " sw-arc--mid", fill) : "";
|
|
430
|
-
return head + mid + tail;
|
|
431
|
-
}
|
|
432
466
|
function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
433
467
|
const cells = groupRowIntoCells(row.notes);
|
|
468
|
+
const ink = arcInk(cells, resolveArcs(cells));
|
|
434
469
|
const out = [];
|
|
435
470
|
if (row.label !== void 0)
|
|
436
471
|
out.push(`<span class="sw-label">${escapeHtml(ctx.tr(row.label))}</span>`);
|
|
@@ -440,11 +475,10 @@ function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
|
440
475
|
const barCls = cell.kind === "bar" ? " sw-bar-cell" : "";
|
|
441
476
|
const notes = cell.notes.map((note, j) => {
|
|
442
477
|
const path = `${partIdx}.${rowIdx}.${cell.start + j}`;
|
|
443
|
-
const cls = isPitched(note) ? " sw-p" : "";
|
|
444
478
|
const bar = note.bar !== void 0 ? ` sw-bar sw-bar--${note.bar}` : "";
|
|
445
|
-
return `<span class="sw-n${
|
|
479
|
+
return `<span class="sw-n${bar}${ink.notes[ci][j]}" data-note="${path}">${renderNote(note, cell.kind, opts[j], ctx)}</span>`;
|
|
446
480
|
}).join("");
|
|
447
|
-
out.push(`<span class="sw-cell${barCls}" style="grid-column:${col}">${notes}</span>`);
|
|
481
|
+
out.push(`<span class="sw-cell${barCls}${ink.cells[ci]}" style="grid-column:${col}">${notes}</span>`);
|
|
448
482
|
const slices = cellLyricSlices(cell);
|
|
449
483
|
if (slices !== void 0) {
|
|
450
484
|
const html = slices.map((t) => `<span>${escapeHtml(lyricText(t, ctx))}</span>`).join("");
|
|
@@ -457,7 +491,6 @@ function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
|
457
491
|
if (below.length > 0)
|
|
458
492
|
out.push(`<span class="sw-below" style="grid-column:${col}">${renderAnnotation(below, ctx)}</span>`);
|
|
459
493
|
});
|
|
460
|
-
for (const arc of resolveArcs(cells)) out.push(renderArc(arc, offset));
|
|
461
494
|
return `<div class="sw-row">${out.join("")}</div>`;
|
|
462
495
|
}
|
|
463
496
|
function renderHeader(meta, ctx) {
|
|
@@ -515,7 +548,7 @@ function fitSwaralipi(root, minScale = 0, zoom = 1, minSpacing = 0.35, minSize =
|
|
|
515
548
|
sheet.style.removeProperty("--sw-size");
|
|
516
549
|
sheet.style.removeProperty("--sw-tight");
|
|
517
550
|
const available = scroll.clientWidth;
|
|
518
|
-
let natural = block.
|
|
551
|
+
let natural = block.offsetWidth;
|
|
519
552
|
if (available === 0 || natural === 0) continue;
|
|
520
553
|
if (natural > available && minSpacing < 1) {
|
|
521
554
|
let loose = minSpacing;
|
|
@@ -523,11 +556,11 @@ function fitSwaralipi(root, minScale = 0, zoom = 1, minSpacing = 0.35, minSize =
|
|
|
523
556
|
for (let i = 0; i < 6; i++) {
|
|
524
557
|
const mid = (loose + tight) / 2;
|
|
525
558
|
sheet.style.setProperty("--sw-tight", String(mid));
|
|
526
|
-
if (block.
|
|
559
|
+
if (block.offsetWidth <= available) loose = mid;
|
|
527
560
|
else tight = mid;
|
|
528
561
|
}
|
|
529
562
|
sheet.style.setProperty("--sw-tight", String(loose));
|
|
530
|
-
natural = block.
|
|
563
|
+
natural = block.offsetWidth;
|
|
531
564
|
}
|
|
532
565
|
const base = parseFloat(getComputedStyle(sheet).fontSize);
|
|
533
566
|
if (!(base > 0)) continue;
|
|
@@ -625,31 +658,45 @@ ${FONT_FACES}
|
|
|
625
658
|
column-gap: calc(var(--sw-cell-gap, 0.4em) * var(--sw-tight, 1));
|
|
626
659
|
align-items: end;
|
|
627
660
|
width: max-content;
|
|
661
|
+
/* The last column's cell spacers overhang half a gap past the grid; clip
|
|
662
|
+
that sideways so it neither counts as width to fit nor raises a scrollbar. */
|
|
663
|
+
overflow-x: clip;
|
|
628
664
|
}
|
|
629
665
|
.sw-part { display: contents; }
|
|
666
|
+
/* Four FIXED bands per row \u2014 above / notation / arc / lyric \u2014 so a beat label,
|
|
667
|
+
* a \u0995\u09A3 or a \u09AE\u09C0\u09A1\u09BC never changes how far a line sits from its words: the
|
|
668
|
+
* notation hugs its lyric and the next system starts a little further off,
|
|
669
|
+
* as the printed \u09B8\u09CD\u09AC\u09B0\u09AC\u09BF\u09A4\u09BE\u09A8 sets it. Content taller than its band overflows
|
|
670
|
+
* rather than pushing the rhythm apart. */
|
|
630
671
|
.sw-row {
|
|
631
672
|
grid-column: 1 / -1;
|
|
632
673
|
display: grid;
|
|
633
674
|
grid-template-columns: subgrid;
|
|
634
|
-
grid-template-rows:
|
|
675
|
+
grid-template-rows: 0.95em 1.35em 0.4em 1.35em;
|
|
635
676
|
align-items: end;
|
|
636
|
-
padding: 0.
|
|
677
|
+
padding-bottom: 0.35em;
|
|
678
|
+
line-height: 1.35;
|
|
637
679
|
}
|
|
638
680
|
.sw-label { grid-column: 1; grid-row: 2; padding-right: 0.5em; font-weight: 600; }
|
|
639
681
|
|
|
640
|
-
/* One column per cell
|
|
641
|
-
* flex
|
|
642
|
-
*
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
.sw-cell
|
|
646
|
-
.sw-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
.sw-
|
|
652
|
-
.sw-
|
|
682
|
+
/* One column per cell, its notes set tight as one word (\u09B0\u09B8\u09BE) and centred by
|
|
683
|
+
* two flex spacers. Each spacer overhangs half the column gap (a min-width
|
|
684
|
+
* paid back by a negative margin, so the column is sized by the notes alone),
|
|
685
|
+
* which lets neighbouring cells' spacers meet mid-gap: a \u09AE\u09C0\u09A1\u09BC crossing cells is
|
|
686
|
+
* inked by these boxes with no hole between them. */
|
|
687
|
+
.sw-cell, .sw-lyr { display: flex; justify-content: center; white-space: nowrap; }
|
|
688
|
+
.sw-cell {
|
|
689
|
+
--sw-half-gap: calc(var(--sw-cell-gap, 0.4em) * var(--sw-tight, 1) / 2);
|
|
690
|
+
grid-row: 2;
|
|
691
|
+
align-self: stretch;
|
|
692
|
+
}
|
|
693
|
+
.sw-cell::before, .sw-cell::after { content: ''; flex: 1 0 0; min-width: var(--sw-half-gap); }
|
|
694
|
+
.sw-cell::before { margin-left: calc(var(--sw-half-gap) * -1); }
|
|
695
|
+
.sw-cell::after { margin-right: calc(var(--sw-half-gap) * -1); }
|
|
696
|
+
.sw-cell > .sw-n { position: relative; }
|
|
697
|
+
.sw-above { grid-row: 1; font-size: 0.62em; line-height: 1; justify-self: center; white-space: nowrap; }
|
|
698
|
+
.sw-below { grid-row: 3; font-size: 0.62em; line-height: 1; justify-self: center; white-space: nowrap; }
|
|
699
|
+
.sw-lyr { grid-row: 4; }
|
|
653
700
|
|
|
654
701
|
/* ---- notation clusters & marks ---- */
|
|
655
702
|
.sw-cl { position: relative; display: inline-block; }
|
|
@@ -670,12 +717,16 @@ ${FONT_FACES}
|
|
|
670
717
|
.sw-ati-taar::before { top: -0.3em; }
|
|
671
718
|
.sw-ati-mandra::before { bottom: -0.35em; }
|
|
672
719
|
|
|
673
|
-
/* grace notes: small raised clusters \u2014 \u0995\u09A3 before the note, \u09B0\u09C7\u09B6 after it
|
|
720
|
+
/* grace notes: small raised clusters \u2014 \u0995\u09A3 before the note, \u09B0\u09C7\u09B6 after it.
|
|
721
|
+
* line-height 0 (here and on .sw-micro): a raised inline box taller than the
|
|
722
|
+
* strut grows its line box and drops that one note's baseline below its
|
|
723
|
+
* neighbours' (css2 \xA710.8). */
|
|
674
724
|
.sw-kan,
|
|
675
725
|
.sw-resh {
|
|
676
726
|
position: relative;
|
|
677
727
|
display: inline-block;
|
|
678
728
|
font-size: 0.58em;
|
|
729
|
+
line-height: 0;
|
|
679
730
|
vertical-align: 0.75em;
|
|
680
731
|
}
|
|
681
732
|
.sw-kan { margin-right: 0.08em; }
|
|
@@ -707,7 +758,7 @@ ${FONT_FACES}
|
|
|
707
758
|
.sw-cut::before { top: 0.2em; }
|
|
708
759
|
.sw-cut::after { top: 0.55em; }
|
|
709
760
|
.sw-br { opacity: 0.8; padding: 0 0.06em; }
|
|
710
|
-
.sw-micro { font-size: 0.6em; vertical-align: super; opacity: 0.8; }
|
|
761
|
+
.sw-micro { font-size: 0.6em; line-height: 0; vertical-align: super; opacity: 0.8; }
|
|
711
762
|
|
|
712
763
|
/* raw fallback: never dropped, visibly non-notation. Swarabitan first \u2014
|
|
713
764
|
* raw IS that font's encoding, so undecoded cells show NLTR's own glyphs
|
|
@@ -727,49 +778,46 @@ ${FONT_FACES}
|
|
|
727
778
|
/* a bar owns a whole column but needs no matra of width */
|
|
728
779
|
.sw-bar-cell { min-width: 0; }
|
|
729
780
|
|
|
730
|
-
/* ---- \u09AE\u09C0\u09A1\u09BC under-arcs (from w/x segments and spec meend) ----
|
|
731
|
-
* Swarabitan
|
|
732
|
-
* flat
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
.sw-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
.sw-
|
|
745
|
-
.sw-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
.sw-
|
|
759
|
-
.sw-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
margin-bottom: -0.01em;
|
|
771
|
-
background: currentColor;
|
|
772
|
-
}
|
|
781
|
+
/* ---- \u09AE\u09C0\u09A1\u09BC under-arcs (from w/x/$ segments and spec meend) ----
|
|
782
|
+
* Swarabitan's own arc, all langs: w is the opening hook, curling down from
|
|
783
|
+
* its tip into a flat, x its mirror, y that flat alone \u2014 and they join
|
|
784
|
+
* hole-free, so one run of text is a whole end (tools/align-check/glyph.html).
|
|
785
|
+
* Each end sets its hook tip on its note's centre and runs the flat toward the
|
|
786
|
+
* other end (render.ts arcInk): across cells it is cut at the mid-gap, where
|
|
787
|
+
* the next cell's run carries on (.sw-mk clips a cell half a gap past each
|
|
788
|
+
* side, as far as its spacers reach); inside one cell, at its note's edge. So
|
|
789
|
+
* a long arc extends the flat and a short one trims it off. line-height 0 sets
|
|
790
|
+
* every run's baseline the same distance below its note. The clip reaches
|
|
791
|
+
* 0.5px further so two cells' anti-aliased edges overlap instead of leaving a
|
|
792
|
+
* hairline seam between their identical flats. */
|
|
793
|
+
.sw-mk { clip-path: inset(-3em calc(var(--sw-half-gap) * -1 - 0.5px)); }
|
|
794
|
+
.sw-mo::after, .sw-mob::after, .sw-mos::after,
|
|
795
|
+
.sw-mc::before, .sw-mcb::before, .sw-mcs::before,
|
|
796
|
+
.sw-mf::before, .sw-mm::before {
|
|
797
|
+
content: 'yyyyyyyyyyyy';
|
|
798
|
+
position: absolute;
|
|
799
|
+
top: calc(100% + 0.05em);
|
|
800
|
+
font-family: Swarabitan;
|
|
801
|
+
font-size: 0.9em;
|
|
802
|
+
line-height: 0;
|
|
803
|
+
white-space: pre;
|
|
804
|
+
overflow-x: clip;
|
|
805
|
+
}
|
|
806
|
+
.sw-mo::after, .sw-mob::after, .sw-mos::after { content: 'wyyyyyyyyyyyy'; }
|
|
807
|
+
/* rtl: a closing run cut short keeps its hook on its right edge */
|
|
808
|
+
.sw-mc::before, .sw-mcb::before, .sw-mcs::before { content: 'yyyyyyyyyyyyx'; direction: rtl; }
|
|
809
|
+
.sw-mo::after, .sw-mob::after { left: 50%; }
|
|
810
|
+
.sw-mc::before, .sw-mcb::before { right: 50%; }
|
|
811
|
+
.sw-mos::after { left: 0; }
|
|
812
|
+
.sw-mcs::before { right: 0; }
|
|
813
|
+
.sw-mf::before { left: 0; right: 0; }
|
|
814
|
+
.sw-mm::before { left: -12em; }
|
|
815
|
+
/* Inside one cell a hook gets half a note to curl in, about a third of its
|
|
816
|
+
* curl: squeezed to half width (same stroke) it lands flat at the cut, so the
|
|
817
|
+
* two flats meet level whatever the notes' widths \u2014 a small arc, not a V. */
|
|
818
|
+
.sw-mob::after, .sw-mos::after, .sw-mcb::before, .sw-mcs::before { width: 100%; transform: scaleX(0.5); }
|
|
819
|
+
.sw-mob::after, .sw-mos::after { transform-origin: left; }
|
|
820
|
+
.sw-mcb::before, .sw-mcs::before { transform-origin: right; }
|
|
773
821
|
|
|
774
822
|
/* ---- annotation content ---- */
|
|
775
823
|
.sw-beat { position: relative; display: inline-block; padding: 0 0.1em; }
|