@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.
@@ -0,0 +1,23 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/render/dom.ts", "../src/chord/guitar.ts", "../src/chords/chordpro.ts", "../src/theory/transpose.ts", "../src/chords/chart.ts", "../src/theory/note-set.ts", "../src/fretboard/fretboard.ts", "../src/fretboard/tuning.ts", "../src/keyboard/keyboard.ts", "../src/theory/chord-symbol.ts", "../src/blocks/block-source.ts", "../src/blocks/note-set-field.ts", "../src/blocks/renderers.ts", "../src/render/render-block.ts"],
4
+ "sourcesContent": [
5
+ "/** DOM and SVG helpers. Everything is created in the host's document (popout windows, Publish). */\n\nexport const SVG_NS = 'http://www.w3.org/2000/svg'\n\ntype Attributes = Record<string, string | number | undefined>\n\nconst apply = (el: Element, attrs: Attributes) => {\n for (const [name, value] of Object.entries(attrs)) {\n if (value !== undefined) el.setAttribute(name, String(value))\n }\n}\n\n/** An SVG element with attributes and children. */\nexport function svg(\n doc: Document,\n tag: string,\n attrs: Attributes = {},\n children: (Element | null)[] = []\n): SVGElement {\n const el = doc.createElementNS(SVG_NS, tag)\n apply(el, attrs)\n for (const child of children) if (child) el.appendChild(child)\n return el\n}\n\n/** An SVG <text>. */\nexport function svgText(doc: Document, text: string, attrs: Attributes = {}): SVGElement {\n const el = svg(doc, 'text', attrs)\n el.textContent = text\n return el\n}\n\n/** An HTML element with a class and optional text. */\nexport function html<K extends keyof HTMLElementTagNameMap>(\n doc: Document,\n tag: K,\n cls?: string,\n text?: string\n): HTMLElementTagNameMap[K] {\n const el = doc.createElement(tag)\n if (cls) el.className = cls\n if (text !== undefined) el.textContent = text\n return el\n}\n\n/** A root <svg> sized by its viewBox, scaling with the container up to its natural width. */\nexport function svgRoot(\n doc: Document,\n width: number,\n height: number,\n cls: string,\n label: string\n): SVGElement {\n return svg(doc, 'svg', {\n 'xmlns': SVG_NS,\n 'viewBox': `0 0 ${width} ${height}`,\n 'width': width,\n 'class': `mn-svg ${cls}`,\n 'role': 'img',\n 'aria-label': label\n })\n}\n",
6
+ "/** Guitar (and any fretted instrument) chord diagrams, as SVG. */\nimport { svg, svgRoot, svgText } from '../render/dom.js'\n\n/** Standard tuning, lowest string first. */\nexport const STANDARD_TUNING = [40, 45, 50, 55, 59, 64]\n\nexport interface FrettedVoicing {\n /** Absolute frets, lowest string first: null = muted, 0 = open. */\n frets: (number | null)[]\n /** Finger per string (\"1\"..\"4\", \"T\"); null = none. */\n fingers: (string | null)[]\n /** First fret shown; computed when absent. */\n baseFret?: number\n}\n\nexport interface GuitarDiagramOptions {\n title?: string\n /** Chroma of the chord's root: its dots are marked. */\n root?: number | null\n /** Open-string pitches (MIDI), lowest first. */\n tuning?: number[]\n}\n\n/** \"x02220\", \"x-10-12-12-11-10\", \"x 10 12 12 11 10\" → frets; null when invalid. */\nexport function parseFrets(text: string): (number | null)[] | null {\n const trimmed = text.trim()\n const tokens = /[\\s,-]/.test(trimmed) ? trimmed.split(/[\\s,-]+/) : [...trimmed]\n const frets = tokens\n .filter(Boolean)\n .map((t) => (/^[xX]$/.test(t) ? null : /^\\d+$/.test(t) ? Number(t) : NaN))\n return frets.length >= 2 && frets.every((f) => f === null || (Number.isInteger(f) && f <= 36))\n ? frets\n : null\n}\n\n/** \"x01230\", \"x-1-3-4-2-1\", \"x 1 3 4 2 1\" → fingers; 0 and x mean no finger. */\nexport function parseFingers(text: string): (string | null)[] {\n const trimmed = text.trim()\n const tokens =\n /[\\s,]/.test(trimmed) || trimmed.includes('-', 1) ? trimmed.split(/[\\s,-]+/) : [...trimmed]\n return tokens.filter(Boolean).map((t) => (/^[0xX]$/.test(t) ? null : t.toUpperCase()))\n}\n\n/** The first fret shown: 1 when the chord fits in the first 4 frets, else its lowest fretted fret. */\nexport function baseFretOf(frets: (number | null)[]): number {\n const fretted = frets.filter((f): f is number => f !== null && f > 0)\n if (!fretted.length || Math.max(...fretted) <= 4) return 1\n return Math.min(...fretted)\n}\n\ninterface Barre {\n fret: number\n from: number\n to: number\n finger: string\n}\n\n/** Strings held by the same finger on the same fret, across at least two strings. */\nfunction barresOf(voicing: FrettedVoicing): Barre[] {\n const byKey = new Map<string, number[]>()\n voicing.frets.forEach((fret, string) => {\n const finger = voicing.fingers[string]\n if (fret === null || fret === 0 || !finger) return\n const key = `${finger}@${fret}`\n byKey.set(key, [...(byKey.get(key) ?? []), string])\n })\n const barres: Barre[] = []\n for (const [key, strings] of byKey) {\n if (strings.length < 2) continue\n const [finger, fret] = key.split('@')\n barres.push({\n fret: Number(fret),\n from: Math.min(...strings),\n to: Math.max(...strings),\n finger: finger!\n })\n }\n return barres\n}\n\nconst SPACING = 18\nconst ROW = 20\nconst LEFT = 30\nconst TITLE = 22\nconst MARKERS = 16\n\nexport function renderGuitarDiagram(\n doc: Document,\n voicing: FrettedVoicing,\n options: GuitarDiagramOptions = {}\n): SVGElement {\n const strings = voicing.frets.length\n const base = voicing.baseFret ?? baseFretOf(voicing.frets)\n const fretted = voicing.frets.filter((f): f is number => f !== null && f > 0)\n const rows = Math.max(4, fretted.length ? Math.max(...fretted) - base + 1 : 0)\n const top = (options.title ? TITLE : 4) + MARKERS\n const width = LEFT + (strings - 1) * SPACING + 14\n const height = top + rows * ROW + 10\n const x = (string: number) => LEFT + string * SPACING\n const y = (row: number) => top + row * ROW\n\n const label = options.title ? `${options.title} chord diagram` : 'Chord diagram'\n const root = svgRoot(doc, width, height, 'mn-guitar-chord', label)\n if (options.title) {\n root.appendChild(\n svgText(doc, options.title, {\n 'class': 'mn-title',\n 'x': width / 2,\n 'y': 15,\n 'text-anchor': 'middle'\n })\n )\n }\n\n for (let row = 0; row <= rows; row++) {\n root.appendChild(\n svg(doc, 'line', {\n class: row === 0 && base === 1 ? 'mn-nut' : 'mn-fret',\n x1: x(0),\n x2: x(strings - 1),\n y1: y(row),\n y2: y(row)\n })\n )\n }\n for (let s = 0; s < strings; s++) {\n root.appendChild(\n svg(doc, 'line', { class: 'mn-string', x1: x(s), x2: x(s), y1: y(0), y2: y(rows) })\n )\n }\n if (base > 1) {\n root.appendChild(\n svgText(doc, `${base}fr`, {\n 'class': 'mn-base-fret',\n 'x': LEFT - 10,\n 'y': y(0.5) + 4,\n 'text-anchor': 'end'\n })\n )\n }\n\n for (const barre of barresOf(voicing)) {\n const cy = y(barre.fret - base + 0.5)\n root.appendChild(\n svg(doc, 'rect', {\n class: 'mn-barre',\n x: x(barre.from) - 7,\n y: cy - 7,\n width: x(barre.to) - x(barre.from) + 14,\n height: 14,\n rx: 7\n })\n )\n }\n\n const tuning = options.tuning ?? (strings === 6 ? STANDARD_TUNING : undefined)\n voicing.frets.forEach((fret, s) => {\n const cx = x(s)\n if (fret === null) {\n root.appendChild(\n svgText(doc, '×', {\n 'class': 'mn-muted',\n 'x': cx,\n 'y': top - 4,\n 'text-anchor': 'middle'\n })\n )\n return\n }\n const open = tuning?.[s]\n const isRoot =\n options.root !== undefined &&\n options.root !== null &&\n open !== undefined &&\n (open + fret) % 12 === options.root\n if (fret === 0) {\n root.appendChild(\n svg(doc, 'circle', {\n class: `mn-open${isRoot ? ' mn-root' : ''}`,\n cx,\n cy: top - 8,\n r: 4\n })\n )\n return\n }\n const cy = y(fret - base + 0.5)\n root.appendChild(\n svg(doc, 'circle', { class: `mn-dot${isRoot ? ' mn-root' : ''}`, cx, cy, r: 7 })\n )\n const finger = voicing.fingers[s]\n if (finger) {\n root.appendChild(\n svgText(doc, finger, {\n 'class': 'mn-dot-text',\n 'x': cx,\n 'y': cy + 4,\n 'text-anchor': 'middle'\n })\n )\n }\n })\n return root\n}\n",
7
+ "/**\n * A ChordPro subset: metadata directives, sections (verse, chorus, bridge, tab, grid), comments,\n * `[C]` chords inside lyrics, `{transpose: n}`. Unknown directives are ignored.\n */\n\nexport interface Segment {\n chord: string | null\n text: string\n}\n\nexport type Line =\n | { kind: 'lyrics'; segments: Segment[] }\n | { kind: 'comment'; text: string }\n | { kind: 'text'; text: string }\n | { kind: 'empty' }\n\nexport interface Section {\n kind: 'verse' | 'chorus' | 'bridge' | 'tab' | 'grid' | 'none'\n label: string | null\n lines: Line[]\n}\n\nexport interface Song {\n title: string | null\n subtitle: string | null\n artist: string | null\n key: string | null\n capo: number | null\n tempo: string | null\n time: string | null\n transpose: number\n sections: Section[]\n}\n\nconst ALIASES: Record<string, string> = {\n t: 'title',\n st: 'subtitle',\n c: 'comment',\n ci: 'comment',\n comment_italic: 'comment',\n cb: 'comment',\n comment_box: 'comment',\n highlight: 'comment',\n soc: 'start_of_chorus',\n eoc: 'end_of_chorus',\n sov: 'start_of_verse',\n eov: 'end_of_verse',\n sob: 'start_of_bridge',\n eob: 'end_of_bridge',\n sot: 'start_of_tab',\n eot: 'end_of_tab',\n sog: 'start_of_grid',\n eog: 'end_of_grid'\n}\n\nconst SECTION_KINDS = new Set(['verse', 'chorus', 'bridge', 'tab', 'grid'])\n\n/** Split \"[C]Hello [G]world\" into chord/text segments. */\nexport function parseLyrics(line: string): Segment[] {\n const segments: Segment[] = []\n const re = /\\[([^\\]]*)\\]/g\n let last = 0\n let chord: string | null = null\n let m: RegExpExecArray | null\n while ((m = re.exec(line))) {\n const text = line.slice(last, m.index)\n if (text || chord !== null) segments.push({ chord, text })\n chord = m[1]!.trim()\n last = m.index + m[0].length\n }\n const rest = line.slice(last)\n if (rest || chord !== null) segments.push({ chord, text: rest })\n return segments\n}\n\n/**\n * \"{title: Song}\", \"{soc}\", \"{start_of_verse Verse 2}\" → name and value; null when the line isn't\n * a directive. No backtracking regex: one malformed line must never freeze the page.\n */\nfunction parseDirective(line: string): { name: string; value: string } | null {\n const trimmed = line.trim()\n if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) return null\n const inner = trimmed.slice(1, -1).trim()\n const m = /^[\\w-]+/.exec(inner)\n if (!m) return null\n const rest = inner.slice(m[0].length)\n if (rest && !/^[\\s:]/.test(rest)) return null\n return { name: m[0].toLowerCase(), value: rest.replace(/^\\s*:?/, '').trim() }\n}\n\nexport function parseChordPro(source: string): Song {\n const song: Song = {\n title: null,\n subtitle: null,\n artist: null,\n key: null,\n capo: null,\n tempo: null,\n time: null,\n transpose: 0,\n sections: []\n }\n let section: Section = { kind: 'none', label: null, lines: [] }\n const open = (kind: Section['kind'], label: string | null) => {\n if (section.lines.length || section.kind !== 'none') song.sections.push(section)\n section = { kind, label, lines: [] }\n }\n\n for (const raw of source.split(/\\r?\\n/)) {\n const line = raw.replace(/\\s+$/, '')\n if (/^\\s*#/.test(line)) continue\n const directive = parseDirective(line)\n if (directive) {\n const { name, value } = directive\n const key = ALIASES[name] ?? name\n const start = /^start_of_(\\w+)$/.exec(key)\n const end = /^end_of_(\\w+)$/.exec(key)\n if (start) {\n const kind = SECTION_KINDS.has(start[1]!) ? (start[1] as Section['kind']) : 'verse'\n open(kind, value.replace(/^label\\s*=\\s*\"?|\"$/g, '') || null)\n } else if (end) {\n open('none', null)\n } else if (key === 'comment') {\n section.lines.push({ kind: 'comment', text: value })\n } else if (\n key === 'title' ||\n key === 'subtitle' ||\n key === 'artist' ||\n key === 'key' ||\n key === 'tempo' ||\n key === 'time'\n ) {\n song[key] = value || null\n } else if (key === 'capo') {\n const capo = Number(value)\n song.capo = Number.isInteger(capo) && capo > 0 ? capo : null\n } else if (key === 'transpose') {\n const semitones = Number(value)\n if (Number.isInteger(semitones)) song.transpose = semitones\n }\n continue\n }\n if (!line.trim()) section.lines.push({ kind: 'empty' })\n else if (section.kind === 'tab' || section.kind === 'grid')\n section.lines.push({ kind: 'text', text: line })\n else section.lines.push({ kind: 'lyrics', segments: parseLyrics(line) })\n }\n open('none', null)\n // Leading and trailing blank lines carry no meaning.\n for (const s of song.sections) {\n while (s.lines[0]?.kind === 'empty') s.lines.shift()\n while (s.lines[s.lines.length - 1]?.kind === 'empty') s.lines.pop()\n }\n song.sections = song.sections.filter((s) => s.lines.length || s.label)\n return song\n}\n",
8
+ "/** Transposing chord symbols and keys by semitones. */\nimport { chromaOf, isFlat, nameOf, splitChordSymbol } from './pitch.js'\n\n/** Keys written with flats (F♯ over G♭, D♭ over C♯): chords transposed into them use flats. */\nconst FLAT_KEYS = new Set([5, 10, 3, 8, 1])\nconst FLAT_MINOR_KEYS = new Set([2, 7, 0, 5, 10, 3])\n\n/** Whether a key (\"F\", \"Bb\", \"Dm\") is written with flats. */\nexport function keyPrefersFlats(key: string): boolean {\n const m = /^([A-G][#♯b♭]?)(m|min|minor)?\\b/.exec(key.trim())\n if (!m) return false\n const chroma = chromaOf(m[1]!)\n if (chroma === null) return false\n if (isFlat(m[1]!)) return true\n return m[2] ? FLAT_MINOR_KEYS.has(chroma) : FLAT_KEYS.has(chroma)\n}\n\nconst shift = (note: string, semitones: number, preferFlats: boolean): string => {\n const chroma = chromaOf(note)\n return chroma === null ? note : nameOf(chroma + semitones, preferFlats)\n}\n\n/**\n * Transpose a chord symbol (\"F#m7/C#\", +2 → \"G#m7/D#\"). Spelling follows `preferFlats`, or the\n * symbol's own when not given. Text that isn't a chord symbol comes back unchanged.\n */\nexport function transposeChord(symbol: string, semitones: number, preferFlats?: boolean): string {\n const trimmed = symbol.trim()\n const parts = splitChordSymbol(trimmed)\n const steps = Math.round(semitones) || 0\n if (!parts || steps % 12 === 0) return trimmed\n const flats = preferFlats ?? isFlat(parts.root)\n const bass = parts.bass ? `/${shift(parts.bass, steps, flats)}` : ''\n return `${shift(parts.root, steps, flats)}${parts.suffix}${bass}`\n}\n\n/** Transpose a key (\"Am\", +3 → \"Cm\"), spelled the way that key is usually written. */\nexport function transposeKey(key: string, semitones: number): string {\n const m = /^([A-G][#♯b♭]?)(.*)$/.exec(key.trim())\n const chroma = m ? chromaOf(m[1]!) : null\n const steps = Math.round(semitones) || 0\n if (!m || chroma === null || steps % 12 === 0) return key.trim()\n const target = (((chroma + steps) % 12) + 12) % 12\n const minor = /^\\s*(m|min|minor)\\b/.test(m[2]!)\n const flats = minor ? FLAT_MINOR_KEYS.has(target) : FLAT_KEYS.has(target)\n return `${nameOf(target, flats)}${m[2]}`\n}\n",
9
+ "/** ChordPro songs rendered as HTML charts: chords above the lyrics, transposable. */\nimport { html } from '../render/dom.js'\nimport { keyPrefersFlats, transposeChord, transposeKey } from '../theory/transpose.js'\nimport type { Song } from './chordpro.js'\n\nexport interface ChartOptions {\n /** Semitones added to the song's own `{transpose}`. */\n transpose?: number\n}\n\nconst SECTION_LABELS: Record<string, string> = {\n chorus: 'Chorus',\n bridge: 'Bridge',\n tab: 'Tab',\n grid: 'Grid'\n}\n\nexport function renderChart(doc: Document, song: Song, options: ChartOptions = {}): HTMLElement {\n const extra = options.transpose ?? 0\n const semitones = song.transpose + (Number.isFinite(extra) ? Math.round(extra) : 0)\n const key = song.key ? transposeKey(song.key, semitones) : null\n const flats = key ? keyPrefersFlats(key) : undefined\n const chord = (symbol: string) => transposeChord(symbol, semitones, flats)\n\n const root = html(doc, 'div', 'mn-chart')\n if (song.title || song.subtitle || song.artist) {\n const header = html(doc, 'div', 'mn-chart-header')\n if (song.title) header.appendChild(html(doc, 'div', 'mn-chart-title', song.title))\n const by = [song.subtitle, song.artist].filter(Boolean).join(' · ')\n if (by) header.appendChild(html(doc, 'div', 'mn-chart-subtitle', by))\n root.appendChild(header)\n }\n const meta = [\n key ? `Key ${key}` : null,\n song.capo ? `Capo ${song.capo}` : null,\n song.tempo ? `${song.tempo} BPM` : null,\n song.time ? song.time : null,\n semitones % 12 ? `Transposed ${semitones > 0 ? '+' : ''}${semitones}` : null\n ].filter((m): m is string => m !== null)\n if (meta.length) root.appendChild(html(doc, 'div', 'mn-chart-meta', meta.join(' · ')))\n\n for (const section of song.sections) {\n const el = html(doc, 'div', `mn-section mn-section-${section.kind}`)\n const label = section.label ?? SECTION_LABELS[section.kind]\n if (label) el.appendChild(html(doc, 'div', 'mn-section-label', label))\n for (const line of section.lines) {\n if (line.kind === 'empty') el.appendChild(html(doc, 'div', 'mn-line mn-line-empty'))\n else if (line.kind === 'comment')\n el.appendChild(html(doc, 'div', 'mn-line mn-comment', line.text))\n else if (line.kind === 'text')\n el.appendChild(html(doc, 'pre', 'mn-line mn-pre', line.text))\n else {\n const row = html(doc, 'div', 'mn-line')\n const chordsOnly = line.segments.every((s) => !s.text.trim())\n if (chordsOnly) row.classList.add('mn-chords-only')\n for (const segment of line.segments) {\n const seg = html(doc, 'span', 'mn-seg')\n seg.appendChild(\n html(\n doc,\n 'span',\n 'mn-chord',\n segment.chord === null ? '' : chord(segment.chord)\n )\n )\n seg.appendChild(\n html(doc, 'span', 'mn-lyric', segment.text || (chordsOnly ? '' : ' '))\n )\n row.appendChild(seg)\n }\n el.appendChild(row)\n }\n }\n root.appendChild(el)\n }\n return root\n}\n",
10
+ "/**\n * What keyboards and fretboards highlight: the notes of a chord, a scale or an explicit list, with\n * the root and each note's label (name, degree, interval).\n */\nimport { Chord, Scale } from 'tonal'\nimport { chromaOf, isFlat, midiOf, splitChordSymbol } from './pitch.js'\n\nexport type NoteLabel = 'notes' | 'degrees' | 'intervals' | 'fingers' | 'none'\n\nexport interface NoteSetMember {\n chroma: number\n /** As spelled by the chord, scale or list (\"Bb\", not \"A#\"). */\n name: string\n /** 1-based position in the chord or scale; null for a plain list. */\n degree: number | null\n /** \"1P\", \"3m\", \"5P\"…; null for a plain list. */\n interval: string | null\n}\n\nexport interface NoteSet {\n /** \"A minor pentatonic\", \"Cmaj7\", or the notes as written. */\n title: string\n members: NoteSetMember[]\n /** Chroma of the root; null for a plain list. */\n root: number | null\n /** Exact pitches (MIDI), when the notes were given with octaves (\"C4 E4 G4\"). */\n pitches: number[] | null\n preferFlats: boolean\n}\n\nconst memberFor = (set: NoteSet, chroma: number): NoteSetMember | undefined =>\n set.members.find((m) => m.chroma === chroma)\n\n/** The member a pitch belongs to, if any. */\nexport const memberOf = (set: NoteSet, midi: number): NoteSetMember | undefined =>\n set.pitches && !set.pitches.includes(midi) ? undefined : memberFor(set, ((midi % 12) + 12) % 12)\n\n/** tonal's \"3m\", \"5d\", \"4A\", \"1P\" → how musicians write them: \"b3\", \"b5\", \"#4\", \"R\". */\nexport function intervalLabel(interval: string): string {\n const m = /^(\\d+)([PMmdA]+)$/.exec(interval)\n if (!m) return interval\n const [, number, quality] = m\n if (number === '1' && quality === 'P') return 'R'\n const perfect = ['1', '4', '5', '8', '11', '12'].includes(\n String(((Number(number) - 1) % 7) + 1)\n )\n if (quality === 'P' || quality === 'M') return number!\n if (quality === 'm') return `b${number}`\n if (quality!.startsWith('A')) return `${'#'.repeat(quality!.length)}${number}`\n // Diminished: one flat below perfect, two below major.\n return `${'b'.repeat(quality!.length + (perfect ? 0 : 1))}${number}`\n}\n\n/** Text shown on a highlighted note. */\nexport function labelOf(member: NoteSetMember, label: NoteLabel, finger?: string): string {\n switch (label) {\n case 'none':\n return ''\n case 'degrees':\n return member.degree === null ? member.name : String(member.degree)\n case 'intervals':\n return member.interval === null ? member.name : intervalLabel(member.interval)\n case 'fingers':\n return finger ?? ''\n default:\n return member.name\n }\n}\n\nconst fromNames = (\n title: string,\n names: string[],\n intervals: string[] | null,\n chord = false\n): NoteSet | null => {\n const members: NoteSetMember[] = []\n names.forEach((name, i) => {\n const chroma = chromaOf(name)\n if (chroma === null || members.some((m) => m.chroma === chroma)) return\n members.push({\n chroma,\n name,\n // A chord's degrees are its interval numbers (1 3 5 7); a scale's, its positions.\n degree: intervals\n ? chord\n ? Number(/^\\d+/.exec(intervals[i] ?? '')?.[0] ?? i + 1)\n : i + 1\n : null,\n interval: intervals?.[i] ?? null\n })\n })\n if (!members.length) return null\n return {\n title,\n members,\n root: intervals ? members[0]!.chroma : null,\n pitches: null,\n preferFlats: names.some(isFlat)\n }\n}\n\n/** Notes of a chord symbol (\"Cmaj7\", \"C/G\"); null when tonal doesn't know it. */\nexport function chordNoteSet(symbol: string): NoteSet | null {\n const parts = splitChordSymbol(symbol)\n if (!parts) return null\n const chord = Chord.get(parts.root + parts.suffix)\n if (chord.empty) return null\n const bass = parts.bass\n const set = fromNames(symbol.trim(), chord.notes, chord.intervals, true)\n const bassChroma = bass ? chromaOf(bass) : null\n if (set && bass && bassChroma !== null && !memberFor(set, bassChroma)) {\n set.members.push({ chroma: bassChroma, name: bass, degree: null, interval: null })\n }\n return set\n}\n\n/** Notes of a scale (\"A minor pentatonic\", \"D dorian\"); null when tonal doesn't know it. */\nexport function scaleNoteSet(name: string): NoteSet | null {\n const scale = Scale.get(name.trim())\n if (scale.empty || !scale.tonic) return null\n return fromNames(name.trim(), scale.notes, scale.intervals)\n}\n\n/** An explicit list: \"A C E\" (pitch classes) or \"A3 C4 E4\" (exact pitches). */\nexport function listNoteSet(text: string): NoteSet | null {\n const tokens = text.split(/[\\s,]+/).filter(Boolean)\n if (!tokens.length) return null\n const withOctaves = tokens.every((t) => midiOf(t) !== null)\n const names = withOctaves ? tokens.map((t) => t.replace(/-?\\d$/, '')) : tokens\n if (!withOctaves && names.some((n) => chromaOf(n) === null)) return null\n const set = fromNames(tokens.join(' '), names, null)\n if (set && withOctaves) set.pitches = tokens.map((t) => midiOf(t)!)\n return set\n}\n",
11
+ "/** Fretboards with the notes of a scale, chord or list, as SVG. High string on top, like tabs. */\nimport { svg, svgRoot, svgText } from '../render/dom.js'\nimport { labelOf, memberOf, type NoteLabel, type NoteSet } from '../theory/note-set.js'\nimport { nameOf } from '../theory/pitch.js'\nimport { STANDARD_TUNING } from '../chord/guitar.js'\n\nexport interface FretboardOptions {\n /** Open-string pitches (MIDI), lowest string first. */\n tuning?: number[]\n /** First and last fret shown; 0 shows the open strings. */\n frets?: [number, number]\n label?: NoteLabel\n title?: string\n}\n\nconst STRING_GAP = 20\nconst FRET_W = 44\nconst OPEN_W = 30\nconst LEFT = 24\nconst TITLE = 22\nconst BOTTOM = 22\nconst SINGLE_INLAYS = new Set([3, 5, 7, 9, 15, 17, 19, 21])\n\nexport function renderFretboard(\n doc: Document,\n set: NoteSet,\n options: FretboardOptions = {}\n): SVGElement {\n const tuning = options.tuning ?? STANDARD_TUNING\n const [from, to] = options.frets ?? [0, 12]\n const first = Math.max(from, 1)\n const strings = tuning.length\n const label = options.label ?? 'notes'\n const top = (options.title ? TITLE : 0) + 12\n const openW = from === 0 ? OPEN_W : 0\n const nutX = LEFT + openW\n const width = nutX + (to - first + 1) * FRET_W + 8\n const height = top + (strings - 1) * STRING_GAP + BOTTOM + 8\n // Row 0 is the highest string.\n const y = (row: number) => top + row * STRING_GAP\n const wireX = (fret: number) => nutX + (fret - first + 1) * FRET_W\n const centerX = (fret: number) => (fret === 0 ? LEFT + OPEN_W / 2 : wireX(fret) - FRET_W / 2)\n\n const root = svgRoot(\n doc,\n width,\n height,\n 'mn-fretboard',\n options.title ?? `Fretboard: ${set.title}`\n )\n if (options.title) {\n root.appendChild(\n svgText(doc, options.title, {\n 'class': 'mn-title',\n 'x': width / 2,\n 'y': 15,\n 'text-anchor': 'middle'\n })\n )\n }\n\n const middle = y((strings - 1) / 2)\n for (let fret = first; fret <= to; fret++) {\n const cx = wireX(fret) - FRET_W / 2\n if (SINGLE_INLAYS.has(fret))\n root.appendChild(svg(doc, 'circle', { class: 'mn-inlay', cx, cy: middle, r: 4 }))\n if (fret % 12 === 0) {\n root.appendChild(\n svg(doc, 'circle', { class: 'mn-inlay', cx, cy: y(strings > 3 ? 1.5 : 0.5), r: 4 })\n )\n root.appendChild(\n svg(doc, 'circle', {\n class: 'mn-inlay',\n cx,\n cy: y(strings > 3 ? strings - 2.5 : strings - 1.5),\n r: 4\n })\n )\n }\n root.appendChild(\n svgText(doc, String(fret), {\n 'class': 'mn-fret-number',\n 'x': cx,\n 'y': y(strings - 1) + BOTTOM,\n 'text-anchor': 'middle'\n })\n )\n }\n root.appendChild(\n svg(doc, 'line', {\n class: first === 1 ? 'mn-nut' : 'mn-fret',\n x1: nutX,\n x2: nutX,\n y1: y(0),\n y2: y(strings - 1)\n })\n )\n for (let fret = first; fret <= to; fret++) {\n root.appendChild(\n svg(doc, 'line', {\n class: 'mn-fret',\n x1: wireX(fret),\n x2: wireX(fret),\n y1: y(0),\n y2: y(strings - 1)\n })\n )\n }\n for (let row = 0; row < strings; row++) {\n const open = tuning[strings - 1 - row]!\n root.appendChild(\n svg(doc, 'line', {\n class: 'mn-string',\n x1: nutX - openW,\n x2: wireX(to),\n y1: y(row),\n y2: y(row)\n })\n )\n root.appendChild(\n svgText(doc, nameOf(open, set.preferFlats), {\n 'class': 'mn-string-name',\n 'x': LEFT - 8,\n 'y': y(row) + 4,\n 'text-anchor': 'end'\n })\n )\n for (let fret = from; fret <= to; fret++) {\n const member = memberOf(set, open + fret)\n if (!member) continue\n const cx = centerX(fret)\n const isRoot = set.root === member.chroma\n root.appendChild(\n svg(doc, 'circle', {\n class: `mn-dot${isRoot ? ' mn-root' : ''}`,\n cx,\n cy: y(row),\n r: 8\n })\n )\n const text = labelOf(member, label)\n if (text) {\n root.appendChild(\n svgText(doc, text, {\n 'class': 'mn-dot-text',\n 'x': cx,\n 'y': y(row) + 4,\n 'text-anchor': 'middle'\n })\n )\n }\n }\n }\n return root\n}\n",
12
+ "/** Tunings of fretted instruments: open-string pitches (MIDI), lowest string first. */\nimport { chromaOf, midiOf } from '../theory/pitch.js'\n\nconst NAMED = new Map<string, string>(\n Object.entries({\n 'standard': 'E2 A2 D3 G3 B3 E4',\n 'drop d': 'D2 A2 D3 G3 B3 E4',\n 'dadgad': 'D2 A2 D3 G3 A3 D4',\n 'open g': 'D2 G2 D3 G3 B3 D4',\n 'open d': 'D2 A2 D3 F#3 A3 D4',\n 'open e': 'E2 B2 E3 G#3 B3 E4',\n 'half step down': 'Eb2 Ab2 Db3 Gb3 Bb3 Eb4',\n 'bass': 'E1 A1 D2 G2',\n 'ukulele': 'G4 C4 E4 A4'\n })\n)\n\n/**\n * \"standard\", \"Drop D\", \"E2 A2 D3 G3 B3 E4\", or letters only (\"DADGAD\", \"EbAbDbGbBbEb\"): letters\n * get octaves going up from the one closest to E2. Null when invalid.\n */\nexport function parseTuning(text: string): number[] | null {\n const trimmed = text.trim()\n const named = NAMED.get(trimmed.toLowerCase().replace(/\\s*\\(.*\\)$/, ''))\n if (named) return parseTuning(named)\n const spaced = trimmed.split(/[\\s,]+/).filter(Boolean)\n if (spaced.length > 1 && spaced.every((t) => midiOf(t) !== null)) {\n return spaced.map((t) => midiOf(t)!)\n }\n // A \"b\" is a flat only after an uppercase letter: \"eadgbe\" is six strings, \"EbAb…\" are flats.\n const names = spaced.length > 1 ? spaced : (trimmed.match(/[A-G][#♯b♭]?|[a-g][#♯♭]?/g) ?? [])\n if (names.length < 2 || names.join('') !== spaced.join('')) return null\n const midis: number[] = []\n for (const name of names) {\n const chroma = chromaOf(name)\n if (chroma === null) return null\n const previous = midis[midis.length - 1]\n if (previous === undefined) {\n // The octave whose pitch is closest to E2 (40).\n const candidates = [28, 40, 52].map((c) => c - (c % 12) + chroma)\n midis.push(candidates.reduce((a, b) => (Math.abs(b - 40) < Math.abs(a - 40) ? b : a)))\n } else {\n let midi = previous - (previous % 12) + chroma\n while (midi <= previous) midi += 12\n midis.push(midi)\n }\n }\n return midis\n}\n",
13
+ "/** Piano keyboards with highlighted notes, as SVG. */\nimport { svg, svgRoot, svgText } from '../render/dom.js'\nimport { labelOf, memberOf, type NoteLabel, type NoteSet } from '../theory/note-set.js'\nimport { midiOf } from '../theory/pitch.js'\n\nexport interface KeyboardOptions {\n /** Lowest and highest MIDI pitch shown; computed from the notes when absent. */\n range?: [number, number]\n label?: NoteLabel\n /** Finger per highlighted pitch (MIDI → \"1\"..\"5\"). */\n fingers?: Map<number, string>\n title?: string\n}\n\nconst BLACK = new Set([1, 3, 6, 8, 10])\nconst WHITE_W = 22\nconst WHITE_H = 104\nconst BLACK_W = 14\nconst BLACK_H = 64\nconst TITLE = 22\n\nconst isBlack = (midi: number) => BLACK.has(((midi % 12) + 12) % 12)\n\n/** \"C3-B4\" → MIDI range; null when invalid. */\nexport function parseKeyRange(text: string): [number, number] | null {\n const parts = text.split(/\\s*-\\s*(?=[A-Ga-g])/)\n if (parts.length !== 2) return null\n const [from, to] = parts.map((n) => midiOf(n))\n return from !== undefined && from !== null && to !== undefined && to !== null && from < to\n ? [from, to]\n : null\n}\n\n/** Whole octaves around the notes: exact pitches, or two octaves from C4 for pitch classes. */\nexport function keyRangeOf(set: NoteSet): [number, number] {\n if (!set.pitches?.length) return [60, 83]\n const low = Math.min(...set.pitches)\n const high = Math.max(...set.pitches)\n return [low - (((low % 12) + 12) % 12), high + 11 - (((high % 12) + 12) % 12)]\n}\n\nexport function renderKeyboard(\n doc: Document,\n set: NoteSet,\n options: KeyboardOptions = {}\n): SVGElement {\n let [from, to] = options.range ?? keyRangeOf(set)\n // Start and end on white keys, so the outline stays clean.\n if (isBlack(from)) from--\n if (isBlack(to)) to++\n const whites: number[] = []\n for (let midi = from; midi <= to; midi++) if (!isBlack(midi)) whites.push(midi)\n\n const top = options.title ? TITLE : 0\n const width = whites.length * WHITE_W + 2\n const height = top + WHITE_H + 2\n const label = options.label ?? 'notes'\n const root = svgRoot(\n doc,\n width,\n height,\n 'mn-keyboard',\n options.title ?? `Keyboard: ${set.title}`\n )\n if (options.title) {\n root.appendChild(\n svgText(doc, options.title, {\n 'class': 'mn-title',\n 'x': width / 2,\n 'y': 15,\n 'text-anchor': 'middle'\n })\n )\n }\n\n const keyClass = (midi: number, black: boolean) => {\n const member = memberOf(set, midi)\n const classes = [black ? 'mn-key-black' : 'mn-key-white']\n if (member) classes.push('mn-on')\n if (member && set.root === member.chroma) classes.push('mn-root')\n return { member, cls: classes.join(' ') }\n }\n const caption = (midi: number, x: number, y: number, black: boolean) => {\n const member = memberOf(set, midi)\n if (!member) return\n const text = labelOf(member, label, options.fingers?.get(midi))\n if (!text) return\n root.appendChild(\n svgText(doc, text, {\n 'class': black ? 'mn-key-label mn-key-label-black' : 'mn-key-label',\n 'x': x,\n 'y': y,\n 'text-anchor': 'middle'\n })\n )\n }\n\n whites.forEach((midi, i) => {\n const x = 1 + i * WHITE_W\n const { cls } = keyClass(midi, false)\n root.appendChild(\n svg(doc, 'rect', { class: cls, x, y: top + 1, width: WHITE_W, height: WHITE_H, rx: 2 })\n )\n caption(midi, x + WHITE_W / 2, top + WHITE_H - 8, false)\n })\n whites.forEach((midi, i) => {\n const black = midi + 1\n if (black > to || !isBlack(black)) return\n const x = 1 + (i + 1) * WHITE_W - BLACK_W / 2\n const { cls } = keyClass(black, true)\n root.appendChild(\n svg(doc, 'rect', { class: cls, x, y: top + 1, width: BLACK_W, height: BLACK_H, rx: 2 })\n )\n caption(black, x + BLACK_W / 2, top + BLACK_H - 8, true)\n })\n return root\n}\n",
14
+ "/**\n * Chord symbols (\"F#m7\", \"C/G\") → root, quality, bass and a name spelled out in ASCII\n * (\"F Sharp Minor 7\"), safe for file names and wikilinks.\n */\nimport { splitChordSymbol } from './pitch.js'\n\n/** Suffix → quality name. Order matters: the first match wins. */\nconst QUALITY_SUFFIXES: [RegExp, string][] = [\n [/^(m7b5|m7-5|ø7?)$/, 'Minor 7 flat 5'],\n [/^(dim7|°7)$/, 'Diminished 7'],\n [/^(maj9|M9|Δ9)$/, 'Major 9'],\n [/^(maj7|M7|Δ7?)$/, 'Major 7'],\n [/^(7sus4)$/, '7sus4'],\n [/^(m9|min9|-9)$/, 'Minor 9'],\n [/^(m7|min7|-7)$/, 'Minor 7'],\n [/^(m6|min6|-6)$/, 'Minor 6'],\n [/^(m|min|-)$/, 'Minor'],\n [/^(dim|°)$/, 'Diminished'],\n [/^(aug|\\+)$/, 'Augmented'],\n [/^(sus2)$/, 'Sus2'],\n [/^(sus4|sus)$/, 'Sus4'],\n [/^(add9|add2)$/, 'Add9'],\n [/^5$/, '5 (Power)'],\n [/^(6\\/9|69)$/, '6/9'],\n [/^(mM7|mMaj7|mmaj7|m\\(maj7\\)|-\\(maj7\\))$/, 'Minor major 7'],\n [/^(13|11|9|7|6)$/, '$1'],\n [/^(maj|M)?$/, 'Major']\n]\n\nexport interface ParsedChord {\n symbol: string\n root: string\n quality: string\n bass: string | null\n /** \"F Sharp Minor 7\", \"C Major over G\". */\n name: string\n}\n\nconst spell = (note: string) =>\n note[0]! + (note[1] === '#' ? ' Sharp' : note[1] === 'b' ? ' Flat' : '')\n\n/** \"5 (Power)\" → \"5\" for names. */\nconst plain = (quality: string) => quality.replace(/\\s*\\([^)]*\\)/g, '').trim()\n\n/** Parse a chord symbol; null when it isn't one. */\nexport function parseChord(symbol: string): ParsedChord | null {\n const parts = splitChordSymbol(symbol)\n if (!parts) return null\n const trimmed = symbol.trim()\n const { root, suffix, bass } = parts\n let quality: string | null = null\n for (const [re, label] of QUALITY_SUFFIXES) {\n const hit = re.exec(suffix)\n if (hit) {\n quality = label.replace('$1', hit[1] ?? '')\n break\n }\n }\n if (!quality) return null\n return {\n symbol: trimmed,\n root,\n quality,\n bass,\n name: `${spell(root)} ${plain(quality)}${bass ? ` over ${spell(bass)}` : ''}`\n }\n}\n",
15
+ "/**\n * Block sources: `key: value` lines, with an optional first line that has no key (the shorthand:\n * \"A x02220 x01230\"). Keys are case-insensitive; the last value wins.\n */\n\nexport interface BlockSource {\n /** The first line when it has no `key:`; null otherwise. */\n head: string | null\n fields: Map<string, string>\n}\n\nconst FIELD = /^([A-Za-z][\\w-]*)\\s*:\\s*(.*)$/\n\nexport function parseBlockSource(source: string): BlockSource {\n const fields = new Map<string, string>()\n let head: string | null = null\n source\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .forEach((line, i) => {\n const m = FIELD.exec(line)\n if (m) fields.set(m[1]!.toLowerCase(), m[2]!.trim())\n else if (i === 0) head = line\n else throw new Error(`Expected \"key: value\", got \"${line}\"`)\n })\n return { head, fields }\n}\n\n/** \"5-8\", \"5\", \"0 - 12\" → [from, to]; null when invalid. */\nexport function parseRange(text: string): [number, number] | null {\n const m = /^(\\d+)\\s*(?:-\\s*(\\d+))?$/.exec(text.trim())\n if (!m) return null\n const from = Number(m[1])\n const to = m[2] === undefined ? from : Number(m[2])\n return from <= to ? [from, to] : null\n}\n",
16
+ "/** Shared by keyboard and fretboard blocks: which notes to show and how to label them. */\nimport type { BlockSource } from './block-source.js'\nimport {\n chordNoteSet,\n listNoteSet,\n scaleNoteSet,\n type NoteLabel,\n type NoteSet\n} from '../theory/note-set.js'\n\nconst LABELS = new Set<NoteLabel>(['notes', 'degrees', 'intervals', 'none'])\n\n/** `scale:`, `chord:` or `notes:`; a shorthand first line is tried as each, in that order. */\nexport function noteSetOf(block: BlockSource): NoteSet {\n const scale = block.fields.get('scale')\n const chord = block.fields.get('chord')\n const notes = block.fields.get('notes')\n if (scale !== undefined) return orFail(scaleNoteSet(scale), `Unknown scale \"${scale}\"`)\n if (chord !== undefined) return orFail(chordNoteSet(chord), `Unknown chord \"${chord}\"`)\n if (notes !== undefined) return orFail(listNoteSet(notes), `Invalid notes \"${notes}\"`)\n if (block.head) {\n // Exact notes (\"C4\", \"E5\") before chords: tonal reads them as quartal or power chords.\n const exact = listNoteSet(block.head)\n const set =\n scaleNoteSet(block.head) ??\n (exact?.pitches ? exact : null) ??\n chordNoteSet(block.head) ??\n exact\n return orFail(set, `\"${block.head}\" is not a scale, chord or list of notes`)\n }\n throw new Error('Add \"scale:\", \"chord:\" or \"notes:\"')\n}\n\n/** `labels:`; `fingers` only where fingering was given (piano chords). */\nexport function labelField(block: BlockSource, fallback: NoteLabel, fingers = false): NoteLabel {\n const value = block.fields.get('labels')?.toLowerCase()\n if (value === undefined) return fallback\n if (!LABELS.has(value as NoteLabel) && !(fingers && value === 'fingers')) {\n const choices = fingers\n ? 'notes, degrees, intervals, fingers or none'\n : 'notes, degrees, intervals or none'\n throw new Error(`Unknown labels \"${value}\": use ${choices}`)\n }\n return value as NoteLabel\n}\n\nexport function orFail<T>(value: T | null, message: string): T {\n if (value === null) throw new Error(message)\n return value\n}\n",
17
+ "/**\n * One renderer per block language. Each takes the block's source and returns the element to\n * show, or throws an Error whose message is meant for the user.\n */\nimport {\n baseFretOf,\n parseFingers,\n parseFrets,\n renderGuitarDiagram,\n type FrettedVoicing\n} from '../chord/guitar.js'\nimport { parseChordPro } from '../chords/chordpro.js'\nimport { renderChart } from '../chords/chart.js'\nimport { renderFretboard } from '../fretboard/fretboard.js'\nimport { parseTuning } from '../fretboard/tuning.js'\nimport { keyRangeOf, parseKeyRange, renderKeyboard } from '../keyboard/keyboard.js'\nimport { chordNoteSet, listNoteSet, type NoteSet } from '../theory/note-set.js'\nimport { chromaOf, splitChordSymbol } from '../theory/pitch.js'\nimport { parseChord } from '../theory/chord-symbol.js'\nimport type { VoicingLookup } from '../voicings/index.js'\nimport { parseBlockSource, parseRange, type BlockSource } from './block-source.js'\nimport { labelField, noteSetOf, orFail } from './note-set-field.js'\n\nexport interface RenderOptions {\n /** Finds guitar voicings for chord blocks that only give a symbol (see `/voicings`). */\n voicings?: VoicingLookup\n /** Semitones to transpose chord charts by. */\n transpose?: number\n}\n\nexport type Renderer = (doc: Document, source: string, options: RenderOptions) => Element\n\nconst field = (block: BlockSource, ...names: string[]) => {\n for (const name of names) {\n const value = block.fields.get(name)\n if (value !== undefined && value !== '') return value\n }\n return undefined\n}\n\n/** The root of any symbol that starts with a note name. */\nconst rootOf = (symbol: string): number | null => {\n const parts = splitChordSymbol(symbol)\n return parts && parseChord(symbol) ? chromaOf(parts.root) : null\n}\n\nconst positiveInt = (text: string, what: string) => {\n const n = Number(text)\n if (!Number.isInteger(n) || n < 1)\n throw new Error(`${what} must be a whole number from 1, got \"${text}\"`)\n return n\n}\n\n/** Chord symbol, frets and fingers from a shorthand line (\"A x02220 x01230\"). */\nfunction shorthand(head: string | null) {\n const tokens = head?.split(/\\s+/).filter(Boolean) ?? []\n const symbol = tokens[0] && !parseFrets(tokens[0]) ? tokens.shift() : undefined\n return { symbol, frets: tokens[0], fingers: tokens[1] }\n}\n\n/** Piano voicing of a chord: root position from octave 4, a slash chord's bass in octave 3. */\nfunction pianoPitches(symbol: string): NoteSet {\n const set = orFail(chordNoteSet(symbol), `Unknown chord \"${symbol}\"`)\n const bassName = splitChordSymbol(symbol)?.bass\n const bass = bassName ? chromaOf(bassName) : null\n const pitches: number[] = []\n let previous = 59\n for (const member of set.members) {\n // A bass outside the chord only sounds below it.\n if (bass !== null && member.chroma === bass && member.degree === null) continue\n let midi = 60 + member.chroma\n while (midi <= previous) midi += 12\n while (midi - 12 > previous) midi -= 12\n pitches.push(midi)\n previous = midi\n }\n if (bass !== null) pitches.unshift(48 + bass)\n return { ...set, pitches }\n}\n\nconst renderChord: Renderer = (doc, source, options) => {\n const block = parseBlockSource(source)\n const short = shorthand(block.head)\n const symbol = field(block, 'symbol', 'chord') ?? short.symbol\n const title = field(block, 'title', 'name') ?? symbol\n const instrument = field(block, 'instrument')?.toLowerCase()\n const pitchesText = field(block, 'pitches', 'notes')\n\n if (instrument === 'piano' || instrument === 'keyboard' || (pitchesText && !instrument)) {\n let set: NoteSet\n if (pitchesText) {\n set = orFail(listNoteSet(pitchesText), `Invalid pitches \"${pitchesText}\"`)\n if (!set.pitches) {\n // Pitch classes only: stack them upwards from octave 4.\n let previous = 59\n set.pitches = set.members.map((m) => {\n let midi = 60 + m.chroma\n while (midi <= previous) midi += 12\n previous = midi\n return midi\n })\n }\n const chord = symbol ? chordNoteSet(symbol) : null\n if (chord) set.root = chord.root\n } else if (symbol) {\n set = pianoPitches(symbol)\n } else {\n throw new Error('Add a chord symbol or \"pitches:\"')\n }\n const fingering = field(block, 'fingering', 'fingers')\n const fingers = new Map<number, string>()\n if (fingering) {\n const sorted = [...set.pitches!].sort((a, b) => a - b)\n fingering\n .split(/[\\s,-]+/)\n .filter(Boolean)\n .forEach((finger, i) => {\n if (sorted[i] !== undefined) fingers.set(sorted[i], finger)\n })\n }\n const range = field(block, 'range')\n return renderKeyboard(doc, set, {\n title,\n label: labelField(block, fingers.size ? 'fingers' : 'notes', fingers.size > 0),\n fingers,\n range: range\n ? orFail(parseKeyRange(range), `Invalid range \"${range}\": use e.g. C3-B4`)\n : keyRangeOf(set)\n })\n }\n\n if (instrument && instrument !== 'guitar' && !field(block, 'tuning')) {\n throw new Error(`Unknown instrument \"${instrument}\": use guitar or piano, or give a tuning`)\n }\n const fretsText = field(block, 'frets') ?? short.frets\n let voicing: FrettedVoicing\n if (fretsText) {\n const frets = orFail(parseFrets(fretsText), `Invalid frets \"${fretsText}\": use e.g. x02220`)\n const fingersText = field(block, 'fingers') ?? short.fingers\n const fingers = fingersText ? parseFingers(fingersText) : []\n if (fingersText && fingers.length !== frets.length) {\n throw new Error(\n `\"${fingersText}\" has ${fingers.length} fingers for ${frets.length} strings`\n )\n }\n voicing = { frets, fingers: frets.map((_, i) => fingers[i] ?? null) }\n } else if (symbol && options.voicings) {\n const n = field(block, 'voicing')\n const index = n ? positiveInt(n, 'voicing') - 1 : 0\n voicing = orFail(\n options.voicings(symbol, index),\n `No voicing ${index + 1} found for \"${symbol}\": add \"frets:\"`\n )\n } else {\n throw new Error(\n symbol\n ? `Add \"frets:\" for ${symbol} (e.g. frets: x02220)`\n : 'Add a chord symbol and \"frets:\"'\n )\n }\n const position = field(block, 'position', 'base')\n if (position) {\n const base = positiveInt(position, 'position')\n const fretted = voicing.frets.filter((f): f is number => f !== null && f > 0)\n if (fretted.length && base > Math.min(...fretted)) {\n throw new Error(\n `position ${base} is above the lowest fretted note (fret ${Math.min(...fretted)})`\n )\n }\n voicing.baseFret = base\n } else voicing.baseFret ??= baseFretOf(voicing.frets)\n const tuningText = field(block, 'tuning')\n const tuning = tuningText\n ? orFail(parseTuning(tuningText), `Unknown tuning \"${tuningText}\"`)\n : undefined\n if (tuning && tuning.length !== voicing.frets.length) {\n throw new Error(\n `The tuning has ${tuning.length} strings, the frets ${voicing.frets.length}`\n )\n }\n // A symbol the theory doesn't know still titles the diagram; only its root marks are lost.\n const root = symbol ? (chordNoteSet(symbol)?.root ?? rootOf(symbol)) : null\n return renderGuitarDiagram(doc, voicing, { title, root, tuning })\n}\n\nconst renderKeyboardBlock: Renderer = (doc, source) => {\n const block = parseBlockSource(source)\n const set = noteSetOf(block)\n const range = field(block, 'range')\n return renderKeyboard(doc, set, {\n title: field(block, 'title'),\n label: labelField(block, 'notes'),\n range: range\n ? orFail(parseKeyRange(range), `Invalid range \"${range}\": use e.g. C3-B4`)\n : undefined\n })\n}\n\nconst renderFretboardBlock: Renderer = (doc, source) => {\n const block = parseBlockSource(source)\n const set = noteSetOf(block)\n const fretsText = field(block, 'frets')\n const frets = fretsText\n ? orFail(parseRange(fretsText), `Invalid frets \"${fretsText}\": use e.g. 5-8`)\n : undefined\n if (frets && frets[1] > 36) throw new Error('Frets go up to 36')\n const tuningText = field(block, 'tuning')\n return renderFretboard(doc, set, {\n title: field(block, 'title'),\n label: labelField(block, 'notes'),\n frets,\n tuning: tuningText\n ? orFail(parseTuning(tuningText), `Unknown tuning \"${tuningText}\"`)\n : undefined\n })\n}\n\nconst renderChordsBlock: Renderer = (doc, source, options) =>\n renderChart(doc, parseChordPro(source), { transpose: options.transpose })\n\n/** Block languages (code fence names) and their renderers. */\nexport const RENDERERS = {\n chord: renderChord,\n chords: renderChordsBlock,\n keyboard: renderKeyboardBlock,\n fretboard: renderFretboardBlock\n} satisfies Record<string, Renderer>\n\nexport type NotationLanguage = keyof typeof RENDERERS\n",
18
+ "/** Render a notation block into a host element. Never throws: errors render inline. */\nimport { RENDERERS, type NotationLanguage, type RenderOptions } from '../blocks/renderers.js'\nimport { html } from './dom.js'\n\nexport const LANGUAGES = Object.keys(RENDERERS) as NotationLanguage[]\n\nexport const isNotationLanguage = (language: string): language is NotationLanguage =>\n Object.prototype.hasOwnProperty.call(RENDERERS, language)\n\nexport interface RenderResult {\n ok: boolean\n /** The block's container, appended to the host. */\n el: HTMLElement\n /** The message shown when ok is false. */\n error: string | null\n /** Removes what was rendered. */\n destroy(): void\n}\n\nexport function renderBlock(\n language: NotationLanguage,\n source: string,\n host: HTMLElement,\n options: RenderOptions = {}\n): RenderResult {\n const doc = host.ownerDocument\n const el = html(doc, 'div', `mn-block mn-block-${language}`)\n let error: string | null = null\n try {\n el.appendChild(RENDERERS[language](doc, source, options))\n } catch (e) {\n error = e instanceof Error ? e.message : String(e)\n el.classList.add('mn-error')\n el.appendChild(html(doc, 'div', 'mn-error-message', `${language}: ${error}`))\n }\n host.appendChild(el)\n return { ok: error === null, el, error, destroy: () => el.remove() }\n}\n"
19
+ ],
20
+ "mappings": "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",
21
+ "debugId": "627AFEC8F03B83D064756E2164756E21",
22
+ "names": []
23
+ }
@@ -0,0 +1,50 @@
1
+ // src/theory/pitch.ts
2
+ var SHARP_NAMES2 = [
3
+ "C",
4
+ "C#",
5
+ "D",
6
+ "D#",
7
+ "E",
8
+ "F",
9
+ "F#",
10
+ "G",
11
+ "G#",
12
+ "A",
13
+ "A#",
14
+ "B"
15
+ ];
16
+ var FLAT_NAMES2 = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
17
+ var LETTER_CHROMA = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };
18
+ function chromaOf2(name) {
19
+ const m = /^([A-Ga-g])([#♯b♭]*)$/.exec(name.trim());
20
+ if (!m)
21
+ return null;
22
+ let chroma = LETTER_CHROMA[m[1].toUpperCase()];
23
+ for (const accidental of m[2])
24
+ chroma += accidental === "#" || accidental === "♯" ? 1 : -1;
25
+ return (chroma % 12 + 12) % 12;
26
+ }
27
+ function midiOf2(note) {
28
+ const m = /^([A-Ga-g][#♯b♭]*)(-?\d)$/.exec(note.trim());
29
+ if (!m)
30
+ return null;
31
+ const chroma = chromaOf2(m[1]);
32
+ if (chroma === null)
33
+ return null;
34
+ const letter = LETTER_CHROMA[m[1][0].toUpperCase()];
35
+ const shift = Math.round((chroma - letter) / 12);
36
+ return (Number(m[2]) + 1) * 12 + chroma - shift * 12;
37
+ }
38
+ var nameOf2 = (chroma, preferFlats = false) => (preferFlats ? FLAT_NAMES2 : SHARP_NAMES2)[((Math.round(chroma) || 0) % 12 + 12) % 12];
39
+ function splitChordSymbol(symbol) {
40
+ const normalized = symbol.trim().replace(/♯/g, "#").replace(/♭/g, "b");
41
+ const m = /^([A-G][#b]?)(.*?)(?:\/([A-G][#b]?))?$/.exec(normalized);
42
+ return m ? { root: m[1], suffix: m[2], bass: m[3] ?? null } : null;
43
+ }
44
+ var noteOf2 = (midi, preferFlats = false) => `${nameOf2(midi, preferFlats)}${Math.floor(midi / 12) - 1}`;
45
+ var isFlat2 = (name) => /^[A-Ga-g][b♭]/.test(name.trim());
46
+
47
+ export { SHARP_NAMES2, FLAT_NAMES2, chromaOf2, midiOf2, nameOf2, splitChordSymbol, noteOf2, isFlat2 };
48
+
49
+ //# debugId=729526F51630A7DA64756E2164756E21
50
+ //# sourceMappingURL=index-y8a78sfx.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/theory/pitch.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Pitch classes and note names. A pitch class is a chroma: 0 = C … 11 = B. Names accept ASCII\n * (`#`, `b`) and Unicode (`♯`, `♭`) accidentals.\n */\n\nexport const SHARP_NAMES = [\n 'C',\n 'C#',\n 'D',\n 'D#',\n 'E',\n 'F',\n 'F#',\n 'G',\n 'G#',\n 'A',\n 'A#',\n 'B'\n] as const\nexport const FLAT_NAMES = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'] as const\n\nconst LETTER_CHROMA: Record<string, number> = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 }\n\n/** \"C#\", \"Db\", \"D♭\", \"E\" → 0..11; null when it isn't a note name. */\nexport function chromaOf(name: string): number | null {\n const m = /^([A-Ga-g])([#♯b♭]*)$/.exec(name.trim())\n if (!m) return null\n let chroma = LETTER_CHROMA[m[1]!.toUpperCase()]!\n for (const accidental of m[2]!) chroma += accidental === '#' || accidental === '♯' ? 1 : -1\n return ((chroma % 12) + 12) % 12\n}\n\n/** A note with its octave, scientific pitch notation (\"C4\" = MIDI 60); null when invalid. */\nexport function midiOf(note: string): number | null {\n const m = /^([A-Ga-g][#♯b♭]*)(-?\\d)$/.exec(note.trim())\n if (!m) return null\n const chroma = chromaOf(m[1]!)\n if (chroma === null) return null\n // B#3 is C4 and Cb4 is B3: the letter decides the octave, the accidental can cross it.\n const letter = LETTER_CHROMA[m[1]![0]!.toUpperCase()]!\n const shift = Math.round((chroma - letter) / 12)\n return (Number(m[2]) + 1) * 12 + chroma - shift * 12\n}\n\n/** Name of a pitch class, with sharps or flats. Non-integers are rounded. */\nexport const nameOf = (chroma: number, preferFlats = false): string =>\n (preferFlats ? FLAT_NAMES : SHARP_NAMES)[(((Math.round(chroma) || 0) % 12) + 12) % 12]!\n\nexport interface ChordSymbolParts {\n root: string\n /** Everything between the root and the bass: \"m7\", \"6/9\", \"\". */\n suffix: string\n bass: string | null\n}\n\n/**\n * \"F♯m7/C#\" → root F#, suffix m7, bass C#. A slash starts a bass only when a note name follows\n * (\"C6/9\" has no bass). ♯ and ♭ become # and b.\n */\nexport function splitChordSymbol(symbol: string): ChordSymbolParts | null {\n const normalized = symbol.trim().replace(/♯/g, '#').replace(/♭/g, 'b')\n const m = /^([A-G][#b]?)(.*?)(?:\\/([A-G][#b]?))?$/.exec(normalized)\n return m ? { root: m[1]!, suffix: m[2]!, bass: m[3] ?? null } : null\n}\n\n/** \"C4\" for MIDI 60. */\nexport const noteOf = (midi: number, preferFlats = false): string =>\n `${nameOf(midi, preferFlats)}${Math.floor(midi / 12) - 1}`\n\n/** Whether a name is spelled with a flat (\"Bb\", \"E♭\"). */\nexport const isFlat = (name: string): boolean => /^[A-Ga-g][b♭]/.test(name.trim())\n"
6
+ ],
7
+ "mappings": ";AAKO,IAAM,eAAc;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AACO,IAAM,cAAa,CAAC,KAAK,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,GAAG;AAE1F,IAAM,gBAAwC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG;AAGnF,SAAS,SAAQ,CAAC,MAA6B;AAAA,EAClD,MAAM,IAAI,wBAAwB,KAAK,KAAK,KAAK,CAAC;AAAA,EAClD,IAAI,CAAC;AAAA,IAAG,OAAO;AAAA,EACf,IAAI,SAAS,cAAc,EAAE,GAAI,YAAY;AAAA,EAC7C,WAAW,cAAc,EAAE;AAAA,IAAK,UAAU,eAAe,OAAO,eAAe,MAAM,IAAI;AAAA,EACzF,QAAS,SAAS,KAAM,MAAM;AAAA;AAI3B,SAAS,OAAM,CAAC,MAA6B;AAAA,EAChD,MAAM,IAAI,4BAA4B,KAAK,KAAK,KAAK,CAAC;AAAA,EACtD,IAAI,CAAC;AAAA,IAAG,OAAO;AAAA,EACf,MAAM,SAAS,UAAS,EAAE,EAAG;AAAA,EAC7B,IAAI,WAAW;AAAA,IAAM,OAAO;AAAA,EAE5B,MAAM,SAAS,cAAc,EAAE,GAAI,GAAI,YAAY;AAAA,EACnD,MAAM,QAAQ,KAAK,OAAO,SAAS,UAAU,EAAE;AAAA,EAC/C,QAAQ,OAAO,EAAE,EAAE,IAAI,KAAK,KAAK,SAAS,QAAQ;AAAA;AAI/C,IAAM,UAAS,CAAC,QAAgB,cAAc,WAChD,cAAc,cAAa,cAAgB,OAAK,MAAM,MAAM,KAAK,KAAK,KAAM,MAAM;AAahF,SAAS,gBAAgB,CAAC,QAAyC;AAAA,EACtE,MAAM,aAAa,OAAO,KAAK,EAAE,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAAA,EACrE,MAAM,IAAI,yCAAyC,KAAK,UAAU;AAAA,EAClE,OAAO,IAAI,EAAE,MAAM,EAAE,IAAK,QAAQ,EAAE,IAAK,MAAM,EAAE,MAAM,KAAK,IAAI;AAAA;AAI7D,IAAM,UAAS,CAAC,MAAc,cAAc,UAC/C,GAAG,QAAO,MAAM,WAAW,IAAI,KAAK,MAAM,OAAO,EAAE,IAAI;AAGpD,IAAM,UAAS,CAAC,SAA0B,gBAAgB,KAAK,KAAK,KAAK,CAAC;",
8
+ "debugId": "729526F51630A7DA64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,10 @@
1
+ import { type NoteLabel, type NoteSet } from '../theory/note-set.js';
2
+ export interface FretboardOptions {
3
+ /** Open-string pitches (MIDI), lowest string first. */
4
+ tuning?: number[];
5
+ /** First and last fret shown; 0 shows the open strings. */
6
+ frets?: [number, number];
7
+ label?: NoteLabel;
8
+ title?: string;
9
+ }
10
+ export declare function renderFretboard(doc: Document, set: NoteSet, options?: FretboardOptions): SVGElement;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * "standard", "Drop D", "E2 A2 D3 G3 B3 E4", or letters only ("DADGAD", "EbAbDbGbBbEb"): letters
3
+ * get octaves going up from the one closest to E2. Null when invalid.
4
+ */
5
+ export declare function parseTuning(text: string): number[] | null;
@@ -0,0 +1,14 @@
1
+ export { renderBlock, LANGUAGES, isNotationLanguage, type RenderResult } from './render/render-block.js';
2
+ export type { RenderOptions, NotationLanguage } from './blocks/renderers.js';
3
+ export { STYLES, injectStyles } from './styles.js';
4
+ export { renderGuitarDiagram, parseFrets, parseFingers, baseFretOf, STANDARD_TUNING, type FrettedVoicing, type GuitarDiagramOptions } from './chord/guitar.js';
5
+ export { renderKeyboard, parseKeyRange, keyRangeOf, type KeyboardOptions } from './keyboard/keyboard.js';
6
+ export { renderFretboard, type FretboardOptions } from './fretboard/fretboard.js';
7
+ export { parseTuning } from './fretboard/tuning.js';
8
+ export { parseChordPro, parseLyrics, type Song, type Section, type Line, type Segment } from './chords/chordpro.js';
9
+ export { renderChart, type ChartOptions } from './chords/chart.js';
10
+ export { parseChord, type ParsedChord } from './theory/chord-symbol.js';
11
+ export { chromaOf, midiOf, nameOf, noteOf, isFlat, SHARP_NAMES, FLAT_NAMES } from './theory/pitch.js';
12
+ export { transposeChord, transposeKey, keyPrefersFlats } from './theory/transpose.js';
13
+ export { chordNoteSet, scaleNoteSet, listNoteSet, memberOf, labelOf, intervalLabel, type NoteSet, type NoteSetMember, type NoteLabel } from './theory/note-set.js';
14
+ export type { VoicingLookup } from './voicings/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,281 @@
1
+ import {
2
+ STANDARD_TUNING2,
3
+ parseFrets2,
4
+ parseFingers2,
5
+ baseFretOf2,
6
+ renderGuitarDiagram2,
7
+ parseLyrics2,
8
+ parseChordPro2,
9
+ keyPrefersFlats2,
10
+ transposeChord2,
11
+ transposeKey2,
12
+ renderChart2,
13
+ memberOf2,
14
+ intervalLabel2,
15
+ labelOf2,
16
+ chordNoteSet2,
17
+ scaleNoteSet2,
18
+ listNoteSet2,
19
+ renderFretboard2,
20
+ parseTuning2,
21
+ parseKeyRange2,
22
+ keyRangeOf2,
23
+ renderKeyboard2,
24
+ parseChord2,
25
+ LANGUAGES2,
26
+ isNotationLanguage2,
27
+ renderBlock2
28
+ } from "./chunks/index-b71brbpf.js";
29
+ import {
30
+ SHARP_NAMES2,
31
+ FLAT_NAMES2,
32
+ chromaOf2,
33
+ midiOf2,
34
+ nameOf2,
35
+ noteOf2,
36
+ isFlat2
37
+ } from "./chunks/index-y8a78sfx.js";
38
+ // src/styles.css
39
+ var styles_default = `/* Music notation. Colors follow Obsidian's theme variables when present, with fallbacks for any page. */
40
+ .mn-block,
41
+ .mn-svg,
42
+ .mn-chart {
43
+ --mn-fg: var(--text-normal, currentColor);
44
+ --mn-muted: var(--text-muted, #8a8a8a);
45
+ --mn-faint: var(--background-modifier-border, #d0d0d0);
46
+ --mn-accent: var(--interactive-accent, #7c3aed);
47
+ --mn-on-accent: var(--text-on-accent, #fff);
48
+ /* Dark enough for white labels in light and dark themes. */
49
+ --mn-root: #c2410c;
50
+ /* Piano keys keep their real colors in every theme. */
51
+ --mn-key-white: #fafafa;
52
+ --mn-key-black: #222;
53
+ font-family: var(--font-interface, system-ui, sans-serif);
54
+ }
55
+
56
+ .mn-block {
57
+ color: var(--mn-fg);
58
+ margin: 0.5em 0;
59
+ overflow-x: auto;
60
+ }
61
+
62
+ .mn-svg {
63
+ display: block;
64
+ max-width: 100%;
65
+ height: auto;
66
+ }
67
+
68
+ .mn-svg text {
69
+ fill: var(--mn-fg);
70
+ font-size: 11px;
71
+ }
72
+
73
+ .mn-svg .mn-title {
74
+ font-size: 14px;
75
+ font-weight: 600;
76
+ }
77
+
78
+ .mn-svg .mn-string,
79
+ .mn-svg .mn-fret {
80
+ stroke: var(--mn-muted);
81
+ stroke-width: 1;
82
+ }
83
+
84
+ .mn-svg .mn-nut {
85
+ stroke: var(--mn-fg);
86
+ stroke-width: 4;
87
+ }
88
+
89
+ .mn-svg .mn-dot,
90
+ .mn-svg .mn-barre {
91
+ fill: var(--mn-accent);
92
+ }
93
+
94
+ .mn-svg .mn-dot.mn-root {
95
+ fill: var(--mn-root);
96
+ }
97
+
98
+ .mn-svg .mn-dot-text {
99
+ fill: var(--mn-on-accent);
100
+ font-size: 10px;
101
+ font-weight: 600;
102
+ }
103
+
104
+ .mn-svg .mn-open {
105
+ fill: none;
106
+ stroke: var(--mn-fg);
107
+ stroke-width: 1.5;
108
+ }
109
+
110
+ .mn-svg .mn-open.mn-root {
111
+ stroke: var(--mn-root);
112
+ }
113
+
114
+ .mn-svg .mn-muted,
115
+ .mn-svg .mn-base-fret,
116
+ .mn-svg .mn-fret-number,
117
+ .mn-svg .mn-string-name {
118
+ fill: var(--mn-muted);
119
+ }
120
+
121
+ .mn-svg .mn-inlay {
122
+ fill: var(--mn-faint);
123
+ }
124
+
125
+ .mn-svg .mn-key-white {
126
+ fill: var(--mn-key-white);
127
+ stroke: #888;
128
+ }
129
+
130
+ .mn-svg .mn-key-black {
131
+ fill: var(--mn-key-black);
132
+ stroke: var(--mn-key-black);
133
+ }
134
+
135
+ .mn-svg .mn-key-white.mn-on,
136
+ .mn-svg .mn-key-black.mn-on {
137
+ fill: var(--mn-accent);
138
+ }
139
+
140
+ .mn-svg .mn-key-white.mn-on.mn-root,
141
+ .mn-svg .mn-key-black.mn-on.mn-root {
142
+ fill: var(--mn-root);
143
+ }
144
+
145
+ .mn-svg .mn-key-label {
146
+ font-size: 10px;
147
+ fill: var(--mn-on-accent);
148
+ font-weight: 600;
149
+ }
150
+
151
+ /* Chord charts */
152
+ .mn-chart {
153
+ line-height: 1.3;
154
+ }
155
+
156
+ .mn-chart-title {
157
+ font-size: 1.3em;
158
+ font-weight: 700;
159
+ }
160
+
161
+ .mn-chart-subtitle,
162
+ .mn-chart-meta {
163
+ color: var(--mn-muted);
164
+ }
165
+
166
+ .mn-chart-meta {
167
+ font-size: 0.9em;
168
+ margin-top: 0.2em;
169
+ }
170
+
171
+ .mn-section {
172
+ margin-top: 0.9em;
173
+ }
174
+
175
+ .mn-section-chorus {
176
+ border-left: 3px solid var(--mn-accent);
177
+ padding-left: 0.75em;
178
+ }
179
+
180
+ .mn-section-label {
181
+ font-weight: 600;
182
+ font-size: 0.85em;
183
+ color: var(--mn-muted);
184
+ text-transform: uppercase;
185
+ letter-spacing: 0.04em;
186
+ }
187
+
188
+ .mn-line {
189
+ display: flex;
190
+ flex-wrap: wrap;
191
+ align-items: flex-end;
192
+ min-height: 1.3em;
193
+ }
194
+
195
+ .mn-seg {
196
+ display: inline-flex;
197
+ flex-direction: column;
198
+ min-width: 0;
199
+ max-width: 100%;
200
+ white-space: pre-wrap;
201
+ }
202
+
203
+ .mn-chord {
204
+ color: var(--mn-accent);
205
+ font-weight: 700;
206
+ min-height: 1.3em;
207
+ padding-right: 0.4em;
208
+ }
209
+
210
+ .mn-chords-only .mn-seg {
211
+ margin-right: 0.6em;
212
+ }
213
+
214
+ .mn-comment {
215
+ font-style: italic;
216
+ color: var(--mn-muted);
217
+ }
218
+
219
+ .mn-pre {
220
+ font-family: var(--font-monospace, ui-monospace, monospace);
221
+ margin: 0;
222
+ white-space: pre;
223
+ }
224
+
225
+ .mn-error-message {
226
+ color: var(--text-error, #c92a2a);
227
+ font-size: 0.9em;
228
+ }
229
+ `;
230
+
231
+ // src/styles.ts
232
+ var STYLES = styles_default;
233
+ var STYLE_ID = "music-notation-styles";
234
+ function injectStyles(doc) {
235
+ if (doc.getElementById(STYLE_ID))
236
+ return;
237
+ const style = doc.createElement("style");
238
+ style.id = STYLE_ID;
239
+ style.textContent = STYLES;
240
+ doc.head.appendChild(style);
241
+ }
242
+ export {
243
+ FLAT_NAMES2 as FLAT_NAMES,
244
+ LANGUAGES2 as LANGUAGES,
245
+ SHARP_NAMES2 as SHARP_NAMES,
246
+ STANDARD_TUNING2 as STANDARD_TUNING,
247
+ STYLES,
248
+ baseFretOf2 as baseFretOf,
249
+ chordNoteSet2 as chordNoteSet,
250
+ chromaOf2 as chromaOf,
251
+ injectStyles,
252
+ intervalLabel2 as intervalLabel,
253
+ isFlat2 as isFlat,
254
+ isNotationLanguage2 as isNotationLanguage,
255
+ keyPrefersFlats2 as keyPrefersFlats,
256
+ keyRangeOf2 as keyRangeOf,
257
+ labelOf2 as labelOf,
258
+ listNoteSet2 as listNoteSet,
259
+ memberOf2 as memberOf,
260
+ midiOf2 as midiOf,
261
+ nameOf2 as nameOf,
262
+ noteOf2 as noteOf,
263
+ parseChord2 as parseChord,
264
+ parseChordPro2 as parseChordPro,
265
+ parseFingers2 as parseFingers,
266
+ parseFrets2 as parseFrets,
267
+ parseKeyRange2 as parseKeyRange,
268
+ parseLyrics2 as parseLyrics,
269
+ parseTuning2 as parseTuning,
270
+ renderBlock2 as renderBlock,
271
+ renderChart2 as renderChart,
272
+ renderFretboard2 as renderFretboard,
273
+ renderGuitarDiagram2 as renderGuitarDiagram,
274
+ renderKeyboard2 as renderKeyboard,
275
+ scaleNoteSet2 as scaleNoteSet,
276
+ transposeChord2 as transposeChord,
277
+ transposeKey2 as transposeKey
278
+ };
279
+
280
+ //# debugId=07DF99AB4B3183CE64756E2164756E21
281
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/styles.ts"],
4
+ "sourcesContent": [
5
+ "import css from './styles.css' with { type: 'text' }\n\n/** The library's stylesheet, for hosts that inject it themselves. */\nexport const STYLES: string = css\n\nconst STYLE_ID = 'music-notation-styles'\n\n/** Add the stylesheet to a document once. */\nexport function injectStyles(doc: Document): void {\n if (doc.getElementById(STYLE_ID)) return\n const style = doc.createElement('style')\n style.id = STYLE_ID\n style.textContent = STYLES\n doc.head.appendChild(style)\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGO,IAAM,SAAiB;AAE9B,IAAM,WAAW;AAGV,SAAS,YAAY,CAAC,KAAqB;AAAA,EAC9C,IAAI,IAAI,eAAe,QAAQ;AAAA,IAAG;AAAA,EAClC,MAAM,QAAQ,IAAI,cAAc,OAAO;AAAA,EACvC,MAAM,KAAK;AAAA,EACX,MAAM,cAAc;AAAA,EACpB,IAAI,KAAK,YAAY,KAAK;AAAA;",
8
+ "debugId": "07DF99AB4B3183CE64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,14 @@
1
+ import { type NoteLabel, type NoteSet } from '../theory/note-set.js';
2
+ export interface KeyboardOptions {
3
+ /** Lowest and highest MIDI pitch shown; computed from the notes when absent. */
4
+ range?: [number, number];
5
+ label?: NoteLabel;
6
+ /** Finger per highlighted pitch (MIDI → "1".."5"). */
7
+ fingers?: Map<number, string>;
8
+ title?: string;
9
+ }
10
+ /** "C3-B4" → MIDI range; null when invalid. */
11
+ export declare function parseKeyRange(text: string): [number, number] | null;
12
+ /** Whole octaves around the notes: exact pitches, or two octaves from C4 for pitch classes. */
13
+ export declare function keyRangeOf(set: NoteSet): [number, number];
14
+ export declare function renderKeyboard(doc: Document, set: NoteSet, options?: KeyboardOptions): SVGElement;