### Install Node Modules Source: https://github.com/martijnversluis/chordsheetjs/blob/master/CONTRIBUTING.md Install all the necessary project dependencies using Yarn. ```bash yarn install ``` -------------------------------- ### Install ChordSheetJS via npm Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Install the ChordSheetJS library using npm. This is the recommended method for projects using package managers. ```bash npm install chordsheetjs ``` -------------------------------- ### Install jsPDF for PDF Formatting Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Install the jsPDF library as a dependency for using the PdfFormatter. This is required for generating PDF output. ```bash npm install jspdf ``` -------------------------------- ### Clone the Repository Source: https://github.com/martijnversluis/chordsheetjs/blob/master/CONTRIBUTING.md Fork the ChordSheetJS repository and clone your fork to your local machine. Ensure you have NodeJS and Yarn installed. ```bash git clone git@github.com:your-username/ChordSheetJS.git ``` -------------------------------- ### Import Chord Class Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Import the Chord class from the chordsheetjs library to start parsing and manipulating chords. ```javascript import { Chord } from 'chordsheetjs'; ``` -------------------------------- ### Get Individual Measurement Timings Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Returns timing information for individual measurements performed by the measurer. Useful for detailed performance analysis. ```javascript getMeasurementTiming() { return this.measurer.getTiming(); } ``` -------------------------------- ### Parse and Format Chord Sheet with ChordsheetJS Source: https://github.com/martijnversluis/chordsheetjs/blob/master/ARCHITECTURE.md Use ChordProParser to parse a chord sheet string into a Song object, then use TextFormatter to convert the Song object back into a formatted text string. Ensure 'chordsheetjs' is installed. ```typescript import { ChordProParser, TextFormatter } from 'chordsheetjs'; const parser = new ChordProParser(); const song = parser.parse(chordProString); const formatter = new TextFormatter(); const output = formatter.format(song); ``` -------------------------------- ### Get Total Analysis Time Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Retrieves the total time taken for the song analysis in milliseconds. This is useful for performance profiling. ```javascript getTotalTime() { return this.totalTime; } ``` -------------------------------- ### Get text width Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html A convenience method to retrieve only the width of a measured text. It uses a default line height of '1.2'. ```javascript getWidth(text, fontFamily, fontSize) { const result = this.measure(text, fontFamily, fontSize, "1.2"); return result.width.value; } ``` -------------------------------- ### Get CSS for HTML Formatters Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Retrieve the default CSS string or a CSS object for HtmlTableFormatter. Can be used to style the output. ```javascript HtmlTableFormatter.cssString(); ``` ```javascript HtmlTableFormatter.cssString('.chordSheetViewer'); ``` ```javascript HtmlTableFormatter.cssObject(); ``` -------------------------------- ### Build Project and Parsers Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md Run this command to build the project, which includes generating parsers from grammar files and compiling TypeScript. It's essential after modifying `.pegjs` files. ```bash yarn build ``` -------------------------------- ### Build Release Files Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md This command builds the necessary files for releasing the project, typically used before publishing to npm. ```bash yarn build -r ``` -------------------------------- ### Run Tests Source: https://github.com/martijnversluis/chordsheetjs/blob/master/CONTRIBUTING.md Execute the test suite to ensure the project is in a working state before making changes and after completing them. ```bash yarn test ``` -------------------------------- ### Perform Full Release Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md This command automates the entire release process, including running checks, building release files, version bumping, committing, tagging, and publishing to npm. Specify 'major', 'minor', or 'patch' for the version bump. ```bash yarn release [major|minor|patch] ``` -------------------------------- ### Initialize ChordTextAnalyzer and ensure fonts are loaded Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Asynchronously initializes the analyzer by ensuring all necessary fonts are loaded using the TextMeasurer. Returns the initialized analyzer instance. ```javascript async init() { await this.measurer.ensureFontsLoaded(); return this; } ``` -------------------------------- ### Get current measurement timing Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Returns the time in milliseconds spent on the last measurement operation using the currently selected method ('canvas' or 'dom'). ```javascript getTiming() { return this.method === 'canvas' ? this.timings.canvas : this.timings.dom; } ``` -------------------------------- ### Import and Use PdfFormatter Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Import the PdfFormatter from the 'chordsheetjs/pdf' entry point and use it to format a song into a PDF document. Save the generated PDF. ```javascript import { PdfFormatter } from 'chordsheetjs/pdf'; const formatter = new PdfFormatter(); const doc = formatter.format(song); doc.save('song.pdf'); ``` -------------------------------- ### Regenerate README.md Source: https://github.com/martijnversluis/chordsheetjs/blob/master/CONTRIBUTING.md After making changes to INTRO.md or JSDoc comments, run this command to regenerate the README.md file. ```bash yarn readme ``` -------------------------------- ### Run Linter Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md Use this command for a quick check of code style issues enforced by ESLint. ```bash yarn lint ``` -------------------------------- ### Publish Canary Release with Skip Confirmation Source: https://github.com/martijnversluis/chordsheetjs/blob/master/RELEASING.md Use the --yes flag to skip the confirmation prompt when publishing a canary release. ```bash yarn publish:canary minor --yes ``` -------------------------------- ### Core Project Structure Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md Overview of the key directories within the ChordSheetJS project, highlighting the separation of concerns for parsers, formatters, core logic, and tests. ```bash src/ ├── parser/ # Parsers (ChordPro, UltimateGuitar, ChordsOverWords) │ └── */peg_parser.ts # GENERATED - don't edit! ├── formatter/ # Output formatters ├── chord_sheet/ # Core classes (Song, Line, ChordLyricsPair, etc.) └── chord/ # Chord parsing and manipulation test/ # Tests mirror src/ structure ``` -------------------------------- ### Use ChordSheetJS Standalone Bundle Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Include the ChordSheetJS standalone bundle file in your HTML and access it via the global namespace. Useful for projects not using build tools. ```html ``` -------------------------------- ### Import ChordSheetJS with CommonJS Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Load the ChordSheetJS library using require() for projects that use CommonJS module system. ```javascript var ChordSheetJS = require('chordsheetjs').default; ``` -------------------------------- ### Pin Exact Canary Version in Dependencies Source: https://github.com/martijnversluis/chordsheetjs/blob/master/RELEASING.md Prefer pinning the exact canary version in your application's dependencies to ensure predictable builds. ```json { "dependencies": { "chordsheetjs": "14.7.0-canary.2" } } ``` -------------------------------- ### Compare Canvas and DOM Rendering Metrics Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Calculates and aggregates differences in lyric and chord widths and heights between canvas and DOM rendering. Tracks significant differences and performance timings. ```javascript asItem.lyric_width && domItem.lyric_width) { const textDiff = Math.abs(canvasItem.lyric_width - domItem.lyric_width); if (textDiff > 1) { // Ignore tiny differences summary.widthDifferences++; summary.maxWidthDiff = Math.max(summary.maxWidthDiff, textDiff); summary.avgWidthDiff += textDiff; // Track significant differences if (textDiff > 5) { summary.differences.push({ type: 'lyric', paragraph: pIdx, line: lIdx, item: iIdx, text: canvasItem.text, canvasWidth: canvasItem.lyric_width, domWidth: domItem.lyric_width, diff: textDiff }); } } } // Compare text heights if (canvasItem.lyric_height && domItem.lyric_height) { const heightDiff = Math.abs(canvasItem.lyric_height - domItem.lyric_height); if (heightDiff > 1) { // Ignore tiny differences summary.heightDifferences++; summary.maxHeightDiff = Math.max(summary.maxHeightDiff, heightDiff); summary.avgHeightDiff += heightDiff; } } // Compare chord widths if (canvasItem.chord_width && domItem.chord_width) { const chordDiff = Math.abs(canvasItem.chord_width - domItem.chord_width); if (chordDiff > 1) { summary.widthDifferences++; summary.maxWidthDiff = Math.max(summary.maxWidthDiff, chordDiff); summary.avgWidthDiff += chordDiff; // Track significant differences if (chordDiff > 5) { summary.differences.push({ type: 'chord', paragraph: pIdx, line: lIdx, item: iIdx, chord: canvasItem.chord, canvasWidth: canvasItem.chord_width, domWidth: domItem.chord_width, diff: chordDiff }); } } } // Compare chord heights if (canvasItem.chord_height && domItem.chord_height) { const chordHeightDiff = Math.abs(canvasItem.chord_height - domItem.chord_height); if (chordHeightDiff > 1) { summary.heightDifferences++; summary.maxHeightDiff = Math.max(summary.maxHeightDiff, chordHeightDiff); summary.avgHeightDiff += chordHeightDiff; } } }); }); }); // Calculate averages if (summary.widthDifferences > 0) { summary.avgWidthDiff /= summary.widthDifferences; } if (summary.heightDifferences > 0) { summary.avgHeightDiff /= summary.heightDifferences; } // Sort differences by magnitude summary.differences.sort((a, b) => b.diff - a.diff); return summary; } ``` -------------------------------- ### ChordTextAnalyzer constructor Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Initializes the ChordTextAnalyzer with a specified measurement method, defaulting to 'canvas'. It creates an instance of TextMeasurer. ```javascript constructor(method = 'canvas') { this.measurer = new TextMeasurer(method); this.method = method; this.totalTime = 0; } ``` -------------------------------- ### Sample Song Object Structure Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Defines the structure for a song object, including title, artist, and an array of paragraphs, where each paragraph contains lines with lyrics and optional chords. ```javascript const sampleSong = { "title": "Echoes of the Valley", "artist": "The Wandering Minstrels", "paragraphs": [ // Verse 1 { "lines": [ { "type": "verse", "items": [ { "chord": "Am", "lyric": "Morning " }, { "lyric": "light breaks through the " }, { "chord": "C", "lyric": "mist, " }, { "lyric": "golden rays the mountains kissed" } ] }, { "type": "verse", "items": [ { "chord": "G", "lyric": "Valley " }, { "lyric": "floor starts to " }, { "chord": "D", "lyric": "glow, " }, { "lyric": "as the gentle breezes blow" } ] }, { "type": "verse", "items": [ { "chord": "F", "lyric": "Rivers " }, { "lyric": "flow with cry ``` -------------------------------- ### Format Song to Chords Over Words Format Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Format a parsed song object into the Chords Over Words format. This displays chords directly above their corresponding lyrics. ```javascript const formatter = new ChordSheetJS.ChordsOverWordsFormatter(); const disp = formatter.format(song); ``` -------------------------------- ### Import ChordSheetJS with ES Modules Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Import the ChordSheetJS library into your project using ES module syntax. This is common in modern JavaScript development. ```javascript import ChordSheetJS from 'chordsheetjs'; ``` -------------------------------- ### Render Song Visualization with Measurements Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Renders a visual representation of a song, including lyrics, chords, and their measurements. It highlights lines that exceed the container width and can optionally highlight break points within lyrics. Ensure the 'options' object contains 'chordSize' and 'lyricSize'. ```javascript function renderVisualization(visualizationElem, analyzed, options, containerWidth) { const header = visualizationElem.querySelector('h3'); const widthLine = visualizationElem.querySelector('.container-width-line'); visualizationElem.innerHTML = ''; visualizationElem.appendChild(header); visualizationElem.appendChild(widthLine); analyzed.paragraphs.forEach(paragraph => { const paragraphElem = document.createElement('div'); paragraphElem.className = 'paragraph'; paragraph.lines.forEach(line => { const lineElem = document.createElement('div'); lineElem.className = 'line'; if (line.total_width > containerWidth) { lineElem.style.color = '#e74c3c'; } line.items.forEach(item => { const itemElem = document.createElement('span'); itemElem.className = 'item'; if (item.chord) { const chordElem = document.createElement('span'); chordElem.className = 'chord'; chordElem.textContent = item.chord; chordElem.style.fontSize = `${options.chordSize}px`; itemElem.appendChild(chordElem); const chordBox = document.createElement('div'); chordBox.className = 'chord-measurement-box'; chordBox.style.width = `${item.chord_width}px`; itemElem.appendChild(chordBox); } if (item.lyric) { const lyricElem = document.createElement('span'); lyricElem.className = 'lyric'; lyricElem.textContent = item.lyric; lyricElem.style.fontSize = `${options.lyricSize}px`; if (item.breakPoints && item.breakPoints.length > 0) { let highlightedText = ''; let lastPos = 0; item.breakPoints.forEach(bp => { highlightedText += item.lyric.substring(lastPos, bp.index); highlightedText += `${item.lyric.charAt(bp.index)}`; lastPos = bp.index + 1; }); highlightedText += item.lyric.substring(lastPos); lyricElem.innerHTML = highlightedText; } itemElem.appendChild(lyricElem); const lyricBox = document.createElement('div'); lyricBox.className = 'measurement-box'; lyricBox.style.width = `${item.lyric_width}px`; itemElem.appendChild(lyricBox); } lineElem.appendChild(itemElem); }); paragraphElem.appendChild(lineElem); }); visualizationElem.appendChild(paragraphElem); }); } ``` -------------------------------- ### Initialize ChordsheetJS Analyzers and Comparison Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Initializes the canvas and DOM analyzers, and a measurement comparison object. Sets up event listeners for user interactions and measurement triggers. This code is essential for setting up the application's core functionality. ```javascript const measureButton = document.getElementById('measure-button'); const canvasResultsElem = document.getElementById('canvas-results'); const domResultsElem = document.getElementById('dom-results'); const canvasVisualizationElem = document.getElementById('canvas-visualization'); const domVisualizationElem = document.getElementById('dom-visualization'); const comparisonSummaryElem = document.getElementById('comparison-summary'); const methodOptions = document.getElementsByName('measure-method'); const canvasTimingElem = document.getElementById('canvas-timing'); const domTimingElem = document.getElementById('dom-timing'); const canvasResultsTimingElem = document.getElementById('canvas-results-timing'); const domResultsTimingElem = document.getElementById('dom-results-timing'); const chordFontSelect = document.getElementById('chord-font'); const lyricFontSelect = document.getElementById('lyric-font'); const chordSizeInput = document.getElementById('chord-size'); const lyricSizeInput = document.getElementById('lyric-size'); const lineHeightInput = document.getElementById('line-height'); const containerWidthInput = document.getElementById('container-width'); const chordSizeValue = document.getElementById('chord-size-value'); const lyricSizeValue = document.getElementById('lyric-size-value'); const lineHeightValue = document.getElementById('line-height-value'); const containerWidthValue = document.getElementById('container-width-value'); const chordFontPreview = document.getElementById('chord-font-preview'); const lyricFontPreview = document.getElementById('lyric-font-preview'); // Initialize analyzers and comparison let canvasAnalyzer; let domAnalyzer; let comparison; let selectedMethod = 'both'; // Initialize the application async function init() { // Initialize the analyzers canvasAnalyzer = await new ChordTextAnalyzer('canvas').init(); domAnalyzer = await new ChordTextAnalyzer('dom').init(); comparison = new MeasurementComparison(); // Set up event listeners measureButton.addEventListener('click', performMeasurement); // Set up measurement method selection methodOptions.forEach(option => { option.addEventListener('change', function() { if (this.checked) { selectedMethod = this.value; updateViewVisibility(); } }); }); // Set up input change handlers chordSizeInput.addEventListener('input', updatePreview); lyricSizeInput.addEventListener('input', updatePreview); lineHeightInput.addEventListener('input', updatePreview); containerWidthInput.addEventListener('input', updateContainerWidth); chordFontSelect.addEventListener('change', updatePreview); lyricFontSelect.addEventListener('change', updatePreview); // Initial updates updatePreview(); updateContainerWidth(); updateViewVisibility(); } // Update view visibility based on selected method function updateViewVisibility() { switch (selectedMethod) { case 'canvas': canvasVisualizationElem.style.display = 'block'; domVisualizationElem.style.display = 'none'; canvasResultsElem.parentElement.style.display = 'block'; domResultsElem.parentElement.style.display = 'none'; comparisonSummaryElem.style.display = 'none'; break; case 'dom': canvasVisualizationElem.style.display = 'none'; domVisualizationElem.style.display = 'block'; canvasResultsElem.parentElement.style.display = 'none'; domResultsElem.parentElement.style.display = 'block'; comparisonSummaryElem.style.display = 'none'; break; case 'both': default: canvasVisualizationElem.style.display = 'block'; ``` -------------------------------- ### Dropdown and Key Control Styling Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/index.html Styles for dropdown containers and key control elements, including alignment and spacing. ```css .dropdown-container { display: flex; align-items: center; gap: 10px; margin-bottom: 5px; } .key-controls { margin-left: auto; display: flex; align-items: center; gap: 10px; } ``` -------------------------------- ### Publish Canary Release Source: https://github.com/martijnversluis/chordsheetjs/blob/master/RELEASING.md Manually publish a canary release from your local machine. Choose the intended stable bump type (patch, minor, or major). ```bash yarn publish:canary patch # or yarn publish:canary minor # or yarn publish:canary major ``` -------------------------------- ### Format Song to Measured HTML Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Format a parsed song object into Measured HTML. This format provides precise text measurement for accurate chord positioning, useful for advanced layout control. ```javascript const formatter = new ChordSheetJS.MeasuredHtmlFormatter(); const disp = formatter.format(song); ``` -------------------------------- ### Analyze song structure with measurements Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Analyzes a song object, adding width and height measurements to its lyrics and chords based on provided font options. It deep clones the song to prevent modification of the original object and resets timings before measurement. ```javascript analyzeSong(song, options) { const startTime = performance.now(); this.measurer.resetTimings(); const { chordFont, lyricFont, chordSize, lyricSize, lineHeight } = options; // Deep clone the song to avoid modifying the original const analyzed = JSON.parse(JSON.stringify(song)); // Measure everything analyzed.paragraphs.forEach(paragraph => { paragraph.lines.forEach(line => { // Track the current line width for line break analysis let currentLineWidth = 0; let maxLineHeight = 0; line.items.forEach(item => { // Measure lyrics if available if (item.lyric) { const lyricMeasurement = this.measurer.measure( item.lyric, lyricFont, lyricSize, lineHeight ); item.lyric_width = lyricMeasurement.width.value; item.lyric_height = lyricMeasurement.height.value; // Update max line height if this item is taller maxLineHeight = Math.max(maxLineHeight, item.lyric_height); // Find potential soft break points item.breakPoints = this.findSoftBreakPo ``` -------------------------------- ### Peggy Parser Data Flow Source: https://github.com/martijnversluis/chordsheetjs/blob/master/ARCHITECTURE.md Describes the process of parsing input strings using Peggy grammar to generate an Abstract Syntax Tree (AST), which is then serialized into the Song data structure. This highlights the parsing pipeline. ```plaintext Input String → Peggy Grammar → AST → ChordSheetSerializer → Song ``` -------------------------------- ### Format Song to HTML Table Layout Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Format a parsed song object into an HTML table-based layout. This formatter creates a structured HTML output suitable for web pages. ```javascript const formatter = new ChordSheetJS.HtmlTableFormatter(); const disp = formatter.format(song); ``` -------------------------------- ### Chord Representation in ChordSheetJS Source: https://github.com/martijnversluis/chordsheetjs/blob/master/ARCHITECTURE.md Illustrates the internal structure of a Chord object, detailing its components like root, modifier, suffix, and bass note. Useful for understanding chord manipulation and representation within the library. ```typescript Chord: "Cmaj7/G" ┌──────────────────────────────────┐ │ root: C │ │ modifier: maj │ │ suffix: 7 │ │ bass: G │ └──────────────────────────────────┘ ``` -------------------------------- ### Format Song to HTML Div Layout Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Format a parsed song object into an HTML div-based layout. This provides an alternative HTML structure for displaying chord sheets. ```javascript const formatter = new ChordSheetJS.HtmlDivFormatter(); const disp = formatter.format(song); ``` -------------------------------- ### Serialize and Deserialize Song Objects Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Convert a Song object to a serializable format and back. Useful for saving or transferring song data. ```javascript const serializedSong = new ChordSheetSerializer().serialize(song); const deserialized = new ChordSheetSerializer().deserialize(serializedSong); ``` -------------------------------- ### Editor and Viewer Layout Styles Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/index.html Defines the height, width, and float properties for the editor and viewer panes. Ensures the viewer has scrollable content. ```css #editors { height: 98vh; width: 49%; float: left; } #viewer { height: 98vh; width: 49%; float: right; overflow: auto; } ``` -------------------------------- ### Generate HTML for Comparison Summary Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Generates an HTML string to display a detailed summary of the comparison between canvas and DOM rendering, including performance metrics and significant differences. ```javascript generateComparisonHTML(summary) { const canvasIsFaster = summary.timings.canvas < summary.timings.dom; let html = `

