Renderer Interface
A renderer is any object with a render(song) method. The method receives the parsed song tree and returns whatever output format the renderer targets.
class MyRenderer {
render(song) {
// Walk the song tree and produce output.
// song.sections — array of Section objects
// section.label — e.g. "Verse", or null
// section.key — key override for this section, or null
// section.rows — array of Row objects (chords only, no comment lines)
// row.bars — array of Bar objects
// bar.cells — array of BeatCells (chord or dot)
// bar.closeBarline — closing barline descriptor
}
}
There are no required base classes or interfaces to extend. Any object that responds to render(song) and returns the appropriate output is a valid renderer.
Song tree structure
Song
| Field |
Type |
Description |
type |
'song' |
Discriminant |
title |
string | null |
Title from front matter |
key |
string | null |
Global key from front matter (e.g. "F# major") |
meter |
string | null |
Meter from front matter (e.g. "4/4", "6/8") |
sections |
Section[] |
Ordered list of sections |
loc |
SourceRange | undefined |
Source location |
Section
| Field |
Type |
Description |
type |
'section' |
Discriminant |
label |
string | null |
Section label (e.g. "Verse"), or null if there is no [Label] line |
key |
string | null |
Per-section key override, or null |
rows |
Row[] |
Rows only — comment lines excluded. Use this for rendering chords. |
preamble |
CommentLine[] | undefined |
Comment lines appearing before the section label |
content |
SectionItem[] | undefined |
Rows and comment lines interleaved in document order, appearing after the label. Use this when you need to faithfully reproduce the section (e.g. round-trip text rendering). |
loc |
SourceRange | undefined |
Source location |
rows is a filtered view of content — the same rows in the same order, with comment lines removed. Most renderers should use rows; use content only when comment lines matter to your output.
Row
| Field |
Type |
Description |
type |
'row' |
Discriminant |
openBarline |
Barline |
The barline that opens the row |
bars |
Bar[] |
Ordered list of bars |
loc |
SourceRange | undefined |
Source location |
Bar
| Field |
Type |
Description |
type |
'bar' |
Discriminant |
cells |
BeatCell[] |
Beat cells — at least one chord, optionally mixed with dots |
timeSignature |
TimeSignature | undefined |
Explicit time signature annotation, if present |
closeBarline |
Barline |
The barline that closes this bar |
loc |
SourceRange | undefined |
Source location |
Barline
| Field |
Type |
Description |
kind |
BarlineKind |
Barline type |
repeatCount |
number | undefined |
Explicit repeat count (e.g. x3), if specified |
type BarlineKind =
| 'single' // |
| 'double' // ||
| 'final' // ||.
| 'startRepeat' // ||:
| 'endRepeat' // :||
| 'endRepeatStartRepeat' // :||:
BeatSlot
type BeatSlot =
| { type: 'chord'; chord: Chord; loc?: SourceRange }
| { type: 'dot'; loc?: SourceRange } // . — hold/sustain
Chord
| Field |
Type |
Description |
type |
'chord' |
Discriminant |
root |
string |
Root note, e.g. "C", "Bb", "F#" |
quality |
Quality |
Chord quality (see below) |
bass |
string | undefined |
Bass note for slash chords, e.g. "G" in C/G |
loc |
SourceRange | undefined |
Source location |
Quality
type Quality =
| 'major' // (no suffix)
| 'minor' // m
| 'dominant7' // 7
| 'maj7' // maj7 or M7
| 'min7' // m7 or -
| 'dim7' // dim7
| 'diminished' // dim
| 'halfDiminished' // m7b5
| 'dom7flat5' // 7b5
TimeSignature
| Field |
Type |
Description |
numerator |
number |
Top number, e.g. 6 in 6/8 |
denominator |
number |
Bottom number, e.g. 8 in 6/8 |
CommentLine
| Field |
Type |
Description |
type |
'comment' |
Discriminant |
text |
string |
Full comment text including the # character |
loc |
SourceRange | undefined |
Source location |
SourceRange
All AST nodes carry an optional loc property with 0-based LSP-style positions.
interface SourceRange {
start: { line: number; character: number };
end: { line: number; character: number };
}
Custom element renderer
To implement a custom renderer as a Web Component that works with <grigson-chart>, implement the GrigsonRendererElement contract:
interface GrigsonRendererElement extends HTMLElement {
renderChart(song: Song): Element;
}
<grigson-chart> calls renderChart() on every child element that implements this method, and places the returned element into its shadow root in DOM order. If no child renderer is present, it falls back to GrigsonHtmlRenderer automatically.
class MyCustomRenderer extends HTMLElement {
renderChart(song) {
const div = document.createElement('div');
// … populate div from song …
return div;
}
}
customElements.define('my-custom-renderer', MyCustomRenderer);
<grigson-chart>
<my-custom-renderer></my-custom-renderer>
<template>| C | Am | F | G |</template>
</grigson-chart>
When your element's configuration changes and <grigson-chart> should re-render, dispatch a GrigsonRendererUpdateEvent:
import { GrigsonRendererUpdateEvent } from 'grigson';
this.dispatchEvent(new GrigsonRendererUpdateEvent());
<grigson-chart> also dispatches events you can listen to:
| Event |
Class |
When |
grigson:parse-error |
GrigsonParseErrorEvent |
The chart source failed to parse; event.error holds the thrown value |
grigson:render-error |
GrigsonRenderErrorEvent |
renderChart() threw; event.error holds the thrown value |