pepperscore 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +46 -2
  2. package/README.md +198 -12
  3. package/dist/PdfOverlay-BmBGsMkr.js +249 -0
  4. package/dist/PdfOverlay-BmBGsMkr.js.map +1 -0
  5. package/dist/THIRD_PARTY_NOTICES.txt +1234 -1
  6. package/dist/assets/engrave.worker-D334KQnx.js +5 -0
  7. package/dist/assets/{import.worker-Rop-z7az.js → import.worker-BAr_ZqtQ.js} +2 -2
  8. package/dist/assets/verovio.worker-C8LY1lw3.js +7 -0
  9. package/dist/components/DrawerControls.d.ts +7 -0
  10. package/dist/components/PdfOverlay.d.ts +19 -0
  11. package/dist/components/PepperScore.d.ts +6 -1
  12. package/dist/components/PitchColourTile.d.ts +4 -0
  13. package/dist/components/SkinBar.d.ts +12 -0
  14. package/dist/engrave-DpVrifuu.js +32 -0
  15. package/dist/engrave-DpVrifuu.js.map +1 -0
  16. package/dist/export-BFhuMW8H.js +320 -0
  17. package/dist/export-BFhuMW8H.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +3567 -2578
  20. package/dist/index.js.map +1 -1
  21. package/dist/lib/asset-fonts.d.ts +4 -0
  22. package/dist/lib/bar-items.d.ts +31 -0
  23. package/dist/lib/chrome.d.ts +6 -4
  24. package/dist/lib/file-state.d.ts +1 -0
  25. package/dist/lib/header-controls.d.ts +2 -2
  26. package/dist/lib/notation/mei-tab.d.ts +8 -1
  27. package/dist/lib/notation/mei-view.d.ts +2 -0
  28. package/dist/lib/notation/musicxml-octave-clef.d.ts +5 -0
  29. package/dist/lib/pdf/annotations.d.ts +12 -0
  30. package/dist/lib/pdf/compose.d.ts +8 -0
  31. package/dist/lib/pdf/engrave.d.ts +29 -0
  32. package/dist/lib/pdf/export.d.ts +19 -0
  33. package/dist/lib/pdf/fit.d.ts +2 -0
  34. package/dist/lib/pdf/fonts.d.ts +3 -0
  35. package/dist/lib/pdf/normalize.d.ts +7 -0
  36. package/dist/lib/pdf/paper.d.ts +32 -0
  37. package/dist/lib/pitch-colours.d.ts +16 -0
  38. package/dist/lib/prefs.d.ts +7 -2
  39. package/dist/lib/render.d.ts +52 -0
  40. package/dist/lib/score.d.ts +6 -0
  41. package/dist/lib/skin.d.ts +25 -0
  42. package/dist/lib/tab/string-instruments.d.ts +65 -0
  43. package/dist/lib/tab/strings/charts.d.ts +29 -0
  44. package/dist/lib/tab/strings/phrase.d.ts +14 -0
  45. package/dist/lib/tab/strings/solver.d.ts +29 -0
  46. package/dist/lib/tab/strings/svg.d.ts +3 -0
  47. package/dist/lib/tab/tab-parts.d.ts +2 -5
  48. package/dist/lib/verovio-protocol.d.ts +1 -1
  49. package/dist/lib/verovio-runtime.d.ts +12 -0
  50. package/dist/style.css +1 -1
  51. package/package.json +5 -2
  52. package/dist/assets/verovio.worker-DQm-ikdG.js +0 -7
@@ -3,3 +3,7 @@
3
3
  * base instead of hardcoded CSS. Font family names are page-global, so the first instance's base wins.
4
4
  */
5
5
  export declare function ensureMusicFonts(base: string, onError?: (failed: string) => void): void;
6
+ /** Start downloading a notation font's faces now (after ensureMusicFonts declared them), rather than on first use. */
7
+ export declare function preloadMusicFont(font: string | undefined): void;
8
+ /** Resolves when a notation font's faces have loaded (or failed), or after `timeoutMs`, whichever is first; never rejects. */
9
+ export declare function musicFontsReady(font: string | undefined, timeoutMs: number): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import type { ChromePreset, PepperScoreControls, ResolvedChrome } from './chrome';
2
+ import { type BarItemId, type PepperScoreSkin } from './skin.ts';
3
+ /** Public skin ids deliberately differ from the historical private header ids. */
4
+ export declare const BAR_HEADER_ID: {
5
+ readonly play: "play";
6
+ readonly stop: "stop";
7
+ readonly tempo: "bpm";
8
+ readonly transpose: "transpose";
9
+ readonly keyFlip: "keyFlip";
10
+ readonly bars: "bars";
11
+ readonly viewToggle: "mode";
12
+ readonly tab: "tab";
13
+ readonly zoom: "staveScale";
14
+ readonly mixer: "mixer";
15
+ readonly openFile: "openFile";
16
+ readonly instrumentSwitch: "instrumentSwitch";
17
+ readonly octaveTool: "octaveTool";
18
+ readonly options: "options";
19
+ readonly noteNames: "noteNames";
20
+ readonly counts: "counts";
21
+ readonly chordSymbols: "chordSymbols";
22
+ readonly pitchColours: "pitchColours";
23
+ readonly pdf: "pdf";
24
+ };
25
+ export declare const BAR_CONTROL_GROUP: Record<BarItemId, keyof PepperScoreControls | null>;
26
+ /** Null retains the legacy header. An empty array explicitly requests no bar. */
27
+ export declare function resolveBar(skin: PepperScoreSkin | undefined, preset: ChromePreset, controls?: PepperScoreControls): BarItemId[] | null;
28
+ /** Called only by the component's development-gated effect. Resolution itself is pure. */
29
+ export declare function warnUnknownBar(skin?: PepperScoreSkin): void;
30
+ /** Missing bar actions, plus PDF in the default drawer. Explicit false controls stay hidden. */
31
+ export declare function drawerExtras(bar: BarItemId[] | null, chrome: ResolvedChrome): BarItemId[];
@@ -1,6 +1,7 @@
1
- /** Chrome presets (docs/specs/pepperscore-embeddable-component.md, "Chrome presets") and per-control overrides. No runtime imports. */
1
+ import type { BarItemId } from './skin';
2
+ /** Chrome presets (docs/specs/pepperscore-embeddable-component.md, "Chrome presets") and per-control overrides. Pure data resolution, also loaded directly by Node validation. */
2
3
  export type ChromePreset = 'full' | 'minimal' | 'none';
