@ugfoundation/swaralipi-js 0.9.2 → 0.10.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.
- package/README.md +17 -0
- package/dist/{batch-IHEXFEGC.js → batch-5ZFEKXKS.js} +5 -5
- package/dist/{batch-IHEXFEGC.js.map → batch-5ZFEKXKS.js.map} +1 -1
- package/dist/{chunk-TZBDIRIA.js → chunk-R6VNIBMG.js} +7 -7
- package/dist/chunk-R6VNIBMG.js.map +1 -0
- package/dist/{chunk-MYVIQU4O.js → chunk-SR7NFEGL.js} +5 -3
- package/dist/chunk-SR7NFEGL.js.map +1 -0
- package/dist/{chunk-F23LZMIU.js → chunk-UPJYLXYX.js} +3 -3
- package/dist/{chunk-F23LZMIU.js.map → chunk-UPJYLXYX.js.map} +1 -1
- package/dist/{chunk-SEN266BY.js → chunk-VCLLSNAK.js} +27 -4
- package/dist/chunk-VCLLSNAK.js.map +1 -0
- package/dist/cli.cjs +7 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/convert/index.cjs +7 -5
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +3 -3
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{lay-CZIzR9cM.d.ts → lay-0LnKv1LL.d.ts} +7 -1
- package/dist/{lay-CfDlPhH2.d.cts → lay-J17-gS6O.d.cts} +7 -1
- package/dist/player/index.cjs +439 -51
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +78 -13
- package/dist/player/index.d.ts +78 -13
- package/dist/player/index.js +414 -53
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-CU-VECDH.d.cts → schedule-B1S53131.d.cts} +28 -2
- package/dist/{schedule-BaovCtmf.d.ts → schedule-DtcYB-o4.d.ts} +28 -2
- package/dist/spec/index.cjs +4 -2
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +2 -2
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +2 -2
- package/dist/viewer/index.cjs +148 -7
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +22 -5
- package/dist/viewer/index.d.ts +22 -5
- package/dist/viewer/index.js +146 -8
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-MYVIQU4O.js.map +0 -1
- package/dist/chunk-SEN266BY.js.map +0 -1
- package/dist/chunk-TZBDIRIA.js.map +0 -1
package/dist/viewer/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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-
|
|
3
|
-
import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-
|
|
2
|
+
import { L as Lay } from '../lay-J17-gS6O.cjs';
|
|
3
|
+
import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-B1S53131.cjs';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import 'tone';
|
|
6
6
|
|
|
@@ -34,6 +34,13 @@ declare const LAY_LABELS: Record<GridLang, Record<Lay, string>>;
|
|
|
34
34
|
/** Printed where a row switches to free rhythm (`row.taal: null`) with no
|
|
35
35
|
* `taalName` of its own. bn is the converter's canonical মুক্তছন্দ. */
|
|
36
36
|
declare const FREE_TAAL_LABELS: Record<GridLang, string>;
|
|
37
|
+
/** The practice surface's words: the sheet's own accessible name (it says
|
|
38
|
+
* what a tap does), a repeat being on, and the learner's turn to sing. */
|
|
39
|
+
declare const PRACTICE_LABELS: Record<GridLang, {
|
|
40
|
+
sheet: string;
|
|
41
|
+
repeat: string;
|
|
42
|
+
turn: string;
|
|
43
|
+
}>;
|
|
37
44
|
/** A composed notation cluster: the font's ASCII text plus any residual CSS
|
|
38
45
|
* classes the font itself cannot express (only the bn ati-octave dot). */
|
|
39
46
|
interface Cluster {
|
|
@@ -209,6 +216,9 @@ declare const ZOOM_STEP = 1.2;
|
|
|
209
216
|
* themselves: `<style>${SWARALIPI_CSS}</style>` next to `renderToHtml()`.
|
|
210
217
|
*
|
|
211
218
|
* Theming hooks (CSS custom properties, all optional):
|
|
219
|
+
* --sw-loop repeated-line band; --sw-loop-edge its edge, the learner's
|
|
220
|
+
* hollow highlight and "your turn" tag (--sw-turn-ink on it)
|
|
221
|
+
* --sw-row-hover hover tint of a tappable line; --sw-focus the focus ring
|
|
212
222
|
* --sw-font font stack (default: Noto Sans Bengali + system Bengali)
|
|
213
223
|
* --sw-size base font size
|
|
214
224
|
* --sw-cell-gap horizontal gap between source cells
|
|
@@ -501,9 +511,16 @@ interface SwaralipiSheetElement extends HTMLElement {
|
|
|
501
511
|
doc: Swaralipi | undefined;
|
|
502
512
|
/** Parse documents against the spec schema before rendering. */
|
|
503
513
|
validate: boolean;
|
|
504
|
-
/** Add the player-highlight class to the note at "part.row.note"
|
|
505
|
-
|
|
514
|
+
/** Add the player-highlight class to the note at "part.row.note" — drawn
|
|
515
|
+
* hollow when it is the learner's turn to sing it. */
|
|
516
|
+
highlight(path: string, yourTurn?: boolean): HTMLElement | null;
|
|
506
517
|
clearHighlights(): void;
|
|
518
|
+
/** Shade the repeated lines (`part.row`, inclusive; null clears) and mark
|
|
519
|
+
* the learner's turn on them. The player's bindSheet calls this. */
|
|
520
|
+
markLoop(loop: {
|
|
521
|
+
from: string;
|
|
522
|
+
to: string;
|
|
523
|
+
} | null, yourTurn?: boolean): void;
|
|
507
524
|
/** Open the print dialog (→ "Save as PDF") for the current document. */
|
|
508
525
|
print(options?: PrintOptions): void;
|
|
509
526
|
/** Options for the `staff` language (instrument label, tonic, bpm…). */
|
|
@@ -551,4 +568,4 @@ type Transliterator = (bengaliText: string) => string;
|
|
|
551
568
|
*/
|
|
552
569
|
declare function loadLyricTransliterator(lang: ViewerLang): Promise<Transliterator | undefined>;
|
|
553
570
|
|
|
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 };
|
|
571
|
+
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, PRACTICE_LABELS, 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 };
|
package/dist/viewer/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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-
|
|
3
|
-
import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-
|
|
2
|
+
import { L as Lay } from '../lay-0LnKv1LL.js';
|
|
3
|
+
import { D as DrumVoice, S as Schedule, P as PlayEvent } from '../schedule-DtcYB-o4.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import 'tone';
|
|
6
6
|
|
|
@@ -34,6 +34,13 @@ declare const LAY_LABELS: Record<GridLang, Record<Lay, string>>;
|
|
|
34
34
|
/** Printed where a row switches to free rhythm (`row.taal: null`) with no
|
|
35
35
|
* `taalName` of its own. bn is the converter's canonical মুক্তছন্দ. */
|
|
36
36
|
declare const FREE_TAAL_LABELS: Record<GridLang, string>;
|
|
37
|
+
/** The practice surface's words: the sheet's own accessible name (it says
|
|
38
|
+
* what a tap does), a repeat being on, and the learner's turn to sing. */
|
|
39
|
+
declare const PRACTICE_LABELS: Record<GridLang, {
|
|
40
|
+
sheet: string;
|
|
41
|
+
repeat: string;
|
|
42
|
+
turn: string;
|
|
43
|
+
}>;
|
|
37
44
|
/** A composed notation cluster: the font's ASCII text plus any residual CSS
|
|
38
45
|
* classes the font itself cannot express (only the bn ati-octave dot). */
|
|
39
46
|
interface Cluster {
|
|
@@ -209,6 +216,9 @@ declare const ZOOM_STEP = 1.2;
|
|
|
209
216
|
* themselves: `<style>${SWARALIPI_CSS}</style>` next to `renderToHtml()`.
|
|
210
217
|
*
|
|
211
218
|
* Theming hooks (CSS custom properties, all optional):
|
|
219
|
+
* --sw-loop repeated-line band; --sw-loop-edge its edge, the learner's
|
|
220
|
+
* hollow highlight and "your turn" tag (--sw-turn-ink on it)
|
|
221
|
+
* --sw-row-hover hover tint of a tappable line; --sw-focus the focus ring
|
|
212
222
|
* --sw-font font stack (default: Noto Sans Bengali + system Bengali)
|
|
213
223
|
* --sw-size base font size
|
|
214
224
|
* --sw-cell-gap horizontal gap between source cells
|
|
@@ -501,9 +511,16 @@ interface SwaralipiSheetElement extends HTMLElement {
|
|
|
501
511
|
doc: Swaralipi | undefined;
|
|
502
512
|
/** Parse documents against the spec schema before rendering. */
|
|
503
513
|
validate: boolean;
|
|
504
|
-
/** Add the player-highlight class to the note at "part.row.note"
|
|
505
|
-
|
|
514
|
+
/** Add the player-highlight class to the note at "part.row.note" — drawn
|
|
515
|
+
* hollow when it is the learner's turn to sing it. */
|
|
516
|
+
highlight(path: string, yourTurn?: boolean): HTMLElement | null;
|
|
506
517
|
clearHighlights(): void;
|
|
518
|
+
/** Shade the repeated lines (`part.row`, inclusive; null clears) and mark
|
|
519
|
+
* the learner's turn on them. The player's bindSheet calls this. */
|
|
520
|
+
markLoop(loop: {
|
|
521
|
+
from: string;
|
|
522
|
+
to: string;
|
|
523
|
+
} | null, yourTurn?: boolean): void;
|
|
507
524
|
/** Open the print dialog (→ "Save as PDF") for the current document. */
|
|
508
525
|
print(options?: PrintOptions): void;
|
|
509
526
|
/** Options for the `staff` language (instrument label, tonic, bpm…). */
|
|
@@ -551,4 +568,4 @@ type Transliterator = (bengaliText: string) => string;
|
|
|
551
568
|
*/
|
|
552
569
|
declare function loadLyricTransliterator(lang: ViewerLang): Promise<Transliterator | undefined>;
|
|
553
570
|
|
|
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 };
|
|
571
|
+
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, PRACTICE_LABELS, 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 };
|
package/dist/viewer/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isArcRaw, glideTargets, buildSchedule, drumPattern } from '../chunk-
|
|
2
|
-
import { hasKomal, isTaalFerta, sameTaal, TAALS, graceNotes } from '../chunk-
|
|
1
|
+
import { isArcRaw, glideTargets, buildSchedule, drumPattern } from '../chunk-VCLLSNAK.js';
|
|
2
|
+
import { hasKomal, isTaalFerta, sameTaal, TAALS, graceNotes } from '../chunk-SR7NFEGL.js';
|
|
3
3
|
|
|
4
4
|
// src/viewer/glyphs.ts
|
|
5
5
|
var SWARABITAN_SHUDDHA = {
|
|
@@ -54,6 +54,11 @@ var FREE_TAAL_LABELS = {
|
|
|
54
54
|
hi: "\u092E\u0941\u0915\u094D\u0924\u091B\u0902\u0926",
|
|
55
55
|
en: "Free rhythm"
|
|
56
56
|
};
|
|
57
|
+
var PRACTICE_LABELS = {
|
|
58
|
+
bn: { sheet: "\u09B8\u09CD\u09AC\u09B0\u09B2\u09BF\u09AA\u09BF \u2014 \u09AF\u09C7 \u09B2\u09BE\u0987\u09A8 \u09A5\u09C7\u0995\u09C7 \u09B6\u09C1\u09A8\u09A4\u09C7 \u099A\u09BE\u09A8, \u09B8\u09C7\u0996\u09BE\u09A8\u09C7 \u099B\u09C1\u0981\u09AF\u09BC\u09C7 \u09A6\u09BF\u09A8", repeat: "\u09B2\u09BE\u0987\u09A8\u099F\u09BF \u09AC\u09BE\u09B0\u09AC\u09BE\u09B0 \u09AC\u09BE\u099C\u09AC\u09C7", turn: "\u098F\u09AC\u09BE\u09B0 \u0986\u09AA\u09A8\u09BF \u0997\u09BE\u09A8" },
|
|
59
|
+
hi: { sheet: "\u0938\u094D\u0935\u0930\u0932\u093F\u092A\u093F \u2014 \u091C\u093F\u0938 \u092A\u0902\u0915\u094D\u0924\u093F \u0938\u0947 \u0938\u0941\u0928\u0928\u093E \u0939\u094B, \u0909\u0938\u0947 \u091B\u0941\u090F\u0901", repeat: "\u092A\u0902\u0915\u094D\u0924\u093F \u0926\u094B\u0939\u0930\u093E\u0908 \u091C\u093E \u0930\u0939\u0940 \u0939\u0948", turn: "\u0905\u092C \u0906\u092A \u0917\u093E\u0907\u090F" },
|
|
60
|
+
en: { sheet: "Swaralipi \u2014 tap a line to play from it", repeat: "Repeating the line", turn: "Your turn \u2014 sing" }
|
|
61
|
+
};
|
|
57
62
|
function composeCluster(letter, opts) {
|
|
58
63
|
const classes = [];
|
|
59
64
|
if (opts.lang === "bn") {
|
|
@@ -496,7 +501,7 @@ function renderRow(row, partIdx, rowIdx, offset, ctx) {
|
|
|
496
501
|
if (below.length > 0)
|
|
497
502
|
out.push(`<span class="sw-below" style="grid-column:${col}">${renderAnnotation(below, ctx)}</span>`);
|
|
498
503
|
});
|
|
499
|
-
return `<div class="sw-row">${out.join("")}</div>`;
|
|
504
|
+
return `<div class="sw-row" data-row="${partIdx}.${rowIdx}">${out.join("")}</div>`;
|
|
500
505
|
}
|
|
501
506
|
function renderHeader(meta, ctx) {
|
|
502
507
|
return renderMetaHeader(meta, ctx.lang, ctx.tr);
|
|
@@ -868,6 +873,55 @@ ${FONT_FACES}
|
|
|
868
873
|
glyph fill/stroke instead (abcjs draws note heads/stems as filled paths). */
|
|
869
874
|
.sw-staff .sw-hl, .sw-staff .sw-hl * { fill: var(--sw-hl-note, #d68400); stroke: var(--sw-hl-note, #d68400); }
|
|
870
875
|
|
|
876
|
+
/* ---- practice: tap a line, repeat it, sing it back ----
|
|
877
|
+
* Only a sheet a player drives is [seekable]. The whole line is the tap
|
|
878
|
+
* target, so it is the line that answers the pointer. A repeat shades its
|
|
879
|
+
* lines and fades the rest (focus, as Soundslice fades outside a loop); on the
|
|
880
|
+
* learner's pass the highlight goes hollow \u2014 it marks where to sing, not what
|
|
881
|
+
* is sounding \u2014 and the repeat's first line wears a "your turn" tag. None of
|
|
882
|
+
* it moves a column: backgrounds, opacity and an out-of-flow tag only. */
|
|
883
|
+
:host([seekable]) .sw-row { cursor: pointer; }
|
|
884
|
+
:host(:focus-visible) { outline: 2px solid var(--sw-focus, currentColor); outline-offset: 0.3em; border-radius: 0.2em; }
|
|
885
|
+
.sw-row { border-radius: 0.3em; }
|
|
886
|
+
@media (hover: hover) {
|
|
887
|
+
:host([seekable]) .sw-row:hover { background: var(--sw-row-hover, rgba(128, 128, 128, 0.08)); }
|
|
888
|
+
}
|
|
889
|
+
.sw-looping .sw-row:not(.sw-loop) { opacity: 0.6; }
|
|
890
|
+
.sw-row.sw-loop {
|
|
891
|
+
background: var(--sw-loop, rgba(255, 196, 64, 0.13));
|
|
892
|
+
box-shadow: inset 0.2em 0 var(--sw-loop-edge, #a85a00);
|
|
893
|
+
}
|
|
894
|
+
.sw-hl.sw-hl-turn { background: none; box-shadow: inset 0 0 0 0.09em var(--sw-loop-edge, #a85a00); }
|
|
895
|
+
/* The tag rides the line's above-band at its right end \u2014 a grid item spanning
|
|
896
|
+
every column, so it sizes no track \u2014 and is sticky, so on a phone where the
|
|
897
|
+
line runs past the screen it waits at the visible edge instead. */
|
|
898
|
+
.sw-row[data-turn]::after {
|
|
899
|
+
content: attr(data-turn);
|
|
900
|
+
grid-column: 1 / -1;
|
|
901
|
+
grid-row: 1;
|
|
902
|
+
justify-self: end;
|
|
903
|
+
align-self: start;
|
|
904
|
+
position: sticky;
|
|
905
|
+
right: 0.2em;
|
|
906
|
+
translate: 0 -0.3em;
|
|
907
|
+
font-size: max(0.6em, 0.7rem);
|
|
908
|
+
font-weight: 600;
|
|
909
|
+
line-height: 1.6;
|
|
910
|
+
padding: 0 0.7em;
|
|
911
|
+
border-radius: 1em;
|
|
912
|
+
color: var(--sw-turn-ink, #fff);
|
|
913
|
+
background: var(--sw-loop-edge, #a85a00);
|
|
914
|
+
}
|
|
915
|
+
.sw-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
916
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
917
|
+
.sw-row { transition: opacity 0.25s, background-color 0.25s; }
|
|
918
|
+
}
|
|
919
|
+
@media print {
|
|
920
|
+
.sw-row { opacity: 1 !important; background: none !important; box-shadow: none !important; }
|
|
921
|
+
.sw-row[data-turn]::after { content: none; }
|
|
922
|
+
.sw-hl { background: none !important; box-shadow: none !important; }
|
|
923
|
+
}
|
|
924
|
+
|
|
871
925
|
/* ---- element error box ---- */
|
|
872
926
|
.sw-error {
|
|
873
927
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
@@ -1303,6 +1357,17 @@ async function loadLyricTransliterator(lang) {
|
|
|
1303
1357
|
|
|
1304
1358
|
// src/viewer/element.ts
|
|
1305
1359
|
var LANGS = /* @__PURE__ */ new Set(["bn", "hi", "en", "staff"]);
|
|
1360
|
+
var KEYS = "Space ArrowUp ArrowDown ArrowLeft ArrowRight Home End R T Escape Shift+ArrowUp Shift+ArrowDown";
|
|
1361
|
+
function nearest(els, x) {
|
|
1362
|
+
let best;
|
|
1363
|
+
let gap = Infinity;
|
|
1364
|
+
for (const el of els) {
|
|
1365
|
+
const r = el.getBoundingClientRect();
|
|
1366
|
+
const d = Math.abs(r.left + r.width / 2 - x);
|
|
1367
|
+
if (d < gap) [best, gap] = [el, d];
|
|
1368
|
+
}
|
|
1369
|
+
return best;
|
|
1370
|
+
}
|
|
1306
1371
|
function ensureNotationFonts() {
|
|
1307
1372
|
if (typeof FontFace === "undefined" || typeof document === "undefined") return;
|
|
1308
1373
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -1315,7 +1380,7 @@ function ensureNotationFonts() {
|
|
|
1315
1380
|
var sharedSheet;
|
|
1316
1381
|
function createElementClass() {
|
|
1317
1382
|
return class SwaralipiSheet extends HTMLElement {
|
|
1318
|
-
static observedAttributes = ["src", "lang", "hide-header", "min-spacing", "min-size"];
|
|
1383
|
+
static observedAttributes = ["src", "lang", "hide-header", "min-spacing", "min-size", "seekable"];
|
|
1319
1384
|
#doc;
|
|
1320
1385
|
#root;
|
|
1321
1386
|
#adopted = false;
|
|
@@ -1327,10 +1392,20 @@ function createElementClass() {
|
|
|
1327
1392
|
#resize;
|
|
1328
1393
|
#fittedAt = -1;
|
|
1329
1394
|
#zoom = 1;
|
|
1395
|
+
/** Where the pointer went down, to tell a tap from a drag. */
|
|
1396
|
+
#down;
|
|
1397
|
+
#marks = { loop: null, turn: false };
|
|
1398
|
+
/** Attributes `seekable` added, and so takes away again. */
|
|
1399
|
+
#owned = [];
|
|
1330
1400
|
validate = false;
|
|
1331
1401
|
constructor() {
|
|
1332
1402
|
super();
|
|
1333
1403
|
this.#root = this.attachShadow({ mode: "open" });
|
|
1404
|
+
this.#root.addEventListener("pointerdown", (e) => {
|
|
1405
|
+
const { clientX, clientY } = e;
|
|
1406
|
+
this.#down = [clientX, clientY];
|
|
1407
|
+
});
|
|
1408
|
+
this.#root.addEventListener("click", (e) => this.#onClick(e));
|
|
1334
1409
|
if (typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype && "adoptedStyleSheets" in this.#root) {
|
|
1335
1410
|
if (!sharedSheet) {
|
|
1336
1411
|
sharedSheet = new CSSStyleSheet();
|
|
@@ -1364,6 +1439,27 @@ function createElementClass() {
|
|
|
1364
1439
|
const attr = this.getAttribute("lang");
|
|
1365
1440
|
return attr !== null && LANGS.has(attr) ? attr : "bn";
|
|
1366
1441
|
}
|
|
1442
|
+
get #practice() {
|
|
1443
|
+
const lang = this.#lang;
|
|
1444
|
+
return PRACTICE_LABELS[lang === "staff" ? "en" : lang];
|
|
1445
|
+
}
|
|
1446
|
+
/** A tap on a line plays from the note nearest the tap: the whole line is
|
|
1447
|
+
* the target (WCAG 2.5.8 counts a position inside one target as picking
|
|
1448
|
+
* a value), fired on the up-event like any click (2.5.2). The player bound
|
|
1449
|
+
* by bindSheet answers it; a host can veto it with preventDefault(). */
|
|
1450
|
+
#onClick(e) {
|
|
1451
|
+
if (!this.hasAttribute("seekable") || e.button !== 0 || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
1452
|
+
if (this.#down !== void 0 && Math.hypot(e.clientX - this.#down[0], e.clientY - this.#down[1]) > 6) return;
|
|
1453
|
+
const target = e.target;
|
|
1454
|
+
const row = target.closest(".sw-row");
|
|
1455
|
+
const notes = row === null ? [] : [...row.querySelectorAll("[data-note]:not(.sw-bar)")];
|
|
1456
|
+
const hit = target.closest("[data-note]:not(.sw-bar)") ?? nearest(notes, e.clientX);
|
|
1457
|
+
const path = hit?.getAttribute("data-note");
|
|
1458
|
+
if (path == null) return;
|
|
1459
|
+
this.dispatchEvent(
|
|
1460
|
+
new CustomEvent("swaralipi-seek", { detail: { path, extend: e.shiftKey }, bubbles: true, cancelable: true })
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1367
1463
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
1368
1464
|
if (oldValue === newValue) return;
|
|
1369
1465
|
if (name === "src") {
|
|
@@ -1374,6 +1470,19 @@ function createElementClass() {
|
|
|
1374
1470
|
this.fit();
|
|
1375
1471
|
return;
|
|
1376
1472
|
}
|
|
1473
|
+
if (name === "seekable") {
|
|
1474
|
+
for (const a of this.#owned) this.removeAttribute(a);
|
|
1475
|
+
this.#owned = [];
|
|
1476
|
+
if (newValue === null) return;
|
|
1477
|
+
const want = { tabindex: "0", role: "region", "aria-label": this.#practice.sheet, "aria-keyshortcuts": KEYS };
|
|
1478
|
+
for (const [a, v] of Object.entries(want)) {
|
|
1479
|
+
if (this.hasAttribute(a)) continue;
|
|
1480
|
+
this.setAttribute(a, v);
|
|
1481
|
+
this.#owned.push(a);
|
|
1482
|
+
}
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
if (name === "lang" && this.#owned.includes("aria-label")) this.setAttribute("aria-label", this.#practice.sheet);
|
|
1377
1486
|
if (name === "lang") this.#ensureTransliterator();
|
|
1378
1487
|
this.#render();
|
|
1379
1488
|
}
|
|
@@ -1486,7 +1595,9 @@ function createElementClass() {
|
|
|
1486
1595
|
if (lang === "staff") {
|
|
1487
1596
|
const doc = this.#doc;
|
|
1488
1597
|
void renderStaffHtml(doc, this.staffOptions).then((html2) => {
|
|
1489
|
-
if (seq
|
|
1598
|
+
if (seq !== this.#renderSeq) return;
|
|
1599
|
+
this.#root.innerHTML = this.#styleFallback() + html2;
|
|
1600
|
+
this.#applyMarks();
|
|
1490
1601
|
}).catch(() => void 0);
|
|
1491
1602
|
return;
|
|
1492
1603
|
}
|
|
@@ -1496,8 +1607,34 @@ function createElementClass() {
|
|
|
1496
1607
|
transliterate: this.#translitLang === lang ? this.#translit : void 0
|
|
1497
1608
|
});
|
|
1498
1609
|
this.#root.innerHTML = this.#styleFallback() + html;
|
|
1610
|
+
this.#applyMarks();
|
|
1499
1611
|
this.#fit();
|
|
1500
1612
|
}
|
|
1613
|
+
markLoop(loop, yourTurn = false) {
|
|
1614
|
+
this.#marks = { loop, turn: yourTurn };
|
|
1615
|
+
this.#applyMarks();
|
|
1616
|
+
}
|
|
1617
|
+
/** Shade the repeated lines and dim the rest; the first of them carries
|
|
1618
|
+
* the "your turn" cue. A polite live region says the same aloud. */
|
|
1619
|
+
#applyMarks() {
|
|
1620
|
+
const { loop, turn } = this.#marks;
|
|
1621
|
+
const rows = [...this.#root.querySelectorAll(".sw-row")];
|
|
1622
|
+
const at = (row) => rows.findIndex((r) => r.dataset.row === row);
|
|
1623
|
+
const [a, b] = loop === null ? [0, -1] : [at(loop.from), at(loop.to)];
|
|
1624
|
+
rows.forEach((r, i) => {
|
|
1625
|
+
r.classList.toggle("sw-loop", i >= a && i <= b);
|
|
1626
|
+
if (turn && i === a) r.dataset.turn = this.#practice.turn;
|
|
1627
|
+
else delete r.dataset.turn;
|
|
1628
|
+
});
|
|
1629
|
+
this.#root.querySelector(".sw-block")?.classList.toggle("sw-looping", loop !== null);
|
|
1630
|
+
let live = this.#root.querySelector(".sw-live");
|
|
1631
|
+
if (live === null) {
|
|
1632
|
+
live = Object.assign(document.createElement("div"), { className: "sw-live" });
|
|
1633
|
+
live.setAttribute("role", "status");
|
|
1634
|
+
this.#root.append(live);
|
|
1635
|
+
}
|
|
1636
|
+
live.textContent = turn ? this.#practice.turn : loop !== null ? this.#practice.repeat : "";
|
|
1637
|
+
}
|
|
1501
1638
|
#renderError(message) {
|
|
1502
1639
|
const div = document.createElement("div");
|
|
1503
1640
|
div.className = "sw-error";
|
|
@@ -1505,14 +1642,15 @@ function createElementClass() {
|
|
|
1505
1642
|
this.#root.innerHTML = this.#styleFallback();
|
|
1506
1643
|
this.#root.appendChild(div);
|
|
1507
1644
|
}
|
|
1508
|
-
highlight(path) {
|
|
1645
|
+
highlight(path, yourTurn = false) {
|
|
1509
1646
|
if (!/^\d+\.\d+\.\d+$/.test(path)) return null;
|
|
1510
1647
|
const el = this.#root.querySelector(`[data-note="${path}"]`);
|
|
1511
1648
|
el?.classList.add("sw-hl");
|
|
1649
|
+
el?.classList.toggle("sw-hl-turn", yourTurn);
|
|
1512
1650
|
return el;
|
|
1513
1651
|
}
|
|
1514
1652
|
clearHighlights() {
|
|
1515
|
-
for (const el of this.#root.querySelectorAll(".sw-hl")) el.classList.remove("sw-hl");
|
|
1653
|
+
for (const el of this.#root.querySelectorAll(".sw-hl")) el.classList.remove("sw-hl", "sw-hl-turn");
|
|
1516
1654
|
}
|
|
1517
1655
|
print(options = {}) {
|
|
1518
1656
|
if (this.#doc === void 0) return;
|
|
@@ -1541,6 +1679,6 @@ function defineSwaralipiSheet(tagName = "swaralipi-sheet") {
|
|
|
1541
1679
|
return ctor;
|
|
1542
1680
|
}
|
|
1543
1681
|
|
|
1544
|
-
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 };
|
|
1682
|
+
export { DEFAULT_NOTATOR, DEFAULT_WATERMARK, DIGITS, FREE_TAAL_LABELS, LABELS, LAY_LABELS, MARKS, MAX_ZOOM, MIN_ZOOM, NOTATION_FONT_URLS, PRACTICE_LABELS, 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 };
|
|
1545
1683
|
//# sourceMappingURL=index.js.map
|
|
1546
1684
|
//# sourceMappingURL=index.js.map
|