@barmark/render 0.2.0 → 0.3.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 +34 -1
- package/dist/html.js +24 -3
- package/dist/model.js +7 -2
- package/dist/styles.js +12 -3
- package/dist/types.d.ts +2 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,37 @@ package uses [semantic versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-11-01
|
|
10
|
+
|
|
11
|
+
Draws gradual tempo changes. Depends on `barmark` ^0.5.0, which reads
|
|
12
|
+
them.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Gradual tempo changes. A `tempo: rit. to 90` line reads
|
|
17
|
+
"→ Tempo rit. to ♩ = 90" (`a tempo` reads as itself), and every bar
|
|
18
|
+
played inside a rit. or accel. has a dashed line along its top, in
|
|
19
|
+
the directive colour, with `data-tempo-change="rit"` or `"accel"`.
|
|
20
|
+
Uses barmark 0.5.0's `tempoChange`.
|
|
21
|
+
|
|
22
|
+
## [0.2.1] - 2026-11-01
|
|
23
|
+
|
|
24
|
+
Two fixes found while moving barmark.dev to the element. Depends on
|
|
25
|
+
`barmark` ^0.3.0, which warns when a chart has no bar lines.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **Triplet brackets work on any paper.** The bracket is drawn either
|
|
30
|
+
side of its number, leaving a gap, instead of under it with the number
|
|
31
|
+
painted over in the paper colour. A see-through `--barmark-paper` used
|
|
32
|
+
to show the number in a box.
|
|
33
|
+
- **A chart from `renderChart` keeps its options when the element
|
|
34
|
+
loads.** The `<barmark-chart>` it wraps the chart in now carries them as
|
|
35
|
+
attributes (`theme`, `bar-numbers="off"`, `rehearsal-letters`). Before,
|
|
36
|
+
a page that rendered a dark chart on the server and imported
|
|
37
|
+
`@barmark/render/element` saw it switch to the system theme, and lose
|
|
38
|
+
its rehearsal letters once given new `text`.
|
|
39
|
+
|
|
9
40
|
## [0.2.0] - 2026-11-01
|
|
10
41
|
|
|
11
42
|
Easier to read, and the bar numbers easier to scan. Every size below was
|
|
@@ -99,6 +130,8 @@ that needs no script or as the `<barmark-chart>` web component. Requires
|
|
|
99
130
|
zoom without sideways scrolling, the element renders and re-renders, and
|
|
100
131
|
a screen reader hears "bar 9: D". Run them with `bun run test:browser`.
|
|
101
132
|
|
|
102
|
-
[Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.
|
|
133
|
+
[Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.3.0...HEAD
|
|
134
|
+
[0.3.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.2.1...render-v0.3.0
|
|
135
|
+
[0.2.1]: https://github.com/kevinpeckham/barmark/compare/render-v0.2.0...render-v0.2.1
|
|
103
136
|
[0.2.0]: https://github.com/kevinpeckham/barmark/compare/render-v0.1.0...render-v0.2.0
|
|
104
137
|
[0.1.0]: https://github.com/kevinpeckham/barmark/releases/tag/render-v0.1.0
|
package/dist/html.js
CHANGED
|
@@ -5,7 +5,16 @@ import { layoutChart } from "./model.js";
|
|
|
5
5
|
import { speakBar } from "./speech.js";
|
|
6
6
|
import { FIT_STEPS, MIN_BAR, stylesheet } from "./styles.js";
|
|
7
7
|
export function renderChart(text, options = {}) {
|
|
8
|
-
return `${fontStyle(options)}<barmark-chart><template shadowrootmode="open">${shadowContent(text, options)}</template></barmark-chart>`;
|
|
8
|
+
return `${fontStyle(options)}<barmark-chart${elementAttributes(options)}><template shadowrootmode="open">${shadowContent(text, options)}</template></barmark-chart>`;
|
|
9
|
+
}
|
|
10
|
+
/** The options as the element's attributes, so it keeps them when it loads or takes new text. */
|
|
11
|
+
function elementAttributes(options) {
|
|
12
|
+
const theme = themeOf(options);
|
|
13
|
+
return [
|
|
14
|
+
theme === "auto" ? "" : ` theme="${theme}"`,
|
|
15
|
+
options.barNumbers === false ? ' bar-numbers="off"' : "",
|
|
16
|
+
options.rehearsalLetters ? " rehearsal-letters" : "",
|
|
17
|
+
].join("");
|
|
9
18
|
}
|
|
10
19
|
export const THEMES = ["auto", "light", "dark"];
|
|
11
20
|
/** Only a known theme reaches the markup: hosts may pass any string through. */
|
|
@@ -32,16 +41,27 @@ function headerHtml(model) {
|
|
|
32
41
|
return "";
|
|
33
42
|
return `<header class="bm-header">${title}${meta ? `<p class="bm-meta" part="meta">${meta}</p>` : ""}</header>`;
|
|
34
43
|
}
|
|
44
|
+
/** "rit. to ♩ = 90", "accel.", "a tempo". */
|
|
45
|
+
function tempoChangeText(c) {
|
|
46
|
+
if (c.kind === "a tempo")
|
|
47
|
+
return c.kind;
|
|
48
|
+
return `${c.kind}.${c.to === null ? "" : ` to ♩ = ${c.to}`}`;
|
|
49
|
+
}
|
|
35
50
|
/** "D major · 4/4 · ♩ = 145 · swing", escaped; labelled, "Key D major · Time 4/4 · …". */
|
|
36
51
|
function metaText(d, labelled = false) {
|
|
37
52
|
return [
|
|
38
53
|
["Key", d.key ? `${glyphs(d.key.tonic)} ${d.key.mode}` : ""],
|
|
39
54
|
["Time", d.time ? `${d.time.n}/${d.time.d}` : ""],
|
|
40
55
|
["Tempo", d.tempo ? `♩ = ${d.tempo}` : ""],
|
|
56
|
+
// "a tempo" names itself; "Tempo a tempo" would stutter.
|
|
57
|
+
[
|
|
58
|
+
d.tempoChange?.kind === "a tempo" ? "" : "Tempo",
|
|
59
|
+
d.tempoChange ? tempoChangeText(d.tempoChange) : "",
|
|
60
|
+
],
|
|
41
61
|
["Feel", d.feel ?? ""],
|
|
42
62
|
]
|
|
43
63
|
.filter(([, value]) => value)
|
|
44
|
-
.map(([label, value]) => escapeHtml(labelled ? `${label} ${value}` : (value ?? "")))
|
|
64
|
+
.map(([label, value]) => escapeHtml(labelled && label ? `${label} ${value}` : (value ?? "")))
|
|
45
65
|
.join(" · ");
|
|
46
66
|
}
|
|
47
67
|
function blockHtml(block, settings) {
|
|
@@ -149,7 +169,8 @@ function cellHtml(cell, barNumber, marks) {
|
|
|
149
169
|
? `<span class="bm-meter"><span>${cell.meter.n}</span><span>${cell.meter.d}</span></span>`
|
|
150
170
|
: "";
|
|
151
171
|
const cut = cell.cut ? `<span class="bm-cut">cut</span>` : "";
|
|
152
|
-
|
|
172
|
+
const change = cell.tempoChange ? ` data-tempo-change="${cell.tempoChange}"` : "";
|
|
173
|
+
return `<div class="bm-bar" part="bar"${cell.cut ? " data-cut" : ""}${change}${marks}>${spoken}<span class="bm-bar-body" aria-hidden="true">${meter}${cell.slots.map(slotHtml).join("")}${cut}</span></div>`;
|
|
153
174
|
}
|
|
154
175
|
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>`;
|
|
155
176
|
function slotHtml(slot) {
|
package/dist/model.js
CHANGED
|
@@ -239,10 +239,15 @@ function layoutCell(cell, played, previous) {
|
|
|
239
239
|
if (cell.kind === "fermata")
|
|
240
240
|
return { kind: "fermata" };
|
|
241
241
|
const from = cell.kind === "slots" ? cell : previous;
|
|
242
|
-
return {
|
|
242
|
+
return withTempoChange({
|
|
243
243
|
kind: "slots",
|
|
244
244
|
meter: from?.meter ?? null,
|
|
245
245
|
slots: played?.slots ?? (cell.kind === "slots" ? cell.slots : []),
|
|
246
246
|
cut: false,
|
|
247
|
-
};
|
|
247
|
+
}, played);
|
|
248
|
+
}
|
|
249
|
+
/** Marks a bar played inside a rit. or accel. */
|
|
250
|
+
function withTempoChange(cell, played) {
|
|
251
|
+
const change = played?.tempoChange?.kind;
|
|
252
|
+
return change ? { ...cell, tempoChange: change } : cell;
|
|
248
253
|
}
|
package/dist/styles.js
CHANGED
|
@@ -31,6 +31,12 @@ const SIZES = {
|
|
|
31
31
|
const GUTTER = 2.06;
|
|
32
32
|
export const MIN_BAR = 4.68;
|
|
33
33
|
const HEADING_GAP = 0.645;
|
|
34
|
+
/**
|
|
35
|
+
* The triplet bracket, in chord sizes: its line sits this far below the top
|
|
36
|
+
* of the number, with hooks this long, drawn either side of the number so
|
|
37
|
+
* nothing is painted over it and any paper shows through.
|
|
38
|
+
*/
|
|
39
|
+
const BRACKET = { top: 0.6, hook: 0.3, line: 0.06 };
|
|
34
40
|
/** The single note's ring, in its own em: padding, and the border it spills past. */
|
|
35
41
|
const RING = { y: 0.21, x: 0.5, border: 0.07 };
|
|
36
42
|
const em = (n) => `${Number(n.toFixed(3))}em`;
|
|
@@ -99,10 +105,13 @@ const css = `
|
|
|
99
105
|
.bm-notes{display:inline-flex;gap:.3em;align-items:baseline}
|
|
100
106
|
.bm-notes u{text-decoration-thickness:.08em;text-underline-offset:.15em}
|
|
101
107
|
.bm-group{position:relative;display:inline-flex;gap:.45em;align-items:baseline}
|
|
102
|
-
.bm-
|
|
103
|
-
.bm-tuplet{
|
|
108
|
+
.bm-tuplet{position:absolute;left:0;right:0;top:-1.25em;display:flex;justify-content:center;font:italic 600 ${em(SIZES.tuplet)}/1 Georgia,serif;color:var(--bm-muted)}
|
|
109
|
+
.bm-tuplet::before,.bm-tuplet::after{content:"";flex:1;align-self:flex-start;margin-top:${at(SIZES.tuplet, BRACKET.top)};height:${at(SIZES.tuplet, BRACKET.hook)};border:${at(SIZES.tuplet, BRACKET.line)} solid var(--bm-muted);border-bottom:none}
|
|
110
|
+
.bm-tuplet::before{border-right:none;margin-right:.3em}
|
|
111
|
+
.bm-tuplet::after{border-left:none;margin-left:.3em}
|
|
104
112
|
.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)}
|
|
105
113
|
.bm-fermata-glyph{display:block;color:var(--bm-ink)}
|
|
114
|
+
.bm-bar[data-tempo-change]::before{content:"";position:absolute;inset:.15em 0 auto;border-top:.08em dashed var(--barmark-directive,var(--bm-muted))}
|
|
106
115
|
.bm-bar[data-cut]{outline:.08em dashed var(--bm-muted);outline-offset:-.25em}
|
|
107
116
|
.bm-cut{position:absolute;top:.3em;right:.45em;font:${em(SIZES.cut)}/1 ${SANS};color:var(--bm-muted)}
|
|
108
117
|
.bm-text{grid-column:1/-1;margin:.2em 0 .5em ${at(SIZES.text, GUTTER)};font-family:${TEXT};font-size:${em(SIZES.text)}}
|
|
@@ -113,7 +122,7 @@ const css = `
|
|
|
113
122
|
.bm-diagnostics .bm-error{color:var(--bm-accent)}
|
|
114
123
|
@media (forced-colors:active){
|
|
115
124
|
.bm-bar,.bm-row>div.bm-bar:first-of-type,.bm-row>div.bm-bar[data-repeat-start],.bm-row>div.bm-bar[data-repeat-end]{border-color:CanvasText}
|
|
116
|
-
.bm-ring,.bm-
|
|
125
|
+
.bm-ring,.bm-tuplet::before,.bm-tuplet::after{border-color:CanvasText}
|
|
117
126
|
.bm-silence{background:CanvasText;forced-color-adjust:none}
|
|
118
127
|
.bm-bar[data-cut]{outline-color:CanvasText}
|
|
119
128
|
}
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barmark/render",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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.5.0"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|