### 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 = `
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
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'} )
| 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)}%) |