@barmark/render 0.0.0-stage → 0.1.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 +68 -0
- package/LICENSE +21 -0
- package/README.md +132 -2
- package/dist/chord.d.ts +18 -0
- package/dist/chord.js +24 -0
- package/dist/element.d.ts +9 -0
- package/dist/element.js +135 -0
- package/dist/escape.d.ts +2 -0
- package/dist/escape.js +9 -0
- package/dist/font.d.ts +8 -0
- package/dist/font.js +11 -0
- package/dist/html.d.ts +16 -0
- package/dist/html.js +199 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/model.d.ts +2 -0
- package/dist/model.js +248 -0
- package/dist/speech.d.ts +10 -0
- package/dist/speech.js +113 -0
- package/dist/styles.d.ts +20 -0
- package/dist/styles.js +83 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +1 -0
- package/fonts/Edwin-Roman.otf +0 -0
- package/fonts/OFL.txt +93 -0
- package/fonts/README.md +16 -0
- package/package.json +55 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@barmark/render` are recorded here. The format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
5
|
+
package uses [semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-10-31
|
|
10
|
+
|
|
11
|
+
The first release: barmark charts as an evenly spaced bar grid, as HTML
|
|
12
|
+
that needs no script or as the `<barmark-chart>` web component. Requires
|
|
13
|
+
`barmark` 0.2.0 or later.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`layoutChart(text)`**: the chart as data to draw. Bar lines become rows
|
|
18
|
+
with their play-order bar numbers (numbering stops after an open repeat),
|
|
19
|
+
`%` becomes the bar it copies, and text lines, directives and lines with
|
|
20
|
+
errors keep their place.
|
|
21
|
+
- **Replays are drawn in full** with their own bar numbers. A replay's own
|
|
22
|
+
text lines (verse 2's words) replace the written section's in order;
|
|
23
|
+
`->` marks the bar the parser cut. Optional rehearsal letters (A, B, …,
|
|
24
|
+
AA) mark each section occurrence in play order.
|
|
25
|
+
- **`renderChart(text, options)`** returns the chart as a `<barmark-chart>`
|
|
26
|
+
element holding a declarative shadow root, and `renderChartUnwrapped`
|
|
27
|
+
the bare markup (`bm-` classes) for pages that bring the stylesheet.
|
|
28
|
+
Options: `theme`, `barNumbers`, `rehearsalLetters`, `diagnostics`. All
|
|
29
|
+
chart text is escaped.
|
|
30
|
+
- **Chords in canonical form,** with ♯ and ♭ glyphs and the extension as a
|
|
31
|
+
superscript; single notes ringed; note sets in braces with the root
|
|
32
|
+
underlined; groups under a tuplet count; fermatas, one-bar meters, N.C.,
|
|
33
|
+
silence and `?` each drawn distinctly.
|
|
34
|
+
- **Spoken names:** every bar reads to a screen reader as music ("bar 9: A
|
|
35
|
+
seven sus four, hold"), with sections labelled by their headings and
|
|
36
|
+
repeats spoken ("play 2 times").
|
|
37
|
+
- **Styles, light and dark.** The chart follows the system's colour scheme
|
|
38
|
+
(`theme: "auto"`), or is forced `light` or `dark`; printing is always
|
|
39
|
+
light, and Windows High Contrast keeps bar lines and marks visible. Both
|
|
40
|
+
palettes meet WCAG AA contrast. Everything is sized in em from
|
|
41
|
+
`--barmark-size`, and a section too wide for its container wraps its
|
|
42
|
+
rows rather than scrolling sideways. Pages restyle the chart through
|
|
43
|
+
`--barmark-font`, `--barmark-size`, `--barmark-ink`, `--barmark-paper`,
|
|
44
|
+
`--barmark-muted`, `--barmark-rule` and `--barmark-accent`, or
|
|
45
|
+
`::part()`.
|
|
46
|
+
- **Edwin ships with the package,** unmodified, with its OFL licence
|
|
47
|
+
(`@barmark/render/fonts/Edwin-Roman.otf`). Pass `fontUrl` to emit its
|
|
48
|
+
`@font-face` rule before the chart, or call `fontFaceCss(url)` once per
|
|
49
|
+
page. The family is registered as "barmark Edwin"; without it the chart
|
|
50
|
+
falls back to Georgia or another serif.
|
|
51
|
+
- **`<barmark-chart>`** (`import "@barmark/render/element"`): renders a
|
|
52
|
+
chart from its `src` URL or a child `<script type="text/barmark">`, and
|
|
53
|
+
again when either changes. Attributes (and matching properties):
|
|
54
|
+
`theme`, `bar-numbers="off"`, `rehearsal-letters`. It registers the font
|
|
55
|
+
itself, reuses a server-rendered shadow root (which still follows
|
|
56
|
+
`theme` and `bar-numbers`), fetches a `src` once, renders once per batch
|
|
57
|
+
of changes, and shows a failed load as an error inside the chart.
|
|
58
|
+
|
|
59
|
+
### Technical
|
|
60
|
+
|
|
61
|
+
- **Browser tests** (Playwright, Chromium) in CI: the chart looks pixel
|
|
62
|
+
for pixel the same on a page with hostile CSS, themes follow the system
|
|
63
|
+
and the page's custom properties, long rows wrap on a phone and at 400%
|
|
64
|
+
zoom without sideways scrolling, the element renders and re-renders, and
|
|
65
|
+
a screen reader hears "bar 9: D". Run them with `bun run test:browser`.
|
|
66
|
+
|
|
67
|
+
[Unreleased]: https://github.com/kevinpeckham/barmark/compare/render-v0.1.0...HEAD
|
|
68
|
+
[0.1.0]: https://github.com/kevinpeckham/barmark/releases/tag/render-v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kevin Peckham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,133 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @barmark/render
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Renders [barmark](https://github.com/kevinpeckham/barmark) chord charts as
|
|
4
|
+
a clean, evenly spaced bar grid that looks the same on any page, follows
|
|
5
|
+
light and dark mode, scales with the reader's text size, and reads to a
|
|
6
|
+
screen reader as music.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
npm install @barmark/render
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## As HTML (no browser JavaScript needed)
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import { renderChart } from "@barmark/render";
|
|
16
|
+
|
|
17
|
+
const html = renderChart(chartText, { fontUrl: "/fonts/Edwin-Roman.otf" });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`renderChart` returns a `<barmark-chart>` element holding a declarative
|
|
21
|
+
shadow root: put it in any page, server-rendered or static, and it shows
|
|
22
|
+
the chart with no script. The shadow root keeps the page's CSS out, so a
|
|
23
|
+
blog's resets and fonts cannot rearrange the bars. Browsers attach a
|
|
24
|
+
declarative shadow root only while parsing HTML: to insert the markup from
|
|
25
|
+
script, use `setHTMLUnsafe` (or the element below), not `innerHTML`.
|
|
26
|
+
|
|
27
|
+
`renderChartUnwrapped(text, options)` returns the same markup without the
|
|
28
|
+
shadow root (classes prefixed `bm-`), for pages that include `stylesheet`
|
|
29
|
+
themselves.
|
|
30
|
+
|
|
31
|
+
## As a web component
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<script type="module">import "@barmark/render/element";</script>
|
|
35
|
+
|
|
36
|
+
<barmark-chart src="/charts/peaceful-dreams.chart.md"></barmark-chart>
|
|
37
|
+
|
|
38
|
+
<barmark-chart theme="dark" rehearsal-letters>
|
|
39
|
+
<script type="text/barmark">
|
|
40
|
+
## Verse
|
|
41
|
+
| D | A | Bm | G |
|
|
42
|
+
</script>
|
|
43
|
+
</barmark-chart>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The element renders from its `src` URL, or from a child
|
|
47
|
+
`<script type="text/barmark">` (indentation is removed), and renders again
|
|
48
|
+
whenever either changes; it fetches a `src` once, and renders once
|
|
49
|
+
however many attributes change together. A chart rendered on the server
|
|
50
|
+
by `renderChart` keeps its music until it has a source of its own, and
|
|
51
|
+
still follows `theme` and `bar-numbers`.
|
|
52
|
+
|
|
53
|
+
The element registers the font itself, from the `fonts/` folder beside its
|
|
54
|
+
own module. If your bundler moves that file, register it yourself before
|
|
55
|
+
the element loads, with `<style data-barmark-font>` holding
|
|
56
|
+
`fontFaceCss(yourUrl)`; the element then leaves it alone.
|
|
57
|
+
|
|
58
|
+
## Options and attributes
|
|
59
|
+
|
|
60
|
+
| `renderChart` option | Element attribute (property) | Default | |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `theme` | `theme` (`theme`) | `"auto"` | `"auto"` follows the system; `"light"` or `"dark"` force one |
|
|
63
|
+
| `barNumbers` | `bar-numbers="off"` (`barNumbers`) | `true` | bar numbers at the start of each row |
|
|
64
|
+
| `rehearsalLetters` | `rehearsal-letters` (`rehearsalLetters`) | `false` | boxed A, B, C… on each section as played |
|
|
65
|
+
| `diagnostics` | | `false` | list the chart's errors and warnings below it |
|
|
66
|
+
| `fontUrl` | | none | emit Edwin's `@font-face` rule before the chart |
|
|
67
|
+
|
|
68
|
+
## Styling on purpose
|
|
69
|
+
|
|
70
|
+
Set these custom properties on the element or anything above it:
|
|
71
|
+
|
|
72
|
+
| Property | What it sets |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `--barmark-font` | the chart's typeface |
|
|
75
|
+
| `--barmark-size` | the base size everything scales from (default `1.25rem`) |
|
|
76
|
+
| `--barmark-ink` | chords and text |
|
|
77
|
+
| `--barmark-paper` | the background |
|
|
78
|
+
| `--barmark-muted` | holds, bar numbers, braces, meta lines |
|
|
79
|
+
| `--barmark-rule` | bar lines |
|
|
80
|
+
| `--barmark-accent` | errors |
|
|
81
|
+
|
|
82
|
+
```css
|
|
83
|
+
barmark-chart {
|
|
84
|
+
--barmark-size: 1rem;
|
|
85
|
+
--barmark-paper: transparent;
|
|
86
|
+
}
|
|
87
|
+
barmark-chart::part(heading) {
|
|
88
|
+
color: teal;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`::part()` names: `title`, `meta`, `heading`, `bar`, `chord`, `text`,
|
|
93
|
+
`bar-number`, `rehearsal`. These, the custom properties and the
|
|
94
|
+
attributes are the public surface: changing them is a breaking change.
|
|
95
|
+
|
|
96
|
+
## Light, dark and print
|
|
97
|
+
|
|
98
|
+
Both palettes meet WCAG AA contrast (4.5:1 for text). `theme="auto"`
|
|
99
|
+
follows `prefers-color-scheme`; a page with its own dark-mode switch sets
|
|
100
|
+
`theme` or the colour properties. Printing always uses the light palette,
|
|
101
|
+
and Windows High Contrast keeps bar lines and marks visible.
|
|
102
|
+
|
|
103
|
+
## Accessibility
|
|
104
|
+
|
|
105
|
+
- Everything is sized in em from `--barmark-size`, so text size and zoom
|
|
106
|
+
scale the chart; a section too wide for its container wraps its rows
|
|
107
|
+
instead of scrolling sideways.
|
|
108
|
+
- Every bar has a spoken name built from the parsed chart: "bar 9: A seven
|
|
109
|
+
sus four, hold", "triplet: D, E, F sharp minor", "note E", "C over E".
|
|
110
|
+
Sections are labelled by their headings, and repeats are spoken ("play
|
|
111
|
+
2 times").
|
|
112
|
+
|
|
113
|
+
## The font
|
|
114
|
+
|
|
115
|
+
Edwin, MuseScore's text font for music, ships unmodified in
|
|
116
|
+
`@barmark/render/fonts/Edwin-Roman.otf` with its SIL Open Font License
|
|
117
|
+
(`fonts/OFL.txt`). It is registered under the family name "barmark
|
|
118
|
+
Edwin". Serve the file and pass its URL as `fontUrl`, or call
|
|
119
|
+
`fontFaceCss(url)` once per page; without it the chart uses Georgia or
|
|
120
|
+
another serif.
|
|
121
|
+
|
|
122
|
+
## What it draws
|
|
123
|
+
|
|
124
|
+
Chords appear in their canonical form whatever the author typed (`Cø`
|
|
125
|
+
shows as Cm⁷(♭5)). `%` is drawn as the bar it copies; a replayed section
|
|
126
|
+
is drawn in full with its own bar numbers, and its own lyrics replace the
|
|
127
|
+
written section's. A line with an error shows as written, underlined,
|
|
128
|
+
with its message. `layoutChart(text)` returns all of this as data, if
|
|
129
|
+
you want to draw it yourself.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT. Edwin is © MuseScore Limited, under the SIL Open Font License 1.1.
|
package/dist/chord.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Chords and notes as display pieces: ♯ and ♭ glyphs, the suffix split for superscript. */
|
|
2
|
+
import type { ChordParts } from "barmark";
|
|
3
|
+
export type ChordPieces = {
|
|
4
|
+
root: string;
|
|
5
|
+
/** m, dim, aug or 5: set on the baseline. */
|
|
6
|
+
quality: string;
|
|
7
|
+
/** Extension, sus and tones: set as a superscript. */
|
|
8
|
+
superscript: string;
|
|
9
|
+
bass: string | null;
|
|
10
|
+
};
|
|
11
|
+
/** `F#` → `F♯`, `b9` → `♭9`, `Bbb` → `B𝄫`. */
|
|
12
|
+
export declare function glyphs(text: string): string;
|
|
13
|
+
export declare function typesetChord(parts: ChordParts): ChordPieces;
|
|
14
|
+
/** A note set's label (`E1`, `F#`) as name and octave. */
|
|
15
|
+
export declare function splitPitch(label: string): {
|
|
16
|
+
name: string;
|
|
17
|
+
octave: string | null;
|
|
18
|
+
};
|
package/dist/chord.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const ACCIDENTALS = [
|
|
2
|
+
[/##/g, "𝄪"],
|
|
3
|
+
[/bb(?=\d|$|\))/g, "𝄫"],
|
|
4
|
+
[/#/g, "♯"],
|
|
5
|
+
[/(?<=[A-G]|^|\(|,)b/g, "♭"],
|
|
6
|
+
];
|
|
7
|
+
/** `F#` → `F♯`, `b9` → `♭9`, `Bbb` → `B𝄫`. */
|
|
8
|
+
export function glyphs(text) {
|
|
9
|
+
return ACCIDENTALS.reduce((t, [re, g]) => t.replace(re, g), text);
|
|
10
|
+
}
|
|
11
|
+
export function typesetChord(parts) {
|
|
12
|
+
const tones = parts.tones.length ? `(${parts.tones.join(",")})` : "";
|
|
13
|
+
return {
|
|
14
|
+
root: glyphs(parts.root),
|
|
15
|
+
quality: parts.quality,
|
|
16
|
+
superscript: glyphs(`${parts.extension ?? ""}${parts.sus ?? ""}${tones}`),
|
|
17
|
+
bass: parts.bass ? glyphs(parts.bass) : null,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** A note set's label (`E1`, `F#`) as name and octave. */
|
|
21
|
+
export function splitPitch(label) {
|
|
22
|
+
const m = /^([A-G](?:##|bb|#|b)?)(-?\d+)?$/.exec(label);
|
|
23
|
+
return { name: glyphs(m?.[1] ?? label), octave: m?.[2] ?? null };
|
|
24
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type RenderOptions } from "./html.js";
|
|
2
|
+
type Attributes = {
|
|
3
|
+
getAttribute(name: string): string | null;
|
|
4
|
+
hasAttribute(name: string): boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function optionsFromAttributes(el: Attributes): RenderOptions;
|
|
7
|
+
/** Chart text from a script element: blank edges and common indentation removed. */
|
|
8
|
+
export declare function inlineChartText(text: string): string;
|
|
9
|
+
export {};
|
package/dist/element.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<barmark-chart>`: renders itself from a `src` URL or from a child
|
|
3
|
+
* `<script type="text/barmark">`, and again whenever either changes. A
|
|
4
|
+
* shadow root rendered on the server is reused. Importing this module
|
|
5
|
+
* registers the element (once) and the font (once per document).
|
|
6
|
+
*/
|
|
7
|
+
import { FONT_FAMILY, fontFaceCss } from "./font.js";
|
|
8
|
+
import { loadErrorContent, shadowContent, THEMES } from "./html.js";
|
|
9
|
+
export function optionsFromAttributes(el) {
|
|
10
|
+
const theme = el.getAttribute("theme");
|
|
11
|
+
return {
|
|
12
|
+
theme: THEMES.find((t) => t === theme) ?? "auto",
|
|
13
|
+
barNumbers: el.getAttribute("bar-numbers") !== "off",
|
|
14
|
+
rehearsalLetters: el.hasAttribute("rehearsal-letters"),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Chart text from a script element: blank edges and common indentation removed. */
|
|
18
|
+
export function inlineChartText(text) {
|
|
19
|
+
const lines = text.replace(/^\s*\n|\n\s*$/g, "").split("\n");
|
|
20
|
+
const indents = lines.filter((l) => l.trim()).map((l) => /^[ \t]*/.exec(l)?.[0].length ?? 0);
|
|
21
|
+
const cut = Math.min(...indents, Number.POSITIVE_INFINITY);
|
|
22
|
+
return lines.map((l) => l.slice(Number.isFinite(cut) ? cut : 0).trimEnd()).join("\n");
|
|
23
|
+
}
|
|
24
|
+
function registerFont(doc) {
|
|
25
|
+
if (doc.querySelector("style[data-barmark-font]"))
|
|
26
|
+
return;
|
|
27
|
+
const style = doc.createElement("style");
|
|
28
|
+
style.dataset.barmarkFont = FONT_FAMILY;
|
|
29
|
+
style.textContent = fontFaceCss(new URL("../fonts/Edwin-Roman.otf", import.meta.url).href);
|
|
30
|
+
doc.head.append(style);
|
|
31
|
+
}
|
|
32
|
+
async function fetchText(src) {
|
|
33
|
+
const response = await fetch(src);
|
|
34
|
+
if (!response.ok)
|
|
35
|
+
throw new Error(`${response.status} ${response.statusText}`.trim());
|
|
36
|
+
return response.text();
|
|
37
|
+
}
|
|
38
|
+
function defineElement() {
|
|
39
|
+
class BarmarkChart extends HTMLElement {
|
|
40
|
+
static observedAttributes = ["src", "theme", "bar-numbers", "rehearsal-letters"];
|
|
41
|
+
#observer = new MutationObserver(() => this.#schedule());
|
|
42
|
+
#renders = 0;
|
|
43
|
+
#scheduled = false;
|
|
44
|
+
/** The text fetched for `src`, kept so a change of options does not fetch again. */
|
|
45
|
+
#fetched = null;
|
|
46
|
+
connectedCallback() {
|
|
47
|
+
registerFont(this.ownerDocument);
|
|
48
|
+
this.#observer.observe(this, { childList: true, subtree: true, characterData: true });
|
|
49
|
+
this.#schedule();
|
|
50
|
+
}
|
|
51
|
+
disconnectedCallback() {
|
|
52
|
+
this.#observer.disconnect();
|
|
53
|
+
}
|
|
54
|
+
attributeChangedCallback() {
|
|
55
|
+
if (this.isConnected)
|
|
56
|
+
this.#schedule();
|
|
57
|
+
}
|
|
58
|
+
get src() {
|
|
59
|
+
return this.getAttribute("src") ?? "";
|
|
60
|
+
}
|
|
61
|
+
set src(v) {
|
|
62
|
+
this.setAttribute("src", v);
|
|
63
|
+
}
|
|
64
|
+
get theme() {
|
|
65
|
+
return optionsFromAttributes(this).theme ?? "auto";
|
|
66
|
+
}
|
|
67
|
+
set theme(v) {
|
|
68
|
+
this.setAttribute("theme", v);
|
|
69
|
+
}
|
|
70
|
+
get barNumbers() {
|
|
71
|
+
return optionsFromAttributes(this).barNumbers ?? true;
|
|
72
|
+
}
|
|
73
|
+
set barNumbers(v) {
|
|
74
|
+
if (v)
|
|
75
|
+
this.removeAttribute("bar-numbers");
|
|
76
|
+
else
|
|
77
|
+
this.setAttribute("bar-numbers", "off");
|
|
78
|
+
}
|
|
79
|
+
get rehearsalLetters() {
|
|
80
|
+
return this.hasAttribute("rehearsal-letters");
|
|
81
|
+
}
|
|
82
|
+
set rehearsalLetters(v) {
|
|
83
|
+
this.toggleAttribute("rehearsal-letters", v);
|
|
84
|
+
}
|
|
85
|
+
/** Every change in one task (an upgrade sets each attribute in turn) renders once. */
|
|
86
|
+
#schedule() {
|
|
87
|
+
if (this.#scheduled)
|
|
88
|
+
return;
|
|
89
|
+
this.#scheduled = true;
|
|
90
|
+
queueMicrotask(() => {
|
|
91
|
+
this.#scheduled = false;
|
|
92
|
+
void this.#render();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async #render() {
|
|
96
|
+
const script = this.querySelector('script[type="text/barmark"]');
|
|
97
|
+
const options = optionsFromAttributes(this);
|
|
98
|
+
// With no source of its own, a server-rendered chart keeps its music and takes the options it can.
|
|
99
|
+
if (!this.src && !script)
|
|
100
|
+
return this.#restyle(options);
|
|
101
|
+
const render = ++this.#renders;
|
|
102
|
+
const content = await this.#content(script, options);
|
|
103
|
+
if (render !== this.#renders)
|
|
104
|
+
return;
|
|
105
|
+
(this.shadowRoot ?? this.attachShadow({ mode: "open" })).innerHTML = content;
|
|
106
|
+
}
|
|
107
|
+
async #content(script, options) {
|
|
108
|
+
if (!this.src)
|
|
109
|
+
return shadowContent(inlineChartText(script?.textContent ?? ""), options);
|
|
110
|
+
try {
|
|
111
|
+
return shadowContent(await this.#text(this.src), options);
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
this.#fetched = null;
|
|
115
|
+
return loadErrorContent(`Could not load ${this.src}: ${e.message}`, options);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
#text(src) {
|
|
119
|
+
if (this.#fetched?.src !== src)
|
|
120
|
+
this.#fetched = { src, text: fetchText(src) };
|
|
121
|
+
return this.#fetched.text;
|
|
122
|
+
}
|
|
123
|
+
#restyle(options) {
|
|
124
|
+
const chart = this.shadowRoot?.querySelector(".bm-chart");
|
|
125
|
+
chart?.setAttribute("data-theme", options.theme ?? "auto");
|
|
126
|
+
if (options.barNumbers === false)
|
|
127
|
+
chart?.setAttribute("data-bar-numbers", "off");
|
|
128
|
+
else
|
|
129
|
+
chart?.removeAttribute("data-bar-numbers");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
customElements.define("barmark-chart", BarmarkChart);
|
|
133
|
+
}
|
|
134
|
+
if (typeof customElements !== "undefined" && !customElements.get("barmark-chart"))
|
|
135
|
+
defineElement();
|
package/dist/escape.d.ts
ADDED
package/dist/escape.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const ENTITIES = {
|
|
2
|
+
"&": "&",
|
|
3
|
+
"<": "<",
|
|
4
|
+
">": ">",
|
|
5
|
+
'"': """,
|
|
6
|
+
"'": "'",
|
|
7
|
+
};
|
|
8
|
+
/** Text from the chart, safe in element content and in quoted attributes. */
|
|
9
|
+
export const escapeHtml = (text) => text.replace(/[&<>"']/g, (c) => ENTITIES[c] ?? c);
|
package/dist/font.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edwin is registered on the host page, not in the shadow root (some
|
|
3
|
+
* browsers ignore @font-face there), under its own family name so it can
|
|
4
|
+
* never collide with a page's fonts. The file ships unmodified (OFL).
|
|
5
|
+
*/
|
|
6
|
+
export declare const FONT_FAMILY = "barmark Edwin";
|
|
7
|
+
/** One @font-face rule for the font at `url`; the URL cannot close the string or the <style>. */
|
|
8
|
+
export declare function fontFaceCss(url: string): string;
|
package/dist/font.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edwin is registered on the host page, not in the shadow root (some
|
|
3
|
+
* browsers ignore @font-face there), under its own family name so it can
|
|
4
|
+
* never collide with a page's fonts. The file ships unmodified (OFL).
|
|
5
|
+
*/
|
|
6
|
+
export const FONT_FAMILY = "barmark Edwin";
|
|
7
|
+
/** One @font-face rule for the font at `url`; the URL cannot close the string or the <style>. */
|
|
8
|
+
export function fontFaceCss(url) {
|
|
9
|
+
const safe = url.replace(/["\\<>\n\r]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`);
|
|
10
|
+
return `@font-face{font-family:"${FONT_FAMILY}";src:url("${safe}") format("opentype");font-display:swap}`;
|
|
11
|
+
}
|
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type RenderOptions = {
|
|
2
|
+
theme?: "auto" | "light" | "dark";
|
|
3
|
+
barNumbers?: boolean;
|
|
4
|
+
rehearsalLetters?: boolean;
|
|
5
|
+
/** List the chart's errors and warnings below it. */
|
|
6
|
+
diagnostics?: boolean;
|
|
7
|
+
/** Where the page serves Edwin-Roman.otf: emits its @font-face rule before the chart. */
|
|
8
|
+
fontUrl?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function renderChart(text: string, options?: RenderOptions): string;
|
|
11
|
+
export declare const THEMES: readonly ["auto", "light", "dark"];
|
|
12
|
+
/** What goes in the shadow root: the styles and the chart. */
|
|
13
|
+
export declare const shadowContent: (text: string, options?: RenderOptions) => string;
|
|
14
|
+
/** A chart that could not be loaded: the message, shown where the chart would be. */
|
|
15
|
+
export declare const loadErrorContent: (message: string, options?: RenderOptions) => string;
|
|
16
|
+
export declare function renderChartUnwrapped(text: string, options?: RenderOptions): string;
|