pepperscore 0.4.0 → 0.4.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/CHANGELOG.md +13 -2
- package/README.md +8 -2
- package/dist/assets/{verovio.worker-DQm-ikdG.js → verovio.worker-DBJku5Ng.js} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2484 -2441
- package/dist/index.js.map +1 -1
- package/dist/lib/asset-fonts.d.ts +4 -0
- package/dist/lib/verovio-protocol.d.ts +1 -1
- package/dist/lib/verovio-runtime.d.ts +12 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.4.
|
|
3
|
+
## 0.4.1 - 2026-10-30
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `preloadVerovio(base?: string)` (main entry): start the Verovio worker and its download, compile and instantiation before any reader mounts (for example before React hydrates). `base` is the same directory as `verovioBaseUrl`. See README, "Verovio".
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Faster first engraving: a reader with a score (or full chrome's sample) starts the Verovio worker on mount, and the worker begins importing and instantiating Verovio as soon as it is configured, in parallel with the score's import instead of after it. The worker still stops when the last reader unmounts. `verovioBaseUrl` is applied before the warm-up; a worker that has only warmed up (no request yet) is replaced if the base URL then changes.
|
|
12
|
+
- The notation font (Bravura, or Petaluma with Petaluma Script) starts downloading on mount, and a draw waits up to 1.5 s for it so the SMuFL text it measures uses the font's metrics. The music fonts keep `font-display: block` (their glyphs are Private Use Area code points; a fallback would show boxes).
|
|
13
|
+
|
|
14
|
+
## 0.4.0 - 2026-10-30
|
|
15
|
+
|
|
16
|
+
Published from main before the first-engrave warm-up merged; that ships in 0.4.1.
|
|
6
17
|
|
|
7
18
|
### Added
|
|
8
19
|
|
package/README.md
CHANGED
|
@@ -203,7 +203,7 @@ export default defineConfig({
|
|
|
203
203
|
| `settings` | `PepperScoreSettings` | | **Initial** reader settings: `noteNames`, `chordSymbols`, `inferChords`, `beatCounts`, `subdivisions` (booleans); `musicFont` (`'Bravura' \| 'Petaluma'`); `staveFont` (`'Sans' \| 'Serif' \| 'Mono'`); `barsPerLine` (`'auto' \| 1–8`, Vertical view; without a `zoom`, an explicit count also sets the on-load size: that many bars fill the width, see below); `zoom` (stave scale, `null` = device default; with a `zoom`, `barsPerLine` only sets the line breaks); `semitones` (−12…12); `concert` (concert vs written pitch); `keyFlip` (spell a transposed view in the enharmonic key, for example Gb major for F# major); `bpm` (initial tempo, 30–240; the score's own tempo otherwise); `overlap` (0–3 lines kept on a page turn); `tabView`, `tabTuning`, `capo` (guitar TAB, see [Guitar TAB](#guitar-tab)); `annotationSizes` (text size per add-on, see below); `instrument`, `instruments`, `rangeLevel`, `autoRange` (the instrument switch, see below). Changing a key in the prop re-applies that key. Drawer edits still work in between. With `persist` on, **a saved per-score record overrides `settings` and `view`** when that score is opened again. The record holds the settings, view, zoom, transposition, part sounds, hidden staves and instrument switches the reader stored for that exact file. The instrument settings are the exception: when they differ from the ones the record was saved under, they apply again (see [Instrument switch](#instrument-switch)). |
|
|
204
204
|
| `persist` | `string \| false` | `'pepperscore'` for `full`, `false` for `minimal`/`none` | Storage namespace, see below. `false` reads and writes nothing. |
|
|
205
205
|
| `assetBaseUrl` | `string` | jsDelivr copy of `pepperscore-sounds-<soundPack>` | Root of a sounds-package layout. Relative values resolve against the page, and a missing trailing slash is added. |
|
|
206
|
-
| `verovioBaseUrl` | `string` | the copy in `dist/verovio/` | A directory with an interface-compatible Verovio build to engrave with, laid out like the `verovio` npm package's `dist/` (`verovio-module.mjs`, `verovio.mjs`), e.g. `https://cdn.jsdelivr.net/npm/verovio@6.3.0/dist/`. Shared by every reader on the page: set it before the first score engraves. See [Verovio](#verovio). |
|
|
206
|
+
| `verovioBaseUrl` | `string` | the copy in `dist/verovio/` | A directory with an interface-compatible Verovio build to engrave with, laid out like the `verovio` npm package's `dist/` (`verovio-module.mjs`, `verovio.mjs`), e.g. `https://cdn.jsdelivr.net/npm/verovio@6.3.0/dist/`. Shared by every reader on the page: set it before the first score engraves (a reader applies it before it warms Verovio up on mount; with `preloadVerovio`, pass it the same value). See [Verovio](#verovio). |
|
|
207
207
|
| `soundPack` | `'core' \| 'complete'` | `'core'` | Picks the default `assetBaseUrl`. With your own `assetBaseUrl`, the `pack.json` found there decides which FluidR3 sounds exist. |
|
|
208
208
|
| `soundSources` | `SoundSource[]` | `[]` | Your own sounds for specific programs or the drum kit, see below. |
|
|
209
209
|
| `onReady` | `(info: { title, composer?, parts: { id, name, sourcePart, switchable }[], measures }) => void` | | Once per loaded score, after it is drawn. `parts` lists every staff (a grand staff's staves share one `sourcePart` number); `switchable` says whether the instrument switch applies. |
|
|
@@ -381,10 +381,16 @@ Use the sounds package version that matches `SOUNDS_VERSION` (exported by this p
|
|
|
381
381
|
|
|
382
382
|
### Verovio
|
|
383
383
|
|
|
384
|
-
Verovio is not bundled into pepperscore's code. The package ships upstream's own files from `verovio@6.3.0`, unmodified apart from a licence comment, in `dist/verovio/` (`verovio-module.mjs`, about 7.3 MB with its WebAssembly inlined, and `verovio.mjs`, the toolkit), and the Verovio worker imports them at runtime when the
|
|
384
|
+
Verovio is not bundled into pepperscore's code. The package ships upstream's own files from `verovio@6.3.0`, unmodified apart from a licence comment, in `dist/verovio/` (`verovio-module.mjs`, about 7.3 MB with its WebAssembly inlined, and `verovio.mjs`, the toolkit), and the Verovio worker imports them at runtime. A reader that has a score to show (or full chrome's sample) starts the worker when it mounts, so Verovio downloads and compiles while the score is still being read, not after. Host bundlers (Vite, Astro) copy them as assets like the worker chunks, so nothing needs configuring.
|
|
385
385
|
|
|
386
386
|
- Serve `.mjs` files as JavaScript (`text/javascript`). Most hosts do; a server that sends them as `application/octet-stream` makes the import fail, and the reader shows a "Could not load Verovio from …" error.
|
|
387
387
|
- To engrave with another build (a modified Verovio, a newer compatible version, or a CDN copy), pass `verovioBaseUrl`, a directory holding both files. A cross-origin directory must send CORS headers. Or replace the files your bundler emitted.
|
|
388
|
+
- **`preloadVerovio(base?)`** (from `pepperscore`) starts that download earlier still, before React hydrates, for example from a page script that runs before the island loads. Pass the same directory as `verovioBaseUrl` (omit both for the shipped copy). It does nothing on the server and is safe to call more than once; readers that mount later reuse the warm worker, and it stops when the last reader unmounts. A worker that has only warmed up is replaced if a reader then sets a different `verovioBaseUrl`.
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import { preloadVerovio } from 'pepperscore';
|
|
392
|
+
preloadVerovio(); // or preloadVerovio('https://cdn.jsdelivr.net/npm/verovio@6.3.0/dist/')
|
|
393
|
+
```
|
|
388
394
|
- The interface pepperscore expects: `verovio-module.mjs` default-exports an async factory returning the Emscripten module (`createVerovioModule`), and `verovio.mjs` exports `VerovioToolkit` and `enableLogToBuffer`, as in `verovio@6.3.0`.
|
|
389
395
|
|
|
390
396
|
**If the assets cannot be loaded** (a wrong `assetBaseUrl`, or nothing published there yet), the reader keeps working: the score still engraves (Verovio's own glyphs; text glyphs fall back), playback runs silently with the usual "playing silently" notice and no error, and the console shows one warning naming the `assetBaseUrl` that failed. A failing `soundSources` entry of your own still reports through `onError`.
|
|
@@ -4,4 +4,4 @@ e),import(
|
|
|
4
4
|
/* @vite-ignore */
|
|
5
5
|
t)]);if(typeof n.default!=`function`||typeof r.VerovioToolkit!=`function`||typeof r.enableLogToBuffer!=`function`)throw Error(`not a compatible Verovio build`);return yt={createModule:n.default,Toolkit:r.VerovioToolkit,enableLogToBuffer:r.enableLogToBuffer}}catch(n){throw vt=void 0,new Dt(`Could not load Verovio from ${e} and ${t}: ${n instanceof Error?n.message:String(n)}`,void 0)}})()}let X,xt;function St(){if(X)return X;let e=bt().then(e=>e.createModule()).then(e=>(yt.enableLogToBuffer(1,e),xt=e,e)).catch(t=>{throw X===e&&(X=void 0),t});return X=e}const Ct=async()=>{let e=await St();return new yt.Toolkit(e)};let wt,Tt,Z;const Q=/* @__PURE__ */ new Map,Et=[];var Dt=class extends Error{code;constructor(e,t){super(e),this.code=t}};function Ot(e){for(let e of[wt,Tt])try{e?.destroy()}catch{}wt=Tt=void 0,Z=void 0,e&&(X=void 0,xt=void 0)}async function kt(e){return e===`main`?wt??=await Ct():Tt??=await Ct()}function At(){let e=xt;return e?.HEAPU8?.length??e?.wasmMemory?.buffer.byteLength}function jt(e){let t=Q.get(e);if(!t)throw new Dt(`The score is no longer loaded in the renderer.`,`missing`);return Q.delete(e),Q.set(e,t),t}function $(e,t,n){if(e.resetOptions(),e.setOptions(n),!e.loadData(t))throw Error(`Verovio could not import this score.`)}const Mt={breaks:`none`,inputFrom:`auto`,xmlIdChecksum:!0};async function Nt(e){let t=await Ct();try{return $(t,e,Mt),t.getMEI()}finally{try{Et.push(t.getLog()||``)}catch{}try{t.destroy()}catch{}}}async function Pt(e,t,n){let r=`${t}-${n}`,i=e.meis.get(r);if(i!==void 0)return e.meis.delete(r),e.meis.set(r,i),i;for(i=await Nt(ft(e.index,t,n)),e.meis.set(r,i);e.meis.size>8;)e.meis.delete(e.meis.keys().next().value);return i}async function Ft(e,t){let n=await Ct();try{return $(n,e,{breaks:`none`,xmlIdChecksum:!0,...t}),n.getMEI()}finally{try{Et.push(n.getLog()||``)}catch{}try{n.destroy()}catch{}}}const It=(e,t,n=0)=>U(e,t,Ft,n);async function Lt(e,t){if(!t)return e.mei;let n=JSON.stringify(t);return e.spelled?.key!==n&&(e.spelled={key:n,mei:await It(e.mei,t)}),e.spelled.mei}async function Rt(e,t){let n=await Lt(e,t.spelling);if(!t.tab)return n;let r=JSON.stringify([t.spelling??null,t.tab]);return e.tabbed?.key!==r&&(e.tabbed={key:r,mei:nt(n,t.tab)}),e.tabbed.mei}async function zt(e,t,n,r){let i=await Bt(e,t,n,r.spelling);if(!r.tab)return i;let a=JSON.stringify([r.spelling??null,r.tab]),o=e.frets.get(a);if(!o)for(o=/* @__PURE__ */ new Map,e.frets.set(a,o);e.frets.size>4;)e.frets.delete(e.frets.keys().next().value);return nt(i,r.tab,o)}async function Bt(e,t,n,r){let i=await Pt(e,t,n);if(!r||!ke(i,r))return i;let a=`${t}-${n}|${JSON.stringify(r)}`,o=e.spelled.get(a);if(o!==void 0)return e.spelled.delete(a),e.spelled.set(a,o),o;for(o=await It(i,r,t),e.spelled.set(a,o);e.spelled.size>8;)e.spelled.delete(e.spelled.keys().next().value);return o}const Vt=(e,t)=>Object.fromEntries(t.map(t=>[t,e[t]??null]));async function Ht(e,t,n,r){let i=JSON.stringify([...e,t,Vt(n,gt)]),a=JSON.stringify(n),o=await kt(`main`);return Z?.loadKey===i?Z.optionsKey!==a&&(Z=void 0,o.resetOptions(),o.setOptions(n),o.redoLayout(),Z={loadKey:i,optionsKey:a}):(Z=void 0,$(o,await r(),n),Z={loadKey:i,optionsKey:a}),o}function Ut(e,t,n){return Math.min(n,Math.max(t*2,qt(e,t,t*4)+1))}const Wt=/\s(?:endid|tstamp2)\s*=\s*"([^"]*)"/g,Gt=/\sxml:id\s*=\s*"([^"]*)"/,Kt=/<measure\b/g;function qt(e,t,n){let r=[];Kt.lastIndex=0;for(let t=Kt.exec(e);t&&r.length<=n;t=Kt.exec(e))r.push(t.index);let i=/* @__PURE__ */ new Set,a=0;for(let n=0;n+1<r.length;n++){let o=e.slice(r[n],r[n+1]);if(n<t)for(let[,e]of o.matchAll(Wt)){let t=/^(\d+)m\+/.exec(e);t?a=Math.max(a,n+Number(t[1])):i.add(e.replace(/^#/,``))}else if(i.size)for(let e of o.match(/<[^>]*\sxml:id\s*=\s*"[^"]*"[^>]*>/g)||[]){let t=Gt.exec(e)?.[1];t&&i.delete(t)&&(a=Math.max(a,n))}if(n>=t&&!i.size)break}return Math.min(n-1,a)}async function Jt(e,t,n){let r=new TextDecoder().decode(t),i=ct(r),a;if(i&&(n||i.parts.length*i.measureCount>=2400)){let{staffDefs:e,groups:t}=d(await Nt(ft(i,0,1)));a={kind:`chunked`,index:i,info:{measureIds:Array.from({length:i.measureCount},(e,t)=>rt(t)),lyricDensity:i.lyricDensity,staffDefs:e,groups:t,chunked:!0},meis:/* @__PURE__ */ new Map,spelled:/* @__PURE__ */ new Map,frets:/* @__PURE__ */ new Map}}else{let e=await kt(`main`);Z=void 0,$(e,r,Mt);let t=e.getMEI();a={kind:`whole`,mei:t,info:d(t)}}for(Q.delete(e),Q.set(e,a);Q.size>12;)Q.delete(Q.keys().next().value);return a.info}function Yt(e,t,n){let r=e.renderToSVG(t),i=/<svg\b[^>]*?\sid="([^"]+)"/.exec(r)?.[1];return!i||!/^[\w-]+$/.test(i)?r:r.replace(RegExp(`(?<=[#"-])${i}(?![\\w-])`,`g`),`${i}-c${n}`)}const Xt=(e,t)=>{let n=new Set(t.hiddenStaves);return Math.max(1,e.staffDefs.filter(e=>!n.has(e.n)).length+(t.tab?.generate.length??0))};async function Zt(e){if(e.kind===`import`)return Jt(e.revision,e.bytes,!!e.chunking?.force);if(e.kind===`release`)return Q.delete(e.revision),null;if(e.simulateOom)throw await new Promise(e=>setTimeout(e,300)),RangeError(`Cannot enlarge memory arrays (simulated).`);let t=jt(e.revision);if(e.kind===`probe`){let n=await kt(`probe`);if(t.kind===`chunked`){let r=t.index.measureCount,i=Math.min(r,e.measures*2),a=e.measures>=r?r:i+mt(t.index,i);return $(n,m(await zt(t,0,a,e.view),e.view),e.options),{svg:Yt(n,1,e.channel),pageCount:n.getPageCount(),measures:a}}let{info:r}=t,i=await Rt(t,e.view),a=e.measures>=r.measureIds.length?r.measureIds.length:Ut(i,e.measures,r.measureIds.length);return $(n,m(i,e.view,a),e.options),{svg:Yt(n,1,e.channel),pageCount:n.getPageCount(),measures:a}}if(t.kind===`whole`){let n=await Ht([e.revision],e.view,e.options,async()=>m(await Rt(t,e.view),e.view)),r=n.getPageCount();return{pageCount:r,pages:e.pages.filter(e=>e>=1&&e<=r).map(t=>({page:t,svg:Yt(n,t,e.channel)}))}}let n=e.view.breakEvery,r=n>0?Math.ceil(t.index.measureCount/n):1,i=/* @__PURE__ */ new Map;for(let a of e.pages){if(a<1||a>r)continue;let o=ht(t.index,a-1,n,Xt(t.info,e.view),e.chunkBudget),s=`${o.from}-${o.to}`;(i.get(s)??i.set(s,{plan:o,pages:[]}).get(s)).pages.push(a)}let a=[];for(let{plan:n,pages:r}of i.values()){let i=await Ht([e.revision,n.from,n.to],e.view,e.options,async()=>m(await zt(t,n.from,n.to,e.view),e.view,1/0,n.from)),o=i.getPageCount();for(let t of r){let r=n.firstPage+(t-1-n.firstSystem);if(r>o)throw Error(`A score chunk laid out ${o} pages where ${r} were expected.`);a.push({page:t,svg:Yt(i,r,e.channel)})}}return{pageCount:r,pages:a}}function Qt(e){if(e instanceof Dt)return{message:e.message,code:e.code};let t=e instanceof Error?`${e.name}: ${e.message}`:String(e),n=globalThis.WebAssembly?.Exception;return n&&e instanceof n||/out of memory|OOM|Cannot enlarge memory|allocation failed|RangeError/i.test(t)?{message:`This score is too large for the notation renderer on this device (it ran out of memory).`,code:`oom`}:{message:e instanceof Error?e.message:String(e)}}function $t(e){let t=Et.splice(0).join(``);try{t+=(wt??Tt)?.getLog()||``}catch{}if(!t)return;let n=t.split(`
|
|
6
6
|
`).filter(Boolean),r=n.filter(e=>e.startsWith(`[Error]`)).length;console.warn(`Verovio ${e}: ${n.length} log message${n.length===1?``:`s`} (${r} error${r===1?``:`s`})${n.length>12?`, first 12`:``}:\n${n.slice(0,12).join(`
|
|
7
|
-
`)}`)}const en=[],tn=/* @__PURE__ */ new Map;let nn=!1;const rn=e=>self.postMessage(e);async function an(){let e=en.shift();if(!e){nn=!1;return}if(e.kind!==`import`&&e.kind!==`release`&&e.ticket<(tn.get(e.channel)??0))rn({id:e.id,error:`Superseded score render.`,code:`superseded`});else try{let t=await Zt(e);$t(e.kind),rn({id:e.id,value:t,heap:At()})}catch(t){$t(e.kind);let{message:n,code:r}=Qt(t);r!==`missing`&&Ot(r===`oom`||!(t instanceof Error&&t.message===`Verovio could not import this score.`)),rn({id:e.id,error:n,code:r,heap:At()})}setTimeout(()=>void an(),0)}self.onmessage=e=>{let t=e.data;if(t.kind===`configure`){_t=t.files;return}(t.kind===`probe`||t.kind===`render`)&&tn.set(t.channel,Math.max(tn.get(t.channel)??0,t.ticket)),en.push(t),nn||(nn=!0,setTimeout(()=>void an(),0))};
|
|
7
|
+
`)}`)}const en=[],tn=/* @__PURE__ */ new Map;let nn=!1;const rn=e=>self.postMessage(e);async function an(){let e=en.shift();if(!e){nn=!1;return}if(e.kind!==`import`&&e.kind!==`release`&&e.ticket<(tn.get(e.channel)??0))rn({id:e.id,error:`Superseded score render.`,code:`superseded`});else try{let t=await Zt(e);$t(e.kind),rn({id:e.id,value:t,heap:At()})}catch(t){$t(e.kind);let{message:n,code:r}=Qt(t);r!==`missing`&&Ot(r===`oom`||!(t instanceof Error&&t.message===`Verovio could not import this score.`)),rn({id:e.id,error:n,code:r,heap:At()})}setTimeout(()=>void an(),0)}self.onmessage=e=>{let t=e.data;if(t.kind===`configure`){_t=t.files,St().catch(()=>{});return}(t.kind===`probe`||t.kind===`render`)&&tn.set(t.channel,Math.max(tn.get(t.channel)??0,t.ticket)),en.push(t),nn||(nn=!0,setTimeout(()=>void an(),0))};
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type { ExternalClock, ClockEvent } from './lib/clock';
|
|
|
10
10
|
export type { SoundSource } from './lib/sounds';
|
|
11
11
|
export type { SoundPackName } from './lib/assets';
|
|
12
12
|
export { SOUNDS_VERSION, defaultAssetBaseUrl } from './lib/assets';
|
|
13
|
+
export { preloadVerovio } from './lib/verovio-runtime';
|
|
13
14
|
/** @deprecated Use PepperScore. Kept for one release. */
|
|
14
15
|
export declare const Reader: typeof PepperScore;
|
|
15
16
|
/** @deprecated Use PepperScoreProps. Kept for one release. */
|