pepperscore 0.5.0 → 0.5.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 +12 -0
- package/README.md +57 -2
- package/dist/{PdfOverlay-BmBGsMkr.js → PdfOverlay-DUgY9mW-.js} +64 -60
- package/dist/PdfOverlay-DUgY9mW-.js.map +1 -0
- package/dist/assets/{engrave.worker-D334KQnx.js → engrave.worker-B0WfvX6e.js} +2 -2
- package/dist/assets/verovio.worker-cxTTfdKx.js +7 -0
- package/dist/{engrave-DpVrifuu.js → engrave-DKNPmehA.js} +2 -2
- package/dist/{engrave-DpVrifuu.js.map → engrave-DKNPmehA.js.map} +1 -1
- package/dist/{export-BFhuMW8H.js → export-Dg2CkokP.js} +138 -66
- package/dist/export-Dg2CkokP.js.map +1 -0
- package/dist/index.js +2173 -2144
- package/dist/index.js.map +1 -1
- package/dist/lib/lazy-import.d.ts +19 -0
- package/dist/lib/pdf/annotations.d.ts +7 -3
- package/dist/lib/pdf/compose.d.ts +4 -1
- package/dist/lib/pdf/cooperative.d.ts +23 -0
- package/dist/lib/pdf/export.d.ts +3 -0
- package/dist/lib/pdf/normalize.d.ts +2 -0
- package/dist/lib/pdf/slices.d.ts +14 -0
- package/dist/lib/tab/strings/phrase.d.ts +2 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/dist/PdfOverlay-BmBGsMkr.js.map +0 -1
- package/dist/assets/verovio.worker-C8LY1lw3.js +0 -7
- package/dist/export-BFhuMW8H.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.1 - 2026-11-12
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Generated violin and cello finger TAB favours staying in position across a phrase, reducing unnecessary position shifts and labels. This changes display-only fingering choices; sounding pitches and playback are unchanged.
|
|
8
|
+
- PDF export is cooperative and cancellable on large scores: pages are annotated and drawn in slices that yield to the browser, so the page stays responsive (a 30-part, one-system-per-page score previously blocked the main thread for seconds per page). Cancel now takes effect within one slice, and the dialog shows “Building PDF… page N of M”. No change to the PDF produced.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Pitch-coloured notehead outlines use the intended default or host-set outline colour in Safari/WebKit instead of drawing solid black. Playback, cursor, selection, transposed and out-of-range highlights retain their own colours.
|
|
13
|
+
- PDF export is verified in Safari/WebKit as well as Chromium and Firefox. In WebKit a failed download of the dialog's lazy chunk stayed failed until the tab was closed, because WebKit keeps the failed `modulepreload` Vite adds. On Vite and native-ESM hosts the dialog now retries once through a cache-busted address, so reopening it or reloading recovers. On other bundlers (for example Webpack) the retry is skipped and the dialog shows its usual error and Reload page button.
|
|
14
|
+
|
|
3
15
|
## 0.5.0 - 2026-11-11
|
|
4
16
|
|
|
5
17
|
### Added
|
package/README.md
CHANGED
|
@@ -313,6 +313,55 @@ const skin = mergeSkins(base, { theme: { surface: '#fff' }, pitchColours: { C: '
|
|
|
313
313
|
<PepperScore abc={tuneAbc} skin={skin} settings={{ pitchColours: true }} />;
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
+
#### Worked example: following a host's accent scopes
|
|
317
|
+
|
|
318
|
+
becreative sets `--accent-ink`, `--accent`, `--accent-wash` and `--accent-wash-strong` on `.accent-blue`, `.accent-orange` and `.accent-pink` section wrappers. Mapping theme roles to those variables (with `accentHover` derived from the ink, since the host has no hover token and an unset `accentHover` falls back to the default green) makes the player change colour with the section, with no re-render or re-engrave. Define the skin once (TypeScript):
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
import { CRF_PITCH_COLOURS, mergeSkins, type PepperScoreSkin } from 'pepperscore';
|
|
322
|
+
|
|
323
|
+
export const becreativeSkin: PepperScoreSkin = {
|
|
324
|
+
theme: {
|
|
325
|
+
accent: 'var(--accent-ink)',
|
|
326
|
+
accentHover: 'color-mix(in srgb, var(--accent-ink) 82%, black)', // becreative has no hover token: darken the section ink
|
|
327
|
+
accentWash: 'var(--accent-wash)',
|
|
328
|
+
accentWashStrong: 'var(--accent-wash-strong)',
|
|
329
|
+
focusRing: 'var(--accent)', // a colour; becreative's --accent-ring is a box-shadow value
|
|
330
|
+
},
|
|
331
|
+
pitchColours: CRF_PITCH_COLOURS,
|
|
332
|
+
bar: ['play', 'tempo', 'instrumentSwitch', 'noteNames', 'counts', 'chordSymbols', 'options'],
|
|
333
|
+
};
|
|
334
|
+
// `static` shows no bar, so give it the theme only.
|
|
335
|
+
export const becreativeStaticSkin: PepperScoreSkin = { theme: becreativeSkin.theme };
|
|
336
|
+
// Same skin with a shorter bar; `mergeSkins` replaces `bar` whole.
|
|
337
|
+
export const quietSkin = mergeSkins(becreativeSkin, { bar: ['play', 'options'] });
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Plain JavaScript uses the same exports; the skin is only data, with no types needed:
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
import { CRF_PITCH_COLOURS, mergeSkins, THEME_TOKENS, BAR_ITEM_IDS } from 'pepperscore';
|
|
344
|
+
|
|
345
|
+
export const becreativeSkin = {
|
|
346
|
+
theme: { accent: 'var(--accent-ink)', accentHover: 'color-mix(in srgb, var(--accent-ink) 82%, black)', accentWash: 'var(--accent-wash)', accentWashStrong: 'var(--accent-wash-strong)', focusRing: 'var(--accent)' },
|
|
347
|
+
pitchColours: CRF_PITCH_COLOURS,
|
|
348
|
+
bar: ['play', 'tempo', 'instrumentSwitch', 'noteNames', 'counts', 'chordSymbols', 'options'],
|
|
349
|
+
};
|
|
350
|
+
console.log(THEME_TOKENS.includes('accent'), BAR_ITEM_IDS.includes('options')); // true true: valid names at runtime
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Then pass it as `skin` on the shapes that carry a bar, and the theme-only skin on the shape that does not:
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
<section className="accent-orange">
|
|
357
|
+
<PepperScore chrome="full" skin={becreativeSkin} abc={tuneAbc} /> {/* reader */}
|
|
358
|
+
<PepperScore chrome="minimal" skin={becreativeSkin} abc={tuneAbc} /> {/* snippet */}
|
|
359
|
+
<PepperScore chrome="none" skin={becreativeStaticSkin} abc={tuneAbc} /> {/* static: no bar */}
|
|
360
|
+
</section>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Changing the section's class (`accent-blue` to `accent-orange` to `accent-pink`) recolours every shape. The reader's embed test page (`/embed-skin?becreative`) runs this at 1280px and 390px and `npm run test:skin` checks it.
|
|
364
|
+
|
|
316
365
|
- **Theme:** a partial map of `ThemeToken` roles to CSS colours (including host `var()` references). Roles: `accent`, `accentHover`, `accentWash`, `accentWashStrong`, `backdrop`, `border`, `borderMuted`, `danger`, `focusRing`, `onAccent`, `playback`, `shadow`, `surface`, `surfaceRaised`, `text`, `textFaint`, `textMuted`, `textSecondary`, `warning`. Omitted roles keep their defaults. Theme changes do not re-engrave.
|
|
317
366
|
- **Bar:** an ordered `BarItemId[]` replacing the full/minimal header controls. Available ids: `play`, `stop`, `tempo`, `transpose`, `keyFlip`, `bars`, `viewToggle`, `tab`, `zoom`, `mixer`, `openFile`, `instrumentSwitch`, `octaveTool`, `options`, `noteNames`, `counts`, `chordSymbols`, `pitchColours`, `pdf`. Duplicates and unknown ids are ignored. Explicit `controls` values of `false` still win; unavailable controls are omitted. A bar with no renderable items restores the preset defaults. `chrome="none"` does not show a skin bar. Without `bar`, the preset header is unchanged.
|
|
318
367
|
- **Drawer:** `options` opens the drawer. Enabled stop, view, bars, zoom, mixer, file and PDF actions omitted from a composed bar appear in its Playback and view panel. Annotation tiles stay in sync with the drawer and saved preferences; `chordSymbols` toggles authored and inferred symbols together.
|
|
@@ -338,11 +387,17 @@ The pitch-colour tile appears in the bar/drawer only when a palette is supplied.
|
|
|
338
387
|
| Paper (`pdfPaper`) | `'a4'` | A4, A3, Letter or Legal. Remembered globally in the reader's preferences when persistence is on, not per score. It is not a `settings` prop. |
|
|
339
388
|
| Bars per line | `4` | Local to the dialog; 1–8, reset on reopen. Does not read or change on-screen `settings.barsPerLine`. Lines may use fewer bars to fit. |
|
|
340
389
|
|
|
341
|
-
The dialog reports “Checking fit…” while measuring, then how many lines need fewer bars. **Save** can proceed without waiting for that check and captures the current view
|
|
390
|
+
The dialog reports “Checking fit…” while measuring, then how many lines need fewer bars. **Save** can proceed without waiting for that check and captures the current view. While it builds, the dialog shows “Building PDF… page N of M”; **Cancel** (or Escape) stops it, discards the work and downloads nothing. Save downloads `<title>.pdf`, with the title normalized to lowercase hyphen-separated text (or `score.pdf` when no usable title exists). Export does not interrupt playback or change the on-screen layout/scroll position.
|
|
391
|
+
|
|
392
|
+
There is no imperative export call: the button, drawer action, skin bar item and `controls.pdf` are the whole public surface, and `pdfPaper` is the only preference.
|
|
342
393
|
|
|
343
394
|
The PDF has vector notation, embedded music/text fonts, title/credits and page numbers. It carries the current transposition/instrument view, visible staves, notation/TAB choice, note names, counts, chord symbols and active pitch colours, including annotation sizes. Screen controls, playhead and selection marks are excluded. Serif/Mono annotation text uses Poppins in PDF; unsupported text glyphs report an error instead of silently disappearing.
|
|
344
395
|
|
|
345
|
-
|
|
396
|
+
**Large scores.** Engraving runs in its own worker. Drawing needs a DOM, so it runs on the page, one page at a time and in slices of a few dozen elements, yielding to the browser between slices, so the page stays responsive and playback keeps running. On a quiet developer machine the longest main-thread block while exporting a 30-part score (one system per page) was about 200 ms in Chromium and Firefox and about 240 ms in WebKit; export time is dominated by engraving, which takes minutes for hundreds of bars (it is not faster, only non-blocking). Cancel (or Escape) is checked at every slice, so it takes effect within one slice, tens of milliseconds, including while the engraving worker is starting; it terminates the worker and removes the temporary fonts and offscreen page the export added. These figures are from the package's validation fixture, not a guarantee on slow devices.
|
|
397
|
+
|
|
398
|
+
**Browsers.** Chromium, Firefox and Safari/WebKit are covered by the package's validation. If the dialog's lazy chunk fails to download (for example after a network drop), the loader retries once through a cache-busted address of the same chunk, so reopening the dialog or using **Reload page** recovers. This is supported on Vite and native-ESM hosts, where the chunk keeps its name and the retry is a plain `import()` (WebKit otherwise answers every later attempt from the failed `modulepreload` Vite adds, for the life of the tab). On other bundlers such as Webpack, recovery is best effort: the retry is skipped when the chunk's name cannot be found, and the dialog shows its usual error with a **Reload page** button.
|
|
399
|
+
|
|
400
|
+
The dialog loads only when opened; the composer, `jspdf` and `svg2pdf.js` load on Save. Those libraries are installed runtime dependencies. The PDF implementation measured about +1.5 KB gzip for initial package JS+CSS; the larger PDF engine/font payload is deferred.
|
|
346
401
|
|
|
347
402
|
### String TAB
|
|
348
403
|
|
|
@@ -40,45 +40,46 @@ async function u({ score: t, prefs: r, hiddenPartIds: a, channel: o, pitchColour
|
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
k
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
var d = (e) => e && e.pages ? `Building PDF… ${e.phase === "composing" ? "drawing" : "preparing"} page ${e.page} of ${e.pages}` : "Building PDF…";
|
|
44
|
+
function f({ paper: e, onPaper: n, snapshot: i, revision: f, font: p, verovioBaseUrl: m, onClose: h }) {
|
|
45
|
+
let [g, _] = l(4), [v, y] = l(!1), [b, x] = l("4"), S = () => {
|
|
46
|
+
let e = Number(b), n = b.trim() && Number.isFinite(e) ? t(e) : g;
|
|
47
|
+
return _(n), x(String(n)), n;
|
|
48
|
+
}, [C, w] = l("Checking fit…"), [T, E] = l(""), [D, O] = l(null), k = c(null), A = c(null), j = c(null), M = c(i);
|
|
49
|
+
M.current = i;
|
|
50
|
+
let N = c(h);
|
|
51
|
+
N.current = h;
|
|
52
|
+
let P = m ? {
|
|
53
|
+
module: new URL("verovio-module.mjs", new URL(m.replace(/\/?$/, "/"), document.baseURI)).href,
|
|
54
|
+
toolkit: new URL("verovio.mjs", new URL(m.replace(/\/?$/, "/"), document.baseURI)).href
|
|
54
55
|
} : void 0;
|
|
55
56
|
s(() => {
|
|
56
57
|
let e = document.activeElement;
|
|
57
|
-
|
|
58
|
-
let t =
|
|
58
|
+
k.current?.querySelector("select")?.focus({ preventScroll: !0 });
|
|
59
|
+
let t = k.current?.closest(".pepperscore");
|
|
59
60
|
return () => {
|
|
60
|
-
|
|
61
|
+
A.current?.abort(), j.current?.abort();
|
|
61
62
|
let n = document.activeElement;
|
|
62
63
|
e?.isConnected && (n === document.body || n && t?.contains(n)) && e.focus({ preventScroll: !0 });
|
|
63
64
|
};
|
|
64
65
|
}, []);
|
|
65
|
-
let
|
|
66
|
+
let F = (e) => {
|
|
66
67
|
if (e.key === "Escape") {
|
|
67
|
-
e.preventDefault(), e.stopPropagation(),
|
|
68
|
+
e.preventDefault(), e.stopPropagation(), h();
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
71
|
if (e.key !== "Tab") return;
|
|
71
|
-
let t = Array.from(
|
|
72
|
+
let t = Array.from(k.current?.querySelectorAll("button:not(:disabled),select:not(:disabled),input:not(:disabled),a[href]") ?? []), n = t[0], r = t.at(-1);
|
|
72
73
|
e.shiftKey && document.activeElement === n ? (e.preventDefault(), r?.focus()) : !e.shiftKey && document.activeElement === r && (e.preventDefault(), n?.focus());
|
|
73
74
|
};
|
|
74
75
|
s(() => {
|
|
75
|
-
if (
|
|
76
|
+
if (v) return;
|
|
76
77
|
let t = new AbortController();
|
|
77
|
-
return
|
|
78
|
+
return A.current = t, w("Checking fit…"), (async () => {
|
|
78
79
|
try {
|
|
79
|
-
let n = await u(
|
|
80
|
+
let n = await u(M.current());
|
|
80
81
|
t.signal.throwIfAborted();
|
|
81
|
-
let { engravePages: i } = await import("./engrave-
|
|
82
|
+
let { engravePages: i } = await import("./engrave-DKNPmehA.js"), a = n.score.parts.filter((e) => !n.visiblePartIds || n.visiblePartIds.includes(e.id)), o = r(a, n.annotations ?? {}), s = [
|
|
82
83
|
n.score.title,
|
|
83
84
|
n.score.subtitle,
|
|
84
85
|
n.score.composer,
|
|
@@ -94,45 +95,48 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
94
95
|
}
|
|
95
96
|
}, {
|
|
96
97
|
paper: e,
|
|
97
|
-
maxBars:
|
|
98
|
-
font:
|
|
99
|
-
files:
|
|
98
|
+
maxBars: g,
|
|
99
|
+
font: p,
|
|
100
|
+
files: P,
|
|
100
101
|
spacingStaff: o.spacingStaff,
|
|
101
102
|
annotationRoom: o.laneHeight,
|
|
102
103
|
headingMm: s.length ? 8 + s.length * 4 : 0
|
|
103
104
|
}, t.signal);
|
|
104
105
|
if (t.signal.aborted) return;
|
|
105
|
-
let l = c.lines.filter((e) => e.count <
|
|
106
|
-
|
|
106
|
+
let l = c.lines.filter((e) => e.count < g).length;
|
|
107
|
+
w(`${l} ${l === 1 ? "line will" : "lines will"} have fewer than ${g} bars on ${e.toUpperCase()}.${c.shrunk ? " A tall system is reduced to fit the page." : ""}`);
|
|
107
108
|
} catch (e) {
|
|
108
|
-
t.signal.aborted ||
|
|
109
|
+
t.signal.aborted || w(`Fit check unavailable: ${e instanceof Error ? e.message : String(e)}. You can still Save.`);
|
|
109
110
|
}
|
|
110
111
|
})(), () => t.abort();
|
|
111
112
|
}, [
|
|
112
113
|
e,
|
|
113
|
-
|
|
114
|
-
f,
|
|
114
|
+
g,
|
|
115
115
|
p,
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
m,
|
|
117
|
+
f,
|
|
118
|
+
v
|
|
118
119
|
]);
|
|
119
|
-
let
|
|
120
|
-
let t =
|
|
121
|
-
|
|
120
|
+
let I = async () => {
|
|
121
|
+
let t = S();
|
|
122
|
+
k.current?.querySelector(".pdf-cancel")?.focus({ preventScroll: !0 }), A.current?.abort();
|
|
122
123
|
let n = new AbortController();
|
|
123
|
-
|
|
124
|
+
j.current = n, y(!0), E(""), O(null);
|
|
124
125
|
try {
|
|
125
126
|
let r = window.frameElement;
|
|
126
127
|
if (r?.hasAttribute("sandbox") && !r.sandbox.contains("allow-downloads")) throw Error("Downloads are blocked by this embed. Open the score outside the embed or enable downloads.");
|
|
127
|
-
let i = await u(
|
|
128
|
+
let i = await u(M.current());
|
|
128
129
|
n.signal.throwIfAborted();
|
|
129
|
-
let { exportScore: a } = await import("./export-
|
|
130
|
+
let { exportScore: a } = await import("./export-Dg2CkokP.js"), o = await a({
|
|
130
131
|
...i,
|
|
131
132
|
options: {
|
|
132
133
|
paper: e,
|
|
133
134
|
maxBars: t,
|
|
134
|
-
font:
|
|
135
|
-
files:
|
|
135
|
+
font: p,
|
|
136
|
+
files: P
|
|
137
|
+
},
|
|
138
|
+
onProgress: (e) => {
|
|
139
|
+
n.signal.aborted || O(e);
|
|
136
140
|
}
|
|
137
141
|
}, n.signal);
|
|
138
142
|
n.signal.throwIfAborted();
|
|
@@ -143,26 +147,26 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
143
147
|
} finally {
|
|
144
148
|
c.remove(), window.setTimeout(() => URL.revokeObjectURL(s), 6e4);
|
|
145
149
|
}
|
|
146
|
-
|
|
150
|
+
N.current();
|
|
147
151
|
} catch (e) {
|
|
148
|
-
n.signal.aborted ||
|
|
152
|
+
n.signal.aborted || E(e instanceof Error ? e.message : String(e));
|
|
149
153
|
} finally {
|
|
150
|
-
n.signal.aborted ||
|
|
154
|
+
n.signal.aborted || y(!1);
|
|
151
155
|
}
|
|
152
156
|
};
|
|
153
157
|
return /* @__PURE__ */ a("div", {
|
|
154
158
|
className: "pdf-backdrop",
|
|
155
159
|
onMouseDown: (e) => {
|
|
156
|
-
e.target === e.currentTarget &&
|
|
160
|
+
e.target === e.currentTarget && h();
|
|
157
161
|
},
|
|
158
162
|
children: /* @__PURE__ */ o("section", {
|
|
159
|
-
ref:
|
|
163
|
+
ref: k,
|
|
160
164
|
className: "pdf-panel",
|
|
161
165
|
role: "dialog",
|
|
162
166
|
"aria-modal": "true",
|
|
163
167
|
"aria-label": "Export PDF",
|
|
164
|
-
"aria-busy":
|
|
165
|
-
onKeyDown:
|
|
168
|
+
"aria-busy": v,
|
|
169
|
+
onKeyDown: F,
|
|
166
170
|
children: [
|
|
167
171
|
/* @__PURE__ */ a("h2", { children: "Export PDF" }),
|
|
168
172
|
/* @__PURE__ */ o("label", {
|
|
@@ -170,7 +174,7 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
170
174
|
children: ["Paper size", /* @__PURE__ */ o("select", {
|
|
171
175
|
className: "pdf-paper",
|
|
172
176
|
value: e,
|
|
173
|
-
disabled:
|
|
177
|
+
disabled: v,
|
|
174
178
|
onChange: (e) => n(e.target.value),
|
|
175
179
|
children: [
|
|
176
180
|
/* @__PURE__ */ a("option", {
|
|
@@ -199,21 +203,21 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
199
203
|
type: "number",
|
|
200
204
|
min: 1,
|
|
201
205
|
max: 8,
|
|
202
|
-
value:
|
|
203
|
-
disabled:
|
|
204
|
-
onChange: (e) =>
|
|
205
|
-
onBlur:
|
|
206
|
+
value: b,
|
|
207
|
+
disabled: v,
|
|
208
|
+
onChange: (e) => x(e.target.value),
|
|
209
|
+
onBlur: S
|
|
206
210
|
})]
|
|
207
211
|
}),
|
|
208
212
|
/* @__PURE__ */ a("p", {
|
|
209
213
|
className: "pdf-fit",
|
|
210
214
|
role: "status",
|
|
211
|
-
children:
|
|
215
|
+
children: v ? d(D) : C
|
|
212
216
|
}),
|
|
213
|
-
|
|
217
|
+
T && /* @__PURE__ */ a("p", {
|
|
214
218
|
className: "pdf-error",
|
|
215
219
|
role: "alert",
|
|
216
|
-
children:
|
|
220
|
+
children: T
|
|
217
221
|
}),
|
|
218
222
|
/* @__PURE__ */ o("div", {
|
|
219
223
|
className: "pdf-actions",
|
|
@@ -221,10 +225,10 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
221
225
|
/* @__PURE__ */ a("button", {
|
|
222
226
|
type: "button",
|
|
223
227
|
className: "pdf-cancel",
|
|
224
|
-
onClick:
|
|
228
|
+
onClick: h,
|
|
225
229
|
children: "Cancel"
|
|
226
230
|
}),
|
|
227
|
-
|
|
231
|
+
T && /* @__PURE__ */ a("button", {
|
|
228
232
|
type: "button",
|
|
229
233
|
className: "pdf-reload",
|
|
230
234
|
onClick: () => window.history.go(0),
|
|
@@ -233,8 +237,8 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
233
237
|
/* @__PURE__ */ a("button", {
|
|
234
238
|
type: "button",
|
|
235
239
|
className: "pdf-save",
|
|
236
|
-
disabled:
|
|
237
|
-
onClick: () => void
|
|
240
|
+
disabled: v,
|
|
241
|
+
onClick: () => void I(),
|
|
238
242
|
children: "Save"
|
|
239
243
|
})
|
|
240
244
|
]
|
|
@@ -244,6 +248,6 @@ function d({ paper: e, onPaper: n, snapshot: i, revision: d, font: f, verovioBas
|
|
|
244
248
|
});
|
|
245
249
|
}
|
|
246
250
|
//#endregion
|
|
247
|
-
export {
|
|
251
|
+
export { f as default };
|
|
248
252
|
|
|
249
|
-
//# sourceMappingURL=PdfOverlay-
|
|
253
|
+
//# sourceMappingURL=PdfOverlay-DUgY9mW-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PdfOverlay-DUgY9mW-.js","names":["verovioBaseline","clampBarsPerLine","planStaves","ANNOTATION_SCALE","prepareSnapshot","saved","score","hidden","hiddenPartIds","info","channel","planned","part","buildingText","progress","PdfOverlay","bars","setBars","useState","busy","setBusy","barsDraft","setBarsDraft","commitBars","number","next","fit","setFit","error","setError","setProgress","panel","useRef","measure","save","snapshotRef","snapshot","closeRef","onClose","files","verovioBaseUrl","previous","owner","active","onKeyDown","event","items","first","last","controller","request","visible","layout","annotationLayout","metadata","result","engravePages","reduced","line","paper","failure","font","revision","download","maxBars","frame","blob","exportScore","update","url","anchor","onPaper"],"sources":["../src/components/PdfOverlay.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { ExportProgress, ExportRequest } from '../lib/pdf/export';\nimport type { Score } from '../lib/score';\nimport type { PitchColourPaint } from '../lib/pitch-colours';\nimport { verovioBaseline } from '../lib/verovio-runtime';\nimport { annotationLayout, planStaves } from '../lib/render';\nimport { ANNOTATION_SCALE, BARS_PER_LINE_MAX, clampBarsPerLine, type Prefs } from '../lib/prefs';\n\ntype Snapshot = Omit<ExportRequest, 'options'>;\nexport type PdfCapture = { score: Score; prefs: Prefs; hiddenPartIds: string[]; channel: number; pitchColours?: PitchColourPaint };\nasync function prepareSnapshot({ score, prefs: saved, hiddenPartIds, channel, pitchColours }: PdfCapture): Promise<Snapshot> {\n if (!score.document) throw new Error('This score has no engraving document.');\n const hidden = new Set(hiddenPartIds);\n const info = await verovioBaseline(score.document, channel);\n const planned = planStaves(info, score, hidden, { view: saved.tabView, tuning: saved.tabTuning, capo: saved.capo });\n return { score, input: { data: score.document.engravingText, view: { breakEvery: 0, hiddenStaves: planned.hiddenStaves,\n spelling: planned.spelling, chordSymbols: saved.chordSymbols, ...(planned.tabPlan.spec ? { tab: planned.tabPlan.spec } : {}) } },\n visiblePartIds: planned.visibleParts.map(part => part.id), tabAliases: planned.tabAlias, tabStaves: planned.tabStaves, pitchColours,\n annotations: { showNames: saved.names, showCounts: saved.counts, subdivisions: saved.subdivisions,\n showChordSymbols: saved.chordSymbols, inferChordSymbols: saved.inferChordSymbols,\n scale: { names: ANNOTATION_SCALE[saved.namesSize], chordSymbols: ANNOTATION_SCALE[saved.chordSymbolsSize], counts: ANNOTATION_SCALE[saved.countsSize], subdivisions: ANNOTATION_SCALE[saved.subdivisionsSize] } } };\n}\n/** Engraving runs in a worker, then each page is drawn in turn: say which. */\nconst buildingText = (progress: ExportProgress | null) => progress && progress.pages ? `Building PDF… ${progress.phase === 'composing' ? 'drawing' : 'preparing'} page ${progress.page} of ${progress.pages}` : 'Building PDF…';\nexport default function PdfOverlay({ paper, onPaper, snapshot, revision, font, verovioBaseUrl, onClose }: {\n paper: Prefs['pdfPaper']; onPaper: (paper: Prefs['pdfPaper']) => void;\n snapshot: () => PdfCapture; revision: unknown; font: string; verovioBaseUrl?: string; onClose: () => void;\n}) {\n const [bars, setBars] = useState(4), [busy, setBusy] = useState(false);\n const [barsDraft, setBarsDraft] = useState('4');\n const commitBars = () => {\n const number = Number(barsDraft);\n const next = barsDraft.trim() && Number.isFinite(number) ? clampBarsPerLine(number) : bars;\n setBars(next); setBarsDraft(String(next));\n return next;\n };\n const [fit, setFit] = useState('Checking fit…'), [error, setError] = useState(''), [progress, setProgress] = useState<ExportProgress | null>(null);\n const panel = useRef<HTMLElement>(null), measure = useRef<AbortController | null>(null), save = useRef<AbortController | null>(null);\n const snapshotRef = useRef(snapshot); snapshotRef.current = snapshot;\n const closeRef = useRef(onClose); closeRef.current = onClose;\n const files = verovioBaseUrl ? { module: new URL('verovio-module.mjs', new URL(verovioBaseUrl.replace(/\\/?$/, '/'), document.baseURI)).href,\n toolkit: new URL('verovio.mjs', new URL(verovioBaseUrl.replace(/\\/?$/, '/'), document.baseURI)).href } : undefined;\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n panel.current?.querySelector<HTMLElement>('select')?.focus({ preventScroll: true });\n const owner = panel.current?.closest('.pepperscore');\n return () => {\n measure.current?.abort(); save.current?.abort();\n const active = document.activeElement;\n if (previous?.isConnected && (active === document.body || (active && owner?.contains(active)))) previous.focus({ preventScroll: true });\n };\n }, []);\n const onKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n if (event.key === 'Escape') { event.preventDefault(); event.stopPropagation(); onClose(); return; }\n if (event.key !== 'Tab') return;\n const items = Array.from(panel.current?.querySelectorAll<HTMLElement>('button:not(:disabled),select:not(:disabled),input:not(:disabled),a[href]') ?? []);\n const first = items[0], last = items.at(-1);\n if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last?.focus(); }\n else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first?.focus(); }\n };\n useEffect(() => {\n if (busy) return;\n const controller = new AbortController(); measure.current = controller;\n setFit('Checking fit…');\n void (async () => {\n try {\n const request = await prepareSnapshot(snapshotRef.current());\n controller.signal.throwIfAborted();\n const { engravePages } = await import('../lib/pdf/engrave');\n const visible = request.score.parts.filter(part => !request.visiblePartIds || request.visiblePartIds.includes(part.id));\n const layout = annotationLayout(visible, request.annotations ?? {});\n const metadata = [request.score.title, request.score.subtitle, request.score.composer, ...(request.score.credits ?? [])].filter(Boolean);\n const result = await engravePages({ ...request.input, view: { breakEvery: 0, hiddenStaves: [], chordSymbols: false, ...request.input.view, harmSize: layout.harmSize } },\n { paper, maxBars: bars, font, files, spacingStaff: layout.spacingStaff, annotationRoom: layout.laneHeight, headingMm: metadata.length ? 8 + metadata.length * 4 : 0 }, controller.signal);\n if (controller.signal.aborted) return;\n const reduced = result.lines.filter(line => line.count < bars).length;\n setFit(`${reduced} ${reduced === 1 ? 'line will' : 'lines will'} have fewer than ${bars} bars on ${paper.toUpperCase()}.${result.shrunk ? ' A tall system is reduced to fit the page.' : ''}`);\n } catch (failure) { if (!controller.signal.aborted) setFit(`Fit check unavailable: ${failure instanceof Error ? failure.message : String(failure)}. You can still Save.`); }\n })();\n return () => controller.abort();\n }, [paper, bars, font, verovioBaseUrl, revision, busy]);\n const download = async () => {\n const maxBars = commitBars();\n panel.current?.querySelector<HTMLButtonElement>('.pdf-cancel')?.focus({ preventScroll: true });\n measure.current?.abort();\n const controller = new AbortController(); save.current = controller;\n setBusy(true); setError(''); setProgress(null);\n try {\n // frameElement belongs to the parent's realm; instanceof against this window's\n // HTMLIFrameElement is false even for a same-origin embed.\n const frame = window.frameElement as HTMLIFrameElement | null;\n if (frame?.hasAttribute('sandbox') && !frame.sandbox.contains('allow-downloads')) throw new Error('Downloads are blocked by this embed. Open the score outside the embed or enable downloads.');\n const request = await prepareSnapshot(snapshotRef.current());\n controller.signal.throwIfAborted();\n const { exportScore } = await import('../lib/pdf/export');\n const blob = await exportScore({ ...request, options: { paper, maxBars, font, files }, onProgress: update => { if (!controller.signal.aborted) setProgress(update); } }, controller.signal);\n controller.signal.throwIfAborted();\n const url = URL.createObjectURL(blob), anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `${request.score.title?.normalize('NFKD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') || 'score'}.pdf`;\n document.body.append(anchor);\n try { anchor.click(); } finally { anchor.remove(); window.setTimeout(() => URL.revokeObjectURL(url), 60000); }\n closeRef.current();\n } catch (failure) { if (!controller.signal.aborted) setError(failure instanceof Error ? failure.message : String(failure)); }\n finally { if (!controller.signal.aborted) setBusy(false); }\n };\n return <div className=\"pdf-backdrop\" onMouseDown={event => { if (event.target === event.currentTarget) onClose(); }}>\n <section ref={panel} className=\"pdf-panel\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Export PDF\" aria-busy={busy} onKeyDown={onKeyDown}>\n <h2>Export PDF</h2>\n <label className=\"pdf-field\">Paper size<select className=\"pdf-paper\" value={paper} disabled={busy} onChange={event => onPaper(event.target.value as Prefs['pdfPaper'])}>\n <option value=\"a4\">A4</option><option value=\"a3\">A3</option><option value=\"letter\">Letter</option><option value=\"legal\">Legal</option>\n </select></label>\n <label className=\"pdf-field\">Bars per line<input className=\"pdf-bars\" type=\"number\" min={1} max={BARS_PER_LINE_MAX} value={barsDraft} disabled={busy} onChange={event => setBarsDraft(event.target.value)} onBlur={commitBars}/></label>\n <p className=\"pdf-fit\" role=\"status\">{busy ? buildingText(progress) : fit}</p>\n {error && <p className=\"pdf-error\" role=\"alert\">{error}</p>}\n <div className=\"pdf-actions\"><button type=\"button\" className=\"pdf-cancel\" onClick={onClose}>Cancel</button>{error && <button type=\"button\" className=\"pdf-reload\" onClick={() => window.history.go(0)}>Reload page</button>}<button type=\"button\" className=\"pdf-save\" disabled={busy} onClick={() => void download()}>Save</button></div>\n </section>\n </div>;\n}\n"],"mappings":"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"}
|