3
- export type PepperScoreControls = Partial<Record<'mixer' | 'openFile' | 'drawer' | 'transport' | 'viewToggle' | 'zoom' | 'tabToggle' | 'instrumentSwitch' | 'octaveTool', boolean>>;
4
+ export type PepperScoreControls = Partial<Record<'mixer' | 'openFile' | 'drawer' | 'transport' | 'viewToggle' | 'zoom' | 'tabToggle' | 'instrumentSwitch' | 'octaveTool' | 'pdf', boolean>>;
4
5
  export type ResolvedChrome = {
5
6
  header: boolean;
6
7
  transport: boolean;
@@ -15,6 +16,7 @@ export type ResolvedChrome = {
15
16
  instrumentSwitch: boolean;
16
17
  /** The reader's octave tool (move notes or passages by an octave). */
17
18
  octaveTool: boolean;
19
+ pdf: boolean;
18
20
  /** The overlaid play/stop button of `minimal`. */
19
21
  compactPlay: boolean;
20
22
  /** Playhead, note highlight and follow. */
@@ -29,6 +31,6 @@ export type ResolvedChrome = {
29
31
  * Open button, file input, open dialog and reopen offer; `mixer` the mixer and the part icons / instrument picker / stave
30
32
  * eyes; `zoom` the stave-scale slider, pinch zoom and the page arrows row; `drawer` the bottom drawer and its handle;
31
33
  * `tabToggle` the Notation / TAB / Both switch (header and drawer); `instrumentSwitch` the instrument switcher;
32
- * `octaveTool` the octave tool. Each defaults to on for `full`, off otherwise.
34
+ * `octaveTool` the octave tool; `pdf` the export button and overlay. Each defaults to on for `full`, off otherwise.
33
35
  */
34
- export declare function resolveChrome(preset?: ChromePreset, controls?: PepperScoreControls): ResolvedChrome;
36
+ export declare function resolveChrome(preset?: ChromePreset, controls?: PepperScoreControls, bar?: BarItemId[] | null, renderableBar?: BarItemId[] | null | undefined): ResolvedChrome;
@@ -15,6 +15,7 @@ export type FileSettings = {
15
15
  mode: 'portrait' | 'landscape';
16
16
  notationFont: 'Bravura' | 'Petaluma';
17
17
  barsPerLine: 'auto' | number;
18
+ pitchColours?: boolean;
18
19
  overlap: number;
19
20
  names: boolean;
20
21
  chordSymbols: boolean;
@@ -9,7 +9,7 @@
9
9
  * header footprint never depends on client-only state. `tab` (the Notation / TAB / Both switch) is the exception: it shows only for a score with a
10
10
  * stave that can take TAB (any pitched part).
11
11
  */
12
- export type HeaderControlId = 'stop' | 'play' | 'openFile' | 'bpm' | 'transpose' | 'keyFlip' | 'mode' | 'tab' | 'bars' | 'staveScale' | 'mixer';
12
+ export type HeaderControlId = 'stop' | 'play' | 'openFile' | 'bpm' | 'transpose' | 'keyFlip' | 'mode' | 'tab' | 'bars' | 'staveScale' | 'mixer' | 'noteNames' | 'counts' | 'chordSymbols' | 'options' | 'instrumentSwitch' | 'octaveTool' | 'pitchColours' | 'pdf';
13
13
  export type HeaderControlTier = 'primary' | 'secondary';
14
14
  export type HeaderControlConfig = {
15
15
  id: HeaderControlId;
@@ -54,4 +54,4 @@ export declare const MOBILE_HEADER_BREAKPOINT = 760;
54
54
  /** Stave scale (zoom) bounds shared by the header slider and pinch zoom live with the defaults in stave-scale.ts. */
55
55
  export { clampStaveScale, STAVE_SCALE_MAX, STAVE_SCALE_MIN, STAVE_SCALE_STEP } from './stave-scale';
56
56
  /** Which `controls` key shows each header control (see chrome.ts). */
57
- export declare const HEADER_CONTROL_GROUP: Record<HeaderControlId, 'transport' | 'viewToggle' | 'openFile' | 'zoom' | 'mixer' | 'tabToggle'>;
57
+ export declare const HEADER_CONTROL_GROUP: Partial<Record<HeaderControlId, 'transport' | 'viewToggle' | 'openFile' | 'zoom' | 'mixer' | 'tabToggle'>>;
@@ -22,13 +22,18 @@
22
22
  * their notes' sounding pitches (the staff's `<tuning>` plus fret) move by the given semitones and are solved again.
23
23
  */
24
24
  import { type FretPos } from '../tab/fret-assign';
25
+ import type { StringFingering } from '../tab/strings/solver';
25
26
  export type TabSpec = {
26
27
  /** Notation staff numbers (`@n`) that get a generated TAB staff `100 + n`. */
27
28
  generate: string[];
29
+ /** Approved fretless charts by notation staff. Custom tuning/capo do not apply. */
30
+ fingers?: Record<string, 'violin' | 'cello'>;
28
31
  /** Staff-bound control events of a hidden notation staff move to this TAB staff (`n` → TAB `n`). */
29
32
  retarget: Record<string, string>;
30
33
  /** Open-string MIDI pitches, low to high. */
31
34
  tuning: number[];
35
+ /** Per notation staff tuning; overrides the global fallback. Physical string order, bottom to top. */
36
+ tunings?: Record<string, number[]>;
32
37
  /** Capo fret; frets are relative to it. */
33
38
  capo: number;
34
39
  /** Use `tab.course`/`tab.fret` already on notation notes as fixed fingerings (false when the view moved pitches). */
@@ -49,7 +54,9 @@ export type TabSpec = {
49
54
  refret?: Record<string, number>;
50
55
  };
51
56
  /** Fingerings by notation note `xml:id`, kept across chunk documents so a note keeps its fret wherever it is drawn. */
52
- export type FretMemory = Map<string, FretPos>;
57
+ export type FretMemory = Map<string, FretPos & {
58
+ finger?: StringFingering;
59
+ }>;
53
60
  /**
54
61
  * Add the TAB staves of `spec` to `mei`, re-finger its `refret` staves and retarget hidden staves' control events.
55
62
  * `memory` (optional) keeps fingerings by note ID across calls: a remembered note is fixed for the solver and new
@@ -49,6 +49,8 @@ export declare function scanBaseline(mei: string): BaselineInfo;
49
49
  export type ViewSpec = {
50
50
  /** Insert a page break before every `breakEvery`-th measure (0: none). Existing sb/pb are always removed. */
51
51
  breakEvery: number;
52
+ /** Explicit zero-based system starts; replaces breakEvery, including when empty. */
53
+ breakBefore?: number[];
52
54
  /** Staff numbers (`@n`) to remove, with their staff-bound control events and spans into removed content. */
53
55
  hiddenStaves: string[];
54
56
  /** Keep `<harm>` chord symbols. */
@@ -0,0 +1,5 @@
1
+ /** Normalize matching octave-clef / octave-transpose encodings to sounding octaves.
2
+ * Only the import copy changes. Keep chromatic/diatonic transposition, IDs, and all other source markup.
3
+ * Capture one attributes/note subtree at a time; workers need no DOM or whole-document tree.
4
+ */
5
+ export declare function normalizeOctaveClefs(text: string): string;
@@ -0,0 +1,12 @@
1
+ import { type PitchColourPaint } from '../pitch-colours';
2
+ import type { Score, ScoreNote } from '../score';
3
+ import { type AnnotationSettings } from '../render';
4
+ /** Attach to the DOM before calling: SVG matrices and glyph boxes require a rendered tree. */
5
+ export declare function annotatePage(svg: SVGSVGElement, score: Score, settings: AnnotationSettings, font?: string, aliases?: ReadonlyMap<string, ScoreNote>, tabStaves?: ReadonlySet<string>): void;
6
+ /** svg2pdf ignores paint-order: retain identical text geometry in each explicit pass. */
7
+ export declare function materializePaint(svg: SVGSVGElement, page: {
8
+ width: number;
9
+ height: number;
10
+ }, textWidth: (element: SVGElement, size: number) => number): void;
11
+ /** Inline vector paint: export pages have no reader CSS or host variables. */
12
+ export declare function paintPitchColours(svg: SVGSVGElement, score: Score, paint: PitchColourPaint, tabStaves?: ReadonlySet<string>): void;
@@ -0,0 +1,8 @@
1
+ import { type PaperId } from './paper';
2
+ type ComposeOptions = {
3
+ paper: PaperId;
4
+ title?: string;
5
+ };
6
+ /** Vector pages in exact physical dimensions. Import this module dynamically from the export UI. */
7
+ export declare function composePdf(pages: string[], opts: ComposeOptions, signal: AbortSignal): Promise<Blob>;
8
+ export {};
@@ -0,0 +1,29 @@
1
+ import type { ViewSpec } from '../notation/mei-view';
2
+ import type { VerovioFiles } from '../verovio-protocol';
3
+ import type { PaperId } from './paper';
4
+ /** A snapshot of source notation and the same view plan used by the reader. */
5
+ export type PageInput = {
6
+ data: string;
7
+ view?: ViewSpec;
8
+ };
9
+ export type PageOptions = {
10
+ paper: PaperId;
11
+ maxBars: number;
12
+ font?: string;
13
+ files?: VerovioFiles;
14
+ spacingStaff?: number;
15
+ annotationRoom?: number;
16
+ headingMm?: number;
17
+ };
18
+ export type LineInfo = {
19
+ start: number;
20
+ count: number;
21
+ width: number;
22
+ };
23
+ export type PagesResult = {
24
+ pages: string[];
25
+ lines: LineInfo[];
26
+ shrunk: boolean;
27
+ };
28
+ /** Dynamically import this module on first PDF use. Each call owns and releases its own WASM worker. */
29
+ export declare function engravePages(input: PageInput, opts: PageOptions, signal: AbortSignal): Promise<PagesResult>;
@@ -0,0 +1,19 @@
1
+ import type { Score, ScoreNote } from '../score';
2
+ import { type AnnotationSettings } from '../render';
3
+ import type { PitchColourPaint } from '../pitch-colours';
4
+ import { type PageInput, type PageOptions } from './engrave';
5
+ /** Immutable Save snapshot: score is already the reader's instrument/transposition view;
6
+ * input.view is its planStaves view (including hidden staves, spelling and TAB).
7
+ * No reader channel or live DOM is used. T3 imports only this module, dynamically.
8
+ */
9
+ export type ExportRequest = {
10
+ input: PageInput;
11
+ options: PageOptions;
12
+ score: Score;
13
+ annotations?: AnnotationSettings;
14
+ pitchColours?: PitchColourPaint;
15
+ visiblePartIds?: string[];
16
+ tabAliases?: ReadonlyMap<string, ScoreNote>;
17
+ tabStaves?: ReadonlySet<string>;
18
+ };
19
+ export declare function exportScore(request: ExportRequest, signal: AbortSignal): Promise<Blob>;
@@ -0,0 +1,2 @@
1
+ /** Greedy zero-based line starts, including 0 for a non-empty score. Oversize bars stand alone. */
2
+ export declare function breakLines(barWidths: number[], capacity: number, maxBars: number): number[];
@@ -0,0 +1,3 @@
1
+ import type { jsPDF } from 'jspdf';
2
+ /** TrueType copies of the same OFL Poppins faces as the reader. Loaded only during export. */
3
+ export declare function embedFonts(pdf: jsPDF, signal: AbortSignal): Promise<FontFace[]>;
@@ -0,0 +1,7 @@
1
+ /** Replace nested viewports by their browser-resolved parent-space matrices.
2
+ * svg2pdf otherwise applies Verovio's definition-scale viewBox twice.
3
+ * Capture all matrices before changing the tree; outer page units remain intact.
4
+ */
5
+ export declare function flattenViewports(svg: SVGSVGElement): void;
6
+ /** Ordinary text and music glyphs have different fonts, even within one chord label. */
7
+ export declare function normalizeText(svg: SVGSVGElement): void;
@@ -0,0 +1,32 @@
1
+ /** Portrait dimensions in millimetres; shared by engraving and PDF composition. */
2
+ export declare const PAPER_SIZES: {
3
+ readonly a4: {
4
+ readonly width: 210;
5
+ readonly height: 297;
6
+ };
7
+ readonly a3: {
8
+ readonly width: 297;
9
+ readonly height: 420;
10
+ };
11
+ readonly letter: {
12
+ readonly width: 215.9;
13
+ readonly height: 279.4;
14
+ };
15
+ readonly legal: {
16
+ readonly width: 215.9;
17
+ readonly height: 355.6;
18
+ };
19
+ };
20
+ export type PaperId = keyof typeof PAPER_SIZES;
21
+ /** Five-line stave height, independent of screen zoom and paper size. */
22
+ export declare const STAVE_MM = 6.5;
23
+ export declare const MM_PER_UNIT: number;
24
+ export declare const MARGIN_MM = 12;
25
+ /** The exact integer geometry sent to Verovio, including the tall-system fallback. */
26
+ export declare function pageGeometry(paper: PaperId, naturalHeight: number, overhead?: number): {
27
+ pageWidth: number;
28
+ pageHeight: number;
29
+ margin: number;
30
+ scale: number;
31
+ capacity: number;
32
+ };
@@ -0,0 +1,16 @@
1
+ import type { ScoreNote, Part } from './score';
2
+ import type { PitchClass, PitchPalette } from './skin';
3
+ export declare const PITCH_CLASSES: readonly PitchClass[];
4
+ export declare const pitchClassOf: (midi: number) => PitchClass;
5
+ export declare const pitchVariable: (pitch: PitchClass) => string;
6
+ export declare function pitchColourStyle(palette?: PitchPalette): Record<string, string>;
7
+ /** Verovio preserves source note order inside chords; prefer exact identity where present. */
8
+ export declare function tagPitchColours(element: Element, event: ScoreNote, part: Pick<Part, 'clef'>, tabStaves?: ReadonlySet<string>): void;
9
+ /** Save-time drawing snapshot, resolved in the same root as the screen's noteheads. */
10
+ export type PitchColourPaint = {
11
+ palette: PitchPalette;
12
+ outline: string;
13
+ strokeWidth: string;
14
+ };
15
+ /** Requires the active player root (palette supplied and pitch-colour pref on). */
16
+ export declare function resolvePitchColours(root: HTMLElement): PitchColourPaint;
@@ -1,4 +1,5 @@
1
1
  import type { FileSettings } from './file-state';
2
+ import type { TabInstruments } from './tab/string-instruments';
2
3
  import type { TabView } from './tab/tab-parts';
3
4
  import type { RangeLevel, SwitchInstrumentId } from './instrument-model';
4
5
  export declare const MIN_BPM = 30, MAX_BPM = 240;
@@ -42,12 +43,14 @@ export type PepperScoreAnnotationSizes = Partial<Record<AnnotationKey, Annotatio
42
43
  /** Reader settings; `zoom` is the score's explicit stave scale, or null for the device default (see stave-scale.ts). */
43
44
  export type Prefs = FileSettings & {
44
45
  zoom: number | null;
46
+ pdfPaper: 'a4' | 'a3' | 'letter' | 'legal';
45
47
  };
46
48
  export declare const defaults: Prefs;
47
49
  /** Saved global prefs, repaired. Zoom is per score (score-db), so a legacy global zoom is ignored. */
48
50
  export declare function sanitizeSavedPrefs(raw: unknown): Prefs;
49
51
  /** Initial reader settings a host passes as `settings` (spec names). Changing a key in the prop re-applies that key. */
50
52
  export type PepperScoreSettings = Partial<{
53
+ pitchColours: boolean;
51
54
  noteNames: boolean;
52
55
  chordSymbols: boolean;
53
56
  inferChords: boolean;
@@ -68,9 +71,11 @@ export type PepperScoreSettings = Partial<{
68
71
  * counts between beats). Only the add-ons given are applied; unknown sizes are ignored.
69
72
  */
70
73
  annotationSizes: PepperScoreAnnotationSizes;
71
- /** Which staves guitar parts show: `'notation'` (default), `'tab'` or `'both'`. */
74
+ /** Which staves fretted string parts show: `'notation'` (default), `'tab'` or `'both'`. */
72
75
  tabView: TabView;
73
- /** Open-string MIDI pitches of generated TAB, low to high (default EADGBE, [40, 45, 50, 55, 59, 64]). */
76
+ /** TAB identity by onReady part id; "none" suppresses TAB. Does not change playback. */
77
+ tabInstruments: TabInstruments;
78
+ /** Open-string MIDI in physical bottom-to-top string order (re-entrant allowed). Overrides generated parts with the same string count; other parts keep instrument defaults. */
74
79
  tabTuning: number[];
75
80
  /** Capo fret 0-12 of generated TAB; frets are relative to it. */
76
81
  capo: number;
@@ -133,6 +133,11 @@ export declare const systemMinimumWidth: (bars: number, barFloor: number) => num
133
133
  * (the gutter never shrinks with the staff), so a bisection finds it. Deterministic, render-free, rounded down to 0.001.
134
134
  */
135
135
  export declare function barFitZoom(viewportWidth: number, bars: number, barFloor: number, gutterAt?: (zoom: number) => number): number;
136
+ /**
137
+ * Bounding box of an element in the outer SVG's user (viewBox) coordinates. `rootInverse` is the outer SVG's inverted
138
+ * screen matrix, when the caller measures many elements of one SVG without layout changes in between.
139
+ */
140
+ export declare function annotationBox(element: Element, svg: SVGSVGElement, rootInverse?: DOMMatrix | null): DOMRect;
136
141
  /**
137
142
  * Map Score parts to Verovio staff numbers. Verovio gives a single-staff
138
143
  * MusicXML part's staffDef the part ID, while a multi-staff part's staffGrp
@@ -159,6 +164,52 @@ export declare function planStaves(info: BaselineInfo, score: Score, hiddenIds:
159
164
  staffCount: number;
160
165
  tabAlias: Map<string, ScoreNote>;
161
166
  };
167
+ export type AnnotationSettings = {
168
+ showNames?: boolean;
169
+ showCounts?: boolean;
170
+ subdivisions?: boolean;
171
+ showChordSymbols?: boolean;
172
+ inferChordSymbols?: boolean;
173
+ scale?: AnnotationScale;
174
+ };
175
+ export declare function annotationLayout(parts: Part[], settings: AnnotationSettings): {
176
+ nameScale: number;
177
+ chordScale: number;
178
+ beatScale: number;
179
+ offbeatScale: number;
180
+ countScale: number;
181
+ nameRows: number;
182
+ laneRows: number;
183
+ nameRow: number;
184
+ countRow: number;
185
+ lanesHeight: number;
186
+ laneHeight: number;
187
+ spacingStaff: number;
188
+ harmSize: number | undefined;
189
+ };
190
+ /** Shared screen/PDF drawing. Coordinates are outer SVG units, independent of its pixel size. */
191
+ export declare function drawEventAnnotations(layer: Element, score: Score, part: Part, measure: Part['measures'][number], event: ScoreNote, x: number, band: {
192
+ top: number;
193
+ laneTop: number;
194
+ }, settings: AnnotationSettings, layout: ReturnType<typeof annotationLayout>): void;
195
+ /**
196
+ * Resolve the rendered event for a model note; a chord wins over any one of its noteheads. A note whose notation
197
+ * staff is hidden (TAB view) resolves to its generated TAB event (`${id}-tab`, notation/mei-tab.ts).
198
+ */
199
+ export declare function findAnnotationNote(byId: Map<string, Element>, score: Score, event: ScoreNote): Element | undefined;
200
+ /** Allowlist sanitizer: only static SVG drawing elements, local references and inert styles survive. */
201
+ export declare function sanitizeSvg(doc: XMLDocument): SVGSVGElement;
202
+ type StaffBand = {
203
+ top: number;
204
+ bottom: number;
205
+ center: number;
206
+ laneTop: number;
207
+ lines: boolean;
208
+ /** A six-line TAB staff (its lines are drawn in segments around the fret numbers). */
209
+ tab?: boolean;
210
+ };
211
+ /** Staff bands and below-staff annotation lanes shared by reader systems and PDF systems. */
212
+ export declare function annotationBands(system: Element, boxOf: (element: Element) => DOMRect, tabStaves: ReadonlySet<string>, withLanes: boolean): Map<string, StaffBand>;
162
213
  /**
163
214
  * Render from the original engraving document through the locally bundled WASM worker.
164
215
  *
@@ -167,3 +218,4 @@ export declare function planStaves(info: BaselineInfo, score: Score, hiddenIds:
167
218
  * small first-measures document, so the first paint does not wait for the whole score's layout.
168
219
  */
169
220
  export declare function renderScore(container: HTMLElement, score: Score, mode: 'portrait' | 'landscape', showNames: boolean, showCounts: boolean, subdivisions: boolean, barsPerLine: 'auto' | number, showChordSymbols: boolean, inferChordSymbols: boolean, availableHeight?: number, zoom?: number | 'fit', firstSystemGutter?: number, options?: RenderOptions): Promise<RenderResult>;
221
+ export {};
@@ -92,6 +92,12 @@ export type MeasureNavigation = {
92
92
  barlines?: BarlineNavigation[];
93
93
  };
94
94
  export type Measure = {
95
+ keyChanges?: {
96
+ start: number;
97
+ key: number;
98
+ keyMode?: KeyMode;
99
+ }[];
100
+ rightBarline?: 'end' | 'dbl';
95
101
  index: number;
96
102
  beats: number;
97
103
  notes: ScoreNote[];
@@ -0,0 +1,25 @@
1
+ /** Public role names. Each CSS use keeps its original colour as its fallback. */
2
+ export declare const THEME_TOKENS: readonly ["accent", "accentHover", "accentWash", "accentWashStrong", "backdrop", "border", "borderMuted", "danger", "focusRing", "onAccent", "playback", "shadow", "surface", "surfaceRaised", "text", "textFaint", "textMuted", "textSecondary", "warning"];
3
+ export type ThemeToken = typeof THEME_TOKENS[number];
4
+ export declare const BAR_ITEM_IDS: readonly ["play", "stop", "tempo", "transpose", "keyFlip", "bars", "viewToggle", "tab", "zoom", "mixer", "openFile", "instrumentSwitch", "octaveTool", "options", "noteNames", "counts", "chordSymbols", "pitchColours", "pdf"];
5
+ export type BarItemId = typeof BAR_ITEM_IDS[number];
6
+ export type PitchClass = 'C' | 'C#' | 'D' | 'D#' | 'E' | 'F' | 'F#' | 'G' | 'G#' | 'A' | 'A#' | 'B';
7
+ export type PitchPalette = Record<PitchClass, string>;
8
+ export declare const CRF_PITCH_COLOURS: PitchPalette;
9
+ export type PepperScoreSkin = {
10
+ theme?: Partial<Record<ThemeToken, string>>;
11
+ pitchColours?: PitchPalette;
12
+ bar?: BarItemId[];
13
+ };
14
+ /** Overrides may replace individual palette entries; a skin itself has a complete palette. */
15
+ type SkinOverride = Omit<PepperScoreSkin, 'pitchColours'> & {
16
+ pitchColours?: Partial<PitchPalette>;
17
+ };
18
+ export declare function mergeSkins(base: PepperScoreSkin, ...overrides: SkinOverride[]): PepperScoreSkin;
19
+ export declare function tokenVariable(token: ThemeToken): string;
20
+ export declare function skinStyle(skin?: PepperScoreSkin): Record<string, string>;
21
+ /** Sort maps, preserve array order. Inline object literals are safe memo inputs. */
22
+ export declare function skinKey(skin?: PepperScoreSkin): string;
23
+ /** Called after mounting, only in development: resolve host var() values in their real scope. */
24
+ export declare function warnInvalidTheme(root: HTMLElement, skin?: PepperScoreSkin): void;
25
+ export {};
@@ -0,0 +1,65 @@
1
+ import { type Part, type Score } from '../score';
2
+ /** TAB instrument identity is independent of the playback sound (ukuleles use nylon guitar). */
3
+ export declare const STRING_INSTRUMENTS: {
4
+ readonly guitar: {
5
+ readonly kind: "fretted";
6
+ readonly tuning: readonly [40, 45, 50, 55, 59, 64];
7
+ };
8
+ readonly bass4: {
9
+ readonly kind: "fretted";
10
+ readonly tuning: readonly [28, 33, 38, 43];
11
+ };
12
+ readonly bass5: {
13
+ readonly kind: "fretted";
14
+ readonly tuning: readonly [23, 28, 33, 38, 43];
15
+ };
16
+ readonly 'ukulele-soprano': {
17
+ readonly kind: "fretted";
18
+ readonly tuning: readonly [67, 60, 64, 69];
19
+ };
20
+ readonly 'ukulele-tenor': {
21
+ readonly kind: "fretted";
22
+ readonly tuning: readonly [67, 60, 64, 69];
23
+ };
24
+ readonly 'ukulele-baritone': {
25
+ readonly kind: "fretted";
26
+ readonly tuning: readonly [50, 55, 59, 64];
27
+ };
28
+ readonly 'ukulele-bass': {
29
+ readonly kind: "fretted";
30
+ readonly tuning: readonly [28, 33, 38, 43];
31
+ };
32
+ readonly violin: {
33
+ readonly kind: "finger";
34
+ readonly tuning: readonly [55, 62, 69, 76];
35
+ };
36
+ readonly viola: {
37
+ readonly kind: "unsupported";
38
+ readonly tuning: readonly [48, 55, 62, 69];
39
+ };
40
+ readonly cello: {
41
+ readonly kind: "finger";
42
+ readonly tuning: readonly [36, 43, 50, 57];
43
+ };
44
+ readonly 'double-bass': {
45
+ readonly kind: "unsupported";
46
+ readonly tuning: readonly [28, 33, 38, 43];
47
+ };
48
+ };
49
+ export type TabInstrument = keyof typeof STRING_INSTRUMENTS;
50
+ export type TabInstruments = Record<string, TabInstrument | 'none'>;
51
+ export declare const validTabInstrument: (id: unknown) => id is TabInstrument;
52
+ /** Render metadata travels with the score through the existing render/PDF planning paths. */
53
+ export type TabScore = Score & {
54
+ tabSettings?: {
55
+ instruments?: TabInstruments;
56
+ tuning?: number[];
57
+ };
58
+ };
59
+ export declare function withTabSettings(score: Score, instruments?: TabInstruments, tuning?: number[]): TabScore;
60
+ /** Switched target ids are exact; source programs gate name-based classification before a switch. */
61
+ export declare function stringInstrument(part: Part, override?: TabInstrument | 'none', switched?: boolean): TabInstrument | undefined;
62
+ /** Overrides use the exact part id reported by onReady; unknown keys/values are ignored. */
63
+ export declare function instrumentFor(score: TabScore, part: Part, switched?: boolean): "guitar" | "double-bass" | "violin" | "viola" | "cello" | "bass4" | "bass5" | "ukulele-soprano" | "ukulele-tenor" | "ukulele-baritone" | "ukulele-bass" | undefined;
64
+ /** Name-only voices are explicit; ambiguous bass needs choir context without guitar/drum evidence. */
65
+ export declare function resolveChoirNames(score: Score): Score;
@@ -0,0 +1,29 @@
1
+ /** Fretless chart data; violin and cello are integrated in the reader.
2
+ * Source: ~/violintab/src/lib/tab/instruments.ts (violin/cello).
3
+ * Strings run high → low; MIDI values are SOUNDING pitches, including bass.
4
+ */
5
+ export type StringInstrumentId = 'violin' | 'viola' | 'cello' | 'double-bass';
6
+ export type FingerRow = readonly [number, number, number, number];
7
+ export interface StringChart {
8
+ readonly id: StringInstrumentId;
9
+ readonly provenance: 'ported' | 'derived';
10
+ readonly source: string;
11
+ readonly strings: readonly string[];
12
+ readonly openMidi: readonly number[];
13
+ /** One position map per string, high → low. */
14
+ readonly fingers: readonly Readonly<Record<number, FingerRow>>[];
15
+ }
16
+ export declare const VIOLIN: StringChart;
17
+ /** Davidoff positions: there is NO 2½. Correct source keys
18
+ * 2.5/3/3.5/4 → 3/3.5/4/4.5, preserving every source pitch.
19
+ */
20
+ export declare const CELLO_POSITIONS: readonly [0.5, 1, 1.5, 2, 3, 3.5, 4, 4.5];
21
+ export declare const CELLO: StringChart;
22
+ /** Provisional analogies, NOT independently verified pedagogical charts.
23
+ * Viola: transpose violin down a fifth. Bass: reuse cello finger offsets on
24
+ * fourths tuning (not a claim about Simandl/1-2-4 or thumb technique).
25
+ * A teacher must approve/replace these before production integration.
26
+ */
27
+ export declare const VIOLA: StringChart;
28
+ export declare const DOUBLE_BASS: StringChart;
29
+ export declare const STRING_CHARTS: Readonly<Record<StringInstrumentId, StringChart>>;
@@ -0,0 +1,14 @@
1
+ import type { StringChart } from './charts';
2
+ import { type PitchSpelling, type StringFingering } from './solver';
3
+ export interface FingerNote {
4
+ id: string;
5
+ onset: number;
6
+ duration: number;
7
+ midi: number | null;
8
+ spelling?: PitchSpelling;
9
+ tieStop?: boolean;
10
+ fixed?: StringFingering;
11
+ }
12
+ /** Solve simultaneous voices together, keeping sounding strings occupied and a common hand position.
13
+ * Unsupported events stay unlabelled rather than inventing a pitch or an impossible double stop. */
14
+ export declare function solveFingerPhrase(chart: StringChart, notes: FingerNote[]): Map<string, StringFingering>;
@@ -0,0 +1,29 @@
1
+ import type { StringChart } from './charts';
2
+ export type FingerModifier = '' | 'L' | 'H';
3
+ /** Written pitch class; a mismatched hint is ignored, never changes MIDI. */
4
+ export interface PitchSpelling {
5
+ step: 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G';
6
+ alter: -2 | -1 | 0 | 1 | 2;
7
+ }
8
+ export interface StringFingering {
9
+ /** 1 = highest string; position 0 denotes an open string, no hand position. */
10
+ string: number;
11
+ position: number;
12
+ finger: number;
13
+ modifier: FingerModifier;
14
+ }
15
+ /** Reverse lookup, useful for fixtures and future fixed-fingering constraints.
16
+ * Open strings require canonical position 0 and no modifier.
17
+ */
18
+ export declare function fingeringPitch(chart: StringChart, note: StringFingering): number | null;
19
+ /** Exact-pitch choices, opens first. Cello/bass prefer plain fingers anywhere
20
+ * over extensions, then I before half position, then higher positions.
21
+ * Violin/viola prefer lower positions, then plain fingers. Modified ties use
22
+ * the matching spelling: raised (including double sharps) → H, otherwise L.
23
+ * Without a valid hint, C#/D#/G# prefer H (high 3 rather than low 4);
24
+ * other pitch classes prefer L, preserving low 2 and the flat-note default.
25
+ * No octave fold, phrase continuity, double-stop or ringing-string solve.
26
+ */
27
+ export declare function fingeringCandidates(chart: StringChart, midi: number, spelling?: PitchSpelling): StringFingering[];
28
+ /** Null means outside the supported chart range, never silently transposed. */
29
+ export declare function solveStringPitch(chart: StringChart, midi: number, spelling?: PitchSpelling): StringFingering | null;
@@ -0,0 +1,3 @@
1
+ /** Verovio preserves note @type as an SVG class, including after getMEI/loadData.
2
+ * Change only that note's numeric text; never rely on chord ordering or a font's pixel size. */
3
+ export declare function substituteFingerLabels(svg: string): string;
@@ -17,12 +17,9 @@ export declare const soundingKeyOf: (part: Part, measure: Measure, note: ScoreNo
17
17
  * nothing (a TAB-only score) stays a normal, performed part. Mutates and returns `score`.
18
18
  */
19
19
  export declare function linkTabParts(score: Score): Score;
20
- /** A guitar part: GM programs 25-32 (1-based; nylon to guitar harmonics), the guitar sound, or "guitar" in its name. */
21
- export declare const isGuitarPart: (part: Part) => boolean;
22
20
  /**
23
- * Notation parts that take TAB: every guitar part (and every part an imported TAB doubles), else the first pitched
24
- * part. A part switched to another instrument (instrument-view.ts) takes TAB only if its new instrument is a guitar
25
- * (its name and its imported TAB no longer count).
21
+ * Fretted and approved violin/cello notation parts take generated TAB. Authored imported TAB remains usable; there is no pitched-part fallback.
22
+ * Instrument switches use the new instrument identity instead of the original name.
26
23
  */
27
24
  export declare function tabParts(score: Score): Part[];
28
25
  /** Whether the Notation / TAB / Both switch has anything to switch. */
@@ -60,7 +60,7 @@ export type VerovioFiles = {
60
60
  module: string;
61
61
  toolkit: string;
62
62
  };
63
- /** Sent once by the runtime right after it starts a worker, before any request. */
63
+ /** Sent once by the runtime right after it starts a worker, before any request; the worker then starts loading Verovio. */
64
64
  export type VerovioConfigure = {
65
65
  kind: 'configure';
66
66
  files: VerovioFiles;
@@ -32,6 +32,18 @@ export declare function verovioFilesAt(base: string, page?: string): VerovioFile
32
32
  * a restart); a blank `base` changes nothing.
33
33
  */
34
34
  export declare function setVerovioBaseUrl(base: string | undefined): void;
35
+ /**
36
+ * Start the shared Verovio worker now and have it fetch, compile and instantiate Verovio, so the first engraving does
37
+ * not wait for that after the score's import. Readers call it on mount when they have a score to show; the worker
38
+ * still goes when the last reader channel closes. Uses the current `setVerovioBaseUrl` (set it first).
39
+ */
40
+ export declare function warmVerovio(): void;
41
+ /**
42
+ * Start loading Verovio before any reader mounts (for example from a page script, before React hydrates), so the
43
+ * first score engraves sooner. `base` is the same directory as PepperScoreProps.verovioBaseUrl (pass the same value);
44
+ * omitted, the copy shipped with pepperscore loads. Safe to call more than once and on the server (it does nothing).
45
+ */
46
+ export declare function preloadVerovio(base?: string): void;
35
47
  export declare function verovioRevision(document: SourceDocument): number;
36
48
  /** A reader mounted: a channel of its own, so its imports and renders never supersede another reader's. */
37
49
  export declare function openVerovioChannel(): number;