Measurement Method Comparison Summary

Total Items Measured: ${summary.totalItems}
Items with Width Differences: ${summary.widthDifferences} (${(summary.widthDifferences / summary.totalItems * 100).toFixed(1)}%)
Items with Height Differences: ${summary.heightDifferences} (${(summary.heightDifferences / summary.totalItems * 100).toFixed(1)}%)
Max Width Difference: ${summary.maxWidthDiff.toFixed(2)}px
Max Height Difference: ${summary.maxHeightDiff.toFixed(2)}px
Average Width Difference: ${summary.avgWidthDiff.toFixed(2)}px
Average Height Difference: ${summary.avgHeightDiff.toFixed(2)}px

Performance Comparison

Canvas API: ${summary.timings.canvas.toFixed(2)}ms
DOM Element: ${summary.timings.dom.toFixed(2)}ms
Difference: ${Math.abs(summary.timings.difference).toFixed(2)}ms (${canvasIsFaster ? 'Canvas is ' : 'DOM is '} ${Math.abs(summary.timings.percentageDiff)}% ${canvasIsFaster ? 'faster' : 'faster'} )

`; if (summary.differences.length > 0) { html += `

Significant Differences

`; summary.differences.slice(0, 10).forEach(diff => { html += ` `; }); html += `
Type Content Canvas Width DOM Width Difference
${diff.type} ${diff.text || diff.chord || `Line ${diff.line + 1}`} ${diff.canvasWidth.toFixed(2)}px ${diff.domWidth.toFixed(2)}px ${diff.diff.toFixed(2)}px (${(diff.diff / diff.canvasWidth * 100).toFixed(1)}%)
`; } html += '
'; return html; } ``` -------------------------------- ### Format Song to Plain Text Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Format a parsed song object into a plain text representation. This is useful for console output or simple text displays. ```javascript const formatter = new ChordSheetJS.TextFormatter(); const disp = formatter.format(song); ``` -------------------------------- ### Clone a Chord Object Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Create a deep copy of a Chord object. Modifications to the clone do not affect the original. ```javascript var chord2 = chord.clone(); ``` -------------------------------- ### Measure Text Height with Unit Handling Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Calculates the height of a line of text, handling unitless line heights by multiplying with font size. It throws an error for unsupported units like '%', 'ch', 'cm', 'em', 'ex'. ```javascript const measureHeight = (size, lineHeight) => { // If the line-height is unitless, // multiply it by the font size. if (!lineHeight.unit) { return units.parse( `${size.value * lineHeight.value}${size.unit}` ); } // units-css requires the user to provide // DOM nodes for these units. We don't want // to pollute our API with that for the time being. const unitBlacklist = ["%", "ch", "cm", "em", "ex"]; if (unitBlacklist.indexOf(lineHeight.unit) !== -1) { throw new Error( `We do not currently support the unit ${lineHeight.unit} from the provided line-height ${lineHeight.value}. Unsupported units include ${unitBlacklist.join(", ")}.` ); } // Otherwise, the height is equivalent // to the provided line height. // Non-px units need conversion. if (lineHeight.unit === "px") { return lineHeight; } return units.parse( units.convert(lineHeight, "px") ); }; ``` -------------------------------- ### Format Song to ChordPro Format Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Format a parsed song object back into the ChordPro format. This is useful for exporting or saving songs in a standard ChordPro structure. ```javascript const formatter = new ChordSheetJS.ChordProFormatter(); const disp = formatter.format(song); ``` -------------------------------- ### Enable Corepack for Yarn Source: https://github.com/martijnversluis/chordsheetjs/blob/master/CONTRIBUTING.md ChordSheetJS uses Yarn 4. Enable Corepack to manage Yarn versions. It's recommended to use Node's built-in Yarn for compatibility. ```bash corepack enable ``` -------------------------------- ### Parse and Convert CSS Units Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html A simple implementation of units-css for parsing string values into numerical values and units, and for converting between compatible units. Useful for handling font sizes and line heights. ```javascript const units = (function() { // Simple units-css implementation function parse(str) { if (typeof str === "number") { return { value: str, unit: "px" }; } const match = String(str).match(/^(-?[\d.]+)([a-z%]*)$/i); if (match) { const value = parseFloat(match[1]); const unit = match[2] || ""; return { value, unit }; } return { value: 0, unit: "px" }; } function convert(unit, targetUnit) { if (unit.unit === targetUnit) { return `${unit.value}${unit.unit}`; } // Basic conversion - in a real implementation, this would handle more unit types if (unit.unit === "pt" && targetUnit === "px") { return `${unit.value * 1.333}${targetUnit}`; } // For simplicity, just return the value in the original unit return `${unit.value}${unit.unit}`; } return { parse, convert }; })(); ``` -------------------------------- ### Use Specific Chord Modifier (# or b) Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Set a chord to use either the sharp (#) or flat (b) modifier. Useful for consistent notation. ```javascript const chord = Chord.parse('Eb/Bb'); const chord2 = chord.useModifier('#'); chord2.toString(); // --> "D#/A#" ``` ```javascript const chord = Chord.parse('Eb/Bb'); const chord2 = chord.useModifier('b'); chord2.toString(); // --> "Eb/Bb" ``` -------------------------------- ### Chord Sheet Element Styling Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/index.html Basic styling for chord sheet elements, currently commented out. ```css .chord-sheet-element { /* border: 1px solid red; */ } ``` -------------------------------- ### Parse ChordPro Format Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Parse chord sheets written in the ChordPro format, which uses directives like {title} and {start_of_chorus}. This parser understands these directives. ```javascript const chordSheet = ` {title: Let it be} {subtitle: ChordSheetJS example version} {start_of_chorus: Chorus} Let it [Am]be, let it [C/G]be, let it [F]be, let it [C]be [C]Whisper words of [G]wisdom, let it [F]be [C/E] [Dm] [C] {end_of_chorus}`.substring(1); const parser = new ChordSheetJS.ChordProParser(); const song = parser.parse(chordSheet); ``` -------------------------------- ### Normalize Chord Keys Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Normalize enharmonic keys (B#, E#, Cb, Fb) to their standard equivalents (C, F, B, E). ```javascript const chord = Chord.parse('E#/B#'); normalizedChord = chord.normalize(); normalizedChord.toString(); // --> "F/C" ``` -------------------------------- ### Set ChordTextAnalyzer measurement method Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Sets the measurement method for the ChordTextAnalyzer and updates the underlying TextMeasurer instance. ```javascript setMethod(method) { this.method = method; this.measurer.setMethod(method); } ``` -------------------------------- ### Compare Measurement Results Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Compares measurement results from Canvas API and DOM Element methods. It highlights differences in dimensions and timings. ```javascript compareResults(canvasResult, domResult, canvasTime, domTime) { this.differences = []; this.timingDiff = domTime - canvasTime; const summary = { totalItems: 0, widthDifferences: 0, heightDifferences: 0, maxWidthDiff: 0, maxHeightDiff: 0, avgWidthDiff: 0, avgHeightDiff: 0, timings: { canvas: canvasTime, dom: domTime, difference: this.timingDiff, percentageDiff: (this.timingDiff / canvasTime * 100).toFixed(1) }, differences: [] }; // Compare paragraphs canvasResult.paragraphs.forEach((canvasParagraph, pIdx) => { const domParagraph = domResult.paragraphs[pIdx]; // Compare lines canvasParagraph.lines.forEach((canvasLine, lIdx) => { const domLine = domParagraph.lines[lIdx]; // Track line differences const lineDiff = Math.abs(canvasLine.total_width - domLine.total_width); const lineHeightDiff = Math.abs(canvasLine.total_height - domLine.total_height); if (lineDiff > 1 || lineHeightDiff > 1) { summary.differences.push({ type: 'line', paragraph: pIdx, line: lIdx, canvasWidth: canvasLine.total_width, domWidth: domLine.total_width, widthDiff: lineDiff, canvasHeight: canvasLine.total_height, domHeight: domLine.total_height, heightDiff: lineHeightDiff }); } // Compare items canvasLine.items.forEach((canvasItem, iIdx) => { const domItem = domLine.items[iIdx]; summary.totalItems++; // Compare text widths if (canv ``` -------------------------------- ### Analyze Song Structure and Measurements Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Analyzes the structure of a song, measuring lyrics and chords to determine line widths and heights. It keeps track of performance timings. ```javascript analyze(song) { const startTime = performance.now(); const analyzed = { paragraphs: [] }; song.paragraphs.forEach((paragraph, pIdx) => { const analyzedParagraph = { lines: [] }; paragraph.lines.forEach((line, lIdx) => { const analyzedLine = { items: [] }; let currentLineWidth = 0; let maxLineHeight = 0; line.items.forEach((item, iIdx) => { const analyzedItem = { ...item }; // Measure lyrics if (item.lyric) { const lyricMeasurement = this.measurer.measure( item.lyric, item.fontFamily || "", item.fontSize || "", item.lineHeight || "" ); analyzedItem.lyric_width = lyricMeasurement.width.value; analyzedItem.lyric_height = lyricMeasurement.height.value; analyzedItem.breakPoints = lyricMeasurement.breakPoints; currentLineWidth += analyzedItem.lyric_width; maxLineHeight = Math.max(maxLineHeight, analyzedItem.lyric_height); } analyzedParagraph.lines.push(analyzedLine); analyzedLine.items.push(analyzedItem); // Measure chords if (item.chord) { const chordMeasurement = this.measurer.measure( item.chord, chordFont, chordSize, lineHeight ); analyzedItem.chord_width = chordMeasurement.width.value; analyzedItem.chord_height = chordMeasurement.height.value; // Keep track of which is wider - chord or lyric if (!item.lyric || item.chord_width > item.lyric_width) { // The chord is wider, so we need to add spacing const extraWidth = item.chord_width - (item.lyric_width || 0); currentLineWidth += extraWidth; } } analyzedItem.position_x = currentLineWidth - (item.lyric_width || 0); }); // Add the total line width and height analyzedLine.total_width = currentLineWidth; analyzedLine.total_height = maxLineHeight; }); analyzed.paragraphs.push(analyzedParagraph); }); this.totalTime = performance.now() - startTime; return analyzed; } ``` -------------------------------- ### Transpose Chord Up Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Transpose a chord up by one semitone. Useful for key changes or practice exercises. ```javascript const chord = Chord.parse('Eb/Bb'); const chord2 = chord.transposeUp(); chord2.toString(); // -> "E/B" ``` -------------------------------- ### Transpose Chord by Interval Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Transpose a chord up or down by a specified number of semitones. Accepts positive or negative integers. ```javascript const chord = Chord.parse('C/E'); const chord2 = chord.transpose(4); chord2.toString(); // -> "E/G#" ``` ```javascript const chord = Chord.parse('C/E'); const chord2 = chord.transpose(-4); chord2.toString(); // -> "Ab/C" ``` -------------------------------- ### Parse Ultimate Guitar Chord Sheets Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Parse chord sheets formatted in the style of Ultimate Guitar. This parser handles specific formatting conventions used on that platform. ```javascript const chordSheet = ` [Chorus] Am C/G F C Let it be, let it be, let it be, let it be C G F C/E Dm C Whisper words of wisdom, let it be`.substring(1); const parser = new ChordSheetJS.UltimateGuitarParser(); const song = parser.parse(chordSheet); ``` -------------------------------- ### TextMeasurer Class with Font Loading and Caching Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html A utility class for measuring text, allowing selection between 'canvas' and 'dom' methods. It includes asynchronous font loading and a measurement cache to improve performance. The constructor initializes default values and timing metrics. ```javascript class TextMeasurer { constructor(method = 'canvas') { this.canvas = document.createElement('canvas'); this.fontLoadPromise = document.fonts.ready; this.measurementCache = {}; this.method = method; // 'canvas' or 'dom' this.timings = { canvas: 0, dom: 0 }; } /** * Wait for all fonts to load before measuring * @returns {Promise} Resolves when fonts are loaded */ async ensureFontsLoaded() { return this.fontLoadPromise; } /** * Check if a speci ``` -------------------------------- ### Update Timing Display with Comparison Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Updates the text content of an element with a formatted time in milliseconds and optionally adds 'fast' or 'slow' classes based on a comparison value. Useful for performance metrics. ```javascript function updateTimingDisplay(element, time, compareTo = null) { element.textContent = `${time.toFixed(2)} ms`; // If we have a comparison value, add styling if (compareTo !== null) { element.classList.remove('fast', 'slow'); if (time < compareTo) { element.classList.add('fast'); } else if (time > compareTo) { element.classList.add('slow'); } } } ``` -------------------------------- ### Parse Numeric Chords (Nashville System) Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Parse chords represented using the Nashville Number System, including inversions and alterations. ```javascript const chord = Chord.parse('b1sus4/#3'); ``` -------------------------------- ### Update Font Preview and CSS Variables Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Updates the font preview elements and sets CSS variables for chord, lyric, and preview fonts based on user selections. This function is called when font or size settings change. ```javascript function updatePreview() { const chordFont = chordFontSelect.value; const lyricFont = lyricFontSelect.value; const chordSize = chordSizeInput.value; const lyricSize = lyricSizeInput.value; const lineHeight = (parseInt(lineHeightInput.value) / 10).toFixed(1); // Convert to 1.0-3.0 range chordSizeValue.textContent = `${chordSize}px`; lyricSizeValue.textContent = `${lyricSize}px`; lineHeightValue.textContent = lineHeight; chordFontPreview.style.fontFamily = chordFont; chordFontPreview.style.fontSize = `${chordSize}px`; chordFontPreview.style.lineHeight = lineHeight; lyricFontPreview.style.fontFamily = lyricFont; lyricFontPreview.style.fontSize = `${lyricSize}px`; lyricFontPreview.style.lineHeight = lineHeight; // Set CSS variables for the visualization document.documentElement.style.setProperty('--chord-font', chordFont); document.documentElement.style.setProperty('--lyric-font', lyricFont); document.documentElement.style.setProperty('--preview-font', lyricFont); } ``` -------------------------------- ### Access Song Metadata Source: https://github.com/martijnversluis/chordsheetjs/blob/master/ARCHITECTURE.md Retrieve metadata properties directly from a Song object after parsing. Available properties include title, artist, key, and capo. ```typescript song.title; // "My Song" song.artist; // "Artist Name" song.key; // Key object song.capo; // "2" ``` -------------------------------- ### Display Measurement Results Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Formats and displays the analysis results, including method used, time taken, container width, font settings, and paragraph details. This function populates the results element with structured data. ```javascript function displayResults(resultsElem, analyzed, breakAnalysis, containerWidth, options, methodName, timeTaken) { // Format the results in a readable way const results = { method: methodName, time_taken: timeTaken.toFixed(2) + ' ms', container_width: containerWidth, font_settings: { chord_font: options.chordFont, lyric_font: options.lyricFont, chord_size: options.chordSize + 'px', lyric_size: options.lyricSize + 'px', line_height: options.lineHeight }, paragraphs: analyzed.paragraphs.map((p, pIdx) ``` -------------------------------- ### Convert Chord Object to String Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Convert a Chord object back into its string representation. Useful for displaying chords. ```javascript const chord = Chord.parse('Ebsus4/Bb'); chord.toString(); // --> "Ebsus4/Bb" ``` -------------------------------- ### Text Viewer and Content Styling Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/index.html Styles for the text viewer, initially hidden and toggled by JavaScript. Positions the content absolutely within the viewer. ```css #textViewer { display: none; /* This will be toggled by JavaScript */ width: 100%; height: 100%; position: relative; } #textContent { width: 100%; height: 100%; position: absolute; top: 0; left: 0; } ``` -------------------------------- ### Parse Regular Chord Sheets Source: https://github.com/martijnversluis/chordsheetjs/blob/master/README.md Parse a standard chord sheet format where chords are placed above lyrics. Ensure the input string is properly formatted. ```javascript const chordSheet = ` Am C/G F C Let it be, let it be, let it be, let it be C G F C/E Dm C Whisper words of wisdom, let it be`.substring(1); const parser = new ChordSheetJS.ChordsOverWordsParser(); const song = parser.parse(chordSheet); ``` -------------------------------- ### Force Rebuild Parsers Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md Use this flag to force a complete regeneration of all parsers, which may be necessary in certain build scenarios. ```bash yarn build -f ``` -------------------------------- ### Perform Text Measurement with Canvas and DOM Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Analyzes a song using both Canvas API and DOM Element methods, calculates line breaks, measures time taken, and updates the display. It handles options for fonts, sizes, and container width. ```javascript async function performMeasurement() { // Get current options const options = { chordFont: chordFontSelect.value, lyricFont: lyricFontSelect.value, chordSize: parseInt(chordSizeInput.value), lyricSize: parseInt(lyricSizeInput.value), lineHeight: (parseInt(lineHeightInput.value) / 10).toString() // Convert to 1.0-3.0 range }; // Get container width const containerWidth = parseInt(containerWidthInput.value); // Analyze the song with Canvas API let canvasAnalyzedSong, canvasBreakAnalysis, canvasTime = 0; if (selectedMethod === 'canvas' || selectedMethod === 'both') { canvasAnalyzedSong = canvasAnalyzer.analyzeSong(sampleSong, options); canvasBreakAnalysis = canvasAnalyzer.calculateLineBreaks(canvasAnalyzedSong, containerWidth); canvasTime = canvasAnalyzer.getTotalTime(); // Update timing display updateTimingDisplay(canvasTimingElem, canvasTime); updateTimingDisplay(canvasResultsTimingElem, canvasTime); } // Analyze the song with DOM Elements let domAnalyzedSong, domBreakAnalysis, domTime = 0; if (selectedMethod === 'dom' || selectedMethod === 'both') { domAnalyzedSong = domAnalyzer.analyzeSong(sampleSong, options); domBreakAnalysis = domAnalyzer.calculateLineBreaks(domAnalyzedSong, containerWidth); domTime = domAnalyzer.getTotalTime(); // Update timing display updateTimingDisplay(domTimingElem, domTime); updateTimingDisplay(domResultsTimingElem, domTime); } // If both methods are used, update comparisons if (selectedMethod === 'both') { updateTimingDisplay(canvasTimingElem, canvasTime, domTime); updateTimingDisplay(domTimingElem, domTime, canvasTime); updateTimingDisplay(canvasResultsTimingElem, canvasTime, domTime); updateTimingDisplay(domResultsTimingElem, domTime, canvasTime); } // Display results based on selected method if (selectedMethod === 'canvas' || selectedMethod === 'both') { displayResults(canvasResultsElem, canvasAnalyzedSong, canvasBreakAnalysis, containerWidth, options, 'Canvas API', canvasTime); renderVisualization(canvasVisualizationElem, canvasAnalyzedSong, options, containerWidth); } if (selectedMethod === 'dom' || selectedMethod === 'both') { displayResults(domResultsElem, domAnalyzedSong, domBreakAnalysis, containerWidth, options, 'DOM Element', domTime); renderVisualization(domVisualizationElem, domAnalyzedSong, options, containerWidth); } // Compare results if both methods are used if (selectedMethod === 'both') { const summary = comparison.compareResults(canvasAnalyzedSong, domAnalyzedSong, canvasTime, domTime); comparisonSummaryElem.innerHTML = comparison.generateComparisonHTML(summary); } } ``` -------------------------------- ### Change Song Key Source: https://github.com/martijnversluis/chordsheetjs/blob/master/ARCHITECTURE.md Change the key of a Song object to a new specified key. This will transpose all chords accordingly. ```typescript const newSong = song.changeKey('G'); // Transpose to key of G ``` -------------------------------- ### Find soft break points in text Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/html-test.html Identifies potential break points within a text string, prioritizing commas, then backslashes, and finally spaces. Returns an array of objects, each indicating the position and type of break point. ```javascript findSoftBreakPoints(text) { const breakPoints = []; // Look for commas let pos = -1; while ((pos = text.indexOf(',', pos + 1)) !== -1) { breakPoints.push({ index: pos, type: 'comma' }); } // Look for backslashes (explicit breaks) pos = -1; while ((pos = text.indexOf('\\', pos + 1)) !== -1) { breakPoints.push({ index: pos, type: 'backslash' }); } // Spaces are lower priority break points pos = -1; while ((pos = text.indexOf(' ', pos + 1)) !== -1) { breakPoints.push({ index: pos, type: 'space' }); } return breakPoints.sort((a, b) => a.index - b.index); } ``` -------------------------------- ### Depend on Floating Canary Tag Source: https://github.com/martijnversluis/chordsheetjs/blob/master/RELEASING.md Avoid depending on the floating 'canary' tag unless automatic version movement is intentionally desired. ```json { "dependencies": { "chordsheetjs": "canary" } } ``` -------------------------------- ### Editor Element Styling Source: https://github.com/martijnversluis/chordsheetjs/blob/master/test/formatter/pdf/index.html Applies a border and top margin to editor elements for visual separation. ```css #editor, #configEditor { border: 1px solid #ccc; margin-top: 10px; } ``` -------------------------------- ### Fix Lint Issues Source: https://github.com/martijnversluis/chordsheetjs/blob/master/agents.md This command attempts to automatically fix auto-fixable linting issues. Manual correction may still be required for remaining errors. ```bash yarn lint:fix ```