@dsebastien/music-notation 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sebastien Dubois
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,108 @@
1
- # Temporary Holding Version
1
+ # Music Notation
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Chord diagrams, piano keyboards, fretboards and ChordPro charts, rendered as SVG and HTML from small text blocks. No framework, no build step for pages that load the browser bundle. Made for [Obsidian](https://obsidian.md) (the [Music Practice](https://github.com/dsebastien/obsidian-music-practice) plugin and Obsidian Publish), usable on any web page.
4
+
5
+ ![Chord diagrams, a piano chord, a keyboard, a fretboard and a chord chart](docs/images/overview.png)
6
+
7
+ ## Blocks
8
+
9
+ Each block is a fenced code block whose language says what it draws:
10
+
11
+ ````markdown
12
+ ```chord
13
+ A x02220 x01230
14
+ ```
15
+
16
+ ```chord
17
+ C/G
18
+ instrument: piano
19
+ fingering: 5-1-2-4
20
+ ```
21
+
22
+ ```keyboard
23
+ D dorian
24
+ labels: degrees
25
+ ```
26
+
27
+ ```fretboard
28
+ scale: A minor pentatonic
29
+ frets: 5-8
30
+ labels: intervals
31
+ ```
32
+
33
+ ```chords
34
+ {title: Example}
35
+ {key: G}
36
+ [G]Hello [D/F#]little [Em]world
37
+ ```
38
+ ````
39
+
40
+ The full syntax is in the [guide](https://dsebastien.github.io/music-notation/). Mistakes show an inline message and leave the source untouched.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ npm install @dsebastien/music-notation
46
+ ```
47
+
48
+ ```ts
49
+ import { renderBlock, injectStyles } from '@dsebastien/music-notation'
50
+ import { lookupGuitarVoicing } from '@dsebastien/music-notation/voicings'
51
+
52
+ injectStyles(document)
53
+ const result = renderBlock('chord', 'Am', container, { voicings: lookupGuitarVoicing })
54
+ if (!result.ok) console.warn(result.error)
55
+ ```
56
+
57
+ - `renderBlock(language, source, host, options)` appends the rendering to `host` and never throws. It returns `{ ok, el, error, destroy() }`.
58
+ - `@dsebastien/music-notation/voicings` adds about 2,000 guitar voicings (`chord` blocks with only a symbol). It's a separate entry point because the data weighs about 230 KB.
59
+ - The renderers (`renderGuitarDiagram`, `renderKeyboard`, `renderFretboard`, `renderChart`), the parsers and the theory helpers (`parseChord`, `transposeChord`, `chordNoteSet`, `scaleNoteSet`…) are exported too.
60
+ - Styles: `injectStyles(document)`, or ship `@dsebastien/music-notation/music-notation.css`. Colors follow Obsidian's theme variables, with fallbacks for any page.
61
+
62
+ ## Obsidian Publish
63
+
64
+ Add this to your site's `publish.js`. It loads the bundle only on pages that have notation blocks:
65
+
66
+ ```js
67
+ ;(() => {
68
+ const VERSION = '0.1.0'
69
+ const SELECTOR = ['chord', 'chords', 'keyboard', 'fretboard']
70
+ .map((l) => `pre > code.language-${l}`)
71
+ .join(', ')
72
+ let requested = false
73
+ const load = () => {
74
+ if (requested || !document.querySelector(SELECTOR)) return
75
+ requested = true
76
+ const script = document.createElement('script')
77
+ script.src = `https://cdn.jsdelivr.net/npm/@dsebastien/music-notation@${VERSION}/dist/music-notation.publish.min.js`
78
+ document.head.appendChild(script)
79
+ }
80
+ load()
81
+ new MutationObserver(load).observe(document.body, { childList: true, subtree: true })
82
+ })()
83
+ ```
84
+
85
+ The bundle renders every block, now and as you navigate, and hides a block's source only once it rendered.
86
+
87
+ ## Roadmap
88
+
89
+ - 0.1: chord diagrams (guitar, piano), keyboards, fretboards, ChordPro charts, the Publish bundle.
90
+ - 0.2: tabs and scores (alphaTab) with playback, imports (Guitar Pro, MusicXML, text tabs, chords over lyrics).
91
+
92
+ ## Credits and licenses
93
+
94
+ MIT. Built on [tonal](https://github.com/tonaljs/tonal) (MIT) for music theory and [chords-db](https://github.com/tombatossals/chords-db) (MIT) for guitar voicings.
95
+
96
+ ## News & support
97
+
98
+ To stay up to date about this library, Obsidian in general, personal knowledge management and note-taking:
99
+
100
+ - Subscribe to [my newsletter](https://dsebastien.net/newsletter)
101
+ - Subscribe to [my YouTube channel](https://youtube.com/@dsebastien)
102
+ - Join the [Knowii community](https://www.store.dsebastien.net/product/knowii-community/)
103
+
104
+ If this is useful to you:
105
+
106
+ - [Become a GitHub Sponsor](https://github.com/sponsors/dsebastien)
107
+ - [Buy me a coffee](https://www.buymeacoffee.com/dsebastien)
108
+ - [Check out my products](https://store.dsebastien.net)
@@ -0,0 +1,57 @@
1
+ # Third-party licenses
2
+
3
+ ## tonal
4
+
5
+ Shipped in: music-notation.publish.min.js
6
+
7
+ ```
8
+ The MIT License (MIT)
9
+
10
+ Copyright (c) 2015 danigb
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ ```
30
+
31
+ ## @tombatossals/chords-db
32
+
33
+ Shipped in: voicings/index.js, music-notation.publish.min.js
34
+
35
+ ```
36
+ MIT License
37
+
38
+ Copyright (c) 2016 David Rubert
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining a copy
41
+ of this software and associated documentation files (the "Software"), to deal
42
+ in the Software without restriction, including without limitation the rights
43
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
44
+ copies of the Software, and to permit persons to whom the Software is
45
+ furnished to do so, subject to the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be included in all
48
+ copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
56
+ SOFTWARE.
57
+ ```
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Block sources: `key: value` lines, with an optional first line that has no key (the shorthand:
3
+ * "A x02220 x01230"). Keys are case-insensitive; the last value wins.
4
+ */
5
+ export interface BlockSource {
6
+ /** The first line when it has no `key:`; null otherwise. */
7
+ head: string | null;
8
+ fields: Map<string, string>;
9
+ }
10
+ export declare function parseBlockSource(source: string): BlockSource;
11
+ /** "5-8", "5", "0 - 12" → [from, to]; null when invalid. */
12
+ export declare function parseRange(text: string): [number, number] | null;
@@ -0,0 +1,8 @@
1
+ /** Shared by keyboard and fretboard blocks: which notes to show and how to label them. */
2
+ import type { BlockSource } from './block-source.js';
3
+ import { type NoteLabel, type NoteSet } from '../theory/note-set.js';
4
+ /** `scale:`, `chord:` or `notes:`; a shorthand first line is tried as each, in that order. */
5
+ export declare function noteSetOf(block: BlockSource): NoteSet;
6
+ /** `labels:`; `fingers` only where fingering was given (piano chords). */
7
+ export declare function labelField(block: BlockSource, fallback: NoteLabel, fingers?: boolean): NoteLabel;
8
+ export declare function orFail<T>(value: T | null, message: string): T;
@@ -0,0 +1,16 @@
1
+ import type { VoicingLookup } from '../voicings/index.js';
2
+ export interface RenderOptions {
3
+ /** Finds guitar voicings for chord blocks that only give a symbol (see `/voicings`). */
4
+ voicings?: VoicingLookup;
5
+ /** Semitones to transpose chord charts by. */
6
+ transpose?: number;
7
+ }
8
+ export type Renderer = (doc: Document, source: string, options: RenderOptions) => Element;
9
+ /** Block languages (code fence names) and their renderers. */
10
+ export declare const RENDERERS: {
11
+ chord: Renderer;
12
+ chords: Renderer;
13
+ keyboard: Renderer;
14
+ fretboard: Renderer;
15
+ };
16
+ export type NotationLanguage = keyof typeof RENDERERS;
@@ -0,0 +1,24 @@
1
+ /** Standard tuning, lowest string first. */
2
+ export declare const STANDARD_TUNING: number[];
3
+ export interface FrettedVoicing {
4
+ /** Absolute frets, lowest string first: null = muted, 0 = open. */
5
+ frets: (number | null)[];
6
+ /** Finger per string ("1".."4", "T"); null = none. */
7
+ fingers: (string | null)[];
8
+ /** First fret shown; computed when absent. */
9
+ baseFret?: number;
10
+ }
11
+ export interface GuitarDiagramOptions {
12
+ title?: string;
13
+ /** Chroma of the chord's root: its dots are marked. */
14
+ root?: number | null;
15
+ /** Open-string pitches (MIDI), lowest first. */
16
+ tuning?: number[];
17
+ }
18
+ /** "x02220", "x-10-12-12-11-10", "x 10 12 12 11 10" → frets; null when invalid. */
19
+ export declare function parseFrets(text: string): (number | null)[] | null;
20
+ /** "x01230", "x-1-3-4-2-1", "x 1 3 4 2 1" → fingers; 0 and x mean no finger. */
21
+ export declare function parseFingers(text: string): (string | null)[];
22
+ /** The first fret shown: 1 when the chord fits in the first 4 frets, else its lowest fretted fret. */
23
+ export declare function baseFretOf(frets: (number | null)[]): number;
24
+ export declare function renderGuitarDiagram(doc: Document, voicing: FrettedVoicing, options?: GuitarDiagramOptions): SVGElement;
@@ -0,0 +1,6 @@
1
+ import type { Song } from './chordpro.js';
2
+ export interface ChartOptions {
3
+ /** Semitones added to the song's own `{transpose}`. */
4
+ transpose?: number;
5
+ }
6
+ export declare function renderChart(doc: Document, song: Song, options?: ChartOptions): HTMLElement;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A ChordPro subset: metadata directives, sections (verse, chorus, bridge, tab, grid), comments,
3
+ * `[C]` chords inside lyrics, `{transpose: n}`. Unknown directives are ignored.
4
+ */
5
+ export interface Segment {
6
+ chord: string | null;
7
+ text: string;
8
+ }
9
+ export type Line = {
10
+ kind: 'lyrics';
11
+ segments: Segment[];
12
+ } | {
13
+ kind: 'comment';
14
+ text: string;
15
+ } | {
16
+ kind: 'text';
17
+ text: string;
18
+ } | {
19
+ kind: 'empty';
20
+ };
21
+ export interface Section {
22
+ kind: 'verse' | 'chorus' | 'bridge' | 'tab' | 'grid' | 'none';
23
+ label: string | null;
24
+ lines: Line[];
25
+ }
26
+ export interface Song {
27
+ title: string | null;
28
+ subtitle: string | null;
29
+ artist: string | null;
30
+ key: string | null;
31
+ capo: number | null;
32
+ tempo: string | null;
33
+ time: string | null;
34
+ transpose: number;
35
+ sections: Section[];
36
+ }
37
+ /** Split "[C]Hello [G]world" into chord/text segments. */
38
+ export declare function parseLyrics(line: string): Segment[];
39
+ export declare function parseChordPro(source: string): Song;