@barmark/render 0.7.3 → 0.9.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.
- package/CHANGELOG.md +36 -1
- package/dist/html.js +97 -9
- package/dist/model.js +16 -6
- package/dist/speech.js +32 -3
- package/dist/styles.js +15 -0
- package/dist/types.d.ts +15 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,39 @@ package uses [semantic versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.0] - 2026-11-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Rehearsal marks** draw as a boxed label at the start of their bar
|
|
14
|
+
(`part="mark"`), spoken "rehearsal B". Bar numbers skip them.
|
|
15
|
+
- **Words on a bar** draw small, above the slot they precede, or at the
|
|
16
|
+
bar's end (`part="words"`), and are spoken in place: "bar 1: “solo” D,
|
|
17
|
+
A".
|
|
18
|
+
- **Accents and hits** draw `>` or `^` above the chord
|
|
19
|
+
(`data-articulation`), spoken "accented D" and "D short hit".
|
|
20
|
+
- **Pushes** draw `‹` (an eighth) or `«` (a sixteenth) before the slot
|
|
21
|
+
(`data-push`), spoken "D pushed" and "D pushed a sixteenth".
|
|
22
|
+
- **Omissions** are spoken "C seven no third".
|
|
23
|
+
- **Voicings** draw the chord's name; a screen reader hears the notes:
|
|
24
|
+
"C major seven voiced E3 G3 B3 C4".
|
|
25
|
+
|
|
26
|
+
## [0.8.0] - 2026-11-03
|
|
27
|
+
|
|
28
|
+
Requires `barmark` 0.11.0.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **Depends on `barmark` `^0.11.0`** (was `^0.10.0`).
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **Endings:** an ending row has a bracket over its bars and a `1.`, `2.`
|
|
37
|
+
or `1.–3.` label; screen readers hear "ending for pass 1".
|
|
38
|
+
- **Directions:** Segno and Coda as their signs, To Coda with the coda
|
|
39
|
+
sign, Fine and the jumps as words. Each is spoken ("dal segno al
|
|
40
|
+
coda"). Bar numbers skip them, since they are not bars.
|
|
41
|
+
|
|
9
42
|
## [0.7.3] - 2026-11-03
|
|
10
43
|
|
|
11
44
|
Requires `barmark` 0.10.0. No change to the renderer's code: a page that
|
|
@@ -223,7 +256,9 @@ that needs no script or as the `<barmark-chart>` web component. Requires
|
|
|
223
256
|
zoom without sideways scrolling, the element renders and re-renders, and
|
|
224
257
|
a screen reader hears "bar 9: D". Run them with `bun run test:browser`.
|
|
225
258
|
|
|
226
|
-
[Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.
|
|
259
|
+
[Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.9.0...HEAD
|
|
260
|
+
[0.9.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.8.0...render-v0.9.0
|
|
261
|
+
[0.8.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.7.3...render-v0.8.0
|
|
227
262
|
[0.7.3]: https://github.com/kevinpeckham/barmark/compare/render-v0.7.2...render-v0.7.3
|
|
228
263
|
[0.7.2]: https://github.com/kevinpeckham/barmark/compare/render-v0.7.1...render-v0.7.2
|
|
229
264
|
[0.7.1]: https://github.com/kevinpeckham/barmark/compare/render-v0.7.0...render-v0.7.1
|
package/dist/html.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chart model as HTML: a <barmark-chart> holding a declarative shadow
|
|
3
|
+
* root, or the bare markup for pages that bring the stylesheet themselves.
|
|
4
|
+
* Every string from the chart is escaped; every bar carries its spoken name.
|
|
5
|
+
*/
|
|
6
|
+
import { printDirection, printPasses, } from "barmark";
|
|
1
7
|
import { glyphs, NUMERALS, splitPitch, typesetChord, typesetNumeral, } from "./chord.js";
|
|
2
8
|
import { escapeHtml } from "./escape.js";
|
|
3
9
|
import { fontFaceCss } from "./font.js";
|
|
@@ -91,11 +97,20 @@ function fitWidth(block) {
|
|
|
91
97
|
return FIT_STEPS.find((step) => step >= total) ?? FIT_STEPS.at(-1) ?? 0;
|
|
92
98
|
}
|
|
93
99
|
/** Em per bar: padding, plus each slot's text and the gap after it. */
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
function cellWidth(cell) {
|
|
101
|
+
if (cell.kind === "direction")
|
|
102
|
+
return 1 + 0.55 * printDirection(cell.direction).length;
|
|
103
|
+
if (cell.kind === "rehearsal")
|
|
104
|
+
return 1.4 + 0.6 * cell.label.length;
|
|
105
|
+
if (cell.kind === "fermata")
|
|
106
|
+
return MIN_BAR;
|
|
107
|
+
return Math.max(MIN_BAR, 1.5 + (cell.meter ? 0.8 : 0) + cell.slots.reduce((w, s) => w + slotWidth(s), 0));
|
|
108
|
+
}
|
|
109
|
+
/** Roughly: 0.65em a baseline character, 0.4em a superscript one, 0.6em between slots, 0.45em a push mark. */
|
|
98
110
|
function slotWidth(slot) {
|
|
111
|
+
return ("push" in slot && slot.push ? 0.45 : 0) + unmarkedWidth(slot);
|
|
112
|
+
}
|
|
113
|
+
function unmarkedWidth(slot) {
|
|
99
114
|
if (slot.kind === "group")
|
|
100
115
|
return slot.members.reduce((w, m) => w + slotWidth(m), 0.2);
|
|
101
116
|
if (slot.kind !== "chord")
|
|
@@ -145,15 +160,28 @@ function rowHtml(row, columns, settings) {
|
|
|
145
160
|
? `<span class="bm-bar-number" part="bar-number" aria-hidden="true">${row.barNumber ?? ""}</span>`
|
|
146
161
|
: "";
|
|
147
162
|
let n = row.barNumber;
|
|
163
|
+
const label = row.ending ? endingLabel(row.ending) : null;
|
|
148
164
|
const cells = row.cells.map((cell, i) => {
|
|
149
|
-
const
|
|
165
|
+
const ending = i === 0 && label ? ` data-ending="${label.shown}"` : "";
|
|
166
|
+
const html = cellHtml(cell, n, repeatMarks(row, i) + ending, settings.numerals);
|
|
150
167
|
if (n !== null && cell.kind === "slots")
|
|
151
168
|
n++;
|
|
152
169
|
return html;
|
|
153
170
|
});
|
|
154
171
|
const empty = `<span class="bm-empty" aria-hidden="true"></span>`.repeat(Math.max(0, columns - row.cells.length));
|
|
155
172
|
const opens = row.opens || typeof row.repeat === "number" || row.repeat === "open";
|
|
156
|
-
|
|
173
|
+
const ending = label ? `<span class="bm-sr">${label.spoken}</span>` : "";
|
|
174
|
+
return `<div class="bm-row${label ? " bm-ending" : ""}">${opens ? `<span class="bm-sr">repeat start</span>` : ""}${ending}${number}${cells.join("")}${empty}<span class="bm-repeat">${rowRepeatHtml(row)}</span></div>`;
|
|
175
|
+
}
|
|
176
|
+
/** An ending's passes as printed, "1.", "1.–3.", "1., 3.", and spoken, "ending for passes 1 to 3". */
|
|
177
|
+
function endingLabel(passes) {
|
|
178
|
+
const parts = printPasses(passes)
|
|
179
|
+
.split(",")
|
|
180
|
+
.map((part) => part.split("-"));
|
|
181
|
+
const shown = parts.map((p) => p.map((n) => `${n}.`).join("–")).join(", ");
|
|
182
|
+
const said = parts.map((p) => p.join(" to "));
|
|
183
|
+
const list = said.length > 1 ? `${said.slice(0, -1).join(", ")} and ${said.at(-1)}` : said[0];
|
|
184
|
+
return { shown, spoken: `ending for pass${passes.length > 1 ? "es" : ""} ${list}` };
|
|
157
185
|
}
|
|
158
186
|
/** Repeat bar lines: ‖: on a row's first bar, :‖ on its last. */
|
|
159
187
|
function repeatMarks(row, i) {
|
|
@@ -170,17 +198,77 @@ function rowRepeatHtml(row) {
|
|
|
170
198
|
function cellHtml(cell, barNumber, marks, numerals) {
|
|
171
199
|
const view = numerals && cell.kind === "slots" && cell.key ? { style: numerals, key: cell.key } : null;
|
|
172
200
|
const spoken = `<span class="bm-sr">${escapeHtml(speakBar(barNumber, cell, view !== null))}</span>`;
|
|
173
|
-
if (cell.kind
|
|
174
|
-
return
|
|
201
|
+
if (cell.kind !== "slots")
|
|
202
|
+
return betweenBarsHtml(cell, spoken, marks);
|
|
175
203
|
const meter = cell.meter
|
|
176
204
|
? `<span class="bm-meter"><span>${cell.meter.n}</span><span>${cell.meter.d}</span></span>`
|
|
177
205
|
: "";
|
|
178
206
|
const cut = cell.cut ? `<span class="bm-cut">cut</span>` : "";
|
|
179
207
|
const change = cell.tempoChange ? ` data-tempo-change="${cell.tempoChange}"` : "";
|
|
180
|
-
|
|
208
|
+
const [slots, end] = slotsWithWords(cell, view);
|
|
209
|
+
return `<div class="bm-bar" part="bar"${cell.cut ? " data-cut" : ""}${change}${marks}>${spoken}<span class="bm-bar-body" aria-hidden="true">${meter}${slots.join("")}${cut}</span>${end}</div>`;
|
|
181
210
|
}
|
|
211
|
+
/** A bar's slots, each under any words before it, and the words after the last slot. */
|
|
212
|
+
function slotsWithWords(cell, view) {
|
|
213
|
+
const words = (at) => (cell.words ?? [])
|
|
214
|
+
.filter((w) => w.at === at)
|
|
215
|
+
.map((w) => escapeHtml(w.text))
|
|
216
|
+
.join(" ");
|
|
217
|
+
const slots = cell.slots.map((s, i) => {
|
|
218
|
+
const said = words(i);
|
|
219
|
+
const html = slotHtml(s, view);
|
|
220
|
+
return said
|
|
221
|
+
? `<span class="bm-worded"><span class="bm-words" part="words">${said}</span>${html}</span>`
|
|
222
|
+
: html;
|
|
223
|
+
});
|
|
224
|
+
const after = words(cell.slots.length);
|
|
225
|
+
return [
|
|
226
|
+
slots,
|
|
227
|
+
after
|
|
228
|
+
? `<span class="bm-words bm-words-end" part="words" aria-hidden="true">${after}</span>`
|
|
229
|
+
: "",
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
/** A fermata, a rehearsal mark or a direction: a place between bars, drawn as its sign. */
|
|
233
|
+
function betweenBarsHtml(cell, spoken, marks) {
|
|
234
|
+
if (cell.kind === "fermata")
|
|
235
|
+
return `<div class="bm-bar bm-fermata" part="bar"${marks}>${spoken}<span aria-hidden="true">${FERMATA}</span></div>`;
|
|
236
|
+
if (cell.kind === "rehearsal")
|
|
237
|
+
return `<div class="bm-bar bm-mark" part="mark"${marks}>${spoken}<span class="bm-box" aria-hidden="true">${escapeHtml(cell.label)}</span></div>`;
|
|
238
|
+
return `<div class="bm-bar bm-direction" part="direction" data-direction="${cell.direction.kind}"${marks}>${spoken}<span aria-hidden="true">${directionGlyph(cell.direction)}</span></div>`;
|
|
239
|
+
}
|
|
240
|
+
/** Segno and coda as the score's signs; To Coda and the jumps as their words. */
|
|
241
|
+
function directionGlyph(d) {
|
|
242
|
+
if (d.kind === "segno")
|
|
243
|
+
return SEGNO;
|
|
244
|
+
if (d.kind === "coda")
|
|
245
|
+
return CODA;
|
|
246
|
+
const words = `<span class="bm-direction-words">${escapeHtml(printDirection(d))}</span>`;
|
|
247
|
+
return d.kind === "to coda" ? `${words} ${CODA}` : words;
|
|
248
|
+
}
|
|
249
|
+
const SEGNO = `<svg class="bm-sign" viewBox="0 0 20 28" width="0.9em" height="1.25em"><path d="M14.5 6.5c-1-3-6.5-3.5-7.5-.5s3 4.5 4.5 5.5 5 3.5 3 7-7.5 2-8-1" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/><path d="M4 24 16 3" stroke="currentColor" stroke-width="1.6"/><circle cx="4.5" cy="13" r="1.6" fill="currentColor"/><circle cx="15.5" cy="15" r="1.6" fill="currentColor"/></svg>`;
|
|
250
|
+
const CODA = `<svg class="bm-sign" viewBox="0 0 24 24" width="1em" height="1em"><circle cx="12" cy="12" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M12 1v22M1 12h22" stroke="currentColor" stroke-width="1.6"/></svg>`;
|
|
182
251
|
const FERMATA = `<svg class="bm-fermata-glyph" viewBox="0 0 24 14" width="1.4em" height="0.8em"><path d="M2 13 A10 10 0 0 1 22 13" fill="none" stroke="currentColor" stroke-width="1.8"/><circle cx="12" cy="11" r="1.8" fill="currentColor"/></svg>`;
|
|
252
|
+
/** `‹` for an eighth push, `«` for a sixteenth. */
|
|
253
|
+
const PUSH_MARK = { eighth: "‹", sixteenth: "«" };
|
|
254
|
+
const ARTICULATION_MARK = { accent: ">", hit: "^" };
|
|
255
|
+
const marksOf = (slot) => ({
|
|
256
|
+
push: "push" in slot ? (slot.push ?? null) : null,
|
|
257
|
+
articulation: slot.kind === "chord" ? (slot.articulation ?? null) : null,
|
|
258
|
+
});
|
|
259
|
+
/** A slot with its push mark before it and its accent or hit above it. */
|
|
183
260
|
function slotHtml(slot, view) {
|
|
261
|
+
const body = unmarkedSlotHtml(slot, view);
|
|
262
|
+
const { push, articulation } = marksOf(slot);
|
|
263
|
+
if (!push && !articulation)
|
|
264
|
+
return body;
|
|
265
|
+
const data = `${push ? ` data-push="${push}"` : ""}${articulation ? ` data-articulation="${articulation}"` : ""}`;
|
|
266
|
+
const above = articulation
|
|
267
|
+
? `<span class="bm-artic">${ARTICULATION_MARK[articulation]}</span>`
|
|
268
|
+
: "";
|
|
269
|
+
return `<span class="bm-marked"${data}>${push ? `<span class="bm-push" aria-hidden="true">${PUSH_MARK[push]}</span>` : ""}${above}${body}</span>`;
|
|
270
|
+
}
|
|
271
|
+
function unmarkedSlotHtml(slot, view) {
|
|
184
272
|
switch (slot.kind) {
|
|
185
273
|
case "chord":
|
|
186
274
|
return chordHtml(slot.chord, view);
|
package/dist/model.js
CHANGED
|
@@ -220,7 +220,7 @@ function layoutRow(l, played, previous, ctx) {
|
|
|
220
220
|
let prev = previous;
|
|
221
221
|
for (const cell of l.cells) {
|
|
222
222
|
const model = layoutCell(cell, played[bar] ?? written[bar], prev);
|
|
223
|
-
if (cell.kind
|
|
223
|
+
if (cell.kind === "slots" || cell.kind === "simile")
|
|
224
224
|
bar++;
|
|
225
225
|
if (model.kind === "slots")
|
|
226
226
|
prev = model;
|
|
@@ -234,6 +234,7 @@ function layoutRow(l, played, previous, ctx) {
|
|
|
234
234
|
opens: l.opens,
|
|
235
235
|
closes: l.closes,
|
|
236
236
|
repeat: l.repeat,
|
|
237
|
+
ending: l.ending,
|
|
237
238
|
cells,
|
|
238
239
|
};
|
|
239
240
|
}
|
|
@@ -241,14 +242,23 @@ function layoutRow(l, played, previous, ctx) {
|
|
|
241
242
|
function layoutCell(cell, played, previous) {
|
|
242
243
|
if (cell.kind === "fermata")
|
|
243
244
|
return { kind: "fermata" };
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
if (cell.kind === "direction" || cell.kind === "rehearsal")
|
|
246
|
+
return cell;
|
|
247
|
+
return withPlayed(layoutBar(cell, played, previous), played);
|
|
248
|
+
}
|
|
249
|
+
/** A bar or `%`, with the slots it plays. */
|
|
250
|
+
function layoutBar(cell, played, previous) {
|
|
251
|
+
const own = cell.kind === "slots" ? cell : null;
|
|
252
|
+
return {
|
|
246
253
|
kind: "slots",
|
|
247
|
-
meter:
|
|
248
|
-
slots: played?.slots ??
|
|
254
|
+
meter: (own ?? previous)?.meter ?? null,
|
|
255
|
+
slots: played?.slots ?? own?.slots ?? [],
|
|
249
256
|
cut: false,
|
|
250
|
-
|
|
257
|
+
...wordsOf(cell),
|
|
258
|
+
};
|
|
251
259
|
}
|
|
260
|
+
/** The words written on a bar, as a field to spread; nothing when it has none. */
|
|
261
|
+
const wordsOf = (cell) => cell.words ? { words: cell.words } : {};
|
|
252
262
|
/** What the parser says about the bar as played: inside a rit. or accel., and its key. */
|
|
253
263
|
function withPlayed(cell, played) {
|
|
254
264
|
const change = played?.tempoChange?.kind;
|
package/dist/speech.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Spoken names: how a screen reader reads a chart as music. Built from the
|
|
3
3
|
* parsed structure, never from the symbol's text.
|
|
4
4
|
*/
|
|
5
|
-
import { degreeOf } from "barmark";
|
|
5
|
+
import { degreeOf, } from "barmark";
|
|
6
6
|
const NUMBERS = {
|
|
7
7
|
"1": "one",
|
|
8
8
|
"2": "two",
|
|
@@ -60,7 +60,10 @@ function speakPitch(label) {
|
|
|
60
60
|
return `${m[1]}${ACCIDENTAL[m[2] ?? ""] ?? ""}${octave}`;
|
|
61
61
|
}
|
|
62
62
|
/** `b9` → "flat nine"; a natural tone is added: `9` → "add nine". */
|
|
63
|
+
const LEFT_OUT = { no3: "no third", no5: "no fifth" };
|
|
63
64
|
function speakTone(tone) {
|
|
65
|
+
if (LEFT_OUT[tone])
|
|
66
|
+
return LEFT_OUT[tone];
|
|
64
67
|
const m = /^(#|b)?(\d+)$/.exec(tone);
|
|
65
68
|
if (!m)
|
|
66
69
|
return tone;
|
|
@@ -102,10 +105,23 @@ export function speakChord(record, key) {
|
|
|
102
105
|
const degree = key ? speakDegree(record.parts.root, key) : "";
|
|
103
106
|
return `${degree ? `${degree}, ` : ""}${speakParts(record.parts)}`;
|
|
104
107
|
}
|
|
108
|
+
const PUSHED = { eighth: "pushed", sixteenth: "pushed a sixteenth" };
|
|
109
|
+
/** A chord with its voicing and articulation: "accented D", "G short hit", "C major seven voiced E3 G3". */
|
|
110
|
+
function speakChordSlot(slot, key) {
|
|
111
|
+
const name = speakChord(slot.chord, key);
|
|
112
|
+
const voiced = slot.voicing ? `${name} voiced ${slot.voicing.pitches.join(" ")}` : name;
|
|
113
|
+
if (slot.articulation === "accent")
|
|
114
|
+
return `accented ${voiced}`;
|
|
115
|
+
return slot.articulation === "hit" ? `${voiced} short hit` : voiced;
|
|
116
|
+
}
|
|
105
117
|
export function speakSlot(slot, key) {
|
|
118
|
+
const said = speakUnpushed(slot, key);
|
|
119
|
+
return "push" in slot && slot.push ? `${said} ${PUSHED[slot.push]}` : said;
|
|
120
|
+
}
|
|
121
|
+
function speakUnpushed(slot, key) {
|
|
106
122
|
switch (slot.kind) {
|
|
107
123
|
case "chord":
|
|
108
|
-
return
|
|
124
|
+
return speakChordSlot(slot, key);
|
|
109
125
|
case "hold":
|
|
110
126
|
return "hold";
|
|
111
127
|
case "nc":
|
|
@@ -118,11 +134,24 @@ export function speakSlot(slot, key) {
|
|
|
118
134
|
return `${GROUPS[slot.members.length] ?? `group of ${slot.members.length}`}: ${slot.members.map((m) => speakSlot(m, key)).join(", ")}`;
|
|
119
135
|
}
|
|
120
136
|
}
|
|
137
|
+
/** "segno", "to coda", "dal segno al coda", "da capo al fine". */
|
|
138
|
+
function speakDirection(d) {
|
|
139
|
+
if (d.kind !== "jump")
|
|
140
|
+
return d.kind;
|
|
141
|
+
return `${d.to === "start" ? "da capo" : "dal segno"}${d.al ? ` al ${d.al}` : ""}`;
|
|
142
|
+
}
|
|
121
143
|
/** "bar 9: G, hold, D, hold"; a fermata is held until cued. With `numerals`, each chord's degree comes first. */
|
|
122
144
|
export function speakBar(barNumber, cell, numerals = false) {
|
|
123
145
|
if (cell.kind === "fermata")
|
|
124
146
|
return "fermata: hold until cued";
|
|
147
|
+
if (cell.kind === "direction")
|
|
148
|
+
return speakDirection(cell.direction);
|
|
149
|
+
if (cell.kind === "rehearsal")
|
|
150
|
+
return `rehearsal ${cell.label}`;
|
|
125
151
|
const meter = cell.meter ? `, in ${cell.meter.n}/${cell.meter.d}` : "";
|
|
126
152
|
const cut = cell.cut ? ", cut on this pass" : "";
|
|
127
|
-
|
|
153
|
+
const words = (at) => (cell.words ?? []).filter((w) => w.at === at).map((w) => `“${w.text}”`);
|
|
154
|
+
const slots = cell.slots.map((s, i) => [...words(i), speakSlot(s, numerals ? cell.key : undefined)].join(" "));
|
|
155
|
+
const spoken = [...slots, ...words(cell.slots.length)].join(", ");
|
|
156
|
+
return `bar${barNumber === null ? "" : ` ${barNumber}`}${meter}: ${spoken}${cut}`;
|
|
128
157
|
}
|
package/dist/styles.js
CHANGED
|
@@ -116,6 +116,21 @@ const css = `
|
|
|
116
116
|
.bm-tuplet::after{border-left:none;margin-left:.3em}
|
|
117
117
|
.bm-meter{display:inline-flex;flex-direction:column;align-items:center;align-self:center;font:700 ${em(SIZES.meter)}/.95 ${SANS};color:var(--bm-muted)}
|
|
118
118
|
.bm-fermata-glyph{display:block;color:var(--bm-ink)}
|
|
119
|
+
.bm-row>div.bm-direction{min-width:0;padding-inline:.35em;border-right:none;align-items:flex-start;color:var(--bm-ink)}
|
|
120
|
+
.bm-sign{display:inline-block;vertical-align:middle}
|
|
121
|
+
.bm-direction-words{font:italic ${em(SIZES.repeat)}/1.2 Georgia,serif;white-space:nowrap}
|
|
122
|
+
.bm-row>div.bm-mark{min-width:0;padding-inline:.3em;border-right:none;align-items:flex-start}
|
|
123
|
+
.bm-box{display:inline-block;padding:.05em .3em;border:.08em solid var(--bm-ink);border-radius:.15em;font:700 .85em/1.1 system-ui,sans-serif;color:var(--bm-ink)}
|
|
124
|
+
.bm-worded{position:relative}
|
|
125
|
+
.bm-marked{position:relative;display:inline-flex;align-items:baseline}
|
|
126
|
+
.bm-push{font:700 .8em/1 system-ui,sans-serif;color:var(--bm-muted);margin-right:.08em}
|
|
127
|
+
.bm-artic{position:absolute;left:50%;top:-.95em;transform:translateX(-50%);font:700 .7em/1 system-ui,sans-serif;color:var(--bm-ink)}
|
|
128
|
+
.bm-marked[data-push]>.bm-artic{left:calc(50% + .25em)}
|
|
129
|
+
.bm-words{position:absolute;left:0;top:-1.05em;font:italic .62em/1 Georgia,serif;color:var(--bm-muted);white-space:nowrap}
|
|
130
|
+
.bm-bar>.bm-words-end{left:auto;right:.5em;top:.15em}
|
|
131
|
+
.bm-bar[data-ending]::after{content:attr(data-ending);position:absolute;left:.45em;top:.2em;font:italic .75em/1 Georgia,serif;color:var(--bm-muted)}
|
|
132
|
+
.bm-ending>.bm-bar{border-top:.06em solid var(--bm-muted)}
|
|
133
|
+
.bm-ending>.bm-bar[data-ending]{box-shadow:inset .06em 0 var(--bm-muted)}
|
|
119
134
|
.bm-bar[data-tempo-change]::before{content:"";position:absolute;inset:.15em 0 auto;border-top:.08em dashed var(--barmark-directive,var(--bm-muted))}
|
|
120
135
|
.bm-bar[data-cut]{outline:.08em dashed var(--bm-muted);outline-offset:-.25em}
|
|
121
136
|
.bm-cut{position:absolute;top:.3em;right:.45em;font:${em(SIZES.cut)}/1 ${SANS};color:var(--bm-muted)}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChartDiagnostic, Directives, Key, Meter, Repeat, Slot } from "barmark";
|
|
1
|
+
import type { ChartDiagnostic, Direction, Directives, Key, Meter, Repeat, Slot, Words } from "barmark";
|
|
2
2
|
export type LayoutOptions = {
|
|
3
3
|
rehearsalLetters?: boolean;
|
|
4
4
|
};
|
|
@@ -30,6 +30,8 @@ export type BlockItem = {
|
|
|
30
30
|
opens: boolean;
|
|
31
31
|
closes: boolean;
|
|
32
32
|
repeat: Repeat;
|
|
33
|
+
/** The passes an ending plays on; null for any other row. */
|
|
34
|
+
ending: number[] | null;
|
|
33
35
|
cells: CellModel[];
|
|
34
36
|
}
|
|
35
37
|
/** `note` marks a `###` line, whose text is given without the marks. */
|
|
@@ -55,6 +57,18 @@ export type CellModel = {
|
|
|
55
57
|
tempoChange?: "rit" | "accel";
|
|
56
58
|
/** The key the bar is played in, when the chart names one. */
|
|
57
59
|
key?: Key;
|
|
60
|
+
/** Words written on the bar (barmark's grammar 2.6), each before slot `at`. */
|
|
61
|
+
words?: Words[];
|
|
58
62
|
} | {
|
|
59
63
|
kind: "fermata";
|
|
64
|
+
}
|
|
65
|
+
/** A segno, coda or jump between bars (barmark's grammar 5.2). */
|
|
66
|
+
| {
|
|
67
|
+
kind: "direction";
|
|
68
|
+
direction: Direction;
|
|
69
|
+
}
|
|
70
|
+
/** A rehearsal mark on the bar after it (barmark's grammar 2.5). */
|
|
71
|
+
| {
|
|
72
|
+
kind: "rehearsal";
|
|
73
|
+
label: string;
|
|
60
74
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barmark/render",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Render barmark chord charts as a clean, accessible bar grid: HTML with Declarative Shadow DOM, or the <barmark-chart> web component.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"url": "https://github.com/kevinpeckham/barmark/issues"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"barmark": "^0.
|
|
50
|
+
"barmark": "^0.12.0"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|