@gblp/chord-finder 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,41 +1,41 @@
1
- # @gblp/chord-finder
2
-
3
- Standalone Angular 22 component for finding guitar chords and rendering SVG chord diagrams.
4
-
5
- [View the package on npm](https://www.npmjs.com/package/@gblp/chord-finder)
6
-
7
- ```bash
8
- npm install @gblp/chord-finder
9
- ```
10
-
11
- ```ts
12
- import { Component } from "@angular/core";
13
- import { ChordFinderComponent } from "@gblp/chord-finder";
14
-
15
- @Component({
16
- imports: [ChordFinderComponent],
17
- template: `<the-chords-chord-finder [language]="'es'" />`,
18
- })
19
- export class App {}
20
- ```
21
-
22
- `language` accepts `en` or `es` and defaults to `en`.
23
-
24
- Override the component theme from any ancestor with these inherited CSS variables:
25
-
26
- ```css
27
- --chords-background;
28
- --chords-background-alt;
29
- --chords-surface;
30
- --chords-surface-elevated;
31
- --chords-text;
32
- --chords-muted;
33
- --chords-primary;
34
- --chords-secondary;
35
- --chords-highlight;
36
- --chords-danger;
37
- --chords-border;
38
- --chords-on-primary;
39
- ```
40
-
41
- Build the package with `npm run build:lib`. The publishable Angular package is written to `dist/chord-finder`.
1
+ # @gblp/chord-finder
2
+
3
+ Standalone Angular 22 component for finding guitar chords and rendering SVG chord diagrams.
4
+
5
+ [View the package on npm](https://www.npmjs.com/package/@gblp/chord-finder)
6
+
7
+ ```bash
8
+ npm install @gblp/chord-finder
9
+ ```
10
+
11
+ ```ts
12
+ import { Component } from "@angular/core";
13
+ import { ChordFinderComponent } from "@gblp/chord-finder";
14
+
15
+ @Component({
16
+ imports: [ChordFinderComponent],
17
+ template: `<the-chords-chord-finder [language]="'es'" />`,
18
+ })
19
+ export class App {}
20
+ ```
21
+
22
+ `language` accepts `en` or `es` and defaults to `en`.
23
+
24
+ Override the component theme from any ancestor with these inherited CSS variables:
25
+
26
+ ```css
27
+ --chords-background;
28
+ --chords-background-alt;
29
+ --chords-surface;
30
+ --chords-surface-elevated;
31
+ --chords-text;
32
+ --chords-muted;
33
+ --chords-primary;
34
+ --chords-secondary;
35
+ --chords-highlight;
36
+ --chords-danger;
37
+ --chords-border;
38
+ --chords-on-primary;
39
+ ```
40
+
41
+ Build the package with `npm run build:lib`. The publishable Angular package is written to `dist/chord-finder`.
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { input, computed, ChangeDetectionStrategy, Component, Injectable, signal, viewChild, effect } from '@angular/core';
3
3
  import * as i2 from '@angular/forms';
4
4
  import { FormsModule } from '@angular/forms';
5
- import guitarDbJson from '@tombatossals/chords-db/lib/guitar.json';
5
+ import guitarDbJson from '@gblp/chords-db/lib/guitar.json';
6
6
 
7
7
  class ChordDiagram {
8
8
  title = input.required(/* @ts-ignore */
@@ -98,11 +98,11 @@ class ChordDiagram {
98
98
  return this.position().frets.map((fret) => (fret === -1 ? 'x' : String(fret))).join('');
99
99
  }
100
100
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ChordDiagram, deps: [], target: i0.ɵɵFactoryTarget.Component });
101
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: ChordDiagram, isStandalone: true, selector: "app-chord-diagram", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: true, transformFunction: null }, positionIndex: { classPropertyName: "positionIndex", publicName: "positionIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<svg class=\"chord-svg\" [attr.viewBox]=\"viewBox\" role=\"img\" [attr.aria-label]=\"title() + ' guitar chord diagram'\">\r\n <title>{{ title() }} - posici\u00F3n {{ positionIndex() + 1 }} - {{ positionCode() }}</title>\r\n\r\n <rect class=\"card-bg\" x=\"0\" y=\"0\" [attr.width]=\"width\" [attr.height]=\"height\" rx=\"24\" fill=\"#ffffff\" />\r\n\r\n <text x=\"120\" y=\"50\" text-anchor=\"middle\" class=\"title\">{{ title() }}</text>\r\n\r\n <g class=\"markers\">\r\n @for (fret of position().frets; track $index) {\r\n <text [attr.x]=\"stringX($index)\" y=\"92\" text-anchor=\"middle\">{{ markerFor(fret) }}</text>\r\n }\r\n </g>\r\n\r\n <text x=\"26\" [attr.y]=\"fretY(0) + 28\" text-anchor=\"middle\" class=\"fret-number\">{{ fretLabel() }}</text>\r\n\r\n <g class=\"grid\">\r\n @for (line of fretLines(); track line) {\r\n <line\r\n [attr.x1]=\"stringX(0)\"\r\n [attr.x2]=\"stringX(5)\"\r\n [attr.y1]=\"fretY(line)\"\r\n [attr.y2]=\"fretY(line)\"\r\n [class.nut]=\"line === 0 && baseFret() === 1\"\r\n />\r\n }\r\n\r\n @for (line of stringLines(); track line) {\r\n <line\r\n [attr.x1]=\"stringX(line)\"\r\n [attr.x2]=\"stringX(line)\"\r\n [attr.y1]=\"fretY(0)\"\r\n [attr.y2]=\"fretY(fretCount())\"\r\n />\r\n }\r\n </g>\r\n\r\n <g class=\"barres\">\r\n @for (barre of barreSpans(); track barre.fret) {\r\n <rect\r\n [attr.x]=\"barre.x\"\r\n [attr.y]=\"barre.y\"\r\n [attr.width]=\"barre.width\"\r\n [attr.height]=\"dotRadius * 2\"\r\n rx=\"13\"\r\n />\r\n @if (barre.finger) {\r\n <text [attr.x]=\"barre.labelX\" [attr.y]=\"barre.labelY\" text-anchor=\"middle\">{{ barre.finger }}</text>\r\n }\r\n }\r\n </g>\r\n\r\n <g class=\"dots\">\r\n @for (dot of visibleDots(); track dot.stringIndex) {\r\n <circle [attr.cx]=\"stringX(dot.stringIndex)\" [attr.cy]=\"dotY(dot.fret)\" [attr.r]=\"dotRadius\" />\r\n @if (dot.finger) {\r\n <text [attr.x]=\"stringX(dot.stringIndex)\" [attr.y]=\"dotY(dot.fret)\" text-anchor=\"middle\">{{ dot.finger }}</text>\r\n }\r\n }\r\n </g>\r\n\r\n <g class=\"string-labels\">\r\n @for (label of stringLabels; track label + $index) {\r\n <text [attr.x]=\"stringX($index)\" [attr.y]=\"fretY(fretCount()) + 32\" text-anchor=\"middle\">{{ label }}</text>\r\n }\r\n </g>\r\n</svg>\r\n", styles: [":host{display:block}.chord-svg{width:240px;height:330px;display:block;font-family:Roboto,Arial,sans-serif;font-weight:400;border-radius:24px;background:#fff;box-shadow:0 18px 36px #00000059}.card-bg{fill:#fff}.title{font-size:42px;font-weight:400;letter-spacing:-.03em;fill:#000}.grid line{stroke:#000;stroke-width:2.6;stroke-linecap:square;vector-effect:non-scaling-stroke}.grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:#000}.barres text,.dots text{fill:#fff;font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle;pointer-events:none}.markers text{fill:#000;font-size:30px;font-weight:400}.fret-number{fill:#000;font-size:42px;font-weight:400}.string-labels text{fill:#000;font-size:18px;font-weight:400}\n"], changeDetection: i0.ChangeDetectionStrategy.Eager });
101
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: ChordDiagram, isStandalone: true, selector: "app-chord-diagram", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: true, transformFunction: null }, positionIndex: { classPropertyName: "positionIndex", publicName: "positionIndex", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<svg class=\"chord-svg\" [attr.viewBox]=\"viewBox\" role=\"img\" [attr.aria-label]=\"title() + ' guitar chord diagram'\">\n <title>{{ title() }} - posici\u00F3n {{ positionIndex() + 1 }} - {{ positionCode() }}</title>\n\n <rect class=\"card-bg\" x=\"0\" y=\"0\" [attr.width]=\"width\" [attr.height]=\"height\" rx=\"24\" fill=\"#ffffff\" />\n\n <text x=\"120\" y=\"50\" text-anchor=\"middle\" class=\"title\">{{ title() }}</text>\n\n <g class=\"markers\">\n @for (fret of position().frets; track $index) {\n <text [attr.x]=\"stringX($index)\" y=\"92\" text-anchor=\"middle\">{{ markerFor(fret) }}</text>\n }\n </g>\n\n <text x=\"26\" [attr.y]=\"fretY(0) + 28\" text-anchor=\"middle\" class=\"fret-number\">{{ fretLabel() }}</text>\n\n <g class=\"grid\">\n @for (line of fretLines(); track line) {\n <line\n [attr.x1]=\"stringX(0)\"\n [attr.x2]=\"stringX(5)\"\n [attr.y1]=\"fretY(line)\"\n [attr.y2]=\"fretY(line)\"\n [class.nut]=\"line === 0 && baseFret() === 1\"\n />\n }\n\n @for (line of stringLines(); track line) {\n <line\n [attr.x1]=\"stringX(line)\"\n [attr.x2]=\"stringX(line)\"\n [attr.y1]=\"fretY(0)\"\n [attr.y2]=\"fretY(fretCount())\"\n />\n }\n </g>\n\n <g class=\"barres\">\n @for (barre of barreSpans(); track barre.fret) {\n <rect\n [attr.x]=\"barre.x\"\n [attr.y]=\"barre.y\"\n [attr.width]=\"barre.width\"\n [attr.height]=\"dotRadius * 2\"\n rx=\"13\"\n />\n @if (barre.finger) {\n <text [attr.x]=\"barre.labelX\" [attr.y]=\"barre.labelY\" text-anchor=\"middle\">{{ barre.finger }}</text>\n }\n }\n </g>\n\n <g class=\"dots\">\n @for (dot of visibleDots(); track dot.stringIndex) {\n <circle [attr.cx]=\"stringX(dot.stringIndex)\" [attr.cy]=\"dotY(dot.fret)\" [attr.r]=\"dotRadius\" />\n @if (dot.finger) {\n <text [attr.x]=\"stringX(dot.stringIndex)\" [attr.y]=\"dotY(dot.fret)\" text-anchor=\"middle\">{{ dot.finger }}</text>\n }\n }\n </g>\n\n <g class=\"string-labels\">\n @for (label of stringLabels; track label + $index) {\n <text [attr.x]=\"stringX($index)\" [attr.y]=\"fretY(fretCount()) + 32\" text-anchor=\"middle\">{{ label }}</text>\n }\n </g>\n</svg>\n", styles: [":host{display:block}.chord-svg{width:240px;height:330px;display:block;font-family:Roboto,Arial,sans-serif;font-weight:400;border-radius:24px;background:#fff;box-shadow:0 18px 36px #00000059}.card-bg{fill:#fff}.title{font-size:42px;font-weight:400;letter-spacing:-.03em;fill:#000}.grid line{stroke:#000;stroke-width:2.6;stroke-linecap:square;vector-effect:non-scaling-stroke}.grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:#000}.barres text,.dots text{fill:#fff;font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle;pointer-events:none}.markers text{fill:#000;font-size:30px;font-weight:400}.fret-number{fill:#000;font-size:42px;font-weight:400}.string-labels text{fill:#000;font-size:18px;font-weight:400}\n"], changeDetection: i0.ChangeDetectionStrategy.Eager });
102
102
  }
103
103
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ChordDiagram, decorators: [{
104
104
  type: Component,
105
- args: [{ selector: 'app-chord-diagram', imports: [], changeDetection: ChangeDetectionStrategy.Eager, template: "<svg class=\"chord-svg\" [attr.viewBox]=\"viewBox\" role=\"img\" [attr.aria-label]=\"title() + ' guitar chord diagram'\">\r\n <title>{{ title() }} - posici\u00F3n {{ positionIndex() + 1 }} - {{ positionCode() }}</title>\r\n\r\n <rect class=\"card-bg\" x=\"0\" y=\"0\" [attr.width]=\"width\" [attr.height]=\"height\" rx=\"24\" fill=\"#ffffff\" />\r\n\r\n <text x=\"120\" y=\"50\" text-anchor=\"middle\" class=\"title\">{{ title() }}</text>\r\n\r\n <g class=\"markers\">\r\n @for (fret of position().frets; track $index) {\r\n <text [attr.x]=\"stringX($index)\" y=\"92\" text-anchor=\"middle\">{{ markerFor(fret) }}</text>\r\n }\r\n </g>\r\n\r\n <text x=\"26\" [attr.y]=\"fretY(0) + 28\" text-anchor=\"middle\" class=\"fret-number\">{{ fretLabel() }}</text>\r\n\r\n <g class=\"grid\">\r\n @for (line of fretLines(); track line) {\r\n <line\r\n [attr.x1]=\"stringX(0)\"\r\n [attr.x2]=\"stringX(5)\"\r\n [attr.y1]=\"fretY(line)\"\r\n [attr.y2]=\"fretY(line)\"\r\n [class.nut]=\"line === 0 && baseFret() === 1\"\r\n />\r\n }\r\n\r\n @for (line of stringLines(); track line) {\r\n <line\r\n [attr.x1]=\"stringX(line)\"\r\n [attr.x2]=\"stringX(line)\"\r\n [attr.y1]=\"fretY(0)\"\r\n [attr.y2]=\"fretY(fretCount())\"\r\n />\r\n }\r\n </g>\r\n\r\n <g class=\"barres\">\r\n @for (barre of barreSpans(); track barre.fret) {\r\n <rect\r\n [attr.x]=\"barre.x\"\r\n [attr.y]=\"barre.y\"\r\n [attr.width]=\"barre.width\"\r\n [attr.height]=\"dotRadius * 2\"\r\n rx=\"13\"\r\n />\r\n @if (barre.finger) {\r\n <text [attr.x]=\"barre.labelX\" [attr.y]=\"barre.labelY\" text-anchor=\"middle\">{{ barre.finger }}</text>\r\n }\r\n }\r\n </g>\r\n\r\n <g class=\"dots\">\r\n @for (dot of visibleDots(); track dot.stringIndex) {\r\n <circle [attr.cx]=\"stringX(dot.stringIndex)\" [attr.cy]=\"dotY(dot.fret)\" [attr.r]=\"dotRadius\" />\r\n @if (dot.finger) {\r\n <text [attr.x]=\"stringX(dot.stringIndex)\" [attr.y]=\"dotY(dot.fret)\" text-anchor=\"middle\">{{ dot.finger }}</text>\r\n }\r\n }\r\n </g>\r\n\r\n <g class=\"string-labels\">\r\n @for (label of stringLabels; track label + $index) {\r\n <text [attr.x]=\"stringX($index)\" [attr.y]=\"fretY(fretCount()) + 32\" text-anchor=\"middle\">{{ label }}</text>\r\n }\r\n </g>\r\n</svg>\r\n", styles: [":host{display:block}.chord-svg{width:240px;height:330px;display:block;font-family:Roboto,Arial,sans-serif;font-weight:400;border-radius:24px;background:#fff;box-shadow:0 18px 36px #00000059}.card-bg{fill:#fff}.title{font-size:42px;font-weight:400;letter-spacing:-.03em;fill:#000}.grid line{stroke:#000;stroke-width:2.6;stroke-linecap:square;vector-effect:non-scaling-stroke}.grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:#000}.barres text,.dots text{fill:#fff;font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle;pointer-events:none}.markers text{fill:#000;font-size:30px;font-weight:400}.fret-number{fill:#000;font-size:42px;font-weight:400}.string-labels text{fill:#000;font-size:18px;font-weight:400}\n"] }]
105
+ args: [{ selector: 'app-chord-diagram', imports: [], changeDetection: ChangeDetectionStrategy.Eager, template: "<svg class=\"chord-svg\" [attr.viewBox]=\"viewBox\" role=\"img\" [attr.aria-label]=\"title() + ' guitar chord diagram'\">\n <title>{{ title() }} - posici\u00F3n {{ positionIndex() + 1 }} - {{ positionCode() }}</title>\n\n <rect class=\"card-bg\" x=\"0\" y=\"0\" [attr.width]=\"width\" [attr.height]=\"height\" rx=\"24\" fill=\"#ffffff\" />\n\n <text x=\"120\" y=\"50\" text-anchor=\"middle\" class=\"title\">{{ title() }}</text>\n\n <g class=\"markers\">\n @for (fret of position().frets; track $index) {\n <text [attr.x]=\"stringX($index)\" y=\"92\" text-anchor=\"middle\">{{ markerFor(fret) }}</text>\n }\n </g>\n\n <text x=\"26\" [attr.y]=\"fretY(0) + 28\" text-anchor=\"middle\" class=\"fret-number\">{{ fretLabel() }}</text>\n\n <g class=\"grid\">\n @for (line of fretLines(); track line) {\n <line\n [attr.x1]=\"stringX(0)\"\n [attr.x2]=\"stringX(5)\"\n [attr.y1]=\"fretY(line)\"\n [attr.y2]=\"fretY(line)\"\n [class.nut]=\"line === 0 && baseFret() === 1\"\n />\n }\n\n @for (line of stringLines(); track line) {\n <line\n [attr.x1]=\"stringX(line)\"\n [attr.x2]=\"stringX(line)\"\n [attr.y1]=\"fretY(0)\"\n [attr.y2]=\"fretY(fretCount())\"\n />\n }\n </g>\n\n <g class=\"barres\">\n @for (barre of barreSpans(); track barre.fret) {\n <rect\n [attr.x]=\"barre.x\"\n [attr.y]=\"barre.y\"\n [attr.width]=\"barre.width\"\n [attr.height]=\"dotRadius * 2\"\n rx=\"13\"\n />\n @if (barre.finger) {\n <text [attr.x]=\"barre.labelX\" [attr.y]=\"barre.labelY\" text-anchor=\"middle\">{{ barre.finger }}</text>\n }\n }\n </g>\n\n <g class=\"dots\">\n @for (dot of visibleDots(); track dot.stringIndex) {\n <circle [attr.cx]=\"stringX(dot.stringIndex)\" [attr.cy]=\"dotY(dot.fret)\" [attr.r]=\"dotRadius\" />\n @if (dot.finger) {\n <text [attr.x]=\"stringX(dot.stringIndex)\" [attr.y]=\"dotY(dot.fret)\" text-anchor=\"middle\">{{ dot.finger }}</text>\n }\n }\n </g>\n\n <g class=\"string-labels\">\n @for (label of stringLabels; track label + $index) {\n <text [attr.x]=\"stringX($index)\" [attr.y]=\"fretY(fretCount()) + 32\" text-anchor=\"middle\">{{ label }}</text>\n }\n </g>\n</svg>\n", styles: [":host{display:block}.chord-svg{width:240px;height:330px;display:block;font-family:Roboto,Arial,sans-serif;font-weight:400;border-radius:24px;background:#fff;box-shadow:0 18px 36px #00000059}.card-bg{fill:#fff}.title{font-size:42px;font-weight:400;letter-spacing:-.03em;fill:#000}.grid line{stroke:#000;stroke-width:2.6;stroke-linecap:square;vector-effect:non-scaling-stroke}.grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:#000}.barres text,.dots text{fill:#fff;font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle;pointer-events:none}.markers text{fill:#000;font-size:30px;font-weight:400}.fret-number{fill:#000;font-size:42px;font-weight:400}.string-labels text{fill:#000;font-size:18px;font-weight:400}\n"] }]
106
106
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: true }] }], positionIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "positionIndex", required: false }] }] } });
107
107
 
108
108
  const ERROR_COPY = {
@@ -468,15 +468,15 @@ class ChordFinderComponent {
468
468
  const fgInverse = this.exportTransparent ? '#ffffff' : this.exportBgColor;
469
469
  for (const [index, svg] of svgs.entries()) {
470
470
  const clone = svg.cloneNode(true);
471
- clone.insertAdjacentHTML('afterbegin', `<style>
472
- .chord-svg{font-family:Roboto,Arial,sans-serif;font-weight:400}
473
- .card-bg{fill:${bg}}.title{font-size:42px;font-weight:400;fill:${fg}}
474
- .grid line{stroke:${fg};stroke-width:2.6;stroke-linecap:square}
475
- .grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:${fg}}
476
- .barres text,.dots text{fill:${fgInverse};font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle}
477
- .markers text{fill:${fg};font-size:30px;font-weight:400}
478
- .fret-number{fill:${fg};font-size:42px;font-weight:400}
479
- .string-labels text{fill:${fg};font-size:18px;font-weight:400}
471
+ clone.insertAdjacentHTML('afterbegin', `<style>
472
+ .chord-svg{font-family:Roboto,Arial,sans-serif;font-weight:400}
473
+ .card-bg{fill:${bg}}.title{font-size:42px;font-weight:400;fill:${fg}}
474
+ .grid line{stroke:${fg};stroke-width:2.6;stroke-linecap:square}
475
+ .grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:${fg}}
476
+ .barres text,.dots text{fill:${fgInverse};font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle}
477
+ .markers text{fill:${fg};font-size:30px;font-weight:400}
478
+ .fret-number{fill:${fg};font-size:42px;font-weight:400}
479
+ .string-labels text{fill:${fg};font-size:18px;font-weight:400}
480
480
  </style>`);
481
481
  const url = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(clone)], {
482
482
  type: 'image/svg+xml',
@@ -537,11 +537,11 @@ class ChordFinderComponent {
537
537
  return result.id;
538
538
  }
539
539
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ChordFinderComponent, deps: [{ token: ChordService }], target: i0.ɵɵFactoryTarget.Component });
540
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: ChordFinderComponent, isStandalone: true, selector: "the-chords-chord-finder", inputs: { language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.lang": "language()" } }, viewQueries: [{ propertyName: "resultsRow", first: true, predicate: ["resultsRow"], descendants: true, isSignal: true }, { propertyName: "sectionsContainer", first: true, predicate: ["sectionsContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<main class=\"page-shell\">\r\n <section class=\"hero-card\">\r\n <p class=\"eyebrow\">{{ text().eyebrow }}</p>\r\n <h1>{{ text().title }}</h1>\r\n\r\n <p class=\"description\">\r\n {{ text().descriptionStart }}\r\n <strong>{{ text().descriptionLimit }}</strong>\r\n {{ text().descriptionEnd }}\r\n <strong>C, F#, C#m, Bb</strong>.\r\n </p>\r\n\r\n <div class=\"search-row\">\r\n <label for=\"chordInput\">{{ text().inputLabel }}</label>\r\n <input\r\n id=\"chordInput\"\r\n type=\"text\"\r\n [(ngModel)]=\"query\"\r\n (input)=\"runSearch()\"\r\n placeholder=\"C, F#, C#m, Bb\"\r\n autocomplete=\"off\"\r\n />\r\n <div class=\"export-wrap\">\r\n <button\r\n type=\"button\"\r\n class=\"export-btn\"\r\n (click)=\"exportPanelOpen.set(!exportPanelOpen())\"\r\n [disabled]=\"!hasResults()\"\r\n [class.open]=\"exportPanelOpen()\"\r\n >\r\n {{ text().exportPng }}\r\n <span class=\"chevron\" aria-hidden=\"true\">\u25BE</span>\r\n </button>\r\n <div class=\"export-panel\" [class.open]=\"exportPanelOpen()\">\r\n <div class=\"export-panel-inner\">\r\n <div class=\"export-options\">\r\n <label class=\"color-opt\">\r\n <span>{{ text().bgColor }}</span>\r\n <div class=\"color-row\">\r\n <input\r\n type=\"color\"\r\n [(ngModel)]=\"exportBgColor\"\r\n [disabled]=\"exportTransparent\"\r\n />\r\n <label class=\"check-label\">\r\n <input type=\"checkbox\" [(ngModel)]=\"exportTransparent\" />\r\n {{ text().transparent }}\r\n </label>\r\n </div>\r\n </label>\r\n <label class=\"color-opt\">\r\n <span>{{ text().lineColor }}</span>\r\n <input type=\"color\" [(ngModel)]=\"exportLineColor\" />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"download-btn\"\r\n (click)=\"exportPng()\"\r\n [disabled]=\"!hasResults()\"\r\n >\r\n {{ text().download }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (inputError()) {\r\n <p class=\"input-error\">{{ inputError() }}</p>\r\n }\r\n\r\n <p class=\"hint\">{{ text().availableTypes }} {{ supportedSuffixes() }}...</p>\r\n </section>\r\n\r\n @if (inputMode() === 'plain') {\r\n <section\r\n #resultsRow\r\n class=\"results-row\"\r\n [attr.aria-label]=\"text().resultsLabel\"\r\n >\r\n @for (result of results(); track trackByResultId($index, result)) {\r\n <article class=\"result-card\" [class.has-error]=\"result.error\">\r\n <header class=\"result-header\">\r\n <div>\r\n <p class=\"result-label\">{{ text().chord }}</p>\r\n <h2>{{ result.displayName }}</h2>\r\n @if (result.dbName && result.raw !== result.displayName) {\r\n <p class=\"db-name\">{{ text().normalizedAs }} {{ result.dbName }}</p>\r\n }\r\n </div>\r\n @if (result.positions.length > 1) {\r\n <label class=\"select-label\">\r\n {{ text().position }}\r\n <select\r\n [ngModel]=\"selectedPositionIndex[result.id]\"\r\n (ngModelChange)=\"selectPosition(result.id, $event)\"\r\n >\r\n @for (position of result.positions; track $index) {\r\n <option [value]=\"$index\">\r\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\r\n </option>\r\n }\r\n </select>\r\n </label>\r\n }\r\n </header>\r\n @if (result.error) {\r\n <p class=\"error-text\">{{ result.error }}</p>\r\n } @else if (selectedPosition(result); as position) {\r\n <div class=\"svg-wrap\">\r\n <app-chord-diagram\r\n [title]=\"result.displayName\"\r\n [position]=\"position\"\r\n [positionIndex]=\"selectedIndex(result.id)\"\r\n />\r\n </div>\r\n }\r\n </article>\r\n }\r\n </section>\r\n } @else if (inputMode() === 'sections') {\r\n <div #sectionsContainer class=\"sections-container\">\r\n @for (section of sections(); let si = $index; track si) {\r\n <section\r\n class=\"section-block\"\r\n [attr.data-section]=\"si\"\r\n [attr.aria-label]=\"section.name\"\r\n >\r\n <h2 class=\"section-name\">{{ section.name }}</h2>\r\n <div class=\"results-row\">\r\n @for (result of section.results; track trackByResultId($index, result)) {\r\n <article class=\"result-card\" [class.has-error]=\"result.error\">\r\n <header class=\"result-header\">\r\n <div>\r\n <p class=\"result-label\">{{ text().chord }}</p>\r\n <h2>{{ result.displayName }}</h2>\r\n @if (result.dbName && result.raw !== result.displayName) {\r\n <p class=\"db-name\">\r\n {{ text().normalizedAs }} {{ result.dbName }}\r\n </p>\r\n }\r\n </div>\r\n @if (result.positions.length > 1) {\r\n <label class=\"select-label\">\r\n {{ text().position }}\r\n <select\r\n [ngModel]=\"selectedPositionIndex[result.id]\"\r\n (ngModelChange)=\"selectPosition(result.id, $event)\"\r\n >\r\n @for (position of result.positions; track $index) {\r\n <option [value]=\"$index\">\r\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\r\n </option>\r\n }\r\n </select>\r\n </label>\r\n }\r\n </header>\r\n @if (result.error) {\r\n <p class=\"error-text\">{{ result.error }}</p>\r\n } @else if (selectedPosition(result); as position) {\r\n <div class=\"svg-wrap\">\r\n <app-chord-diagram\r\n [title]=\"result.displayName\"\r\n [position]=\"position\"\r\n [positionIndex]=\"selectedIndex(result.id)\"\r\n />\r\n </div>\r\n }\r\n </article>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </div>\r\n }\r\n\r\n <footer class=\"app-footer\">\r\n <p>\r\n {{ text().openSource }}\r\n <a\r\n href=\"https://github.com/elparaquecosadeque/chord-generator\"\r\n target=\"_blank\"\r\n rel=\"noreferrer\"\r\n >\r\n elparaquecosadeque/chord-generator </a\r\n >. {{ text().contributions }}\r\n </p>\r\n <p>{{ text().assistance }}</p>\r\n </footer>\r\n</main>\r\n", styles: [":host{--_chords-background: var(--chords-background, #020004);--_chords-background-alt: var(--chords-background-alt, #050012);--_chords-surface: var(--chords-surface, #000000);--_chords-surface-elevated: var( --chords-surface-elevated, rgba(0, 0, 0, .8) );--_chords-text: var(--chords-text, #f8f8ff);--_chords-muted: var(--chords-muted, #dedbff);--_chords-primary: var(--chords-primary, #00fff0);--_chords-secondary: var(--chords-secondary, #ff2bd6);--_chords-highlight: var(--chords-highlight, #ffee00);--_chords-danger: var(--chords-danger, #ff315a);--_chords-border: var(--chords-border, rgba(0, 255, 240, .76));--_chords-on-primary: var(--chords-on-primary, #000000);box-sizing:border-box;display:block;min-height:100%;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}:host *,:host *:before,:host *:after{box-sizing:inherit}.page-shell{min-height:100%;padding:clamp(24px,5vw,56px);color:var(--_chords-text);background:radial-gradient(circle at 10% 12%,color-mix(in srgb,var(--_chords-primary) 25%,transparent),transparent 28%),radial-gradient(circle at 92% 4%,color-mix(in srgb,var(--_chords-secondary) 25%,transparent),transparent 24%),radial-gradient(circle at 50% 110%,color-mix(in srgb,var(--_chords-highlight) 16%,transparent),transparent 30%),linear-gradient(135deg,var(--_chords-background) 0%,var(--_chords-background-alt) 48%,var(--_chords-surface) 100%)}.hero-card{max-width:1020px;padding:clamp(24px,4vw,38px);border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 30px color-mix(in srgb,var(--_chords-primary) 32%,transparent),inset 0 0 26px color-mix(in srgb,var(--_chords-secondary) 13%,transparent)}.eyebrow{margin:0 0 14px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.22em;text-transform:uppercase}h1{margin:0;max-width:820px;font-size:clamp(44px,8vw,92px);line-height:.88;letter-spacing:-.06em;text-shadow:0 0 18px color-mix(in srgb,var(--_chords-primary) 80%,transparent),0 0 32px color-mix(in srgb,var(--_chords-secondary) 50%,transparent)}.description,.hint{max-width:760px;margin:22px 0 0;color:var(--_chords-muted);font-size:18px;line-height:1.55}.description strong{color:var(--_chords-highlight)}.search-row{display:grid;gap:10px;margin-top:28px}.search-row label,.select-label,.result-label{color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase}.search-row input:not([type=color]):not([type=checkbox]){width:min(760px,100%);padding:18px 20px;color:var(--_chords-text);background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:18px;font-size:clamp(22px,4vw,32px);font-weight:900;outline:none;box-shadow:0 0 18px color-mix(in srgb,var(--_chords-secondary) 58%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-primary) 12%,transparent)}.search-row input:not([type=color]):not([type=checkbox]):focus{border-color:var(--_chords-primary);box-shadow:0 0 24px color-mix(in srgb,var(--_chords-primary) 78%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 20%,transparent)}.search-row button{width:fit-content;padding:12px 18px;color:var(--_chords-on-primary);background:var(--_chords-primary);border:0;border-radius:12px;font-weight:900;cursor:pointer}.search-row button:disabled{opacity:.45;cursor:not-allowed}.warning{margin:16px 0 0;color:var(--_chords-highlight);font-weight:900}.input-error{margin:12px 0 0;color:var(--_chords-danger);font-size:14px;font-weight:700}.results-row{display:flex;flex-wrap:wrap;gap:32px;margin-top:34px;align-items:flex-start}.result-card{width:292px;padding:18px;border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-primary) 25%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 12%,transparent)}.result-card.has-error{border-color:var(--_chords-danger);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-danger) 35%,transparent)}.result-header{display:grid;gap:12px;margin-bottom:16px}.result-label,.db-name{margin:0}.result-header h2{margin:4px 0 0;color:var(--_chords-text);font-size:30px}.db-name{margin-top:6px;color:var(--_chords-primary);font-size:12px;font-weight:800}.select-label{display:grid;gap:8px}select{width:100%;padding:11px 12px;color:var(--_chords-text);background:var(--_chords-background-alt);border:1px solid var(--_chords-secondary);border-radius:12px;font-weight:800;outline:none}select:focus{border-color:var(--_chords-primary)}.svg-wrap{display:grid;place-items:center;padding:0;background:transparent;border-radius:28px}.error-text{color:var(--_chords-danger);font-weight:800;line-height:1.45}.app-footer{max-width:1020px;margin-top:40px;padding-top:20px;border-top:1px solid color-mix(in srgb,var(--_chords-primary) 42%,transparent);color:var(--_chords-muted);font-size:14px;line-height:1.6}.app-footer p{margin:0}.app-footer p+p{margin-top:6px}.app-footer a{color:var(--_chords-primary);font-weight:900}.export-wrap{display:flex;flex-direction:column;align-items:flex-start}.export-btn{display:flex;align-items:center;gap:8px}.export-btn .chevron{display:inline-block;transition:transform .28s ease}.export-btn.open .chevron{transform:rotate(180deg)}.export-panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows .28s ease}.export-panel.open{grid-template-rows:1fr}.export-panel-inner{overflow:hidden}.export-options{display:flex;flex-wrap:wrap;align-items:flex-end;gap:16px;padding:14px 2px 4px}.color-opt{display:flex;flex-direction:column;gap:6px;color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase;cursor:default}.color-row{display:flex;align-items:center;gap:10px}.check-label{display:flex;align-items:center;gap:6px;color:var(--_chords-muted);font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;cursor:pointer}.check-label input[type=checkbox]{accent-color:var(--_chords-primary);width:16px;height:16px;cursor:pointer}input[type=color]{width:44px;height:36px;padding:2px;background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:8px;cursor:pointer}input[type=color]:disabled{opacity:.35;cursor:not-allowed}.search-row .download-btn{background:var(--_chords-secondary)}.sections-container{display:flex;flex-direction:column;gap:40px;margin-top:34px}.section-block{display:flex;flex-direction:column;gap:0}.section-name{margin:0 0 16px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.2em;text-transform:uppercase;border-bottom:1px solid color-mix(in srgb,var(--_chords-primary) 35%,transparent);padding-bottom:10px}.section-block .results-row{margin-top:0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ChordDiagram, selector: "app-chord-diagram", inputs: ["title", "position", "positionIndex"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
540
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: ChordFinderComponent, isStandalone: true, selector: "the-chords-chord-finder", inputs: { language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.lang": "language()" } }, viewQueries: [{ propertyName: "resultsRow", first: true, predicate: ["resultsRow"], descendants: true, isSignal: true }, { propertyName: "sectionsContainer", first: true, predicate: ["sectionsContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<main class=\"page-shell\">\n <section class=\"hero-card\">\n <p class=\"eyebrow\">{{ text().eyebrow }}</p>\n <h1>{{ text().title }}</h1>\n\n <p class=\"description\">\n {{ text().descriptionStart }}\n <strong>{{ text().descriptionLimit }}</strong>\n {{ text().descriptionEnd }}\n <strong>C, F#, C#m, Bb</strong>.\n </p>\n\n <div class=\"search-row\">\n <label for=\"chordInput\">{{ text().inputLabel }}</label>\n <input\n id=\"chordInput\"\n type=\"text\"\n [(ngModel)]=\"query\"\n (input)=\"runSearch()\"\n placeholder=\"C, F#, C#m, Bb\"\n autocomplete=\"off\"\n />\n <div class=\"export-wrap\">\n <button\n type=\"button\"\n class=\"export-btn\"\n (click)=\"exportPanelOpen.set(!exportPanelOpen())\"\n [disabled]=\"!hasResults()\"\n [class.open]=\"exportPanelOpen()\"\n >\n {{ text().exportPng }}\n <span class=\"chevron\" aria-hidden=\"true\">\u25BE</span>\n </button>\n <div class=\"export-panel\" [class.open]=\"exportPanelOpen()\">\n <div class=\"export-panel-inner\">\n <div class=\"export-options\">\n <label class=\"color-opt\">\n <span>{{ text().bgColor }}</span>\n <div class=\"color-row\">\n <input\n type=\"color\"\n [(ngModel)]=\"exportBgColor\"\n [disabled]=\"exportTransparent\"\n />\n <label class=\"check-label\">\n <input type=\"checkbox\" [(ngModel)]=\"exportTransparent\" />\n {{ text().transparent }}\n </label>\n </div>\n </label>\n <label class=\"color-opt\">\n <span>{{ text().lineColor }}</span>\n <input type=\"color\" [(ngModel)]=\"exportLineColor\" />\n </label>\n <button\n type=\"button\"\n class=\"download-btn\"\n (click)=\"exportPng()\"\n [disabled]=\"!hasResults()\"\n >\n {{ text().download }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n @if (inputError()) {\n <p class=\"input-error\">{{ inputError() }}</p>\n }\n\n <p class=\"hint\">{{ text().availableTypes }} {{ supportedSuffixes() }}...</p>\n </section>\n\n @if (inputMode() === 'plain') {\n <section\n #resultsRow\n class=\"results-row\"\n [attr.aria-label]=\"text().resultsLabel\"\n >\n @for (result of results(); track trackByResultId($index, result)) {\n <article class=\"result-card\" [class.has-error]=\"result.error\">\n <header class=\"result-header\">\n <div>\n <p class=\"result-label\">{{ text().chord }}</p>\n <h2>{{ result.displayName }}</h2>\n @if (result.dbName && result.raw !== result.displayName) {\n <p class=\"db-name\">{{ text().normalizedAs }} {{ result.dbName }}</p>\n }\n </div>\n @if (result.positions.length > 1) {\n <label class=\"select-label\">\n {{ text().position }}\n <select\n [ngModel]=\"selectedPositionIndex[result.id]\"\n (ngModelChange)=\"selectPosition(result.id, $event)\"\n >\n @for (position of result.positions; track $index) {\n <option [value]=\"$index\">\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\n </option>\n }\n </select>\n </label>\n }\n </header>\n @if (result.error) {\n <p class=\"error-text\">{{ result.error }}</p>\n } @else if (selectedPosition(result); as position) {\n <div class=\"svg-wrap\">\n <app-chord-diagram\n [title]=\"result.displayName\"\n [position]=\"position\"\n [positionIndex]=\"selectedIndex(result.id)\"\n />\n </div>\n }\n </article>\n }\n </section>\n } @else if (inputMode() === 'sections') {\n <div #sectionsContainer class=\"sections-container\">\n @for (section of sections(); let si = $index; track si) {\n <section\n class=\"section-block\"\n [attr.data-section]=\"si\"\n [attr.aria-label]=\"section.name\"\n >\n <h2 class=\"section-name\">{{ section.name }}</h2>\n <div class=\"results-row\">\n @for (result of section.results; track trackByResultId($index, result)) {\n <article class=\"result-card\" [class.has-error]=\"result.error\">\n <header class=\"result-header\">\n <div>\n <p class=\"result-label\">{{ text().chord }}</p>\n <h2>{{ result.displayName }}</h2>\n @if (result.dbName && result.raw !== result.displayName) {\n <p class=\"db-name\">\n {{ text().normalizedAs }} {{ result.dbName }}\n </p>\n }\n </div>\n @if (result.positions.length > 1) {\n <label class=\"select-label\">\n {{ text().position }}\n <select\n [ngModel]=\"selectedPositionIndex[result.id]\"\n (ngModelChange)=\"selectPosition(result.id, $event)\"\n >\n @for (position of result.positions; track $index) {\n <option [value]=\"$index\">\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\n </option>\n }\n </select>\n </label>\n }\n </header>\n @if (result.error) {\n <p class=\"error-text\">{{ result.error }}</p>\n } @else if (selectedPosition(result); as position) {\n <div class=\"svg-wrap\">\n <app-chord-diagram\n [title]=\"result.displayName\"\n [position]=\"position\"\n [positionIndex]=\"selectedIndex(result.id)\"\n />\n </div>\n }\n </article>\n }\n </div>\n </section>\n }\n </div>\n }\n\n <footer class=\"app-footer\">\n <p>\n {{ text().openSource }}\n <a\n href=\"https://github.com/elparaquecosadeque/chord-generator\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n elparaquecosadeque/chord-generator </a\n >. {{ text().contributions }}\n </p>\n <p>{{ text().assistance }}</p>\n </footer>\n</main>\n", styles: [":host{--_chords-background: var(--chords-background, #020004);--_chords-background-alt: var(--chords-background-alt, #050012);--_chords-surface: var(--chords-surface, #000000);--_chords-surface-elevated: var( --chords-surface-elevated, rgba(0, 0, 0, .8) );--_chords-text: var(--chords-text, #f8f8ff);--_chords-muted: var(--chords-muted, #dedbff);--_chords-primary: var(--chords-primary, #00fff0);--_chords-secondary: var(--chords-secondary, #ff2bd6);--_chords-highlight: var(--chords-highlight, #ffee00);--_chords-danger: var(--chords-danger, #ff315a);--_chords-border: var(--chords-border, rgba(0, 255, 240, .76));--_chords-on-primary: var(--chords-on-primary, #000000);box-sizing:border-box;display:block;min-height:100%;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}:host *,:host *:before,:host *:after{box-sizing:inherit}.page-shell{min-height:100%;padding:clamp(24px,5vw,56px);color:var(--_chords-text);background:radial-gradient(circle at 10% 12%,color-mix(in srgb,var(--_chords-primary) 25%,transparent),transparent 28%),radial-gradient(circle at 92% 4%,color-mix(in srgb,var(--_chords-secondary) 25%,transparent),transparent 24%),radial-gradient(circle at 50% 110%,color-mix(in srgb,var(--_chords-highlight) 16%,transparent),transparent 30%),linear-gradient(135deg,var(--_chords-background) 0%,var(--_chords-background-alt) 48%,var(--_chords-surface) 100%)}.hero-card{max-width:1020px;padding:clamp(24px,4vw,38px);border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 30px color-mix(in srgb,var(--_chords-primary) 32%,transparent),inset 0 0 26px color-mix(in srgb,var(--_chords-secondary) 13%,transparent)}.eyebrow{margin:0 0 14px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.22em;text-transform:uppercase}h1{margin:0;max-width:820px;font-size:clamp(44px,8vw,92px);line-height:.88;letter-spacing:-.06em;text-shadow:0 0 18px color-mix(in srgb,var(--_chords-primary) 80%,transparent),0 0 32px color-mix(in srgb,var(--_chords-secondary) 50%,transparent)}.description,.hint{max-width:760px;margin:22px 0 0;color:var(--_chords-muted);font-size:18px;line-height:1.55}.description strong{color:var(--_chords-highlight)}.search-row{display:grid;gap:10px;margin-top:28px}.search-row label,.select-label,.result-label{color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase}.search-row input:not([type=color]):not([type=checkbox]){width:min(760px,100%);padding:18px 20px;color:var(--_chords-text);background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:18px;font-size:clamp(22px,4vw,32px);font-weight:900;outline:none;box-shadow:0 0 18px color-mix(in srgb,var(--_chords-secondary) 58%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-primary) 12%,transparent)}.search-row input:not([type=color]):not([type=checkbox]):focus{border-color:var(--_chords-primary);box-shadow:0 0 24px color-mix(in srgb,var(--_chords-primary) 78%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 20%,transparent)}.search-row button{width:fit-content;padding:12px 18px;color:var(--_chords-on-primary);background:var(--_chords-primary);border:0;border-radius:12px;font-weight:900;cursor:pointer}.search-row button:disabled{opacity:.45;cursor:not-allowed}.warning{margin:16px 0 0;color:var(--_chords-highlight);font-weight:900}.input-error{margin:12px 0 0;color:var(--_chords-danger);font-size:14px;font-weight:700}.results-row{display:flex;flex-wrap:wrap;gap:32px;margin-top:34px;align-items:flex-start}.result-card{width:292px;padding:18px;border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-primary) 25%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 12%,transparent)}.result-card.has-error{border-color:var(--_chords-danger);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-danger) 35%,transparent)}.result-header{display:grid;gap:12px;margin-bottom:16px}.result-label,.db-name{margin:0}.result-header h2{margin:4px 0 0;color:var(--_chords-text);font-size:30px}.db-name{margin-top:6px;color:var(--_chords-primary);font-size:12px;font-weight:800}.select-label{display:grid;gap:8px}select{width:100%;padding:11px 12px;color:var(--_chords-text);background:var(--_chords-background-alt);border:1px solid var(--_chords-secondary);border-radius:12px;font-weight:800;outline:none}select:focus{border-color:var(--_chords-primary)}.svg-wrap{display:grid;place-items:center;padding:0;background:transparent;border-radius:28px}.error-text{color:var(--_chords-danger);font-weight:800;line-height:1.45}.app-footer{max-width:1020px;margin-top:40px;padding-top:20px;border-top:1px solid color-mix(in srgb,var(--_chords-primary) 42%,transparent);color:var(--_chords-muted);font-size:14px;line-height:1.6}.app-footer p{margin:0}.app-footer p+p{margin-top:6px}.app-footer a{color:var(--_chords-primary);font-weight:900}.export-wrap{display:flex;flex-direction:column;align-items:flex-start}.export-btn{display:flex;align-items:center;gap:8px}.export-btn .chevron{display:inline-block;transition:transform .28s ease}.export-btn.open .chevron{transform:rotate(180deg)}.export-panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows .28s ease}.export-panel.open{grid-template-rows:1fr}.export-panel-inner{overflow:hidden}.export-options{display:flex;flex-wrap:wrap;align-items:flex-end;gap:16px;padding:14px 2px 4px}.color-opt{display:flex;flex-direction:column;gap:6px;color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase;cursor:default}.color-row{display:flex;align-items:center;gap:10px}.check-label{display:flex;align-items:center;gap:6px;color:var(--_chords-muted);font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;cursor:pointer}.check-label input[type=checkbox]{accent-color:var(--_chords-primary);width:16px;height:16px;cursor:pointer}input[type=color]{width:44px;height:36px;padding:2px;background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:8px;cursor:pointer}input[type=color]:disabled{opacity:.35;cursor:not-allowed}.search-row .download-btn{background:var(--_chords-secondary)}.sections-container{display:flex;flex-direction:column;gap:40px;margin-top:34px}.section-block{display:flex;flex-direction:column;gap:0}.section-name{margin:0 0 16px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.2em;text-transform:uppercase;border-bottom:1px solid color-mix(in srgb,var(--_chords-primary) 35%,transparent);padding-bottom:10px}.section-block .results-row{margin-top:0}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ChordDiagram, selector: "app-chord-diagram", inputs: ["title", "position", "positionIndex"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
541
541
  }
542
542
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: ChordFinderComponent, decorators: [{
543
543
  type: Component,
544
- args: [{ selector: 'the-chords-chord-finder', standalone: true, imports: [FormsModule, ChordDiagram], changeDetection: ChangeDetectionStrategy.Eager, host: { '[attr.lang]': 'language()' }, template: "<main class=\"page-shell\">\r\n <section class=\"hero-card\">\r\n <p class=\"eyebrow\">{{ text().eyebrow }}</p>\r\n <h1>{{ text().title }}</h1>\r\n\r\n <p class=\"description\">\r\n {{ text().descriptionStart }}\r\n <strong>{{ text().descriptionLimit }}</strong>\r\n {{ text().descriptionEnd }}\r\n <strong>C, F#, C#m, Bb</strong>.\r\n </p>\r\n\r\n <div class=\"search-row\">\r\n <label for=\"chordInput\">{{ text().inputLabel }}</label>\r\n <input\r\n id=\"chordInput\"\r\n type=\"text\"\r\n [(ngModel)]=\"query\"\r\n (input)=\"runSearch()\"\r\n placeholder=\"C, F#, C#m, Bb\"\r\n autocomplete=\"off\"\r\n />\r\n <div class=\"export-wrap\">\r\n <button\r\n type=\"button\"\r\n class=\"export-btn\"\r\n (click)=\"exportPanelOpen.set(!exportPanelOpen())\"\r\n [disabled]=\"!hasResults()\"\r\n [class.open]=\"exportPanelOpen()\"\r\n >\r\n {{ text().exportPng }}\r\n <span class=\"chevron\" aria-hidden=\"true\">\u25BE</span>\r\n </button>\r\n <div class=\"export-panel\" [class.open]=\"exportPanelOpen()\">\r\n <div class=\"export-panel-inner\">\r\n <div class=\"export-options\">\r\n <label class=\"color-opt\">\r\n <span>{{ text().bgColor }}</span>\r\n <div class=\"color-row\">\r\n <input\r\n type=\"color\"\r\n [(ngModel)]=\"exportBgColor\"\r\n [disabled]=\"exportTransparent\"\r\n />\r\n <label class=\"check-label\">\r\n <input type=\"checkbox\" [(ngModel)]=\"exportTransparent\" />\r\n {{ text().transparent }}\r\n </label>\r\n </div>\r\n </label>\r\n <label class=\"color-opt\">\r\n <span>{{ text().lineColor }}</span>\r\n <input type=\"color\" [(ngModel)]=\"exportLineColor\" />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"download-btn\"\r\n (click)=\"exportPng()\"\r\n [disabled]=\"!hasResults()\"\r\n >\r\n {{ text().download }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (inputError()) {\r\n <p class=\"input-error\">{{ inputError() }}</p>\r\n }\r\n\r\n <p class=\"hint\">{{ text().availableTypes }} {{ supportedSuffixes() }}...</p>\r\n </section>\r\n\r\n @if (inputMode() === 'plain') {\r\n <section\r\n #resultsRow\r\n class=\"results-row\"\r\n [attr.aria-label]=\"text().resultsLabel\"\r\n >\r\n @for (result of results(); track trackByResultId($index, result)) {\r\n <article class=\"result-card\" [class.has-error]=\"result.error\">\r\n <header class=\"result-header\">\r\n <div>\r\n <p class=\"result-label\">{{ text().chord }}</p>\r\n <h2>{{ result.displayName }}</h2>\r\n @if (result.dbName && result.raw !== result.displayName) {\r\n <p class=\"db-name\">{{ text().normalizedAs }} {{ result.dbName }}</p>\r\n }\r\n </div>\r\n @if (result.positions.length > 1) {\r\n <label class=\"select-label\">\r\n {{ text().position }}\r\n <select\r\n [ngModel]=\"selectedPositionIndex[result.id]\"\r\n (ngModelChange)=\"selectPosition(result.id, $event)\"\r\n >\r\n @for (position of result.positions; track $index) {\r\n <option [value]=\"$index\">\r\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\r\n </option>\r\n }\r\n </select>\r\n </label>\r\n }\r\n </header>\r\n @if (result.error) {\r\n <p class=\"error-text\">{{ result.error }}</p>\r\n } @else if (selectedPosition(result); as position) {\r\n <div class=\"svg-wrap\">\r\n <app-chord-diagram\r\n [title]=\"result.displayName\"\r\n [position]=\"position\"\r\n [positionIndex]=\"selectedIndex(result.id)\"\r\n />\r\n </div>\r\n }\r\n </article>\r\n }\r\n </section>\r\n } @else if (inputMode() === 'sections') {\r\n <div #sectionsContainer class=\"sections-container\">\r\n @for (section of sections(); let si = $index; track si) {\r\n <section\r\n class=\"section-block\"\r\n [attr.data-section]=\"si\"\r\n [attr.aria-label]=\"section.name\"\r\n >\r\n <h2 class=\"section-name\">{{ section.name }}</h2>\r\n <div class=\"results-row\">\r\n @for (result of section.results; track trackByResultId($index, result)) {\r\n <article class=\"result-card\" [class.has-error]=\"result.error\">\r\n <header class=\"result-header\">\r\n <div>\r\n <p class=\"result-label\">{{ text().chord }}</p>\r\n <h2>{{ result.displayName }}</h2>\r\n @if (result.dbName && result.raw !== result.displayName) {\r\n <p class=\"db-name\">\r\n {{ text().normalizedAs }} {{ result.dbName }}\r\n </p>\r\n }\r\n </div>\r\n @if (result.positions.length > 1) {\r\n <label class=\"select-label\">\r\n {{ text().position }}\r\n <select\r\n [ngModel]=\"selectedPositionIndex[result.id]\"\r\n (ngModelChange)=\"selectPosition(result.id, $event)\"\r\n >\r\n @for (position of result.positions; track $index) {\r\n <option [value]=\"$index\">\r\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\r\n </option>\r\n }\r\n </select>\r\n </label>\r\n }\r\n </header>\r\n @if (result.error) {\r\n <p class=\"error-text\">{{ result.error }}</p>\r\n } @else if (selectedPosition(result); as position) {\r\n <div class=\"svg-wrap\">\r\n <app-chord-diagram\r\n [title]=\"result.displayName\"\r\n [position]=\"position\"\r\n [positionIndex]=\"selectedIndex(result.id)\"\r\n />\r\n </div>\r\n }\r\n </article>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </div>\r\n }\r\n\r\n <footer class=\"app-footer\">\r\n <p>\r\n {{ text().openSource }}\r\n <a\r\n href=\"https://github.com/elparaquecosadeque/chord-generator\"\r\n target=\"_blank\"\r\n rel=\"noreferrer\"\r\n >\r\n elparaquecosadeque/chord-generator </a\r\n >. {{ text().contributions }}\r\n </p>\r\n <p>{{ text().assistance }}</p>\r\n </footer>\r\n</main>\r\n", styles: [":host{--_chords-background: var(--chords-background, #020004);--_chords-background-alt: var(--chords-background-alt, #050012);--_chords-surface: var(--chords-surface, #000000);--_chords-surface-elevated: var( --chords-surface-elevated, rgba(0, 0, 0, .8) );--_chords-text: var(--chords-text, #f8f8ff);--_chords-muted: var(--chords-muted, #dedbff);--_chords-primary: var(--chords-primary, #00fff0);--_chords-secondary: var(--chords-secondary, #ff2bd6);--_chords-highlight: var(--chords-highlight, #ffee00);--_chords-danger: var(--chords-danger, #ff315a);--_chords-border: var(--chords-border, rgba(0, 255, 240, .76));--_chords-on-primary: var(--chords-on-primary, #000000);box-sizing:border-box;display:block;min-height:100%;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}:host *,:host *:before,:host *:after{box-sizing:inherit}.page-shell{min-height:100%;padding:clamp(24px,5vw,56px);color:var(--_chords-text);background:radial-gradient(circle at 10% 12%,color-mix(in srgb,var(--_chords-primary) 25%,transparent),transparent 28%),radial-gradient(circle at 92% 4%,color-mix(in srgb,var(--_chords-secondary) 25%,transparent),transparent 24%),radial-gradient(circle at 50% 110%,color-mix(in srgb,var(--_chords-highlight) 16%,transparent),transparent 30%),linear-gradient(135deg,var(--_chords-background) 0%,var(--_chords-background-alt) 48%,var(--_chords-surface) 100%)}.hero-card{max-width:1020px;padding:clamp(24px,4vw,38px);border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 30px color-mix(in srgb,var(--_chords-primary) 32%,transparent),inset 0 0 26px color-mix(in srgb,var(--_chords-secondary) 13%,transparent)}.eyebrow{margin:0 0 14px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.22em;text-transform:uppercase}h1{margin:0;max-width:820px;font-size:clamp(44px,8vw,92px);line-height:.88;letter-spacing:-.06em;text-shadow:0 0 18px color-mix(in srgb,var(--_chords-primary) 80%,transparent),0 0 32px color-mix(in srgb,var(--_chords-secondary) 50%,transparent)}.description,.hint{max-width:760px;margin:22px 0 0;color:var(--_chords-muted);font-size:18px;line-height:1.55}.description strong{color:var(--_chords-highlight)}.search-row{display:grid;gap:10px;margin-top:28px}.search-row label,.select-label,.result-label{color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase}.search-row input:not([type=color]):not([type=checkbox]){width:min(760px,100%);padding:18px 20px;color:var(--_chords-text);background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:18px;font-size:clamp(22px,4vw,32px);font-weight:900;outline:none;box-shadow:0 0 18px color-mix(in srgb,var(--_chords-secondary) 58%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-primary) 12%,transparent)}.search-row input:not([type=color]):not([type=checkbox]):focus{border-color:var(--_chords-primary);box-shadow:0 0 24px color-mix(in srgb,var(--_chords-primary) 78%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 20%,transparent)}.search-row button{width:fit-content;padding:12px 18px;color:var(--_chords-on-primary);background:var(--_chords-primary);border:0;border-radius:12px;font-weight:900;cursor:pointer}.search-row button:disabled{opacity:.45;cursor:not-allowed}.warning{margin:16px 0 0;color:var(--_chords-highlight);font-weight:900}.input-error{margin:12px 0 0;color:var(--_chords-danger);font-size:14px;font-weight:700}.results-row{display:flex;flex-wrap:wrap;gap:32px;margin-top:34px;align-items:flex-start}.result-card{width:292px;padding:18px;border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-primary) 25%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 12%,transparent)}.result-card.has-error{border-color:var(--_chords-danger);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-danger) 35%,transparent)}.result-header{display:grid;gap:12px;margin-bottom:16px}.result-label,.db-name{margin:0}.result-header h2{margin:4px 0 0;color:var(--_chords-text);font-size:30px}.db-name{margin-top:6px;color:var(--_chords-primary);font-size:12px;font-weight:800}.select-label{display:grid;gap:8px}select{width:100%;padding:11px 12px;color:var(--_chords-text);background:var(--_chords-background-alt);border:1px solid var(--_chords-secondary);border-radius:12px;font-weight:800;outline:none}select:focus{border-color:var(--_chords-primary)}.svg-wrap{display:grid;place-items:center;padding:0;background:transparent;border-radius:28px}.error-text{color:var(--_chords-danger);font-weight:800;line-height:1.45}.app-footer{max-width:1020px;margin-top:40px;padding-top:20px;border-top:1px solid color-mix(in srgb,var(--_chords-primary) 42%,transparent);color:var(--_chords-muted);font-size:14px;line-height:1.6}.app-footer p{margin:0}.app-footer p+p{margin-top:6px}.app-footer a{color:var(--_chords-primary);font-weight:900}.export-wrap{display:flex;flex-direction:column;align-items:flex-start}.export-btn{display:flex;align-items:center;gap:8px}.export-btn .chevron{display:inline-block;transition:transform .28s ease}.export-btn.open .chevron{transform:rotate(180deg)}.export-panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows .28s ease}.export-panel.open{grid-template-rows:1fr}.export-panel-inner{overflow:hidden}.export-options{display:flex;flex-wrap:wrap;align-items:flex-end;gap:16px;padding:14px 2px 4px}.color-opt{display:flex;flex-direction:column;gap:6px;color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase;cursor:default}.color-row{display:flex;align-items:center;gap:10px}.check-label{display:flex;align-items:center;gap:6px;color:var(--_chords-muted);font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;cursor:pointer}.check-label input[type=checkbox]{accent-color:var(--_chords-primary);width:16px;height:16px;cursor:pointer}input[type=color]{width:44px;height:36px;padding:2px;background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:8px;cursor:pointer}input[type=color]:disabled{opacity:.35;cursor:not-allowed}.search-row .download-btn{background:var(--_chords-secondary)}.sections-container{display:flex;flex-direction:column;gap:40px;margin-top:34px}.section-block{display:flex;flex-direction:column;gap:0}.section-name{margin:0 0 16px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.2em;text-transform:uppercase;border-bottom:1px solid color-mix(in srgb,var(--_chords-primary) 35%,transparent);padding-bottom:10px}.section-block .results-row{margin-top:0}\n"] }]
544
+ args: [{ selector: 'the-chords-chord-finder', standalone: true, imports: [FormsModule, ChordDiagram], changeDetection: ChangeDetectionStrategy.Eager, host: { '[attr.lang]': 'language()' }, template: "<main class=\"page-shell\">\n <section class=\"hero-card\">\n <p class=\"eyebrow\">{{ text().eyebrow }}</p>\n <h1>{{ text().title }}</h1>\n\n <p class=\"description\">\n {{ text().descriptionStart }}\n <strong>{{ text().descriptionLimit }}</strong>\n {{ text().descriptionEnd }}\n <strong>C, F#, C#m, Bb</strong>.\n </p>\n\n <div class=\"search-row\">\n <label for=\"chordInput\">{{ text().inputLabel }}</label>\n <input\n id=\"chordInput\"\n type=\"text\"\n [(ngModel)]=\"query\"\n (input)=\"runSearch()\"\n placeholder=\"C, F#, C#m, Bb\"\n autocomplete=\"off\"\n />\n <div class=\"export-wrap\">\n <button\n type=\"button\"\n class=\"export-btn\"\n (click)=\"exportPanelOpen.set(!exportPanelOpen())\"\n [disabled]=\"!hasResults()\"\n [class.open]=\"exportPanelOpen()\"\n >\n {{ text().exportPng }}\n <span class=\"chevron\" aria-hidden=\"true\">\u25BE</span>\n </button>\n <div class=\"export-panel\" [class.open]=\"exportPanelOpen()\">\n <div class=\"export-panel-inner\">\n <div class=\"export-options\">\n <label class=\"color-opt\">\n <span>{{ text().bgColor }}</span>\n <div class=\"color-row\">\n <input\n type=\"color\"\n [(ngModel)]=\"exportBgColor\"\n [disabled]=\"exportTransparent\"\n />\n <label class=\"check-label\">\n <input type=\"checkbox\" [(ngModel)]=\"exportTransparent\" />\n {{ text().transparent }}\n </label>\n </div>\n </label>\n <label class=\"color-opt\">\n <span>{{ text().lineColor }}</span>\n <input type=\"color\" [(ngModel)]=\"exportLineColor\" />\n </label>\n <button\n type=\"button\"\n class=\"download-btn\"\n (click)=\"exportPng()\"\n [disabled]=\"!hasResults()\"\n >\n {{ text().download }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n @if (inputError()) {\n <p class=\"input-error\">{{ inputError() }}</p>\n }\n\n <p class=\"hint\">{{ text().availableTypes }} {{ supportedSuffixes() }}...</p>\n </section>\n\n @if (inputMode() === 'plain') {\n <section\n #resultsRow\n class=\"results-row\"\n [attr.aria-label]=\"text().resultsLabel\"\n >\n @for (result of results(); track trackByResultId($index, result)) {\n <article class=\"result-card\" [class.has-error]=\"result.error\">\n <header class=\"result-header\">\n <div>\n <p class=\"result-label\">{{ text().chord }}</p>\n <h2>{{ result.displayName }}</h2>\n @if (result.dbName && result.raw !== result.displayName) {\n <p class=\"db-name\">{{ text().normalizedAs }} {{ result.dbName }}</p>\n }\n </div>\n @if (result.positions.length > 1) {\n <label class=\"select-label\">\n {{ text().position }}\n <select\n [ngModel]=\"selectedPositionIndex[result.id]\"\n (ngModelChange)=\"selectPosition(result.id, $event)\"\n >\n @for (position of result.positions; track $index) {\n <option [value]=\"$index\">\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\n </option>\n }\n </select>\n </label>\n }\n </header>\n @if (result.error) {\n <p class=\"error-text\">{{ result.error }}</p>\n } @else if (selectedPosition(result); as position) {\n <div class=\"svg-wrap\">\n <app-chord-diagram\n [title]=\"result.displayName\"\n [position]=\"position\"\n [positionIndex]=\"selectedIndex(result.id)\"\n />\n </div>\n }\n </article>\n }\n </section>\n } @else if (inputMode() === 'sections') {\n <div #sectionsContainer class=\"sections-container\">\n @for (section of sections(); let si = $index; track si) {\n <section\n class=\"section-block\"\n [attr.data-section]=\"si\"\n [attr.aria-label]=\"section.name\"\n >\n <h2 class=\"section-name\">{{ section.name }}</h2>\n <div class=\"results-row\">\n @for (result of section.results; track trackByResultId($index, result)) {\n <article class=\"result-card\" [class.has-error]=\"result.error\">\n <header class=\"result-header\">\n <div>\n <p class=\"result-label\">{{ text().chord }}</p>\n <h2>{{ result.displayName }}</h2>\n @if (result.dbName && result.raw !== result.displayName) {\n <p class=\"db-name\">\n {{ text().normalizedAs }} {{ result.dbName }}\n </p>\n }\n </div>\n @if (result.positions.length > 1) {\n <label class=\"select-label\">\n {{ text().position }}\n <select\n [ngModel]=\"selectedPositionIndex[result.id]\"\n (ngModelChange)=\"selectPosition(result.id, $event)\"\n >\n @for (position of result.positions; track $index) {\n <option [value]=\"$index\">\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\n </option>\n }\n </select>\n </label>\n }\n </header>\n @if (result.error) {\n <p class=\"error-text\">{{ result.error }}</p>\n } @else if (selectedPosition(result); as position) {\n <div class=\"svg-wrap\">\n <app-chord-diagram\n [title]=\"result.displayName\"\n [position]=\"position\"\n [positionIndex]=\"selectedIndex(result.id)\"\n />\n </div>\n }\n </article>\n }\n </div>\n </section>\n }\n </div>\n }\n\n <footer class=\"app-footer\">\n <p>\n {{ text().openSource }}\n <a\n href=\"https://github.com/elparaquecosadeque/chord-generator\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n elparaquecosadeque/chord-generator </a\n >. {{ text().contributions }}\n </p>\n <p>{{ text().assistance }}</p>\n </footer>\n</main>\n", styles: [":host{--_chords-background: var(--chords-background, #020004);--_chords-background-alt: var(--chords-background-alt, #050012);--_chords-surface: var(--chords-surface, #000000);--_chords-surface-elevated: var( --chords-surface-elevated, rgba(0, 0, 0, .8) );--_chords-text: var(--chords-text, #f8f8ff);--_chords-muted: var(--chords-muted, #dedbff);--_chords-primary: var(--chords-primary, #00fff0);--_chords-secondary: var(--chords-secondary, #ff2bd6);--_chords-highlight: var(--chords-highlight, #ffee00);--_chords-danger: var(--chords-danger, #ff315a);--_chords-border: var(--chords-border, rgba(0, 255, 240, .76));--_chords-on-primary: var(--chords-on-primary, #000000);box-sizing:border-box;display:block;min-height:100%;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}:host *,:host *:before,:host *:after{box-sizing:inherit}.page-shell{min-height:100%;padding:clamp(24px,5vw,56px);color:var(--_chords-text);background:radial-gradient(circle at 10% 12%,color-mix(in srgb,var(--_chords-primary) 25%,transparent),transparent 28%),radial-gradient(circle at 92% 4%,color-mix(in srgb,var(--_chords-secondary) 25%,transparent),transparent 24%),radial-gradient(circle at 50% 110%,color-mix(in srgb,var(--_chords-highlight) 16%,transparent),transparent 30%),linear-gradient(135deg,var(--_chords-background) 0%,var(--_chords-background-alt) 48%,var(--_chords-surface) 100%)}.hero-card{max-width:1020px;padding:clamp(24px,4vw,38px);border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 30px color-mix(in srgb,var(--_chords-primary) 32%,transparent),inset 0 0 26px color-mix(in srgb,var(--_chords-secondary) 13%,transparent)}.eyebrow{margin:0 0 14px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.22em;text-transform:uppercase}h1{margin:0;max-width:820px;font-size:clamp(44px,8vw,92px);line-height:.88;letter-spacing:-.06em;text-shadow:0 0 18px color-mix(in srgb,var(--_chords-primary) 80%,transparent),0 0 32px color-mix(in srgb,var(--_chords-secondary) 50%,transparent)}.description,.hint{max-width:760px;margin:22px 0 0;color:var(--_chords-muted);font-size:18px;line-height:1.55}.description strong{color:var(--_chords-highlight)}.search-row{display:grid;gap:10px;margin-top:28px}.search-row label,.select-label,.result-label{color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase}.search-row input:not([type=color]):not([type=checkbox]){width:min(760px,100%);padding:18px 20px;color:var(--_chords-text);background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:18px;font-size:clamp(22px,4vw,32px);font-weight:900;outline:none;box-shadow:0 0 18px color-mix(in srgb,var(--_chords-secondary) 58%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-primary) 12%,transparent)}.search-row input:not([type=color]):not([type=checkbox]):focus{border-color:var(--_chords-primary);box-shadow:0 0 24px color-mix(in srgb,var(--_chords-primary) 78%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 20%,transparent)}.search-row button{width:fit-content;padding:12px 18px;color:var(--_chords-on-primary);background:var(--_chords-primary);border:0;border-radius:12px;font-weight:900;cursor:pointer}.search-row button:disabled{opacity:.45;cursor:not-allowed}.warning{margin:16px 0 0;color:var(--_chords-highlight);font-weight:900}.input-error{margin:12px 0 0;color:var(--_chords-danger);font-size:14px;font-weight:700}.results-row{display:flex;flex-wrap:wrap;gap:32px;margin-top:34px;align-items:flex-start}.result-card{width:292px;padding:18px;border:1px solid var(--_chords-border);border-radius:30px;background:var(--_chords-surface-elevated);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-primary) 25%,transparent),inset 0 0 18px color-mix(in srgb,var(--_chords-secondary) 12%,transparent)}.result-card.has-error{border-color:var(--_chords-danger);box-shadow:0 0 22px color-mix(in srgb,var(--_chords-danger) 35%,transparent)}.result-header{display:grid;gap:12px;margin-bottom:16px}.result-label,.db-name{margin:0}.result-header h2{margin:4px 0 0;color:var(--_chords-text);font-size:30px}.db-name{margin-top:6px;color:var(--_chords-primary);font-size:12px;font-weight:800}.select-label{display:grid;gap:8px}select{width:100%;padding:11px 12px;color:var(--_chords-text);background:var(--_chords-background-alt);border:1px solid var(--_chords-secondary);border-radius:12px;font-weight:800;outline:none}select:focus{border-color:var(--_chords-primary)}.svg-wrap{display:grid;place-items:center;padding:0;background:transparent;border-radius:28px}.error-text{color:var(--_chords-danger);font-weight:800;line-height:1.45}.app-footer{max-width:1020px;margin-top:40px;padding-top:20px;border-top:1px solid color-mix(in srgb,var(--_chords-primary) 42%,transparent);color:var(--_chords-muted);font-size:14px;line-height:1.6}.app-footer p{margin:0}.app-footer p+p{margin-top:6px}.app-footer a{color:var(--_chords-primary);font-weight:900}.export-wrap{display:flex;flex-direction:column;align-items:flex-start}.export-btn{display:flex;align-items:center;gap:8px}.export-btn .chevron{display:inline-block;transition:transform .28s ease}.export-btn.open .chevron{transform:rotate(180deg)}.export-panel{display:grid;grid-template-rows:0fr;transition:grid-template-rows .28s ease}.export-panel.open{grid-template-rows:1fr}.export-panel-inner{overflow:hidden}.export-options{display:flex;flex-wrap:wrap;align-items:flex-end;gap:16px;padding:14px 2px 4px}.color-opt{display:flex;flex-direction:column;gap:6px;color:var(--_chords-secondary);font-size:12px;font-weight:900;letter-spacing:.14em;text-transform:uppercase;cursor:default}.color-row{display:flex;align-items:center;gap:10px}.check-label{display:flex;align-items:center;gap:6px;color:var(--_chords-muted);font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;cursor:pointer}.check-label input[type=checkbox]{accent-color:var(--_chords-primary);width:16px;height:16px;cursor:pointer}input[type=color]{width:44px;height:36px;padding:2px;background:var(--_chords-surface);border:2px solid var(--_chords-secondary);border-radius:8px;cursor:pointer}input[type=color]:disabled{opacity:.35;cursor:not-allowed}.search-row .download-btn{background:var(--_chords-secondary)}.sections-container{display:flex;flex-direction:column;gap:40px;margin-top:34px}.section-block{display:flex;flex-direction:column;gap:0}.section-name{margin:0 0 16px;color:var(--_chords-primary);font-size:13px;font-weight:900;letter-spacing:.2em;text-transform:uppercase;border-bottom:1px solid color-mix(in srgb,var(--_chords-primary) 35%,transparent);padding-bottom:10px}.section-block .results-row{margin-top:0}\n"] }]
545
545
  }], ctorParameters: () => [{ type: ChordService }], propDecorators: { language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }], resultsRow: [{ type: i0.ViewChild, args: ['resultsRow', { isSignal: true }] }], sectionsContainer: [{ type: i0.ViewChild, args: ['sectionsContainer', { isSignal: true }] }] } });
546
546
 
547
547
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"gblp-chord-finder.mjs","sources":["../../../projects/chord-finder/src/lib/components/chord-diagram/chord-diagram.ts","../../../projects/chord-finder/src/lib/components/chord-diagram/chord-diagram.html","../../../projects/chord-finder/src/lib/services/chord.service.ts","../../../projects/chord-finder/src/lib/chord-finder.ts","../../../projects/chord-finder/src/lib/chord-finder.html","../../../projects/chord-finder/src/public-api.ts","../../../projects/chord-finder/src/gblp-chord-finder.ts"],"sourcesContent":["import { Component, computed, input, ChangeDetectionStrategy } from '@angular/core';\r\nimport { ChordsDbPosition } from '../../models/chord.model';\r\n\r\ninterface BarreSpan {\r\n fret: number;\r\n x: number;\r\n y: number;\r\n width: number;\r\n labelX: number;\r\n labelY: number;\r\n finger: number | undefined;\r\n}\r\n\r\ninterface DotMarker {\r\n fret: number;\r\n stringIndex: number;\r\n finger?: number;\r\n}\r\n\r\n@Component({\r\n selector: 'app-chord-diagram',\r\n imports: [],\r\n templateUrl: './chord-diagram.html',\r\n changeDetection: ChangeDetectionStrategy.Eager,\r\n styleUrl: './chord-diagram.scss'\r\n})\r\nexport class ChordDiagram {\r\n title = input.required<string>();\r\n position = input.required<ChordsDbPosition>();\r\n positionIndex = input<number>(0);\r\n\r\n readonly width = 240;\r\n readonly height = 330;\r\n readonly left = 54;\r\n readonly top = 112;\r\n readonly stringGap = 28;\r\n readonly fretGap = 34;\r\n readonly dotRadius = 13;\r\n readonly stringLabels = ['E', 'A', 'D', 'G', 'B', 'E'];\r\n\r\n fretCount = computed(() => Math.max(5, ...this.position().frets.filter((fret) => fret > 0)));\r\n\r\n fretLines = computed(() => Array.from({ length: this.fretCount() + 1 }, (_, index) => index));\r\n\r\n stringLines = computed(() => Array.from({ length: 6 }, (_, index) => index));\r\n\r\n visibleDots = computed<DotMarker[]>(() => {\r\n const barres = new Set(this.position().barres ?? []);\r\n const fingers = this.position().fingers ?? [];\r\n\r\n return this.position().frets\r\n .map((fret, stringIndex) => ({\r\n fret,\r\n stringIndex,\r\n finger: fingers[stringIndex] > 0 ? fingers[stringIndex] : undefined\r\n }))\r\n .filter(({ fret }) => fret > 0)\r\n .filter(({ fret }) => !barres.has(fret));\r\n });\r\n\r\n barreSpans = computed<BarreSpan[]>(() => {\r\n const barres = this.position().barres ?? [];\r\n const fingers = this.position().fingers ?? [];\r\n\r\n return barres\r\n .map((barreFret) => {\r\n const playableStrings = this.position().frets\r\n .map((fret, index) => ({ fret, index }))\r\n .filter(({ fret }) => fret >= barreFret);\r\n\r\n if (playableStrings.length < 2) return null;\r\n\r\n const firstString = playableStrings[0].index;\r\n const lastString = playableStrings[playableStrings.length - 1].index;\r\n const x = this.stringX(firstString) - this.dotRadius;\r\n const y = this.dotY(barreFret) - this.dotRadius;\r\n const width = this.stringX(lastString) - this.stringX(firstString) + this.dotRadius * 2;\r\n const labelX = x + width / 2;\r\n const labelY = y + this.dotRadius;\r\n const finger = fingers[firstString] > 0 ? fingers[firstString] : undefined;\r\n\r\n return {\r\n fret: barreFret,\r\n x,\r\n y,\r\n width,\r\n labelX,\r\n labelY,\r\n finger\r\n } satisfies BarreSpan;\r\n })\r\n .filter((span): span is BarreSpan => span !== null);\r\n });\r\n\r\n get viewBox(): string {\r\n return `0 0 ${this.width} ${this.height}`;\r\n }\r\n\r\n baseFret(): number {\r\n return this.position().baseFret ?? 1;\r\n }\r\n\r\n stringX(index: number): number {\r\n return this.left + index * this.stringGap;\r\n }\r\n\r\n fretY(index: number): number {\r\n return this.top + index * this.fretGap;\r\n }\r\n\r\n dotY(fret: number): number {\r\n return this.top + (fret - 0.5) * this.fretGap;\r\n }\r\n\r\n markerFor(fret: number): string {\r\n if (fret === -1) return '×';\r\n if (fret === 0) return '○';\r\n return '';\r\n }\r\n\r\n fretLabel(): string {\r\n return String(this.baseFret());\r\n }\r\n\r\n positionCode(): string {\r\n return this.position().frets.map((fret) => (fret === -1 ? 'x' : String(fret))).join('');\r\n }\r\n}\r\n","<svg class=\"chord-svg\" [attr.viewBox]=\"viewBox\" role=\"img\" [attr.aria-label]=\"title() + ' guitar chord diagram'\">\r\n <title>{{ title() }} - posición {{ positionIndex() + 1 }} - {{ positionCode() }}</title>\r\n\r\n <rect class=\"card-bg\" x=\"0\" y=\"0\" [attr.width]=\"width\" [attr.height]=\"height\" rx=\"24\" fill=\"#ffffff\" />\r\n\r\n <text x=\"120\" y=\"50\" text-anchor=\"middle\" class=\"title\">{{ title() }}</text>\r\n\r\n <g class=\"markers\">\r\n @for (fret of position().frets; track $index) {\r\n <text [attr.x]=\"stringX($index)\" y=\"92\" text-anchor=\"middle\">{{ markerFor(fret) }}</text>\r\n }\r\n </g>\r\n\r\n <text x=\"26\" [attr.y]=\"fretY(0) + 28\" text-anchor=\"middle\" class=\"fret-number\">{{ fretLabel() }}</text>\r\n\r\n <g class=\"grid\">\r\n @for (line of fretLines(); track line) {\r\n <line\r\n [attr.x1]=\"stringX(0)\"\r\n [attr.x2]=\"stringX(5)\"\r\n [attr.y1]=\"fretY(line)\"\r\n [attr.y2]=\"fretY(line)\"\r\n [class.nut]=\"line === 0 && baseFret() === 1\"\r\n />\r\n }\r\n\r\n @for (line of stringLines(); track line) {\r\n <line\r\n [attr.x1]=\"stringX(line)\"\r\n [attr.x2]=\"stringX(line)\"\r\n [attr.y1]=\"fretY(0)\"\r\n [attr.y2]=\"fretY(fretCount())\"\r\n />\r\n }\r\n </g>\r\n\r\n <g class=\"barres\">\r\n @for (barre of barreSpans(); track barre.fret) {\r\n <rect\r\n [attr.x]=\"barre.x\"\r\n [attr.y]=\"barre.y\"\r\n [attr.width]=\"barre.width\"\r\n [attr.height]=\"dotRadius * 2\"\r\n rx=\"13\"\r\n />\r\n @if (barre.finger) {\r\n <text [attr.x]=\"barre.labelX\" [attr.y]=\"barre.labelY\" text-anchor=\"middle\">{{ barre.finger }}</text>\r\n }\r\n }\r\n </g>\r\n\r\n <g class=\"dots\">\r\n @for (dot of visibleDots(); track dot.stringIndex) {\r\n <circle [attr.cx]=\"stringX(dot.stringIndex)\" [attr.cy]=\"dotY(dot.fret)\" [attr.r]=\"dotRadius\" />\r\n @if (dot.finger) {\r\n <text [attr.x]=\"stringX(dot.stringIndex)\" [attr.y]=\"dotY(dot.fret)\" text-anchor=\"middle\">{{ dot.finger }}</text>\r\n }\r\n }\r\n </g>\r\n\r\n <g class=\"string-labels\">\r\n @for (label of stringLabels; track label + $index) {\r\n <text [attr.x]=\"stringX($index)\" [attr.y]=\"fretY(fretCount()) + 32\" text-anchor=\"middle\">{{ label }}</text>\r\n }\r\n </g>\r\n</svg>\r\n","import { Injectable } from '@angular/core';\r\nimport guitarDbJson from '@tombatossals/chords-db/lib/guitar.json';\r\nimport {\r\n ChordSearchResult,\r\n ChordSection,\r\n ChordsDbInstrument,\r\n Language,\r\n ParsedChord,\r\n} from '../models/chord.model';\r\n\r\nconst ERROR_COPY = {\r\n en: {\r\n invalid: 'Invalid chord name. Try C, F#, C#m, Bb, Am7, or Dsus4.',\r\n missingRoot: 'Chord root not found in chords-db.',\r\n missingType: (suffix: string) =>\r\n `The type \"${suffix}\" is not available for this chord.`,\r\n tooManySections: (max: number) =>\r\n `Too many sections — maximum allowed is ${max}.`,\r\n tooManyChords: (section: string, max: number) =>\r\n `Section \"${section}\" exceeds the maximum of ${max} chords.`,\r\n emptySectionName: 'Section name cannot be empty.',\r\n emptySection: (name: string) => `Section \"${name}\" has no chords.`,\r\n noValidSections:\r\n 'No valid sections found. Expected format: \"name: chord1, chord2; name2: chord3\".',\r\n },\r\n es: {\r\n invalid: 'Nombre inválido. Prueba C, F#, C#m, Bb, Am7 o Dsus4.',\r\n missingRoot: 'Raíz no encontrada en chords-db.',\r\n missingType: (suffix: string) =>\r\n `El tipo \"${suffix}\" no existe para este acorde en la base actual.`,\r\n tooManySections: (max: number) =>\r\n `Demasiadas secciones — el máximo permitido es ${max}.`,\r\n tooManyChords: (section: string, max: number) =>\r\n `La sección \"${section}\" supera el máximo de ${max} acordes.`,\r\n emptySectionName: 'El nombre de la sección no puede estar vacío.',\r\n emptySection: (name: string) =>\r\n `La sección \"${name}\" no tiene acordes.`,\r\n noValidSections:\r\n 'No se encontraron secciones válidas. Formato esperado: \"nombre: acorde1, acorde2; nombre2: acorde3\".',\r\n },\r\n} satisfies Record<\r\n Language,\r\n {\r\n invalid: string;\r\n missingRoot: string;\r\n missingType: (suffix: string) => string;\r\n tooManySections: (max: number) => string;\r\n tooManyChords: (section: string, max: number) => string;\r\n emptySectionName: string;\r\n emptySection: (name: string) => string;\r\n noValidSections: string;\r\n }\r\n>;\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class ChordService {\r\n private readonly MAX_SECTIONS = 6;\r\n private readonly MAX_CHORDS_PER_SECTION = 6;\r\n\r\n private readonly guitarDb = guitarDbJson as ChordsDbInstrument;\r\n\r\n private readonly dbRootMap: Record<string, string> = {\r\n C: 'C',\r\n 'C#': 'Csharp',\r\n Db: 'Csharp',\r\n D: 'D',\r\n 'D#': 'Eb',\r\n Eb: 'Eb',\r\n E: 'E',\r\n Fb: 'E',\r\n 'E#': 'F',\r\n F: 'F',\r\n 'F#': 'Fsharp',\r\n Gb: 'Fsharp',\r\n G: 'G',\r\n 'G#': 'Ab',\r\n Ab: 'Ab',\r\n A: 'A',\r\n 'A#': 'Bb',\r\n Bb: 'Bb',\r\n B: 'B',\r\n Cb: 'B',\r\n 'B#': 'C',\r\n };\r\n\r\n private readonly suffixAliases: Record<string, string> = {\r\n '': 'major',\r\n M: 'major',\r\n maj: 'major',\r\n major: 'major',\r\n m: 'minor',\r\n min: 'minor',\r\n '-': 'minor',\r\n minor: 'minor',\r\n Δ: 'maj7',\r\n maj7: 'maj7',\r\n M7: 'maj7',\r\n m7: 'm7',\r\n min7: 'm7',\r\n dim: 'dim',\r\n diminished: 'dim',\r\n aug: 'aug',\r\n augmented: 'aug',\r\n sus: 'sus4',\r\n sus2: 'sus2',\r\n sus4: 'sus4',\r\n add9: 'add9',\r\n 4: 'sus4',\r\n };\r\n\r\n search(\r\n input: string,\r\n language: Language = 'en',\r\n ): ChordSearchResult[] {\r\n return input\r\n .split(',')\r\n .map((token) => token.trim())\r\n .filter(Boolean)\r\n .map((token, index) => this.searchSingle(token, index, language));\r\n }\r\n\r\n isSectionFormat(input: string): boolean {\r\n return input.includes(':');\r\n }\r\n\r\n searchSections(\r\n input: string,\r\n language: Language = 'en',\r\n ): { sections: ChordSection[]; error: string | null } {\r\n const copy = ERROR_COPY[language];\r\n\r\n const rawSections = input\r\n .split(';')\r\n .map((s) => s.trim())\r\n .filter(Boolean);\r\n\r\n if (!rawSections.length) {\r\n return { sections: [], error: copy.noValidSections };\r\n }\r\n\r\n if (rawSections.length > this.MAX_SECTIONS) {\r\n return { sections: [], error: copy.tooManySections(this.MAX_SECTIONS) };\r\n }\r\n\r\n const sections: ChordSection[] = [];\r\n\r\n for (const [si, raw] of rawSections.entries()) {\r\n const colonIndex = raw.indexOf(':');\r\n if (colonIndex === -1) {\r\n return { sections: [], error: copy.noValidSections };\r\n }\r\n\r\n const name = raw.slice(0, colonIndex).trim();\r\n const chordsStr = raw.slice(colonIndex + 1).trim();\r\n\r\n if (!name) {\r\n return { sections: [], error: copy.emptySectionName };\r\n }\r\n\r\n const tokens = chordsStr\r\n .split(',')\r\n .map((t) => t.trim())\r\n .filter(Boolean);\r\n\r\n if (!tokens.length) {\r\n return { sections: [], error: copy.emptySection(name) };\r\n }\r\n\r\n if (tokens.length > this.MAX_CHORDS_PER_SECTION) {\r\n return {\r\n sections: [],\r\n error: copy.tooManyChords(name, this.MAX_CHORDS_PER_SECTION),\r\n };\r\n }\r\n\r\n sections.push({\r\n name,\r\n results: tokens.map((token, ci) =>\r\n // ponytail: offset by si*10 to keep IDs unique across sections (max 6 chords × 6 sections = 36 < 60)\r\n this.searchSingle(token, si * 10 + ci, language),\r\n ),\r\n });\r\n }\r\n\r\n return { sections, error: null };\r\n }\r\n\r\n suffixes(): string[] {\r\n return this.guitarDb.suffixes;\r\n }\r\n\r\n private searchSingle(\r\n token: string,\r\n index: number,\r\n language: Language,\r\n ): ChordSearchResult {\r\n const parsed = this.parseChordName(token);\r\n const copy = ERROR_COPY[language];\r\n\r\n if (!parsed) {\r\n return {\r\n id: `${index}-${token}`,\r\n raw: token,\r\n displayName: token,\r\n positions: [],\r\n error: copy.invalid,\r\n };\r\n }\r\n const chordFamily = this.guitarDb.chords[parsed.dbRoot];\r\n\r\n if (!chordFamily) {\r\n return {\r\n id: `${index}-${parsed.displayName}`,\r\n raw: token,\r\n displayName: parsed.displayName,\r\n positions: [],\r\n error: copy.missingRoot,\r\n };\r\n }\r\n\r\n const chord = chordFamily.find((item) => item.suffix === parsed.suffix);\r\n\r\n if (!chord) {\r\n return {\r\n id: `${index}-${parsed.displayName}`,\r\n raw: token,\r\n displayName: parsed.displayName,\r\n dbName: `${parsed.dbRoot} ${parsed.suffix}`,\r\n positions: [],\r\n error: copy.missingType(parsed.suffix),\r\n };\r\n }\r\n\r\n return {\r\n id: `${index}-${parsed.dbRoot}-${parsed.suffix}`,\r\n raw: token,\r\n displayName: parsed.displayName,\r\n dbName: `${chord.key} ${chord.suffix}`,\r\n chord,\r\n positions: chord.positions,\r\n };\r\n }\r\n\r\n private parseChordName(raw: string): ParsedChord | null {\r\n const cleaned = raw\r\n .replaceAll('♯', '#')\r\n .replaceAll('♭', 'b')\r\n .replace(/\\s+/g, '');\r\n\r\n const match = cleaned.match(/^([A-Ga-g])([#b]?)(.*)$/);\r\n if (!match) return null;\r\n\r\n const letter = match[1].toUpperCase();\r\n const accidental = match[2] ?? '';\r\n const suffixRaw = match[3] ?? '';\r\n const root = `${letter}${accidental}`;\r\n const dbRoot = this.dbRootMap[root];\r\n\r\n if (!dbRoot) return null;\r\n\r\n const suffix = this.normalizeSuffix(suffixRaw);\r\n if (!suffix) return null;\r\n\r\n return {\r\n raw,\r\n root,\r\n dbRoot,\r\n displayName: `${root}${this.displaySuffix(suffix)}`,\r\n suffix,\r\n };\r\n }\r\n\r\n private normalizeSuffix(rawSuffix: string): string | null {\r\n const trimmed = rawSuffix.trim();\r\n\r\n if (this.suffixAliases[trimmed] !== undefined) {\r\n return this.suffixAliases[trimmed];\r\n }\r\n\r\n const lower = trimmed.toLowerCase();\r\n if (this.suffixAliases[lower] !== undefined) {\r\n return this.suffixAliases[lower];\r\n }\r\n\r\n if (this.guitarDb.suffixes.includes(trimmed)) {\r\n return trimmed;\r\n }\r\n\r\n if (this.guitarDb.suffixes.includes(lower)) {\r\n return lower;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private displaySuffix(suffix: string): string {\r\n if (suffix === 'major') return '';\r\n if (suffix === 'minor') return 'm';\r\n return suffix;\r\n }\r\n}\r\n","import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n computed,\r\n effect,\r\n input,\r\n signal,\r\n viewChild,\r\n} from '@angular/core';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { ChordDiagram } from './components/chord-diagram/chord-diagram';\r\nimport {\r\n ChordSearchResult,\r\n ChordSection,\r\n ChordsDbPosition,\r\n Language,\r\n} from './models/chord.model';\r\nimport { ChordService } from './services/chord.service';\r\n\r\nconst COPY = {\r\n en: {\r\n eyebrow: 'Angular · SVG · chords-db',\r\n title: 'Chord Finder',\r\n descriptionStart: 'Enter up to',\r\n descriptionLimit: '5 chords',\r\n descriptionEnd: 'separated by commas. Sharps and flats are supported:',\r\n inputLabel: 'Chords',\r\n exportPng: 'Export PNG',\r\n bgColor: 'Background',\r\n lineColor: 'Diagram color',\r\n transparent: 'Transparent',\r\n download: 'Download',\r\n availableTypes: 'Available chord types:',\r\n resultsLabel: 'Chord results',\r\n chord: 'Chord',\r\n normalizedAs: 'Normalized as',\r\n position: 'Position',\r\n of: 'of',\r\n openSource: 'Open source project:',\r\n contributions: 'Contributions are welcome.',\r\n assistance:\r\n 'Built with AI assistance under supervision from the repository owner.',\r\n },\r\n es: {\r\n eyebrow: 'Angular · SVG · chords-db',\r\n title: 'Buscador de acordes',\r\n descriptionStart: 'Escribe hasta',\r\n descriptionLimit: '5 acordes',\r\n descriptionEnd: 'separados por coma. Soporta sostenidos y bemoles:',\r\n inputLabel: 'Acordes',\r\n exportPng: 'Exportar PNG',\r\n bgColor: 'Fondo',\r\n lineColor: 'Color del diagrama',\r\n transparent: 'Transparente',\r\n download: 'Descargar',\r\n availableTypes: 'Tipos de acorde disponibles:',\r\n resultsLabel: 'Resultados de acordes',\r\n chord: 'Acorde',\r\n normalizedAs: 'Normalizado como',\r\n position: 'Posición',\r\n of: 'de',\r\n openSource: 'Proyecto de código abierto:',\r\n contributions: 'Las contribuciones son bienvenidas.',\r\n assistance:\r\n 'Creado con asistencia de IA bajo la supervisión del propietario del repositorio.',\r\n },\r\n} as const;\r\n\r\n@Component({\r\n selector: 'the-chords-chord-finder',\r\n standalone: true,\r\n imports: [FormsModule, ChordDiagram],\r\n templateUrl: './chord-finder.html',\r\n changeDetection: ChangeDetectionStrategy.Eager,\r\n styleUrl: './chord-finder.scss',\r\n host: { '[attr.lang]': 'language()' },\r\n})\r\nexport class ChordFinderComponent {\r\n readonly language = input<Language>('en');\r\n readonly text = computed(() => COPY[this.language()]);\r\n query = 'C';\r\n results = signal<ChordSearchResult[]>([]);\r\n sections = signal<ChordSection[]>([]);\r\n inputMode = signal<'plain' | 'sections'>('plain');\r\n inputError = signal<string | null>(null);\r\n selectedPositionIndex: Record<string, number> = {};\r\n supportedSuffixes = computed(() =>\r\n this.chordService.suffixes().slice(0, 18).join(', '),\r\n );\r\n readonly hasResults = computed(() =>\r\n this.inputMode() === 'sections'\r\n ? this.sections().length > 0\r\n : this.results().length > 0,\r\n );\r\n\r\n resultsRow = viewChild<ElementRef<HTMLElement>>('resultsRow');\r\n sectionsContainer = viewChild<ElementRef<HTMLElement>>('sectionsContainer');\r\n\r\n exportPanelOpen = signal(false);\r\n exportBgColor = '#ffffff';\r\n exportLineColor = '#000000';\r\n exportTransparent = false;\r\n\r\n constructor(private readonly chordService: ChordService) {\r\n effect(() => this.runSearch(this.language()));\r\n }\r\n\r\n async exportPng(): Promise<void> {\r\n if (this.inputMode() === 'sections') {\r\n const container = this.sectionsContainer()?.nativeElement;\r\n if (!container) return;\r\n for (const [i, section] of this.sections().entries()) {\r\n const sectionEl = container.querySelector<HTMLElement>(\r\n `[data-section=\"${i}\"]`,\r\n );\r\n if (!sectionEl) continue;\r\n const svgs = Array.from(\r\n sectionEl.querySelectorAll<SVGSVGElement>('svg.chord-svg'),\r\n );\r\n if (!svgs.length) continue;\r\n const filename =\r\n section.name\r\n .trim()\r\n .toLowerCase()\r\n .replace(/\\s+/g, '-')\r\n .replace(/[^a-z0-9-]/g, '') || 'section';\r\n await this.renderPng(svgs, filename);\r\n }\r\n } else {\r\n const svgs = Array.from(\r\n this.resultsRow()?.nativeElement.querySelectorAll<SVGSVGElement>(\r\n 'svg.chord-svg',\r\n ) ?? [],\r\n );\r\n if (!svgs.length) return;\r\n await this.renderPng(svgs, 'chords');\r\n }\r\n }\r\n\r\n private async renderPng(\r\n svgs: SVGSVGElement[],\r\n filename: string,\r\n ): Promise<void> {\r\n const padding = 32;\r\n const gap = 24;\r\n const width = 240;\r\n const height = 330;\r\n const scale = 2;\r\n\r\n const canvas = document.createElement('canvas');\r\n canvas.width =\r\n (padding * 2 + svgs.length * width + (svgs.length - 1) * gap) * scale;\r\n canvas.height = (padding * 2 + height) * scale;\r\n\r\n const ctx = canvas.getContext('2d');\r\n if (!ctx) return;\r\n\r\n ctx.scale(scale, scale);\r\n\r\n if (!this.exportTransparent) {\r\n ctx.fillStyle = this.exportBgColor;\r\n ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n }\r\n\r\n const bg = this.exportTransparent ? 'transparent' : this.exportBgColor;\r\n const fg = this.exportLineColor;\r\n // ponytail: text inside filled dots/barres inverts bg; transparent defaults to white\r\n const fgInverse = this.exportTransparent ? '#ffffff' : this.exportBgColor;\r\n\r\n for (const [index, svg] of svgs.entries()) {\r\n const clone = svg.cloneNode(true) as SVGSVGElement;\r\n\r\n clone.insertAdjacentHTML(\r\n 'afterbegin',\r\n `<style>\r\n .chord-svg{font-family:Roboto,Arial,sans-serif;font-weight:400}\r\n .card-bg{fill:${bg}}.title{font-size:42px;font-weight:400;fill:${fg}}\r\n .grid line{stroke:${fg};stroke-width:2.6;stroke-linecap:square}\r\n .grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:${fg}}\r\n .barres text,.dots text{fill:${fgInverse};font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle}\r\n .markers text{fill:${fg};font-size:30px;font-weight:400}\r\n .fret-number{fill:${fg};font-size:42px;font-weight:400}\r\n .string-labels text{fill:${fg};font-size:18px;font-weight:400}\r\n </style>`,\r\n );\r\n\r\n const url = URL.createObjectURL(\r\n new Blob([new XMLSerializer().serializeToString(clone)], {\r\n type: 'image/svg+xml',\r\n }),\r\n );\r\n const image = new Image();\r\n image.src = url;\r\n await image.decode();\r\n\r\n ctx.drawImage(\r\n image,\r\n padding + index * (width + gap),\r\n padding,\r\n width,\r\n height,\r\n );\r\n URL.revokeObjectURL(url);\r\n }\r\n\r\n const link = document.createElement('a');\r\n link.download = `${filename}.png`;\r\n link.href = canvas.toDataURL('image/png');\r\n link.click();\r\n }\r\n\r\n runSearch(language: Language = this.language()): void {\r\n const query = this.query.trim();\r\n\r\n if (this.chordService.isSectionFormat(query)) {\r\n this.inputMode.set('sections');\r\n const { sections, error } = this.chordService.searchSections(\r\n query,\r\n language,\r\n );\r\n this.inputError.set(error);\r\n this.sections.set(sections);\r\n this.results.set([]);\r\n for (const section of sections) {\r\n for (const result of section.results) {\r\n if (this.selectedPositionIndex[result.id] === undefined) {\r\n this.selectedPositionIndex[result.id] = 0;\r\n }\r\n }\r\n }\r\n } else {\r\n this.inputMode.set('plain');\r\n this.inputError.set(null);\r\n const results = this.chordService.search(query, language);\r\n this.results.set(results);\r\n this.sections.set([]);\r\n for (const result of results) {\r\n if (this.selectedPositionIndex[result.id] === undefined) {\r\n this.selectedPositionIndex[result.id] = 0;\r\n }\r\n }\r\n }\r\n }\r\n\r\n selectPosition(resultId: string, value: string | number): void {\r\n this.selectedPositionIndex[resultId] = Number(value);\r\n }\r\n\r\n selectedIndex(resultId: string): number {\r\n return this.selectedPositionIndex[resultId] ?? 0;\r\n }\r\n\r\n selectedPosition(result: ChordSearchResult): ChordsDbPosition | null {\r\n if (!result.positions.length) return null;\r\n return (\r\n result.positions[this.selectedPositionIndex[result.id] ?? 0] ??\r\n result.positions[0]\r\n );\r\n }\r\n\r\n trackByResultId(_: number, result: ChordSearchResult): string {\r\n return result.id;\r\n }\r\n}\r\n","<main class=\"page-shell\">\r\n <section class=\"hero-card\">\r\n <p class=\"eyebrow\">{{ text().eyebrow }}</p>\r\n <h1>{{ text().title }}</h1>\r\n\r\n <p class=\"description\">\r\n {{ text().descriptionStart }}\r\n <strong>{{ text().descriptionLimit }}</strong>\r\n {{ text().descriptionEnd }}\r\n <strong>C, F#, C#m, Bb</strong>.\r\n </p>\r\n\r\n <div class=\"search-row\">\r\n <label for=\"chordInput\">{{ text().inputLabel }}</label>\r\n <input\r\n id=\"chordInput\"\r\n type=\"text\"\r\n [(ngModel)]=\"query\"\r\n (input)=\"runSearch()\"\r\n placeholder=\"C, F#, C#m, Bb\"\r\n autocomplete=\"off\"\r\n />\r\n <div class=\"export-wrap\">\r\n <button\r\n type=\"button\"\r\n class=\"export-btn\"\r\n (click)=\"exportPanelOpen.set(!exportPanelOpen())\"\r\n [disabled]=\"!hasResults()\"\r\n [class.open]=\"exportPanelOpen()\"\r\n >\r\n {{ text().exportPng }}\r\n <span class=\"chevron\" aria-hidden=\"true\">▾</span>\r\n </button>\r\n <div class=\"export-panel\" [class.open]=\"exportPanelOpen()\">\r\n <div class=\"export-panel-inner\">\r\n <div class=\"export-options\">\r\n <label class=\"color-opt\">\r\n <span>{{ text().bgColor }}</span>\r\n <div class=\"color-row\">\r\n <input\r\n type=\"color\"\r\n [(ngModel)]=\"exportBgColor\"\r\n [disabled]=\"exportTransparent\"\r\n />\r\n <label class=\"check-label\">\r\n <input type=\"checkbox\" [(ngModel)]=\"exportTransparent\" />\r\n {{ text().transparent }}\r\n </label>\r\n </div>\r\n </label>\r\n <label class=\"color-opt\">\r\n <span>{{ text().lineColor }}</span>\r\n <input type=\"color\" [(ngModel)]=\"exportLineColor\" />\r\n </label>\r\n <button\r\n type=\"button\"\r\n class=\"download-btn\"\r\n (click)=\"exportPng()\"\r\n [disabled]=\"!hasResults()\"\r\n >\r\n {{ text().download }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (inputError()) {\r\n <p class=\"input-error\">{{ inputError() }}</p>\r\n }\r\n\r\n <p class=\"hint\">{{ text().availableTypes }} {{ supportedSuffixes() }}...</p>\r\n </section>\r\n\r\n @if (inputMode() === 'plain') {\r\n <section\r\n #resultsRow\r\n class=\"results-row\"\r\n [attr.aria-label]=\"text().resultsLabel\"\r\n >\r\n @for (result of results(); track trackByResultId($index, result)) {\r\n <article class=\"result-card\" [class.has-error]=\"result.error\">\r\n <header class=\"result-header\">\r\n <div>\r\n <p class=\"result-label\">{{ text().chord }}</p>\r\n <h2>{{ result.displayName }}</h2>\r\n @if (result.dbName && result.raw !== result.displayName) {\r\n <p class=\"db-name\">{{ text().normalizedAs }} {{ result.dbName }}</p>\r\n }\r\n </div>\r\n @if (result.positions.length > 1) {\r\n <label class=\"select-label\">\r\n {{ text().position }}\r\n <select\r\n [ngModel]=\"selectedPositionIndex[result.id]\"\r\n (ngModelChange)=\"selectPosition(result.id, $event)\"\r\n >\r\n @for (position of result.positions; track $index) {\r\n <option [value]=\"$index\">\r\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\r\n </option>\r\n }\r\n </select>\r\n </label>\r\n }\r\n </header>\r\n @if (result.error) {\r\n <p class=\"error-text\">{{ result.error }}</p>\r\n } @else if (selectedPosition(result); as position) {\r\n <div class=\"svg-wrap\">\r\n <app-chord-diagram\r\n [title]=\"result.displayName\"\r\n [position]=\"position\"\r\n [positionIndex]=\"selectedIndex(result.id)\"\r\n />\r\n </div>\r\n }\r\n </article>\r\n }\r\n </section>\r\n } @else if (inputMode() === 'sections') {\r\n <div #sectionsContainer class=\"sections-container\">\r\n @for (section of sections(); let si = $index; track si) {\r\n <section\r\n class=\"section-block\"\r\n [attr.data-section]=\"si\"\r\n [attr.aria-label]=\"section.name\"\r\n >\r\n <h2 class=\"section-name\">{{ section.name }}</h2>\r\n <div class=\"results-row\">\r\n @for (result of section.results; track trackByResultId($index, result)) {\r\n <article class=\"result-card\" [class.has-error]=\"result.error\">\r\n <header class=\"result-header\">\r\n <div>\r\n <p class=\"result-label\">{{ text().chord }}</p>\r\n <h2>{{ result.displayName }}</h2>\r\n @if (result.dbName && result.raw !== result.displayName) {\r\n <p class=\"db-name\">\r\n {{ text().normalizedAs }} {{ result.dbName }}\r\n </p>\r\n }\r\n </div>\r\n @if (result.positions.length > 1) {\r\n <label class=\"select-label\">\r\n {{ text().position }}\r\n <select\r\n [ngModel]=\"selectedPositionIndex[result.id]\"\r\n (ngModelChange)=\"selectPosition(result.id, $event)\"\r\n >\r\n @for (position of result.positions; track $index) {\r\n <option [value]=\"$index\">\r\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\r\n </option>\r\n }\r\n </select>\r\n </label>\r\n }\r\n </header>\r\n @if (result.error) {\r\n <p class=\"error-text\">{{ result.error }}</p>\r\n } @else if (selectedPosition(result); as position) {\r\n <div class=\"svg-wrap\">\r\n <app-chord-diagram\r\n [title]=\"result.displayName\"\r\n [position]=\"position\"\r\n [positionIndex]=\"selectedIndex(result.id)\"\r\n />\r\n </div>\r\n }\r\n </article>\r\n }\r\n </div>\r\n </section>\r\n }\r\n </div>\r\n }\r\n\r\n <footer class=\"app-footer\">\r\n <p>\r\n {{ text().openSource }}\r\n <a\r\n href=\"https://github.com/elparaquecosadeque/chord-generator\"\r\n target=\"_blank\"\r\n rel=\"noreferrer\"\r\n >\r\n elparaquecosadeque/chord-generator </a\r\n >. {{ text().contributions }}\r\n </p>\r\n <p>{{ text().assistance }}</p>\r\n </footer>\r\n</main>\r\n","/*\r\n * Public API Surface of chord-finder\r\n */\r\n\r\nexport * from './lib/chord-finder';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.ChordService"],"mappings":"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"}
1
+ {"version":3,"file":"gblp-chord-finder.mjs","sources":["../../../projects/chord-finder/src/lib/components/chord-diagram/chord-diagram.ts","../../../projects/chord-finder/src/lib/components/chord-diagram/chord-diagram.html","../../../projects/chord-finder/src/lib/services/chord.service.ts","../../../projects/chord-finder/src/lib/chord-finder.ts","../../../projects/chord-finder/src/lib/chord-finder.html","../../../projects/chord-finder/src/public-api.ts","../../../projects/chord-finder/src/gblp-chord-finder.ts"],"sourcesContent":["import { Component, computed, input, ChangeDetectionStrategy } from '@angular/core';\nimport { ChordsDbPosition } from '../../models/chord.model';\n\ninterface BarreSpan {\n fret: number;\n x: number;\n y: number;\n width: number;\n labelX: number;\n labelY: number;\n finger: number | undefined;\n}\n\ninterface DotMarker {\n fret: number;\n stringIndex: number;\n finger?: number;\n}\n\n@Component({\n selector: 'app-chord-diagram',\n imports: [],\n templateUrl: './chord-diagram.html',\n changeDetection: ChangeDetectionStrategy.Eager,\n styleUrl: './chord-diagram.scss'\n})\nexport class ChordDiagram {\n title = input.required<string>();\n position = input.required<ChordsDbPosition>();\n positionIndex = input<number>(0);\n\n readonly width = 240;\n readonly height = 330;\n readonly left = 54;\n readonly top = 112;\n readonly stringGap = 28;\n readonly fretGap = 34;\n readonly dotRadius = 13;\n readonly stringLabels = ['E', 'A', 'D', 'G', 'B', 'E'];\n\n fretCount = computed(() => Math.max(5, ...this.position().frets.filter((fret) => fret > 0)));\n\n fretLines = computed(() => Array.from({ length: this.fretCount() + 1 }, (_, index) => index));\n\n stringLines = computed(() => Array.from({ length: 6 }, (_, index) => index));\n\n visibleDots = computed<DotMarker[]>(() => {\n const barres = new Set(this.position().barres ?? []);\n const fingers = this.position().fingers ?? [];\n\n return this.position().frets\n .map((fret, stringIndex) => ({\n fret,\n stringIndex,\n finger: fingers[stringIndex] > 0 ? fingers[stringIndex] : undefined\n }))\n .filter(({ fret }) => fret > 0)\n .filter(({ fret }) => !barres.has(fret));\n });\n\n barreSpans = computed<BarreSpan[]>(() => {\n const barres = this.position().barres ?? [];\n const fingers = this.position().fingers ?? [];\n\n return barres\n .map((barreFret) => {\n const playableStrings = this.position().frets\n .map((fret, index) => ({ fret, index }))\n .filter(({ fret }) => fret >= barreFret);\n\n if (playableStrings.length < 2) return null;\n\n const firstString = playableStrings[0].index;\n const lastString = playableStrings[playableStrings.length - 1].index;\n const x = this.stringX(firstString) - this.dotRadius;\n const y = this.dotY(barreFret) - this.dotRadius;\n const width = this.stringX(lastString) - this.stringX(firstString) + this.dotRadius * 2;\n const labelX = x + width / 2;\n const labelY = y + this.dotRadius;\n const finger = fingers[firstString] > 0 ? fingers[firstString] : undefined;\n\n return {\n fret: barreFret,\n x,\n y,\n width,\n labelX,\n labelY,\n finger\n } satisfies BarreSpan;\n })\n .filter((span): span is BarreSpan => span !== null);\n });\n\n get viewBox(): string {\n return `0 0 ${this.width} ${this.height}`;\n }\n\n baseFret(): number {\n return this.position().baseFret ?? 1;\n }\n\n stringX(index: number): number {\n return this.left + index * this.stringGap;\n }\n\n fretY(index: number): number {\n return this.top + index * this.fretGap;\n }\n\n dotY(fret: number): number {\n return this.top + (fret - 0.5) * this.fretGap;\n }\n\n markerFor(fret: number): string {\n if (fret === -1) return '×';\n if (fret === 0) return '○';\n return '';\n }\n\n fretLabel(): string {\n return String(this.baseFret());\n }\n\n positionCode(): string {\n return this.position().frets.map((fret) => (fret === -1 ? 'x' : String(fret))).join('');\n }\n}\n","<svg class=\"chord-svg\" [attr.viewBox]=\"viewBox\" role=\"img\" [attr.aria-label]=\"title() + ' guitar chord diagram'\">\n <title>{{ title() }} - posición {{ positionIndex() + 1 }} - {{ positionCode() }}</title>\n\n <rect class=\"card-bg\" x=\"0\" y=\"0\" [attr.width]=\"width\" [attr.height]=\"height\" rx=\"24\" fill=\"#ffffff\" />\n\n <text x=\"120\" y=\"50\" text-anchor=\"middle\" class=\"title\">{{ title() }}</text>\n\n <g class=\"markers\">\n @for (fret of position().frets; track $index) {\n <text [attr.x]=\"stringX($index)\" y=\"92\" text-anchor=\"middle\">{{ markerFor(fret) }}</text>\n }\n </g>\n\n <text x=\"26\" [attr.y]=\"fretY(0) + 28\" text-anchor=\"middle\" class=\"fret-number\">{{ fretLabel() }}</text>\n\n <g class=\"grid\">\n @for (line of fretLines(); track line) {\n <line\n [attr.x1]=\"stringX(0)\"\n [attr.x2]=\"stringX(5)\"\n [attr.y1]=\"fretY(line)\"\n [attr.y2]=\"fretY(line)\"\n [class.nut]=\"line === 0 && baseFret() === 1\"\n />\n }\n\n @for (line of stringLines(); track line) {\n <line\n [attr.x1]=\"stringX(line)\"\n [attr.x2]=\"stringX(line)\"\n [attr.y1]=\"fretY(0)\"\n [attr.y2]=\"fretY(fretCount())\"\n />\n }\n </g>\n\n <g class=\"barres\">\n @for (barre of barreSpans(); track barre.fret) {\n <rect\n [attr.x]=\"barre.x\"\n [attr.y]=\"barre.y\"\n [attr.width]=\"barre.width\"\n [attr.height]=\"dotRadius * 2\"\n rx=\"13\"\n />\n @if (barre.finger) {\n <text [attr.x]=\"barre.labelX\" [attr.y]=\"barre.labelY\" text-anchor=\"middle\">{{ barre.finger }}</text>\n }\n }\n </g>\n\n <g class=\"dots\">\n @for (dot of visibleDots(); track dot.stringIndex) {\n <circle [attr.cx]=\"stringX(dot.stringIndex)\" [attr.cy]=\"dotY(dot.fret)\" [attr.r]=\"dotRadius\" />\n @if (dot.finger) {\n <text [attr.x]=\"stringX(dot.stringIndex)\" [attr.y]=\"dotY(dot.fret)\" text-anchor=\"middle\">{{ dot.finger }}</text>\n }\n }\n </g>\n\n <g class=\"string-labels\">\n @for (label of stringLabels; track label + $index) {\n <text [attr.x]=\"stringX($index)\" [attr.y]=\"fretY(fretCount()) + 32\" text-anchor=\"middle\">{{ label }}</text>\n }\n </g>\n</svg>\n","import { Injectable } from '@angular/core';\nimport guitarDbJson from '@gblp/chords-db/lib/guitar.json';\nimport {\n ChordSearchResult,\n ChordSection,\n ChordsDbInstrument,\n Language,\n ParsedChord,\n} from '../models/chord.model';\n\nconst ERROR_COPY = {\n en: {\n invalid: 'Invalid chord name. Try C, F#, C#m, Bb, Am7, or Dsus4.',\n missingRoot: 'Chord root not found in chords-db.',\n missingType: (suffix: string) =>\n `The type \"${suffix}\" is not available for this chord.`,\n tooManySections: (max: number) =>\n `Too many sections — maximum allowed is ${max}.`,\n tooManyChords: (section: string, max: number) =>\n `Section \"${section}\" exceeds the maximum of ${max} chords.`,\n emptySectionName: 'Section name cannot be empty.',\n emptySection: (name: string) => `Section \"${name}\" has no chords.`,\n noValidSections:\n 'No valid sections found. Expected format: \"name: chord1, chord2; name2: chord3\".',\n },\n es: {\n invalid: 'Nombre inválido. Prueba C, F#, C#m, Bb, Am7 o Dsus4.',\n missingRoot: 'Raíz no encontrada en chords-db.',\n missingType: (suffix: string) =>\n `El tipo \"${suffix}\" no existe para este acorde en la base actual.`,\n tooManySections: (max: number) =>\n `Demasiadas secciones — el máximo permitido es ${max}.`,\n tooManyChords: (section: string, max: number) =>\n `La sección \"${section}\" supera el máximo de ${max} acordes.`,\n emptySectionName: 'El nombre de la sección no puede estar vacío.',\n emptySection: (name: string) =>\n `La sección \"${name}\" no tiene acordes.`,\n noValidSections:\n 'No se encontraron secciones válidas. Formato esperado: \"nombre: acorde1, acorde2; nombre2: acorde3\".',\n },\n} satisfies Record<\n Language,\n {\n invalid: string;\n missingRoot: string;\n missingType: (suffix: string) => string;\n tooManySections: (max: number) => string;\n tooManyChords: (section: string, max: number) => string;\n emptySectionName: string;\n emptySection: (name: string) => string;\n noValidSections: string;\n }\n>;\n\n@Injectable({ providedIn: 'root' })\nexport class ChordService {\n private readonly MAX_SECTIONS = 6;\n private readonly MAX_CHORDS_PER_SECTION = 6;\n\n private readonly guitarDb = guitarDbJson as ChordsDbInstrument;\n\n private readonly dbRootMap: Record<string, string> = {\n C: 'C',\n 'C#': 'Csharp',\n Db: 'Csharp',\n D: 'D',\n 'D#': 'Eb',\n Eb: 'Eb',\n E: 'E',\n Fb: 'E',\n 'E#': 'F',\n F: 'F',\n 'F#': 'Fsharp',\n Gb: 'Fsharp',\n G: 'G',\n 'G#': 'Ab',\n Ab: 'Ab',\n A: 'A',\n 'A#': 'Bb',\n Bb: 'Bb',\n B: 'B',\n Cb: 'B',\n 'B#': 'C',\n };\n\n private readonly suffixAliases: Record<string, string> = {\n '': 'major',\n M: 'major',\n maj: 'major',\n major: 'major',\n m: 'minor',\n min: 'minor',\n '-': 'minor',\n minor: 'minor',\n Δ: 'maj7',\n maj7: 'maj7',\n M7: 'maj7',\n m7: 'm7',\n min7: 'm7',\n dim: 'dim',\n diminished: 'dim',\n aug: 'aug',\n augmented: 'aug',\n sus: 'sus4',\n sus2: 'sus2',\n sus4: 'sus4',\n add9: 'add9',\n 4: 'sus4',\n };\n\n search(\n input: string,\n language: Language = 'en',\n ): ChordSearchResult[] {\n return input\n .split(',')\n .map((token) => token.trim())\n .filter(Boolean)\n .map((token, index) => this.searchSingle(token, index, language));\n }\n\n isSectionFormat(input: string): boolean {\n return input.includes(':');\n }\n\n searchSections(\n input: string,\n language: Language = 'en',\n ): { sections: ChordSection[]; error: string | null } {\n const copy = ERROR_COPY[language];\n\n const rawSections = input\n .split(';')\n .map((s) => s.trim())\n .filter(Boolean);\n\n if (!rawSections.length) {\n return { sections: [], error: copy.noValidSections };\n }\n\n if (rawSections.length > this.MAX_SECTIONS) {\n return { sections: [], error: copy.tooManySections(this.MAX_SECTIONS) };\n }\n\n const sections: ChordSection[] = [];\n\n for (const [si, raw] of rawSections.entries()) {\n const colonIndex = raw.indexOf(':');\n if (colonIndex === -1) {\n return { sections: [], error: copy.noValidSections };\n }\n\n const name = raw.slice(0, colonIndex).trim();\n const chordsStr = raw.slice(colonIndex + 1).trim();\n\n if (!name) {\n return { sections: [], error: copy.emptySectionName };\n }\n\n const tokens = chordsStr\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n\n if (!tokens.length) {\n return { sections: [], error: copy.emptySection(name) };\n }\n\n if (tokens.length > this.MAX_CHORDS_PER_SECTION) {\n return {\n sections: [],\n error: copy.tooManyChords(name, this.MAX_CHORDS_PER_SECTION),\n };\n }\n\n sections.push({\n name,\n results: tokens.map((token, ci) =>\n // ponytail: offset by si*10 to keep IDs unique across sections (max 6 chords × 6 sections = 36 < 60)\n this.searchSingle(token, si * 10 + ci, language),\n ),\n });\n }\n\n return { sections, error: null };\n }\n\n suffixes(): string[] {\n return this.guitarDb.suffixes;\n }\n\n private searchSingle(\n token: string,\n index: number,\n language: Language,\n ): ChordSearchResult {\n const parsed = this.parseChordName(token);\n const copy = ERROR_COPY[language];\n\n if (!parsed) {\n return {\n id: `${index}-${token}`,\n raw: token,\n displayName: token,\n positions: [],\n error: copy.invalid,\n };\n }\n const chordFamily = this.guitarDb.chords[parsed.dbRoot];\n\n if (!chordFamily) {\n return {\n id: `${index}-${parsed.displayName}`,\n raw: token,\n displayName: parsed.displayName,\n positions: [],\n error: copy.missingRoot,\n };\n }\n\n const chord = chordFamily.find((item) => item.suffix === parsed.suffix);\n\n if (!chord) {\n return {\n id: `${index}-${parsed.displayName}`,\n raw: token,\n displayName: parsed.displayName,\n dbName: `${parsed.dbRoot} ${parsed.suffix}`,\n positions: [],\n error: copy.missingType(parsed.suffix),\n };\n }\n\n return {\n id: `${index}-${parsed.dbRoot}-${parsed.suffix}`,\n raw: token,\n displayName: parsed.displayName,\n dbName: `${chord.key} ${chord.suffix}`,\n chord,\n positions: chord.positions,\n };\n }\n\n private parseChordName(raw: string): ParsedChord | null {\n const cleaned = raw\n .replaceAll('♯', '#')\n .replaceAll('♭', 'b')\n .replace(/\\s+/g, '');\n\n const match = cleaned.match(/^([A-Ga-g])([#b]?)(.*)$/);\n if (!match) return null;\n\n const letter = match[1].toUpperCase();\n const accidental = match[2] ?? '';\n const suffixRaw = match[3] ?? '';\n const root = `${letter}${accidental}`;\n const dbRoot = this.dbRootMap[root];\n\n if (!dbRoot) return null;\n\n const suffix = this.normalizeSuffix(suffixRaw);\n if (!suffix) return null;\n\n return {\n raw,\n root,\n dbRoot,\n displayName: `${root}${this.displaySuffix(suffix)}`,\n suffix,\n };\n }\n\n private normalizeSuffix(rawSuffix: string): string | null {\n const trimmed = rawSuffix.trim();\n\n if (this.suffixAliases[trimmed] !== undefined) {\n return this.suffixAliases[trimmed];\n }\n\n const lower = trimmed.toLowerCase();\n if (this.suffixAliases[lower] !== undefined) {\n return this.suffixAliases[lower];\n }\n\n if (this.guitarDb.suffixes.includes(trimmed)) {\n return trimmed;\n }\n\n if (this.guitarDb.suffixes.includes(lower)) {\n return lower;\n }\n\n return null;\n }\n\n private displaySuffix(suffix: string): string {\n if (suffix === 'major') return '';\n if (suffix === 'minor') return 'm';\n return suffix;\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n computed,\n effect,\n input,\n signal,\n viewChild,\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ChordDiagram } from './components/chord-diagram/chord-diagram';\nimport {\n ChordSearchResult,\n ChordSection,\n ChordsDbPosition,\n Language,\n} from './models/chord.model';\nimport { ChordService } from './services/chord.service';\n\nconst COPY = {\n en: {\n eyebrow: 'Angular · SVG · chords-db',\n title: 'Chord Finder',\n descriptionStart: 'Enter up to',\n descriptionLimit: '5 chords',\n descriptionEnd: 'separated by commas. Sharps and flats are supported:',\n inputLabel: 'Chords',\n exportPng: 'Export PNG',\n bgColor: 'Background',\n lineColor: 'Diagram color',\n transparent: 'Transparent',\n download: 'Download',\n availableTypes: 'Available chord types:',\n resultsLabel: 'Chord results',\n chord: 'Chord',\n normalizedAs: 'Normalized as',\n position: 'Position',\n of: 'of',\n openSource: 'Open source project:',\n contributions: 'Contributions are welcome.',\n assistance:\n 'Built with AI assistance under supervision from the repository owner.',\n },\n es: {\n eyebrow: 'Angular · SVG · chords-db',\n title: 'Buscador de acordes',\n descriptionStart: 'Escribe hasta',\n descriptionLimit: '5 acordes',\n descriptionEnd: 'separados por coma. Soporta sostenidos y bemoles:',\n inputLabel: 'Acordes',\n exportPng: 'Exportar PNG',\n bgColor: 'Fondo',\n lineColor: 'Color del diagrama',\n transparent: 'Transparente',\n download: 'Descargar',\n availableTypes: 'Tipos de acorde disponibles:',\n resultsLabel: 'Resultados de acordes',\n chord: 'Acorde',\n normalizedAs: 'Normalizado como',\n position: 'Posición',\n of: 'de',\n openSource: 'Proyecto de código abierto:',\n contributions: 'Las contribuciones son bienvenidas.',\n assistance:\n 'Creado con asistencia de IA bajo la supervisión del propietario del repositorio.',\n },\n} as const;\n\n@Component({\n selector: 'the-chords-chord-finder',\n standalone: true,\n imports: [FormsModule, ChordDiagram],\n templateUrl: './chord-finder.html',\n changeDetection: ChangeDetectionStrategy.Eager,\n styleUrl: './chord-finder.scss',\n host: { '[attr.lang]': 'language()' },\n})\nexport class ChordFinderComponent {\n readonly language = input<Language>('en');\n readonly text = computed(() => COPY[this.language()]);\n query = 'C';\n results = signal<ChordSearchResult[]>([]);\n sections = signal<ChordSection[]>([]);\n inputMode = signal<'plain' | 'sections'>('plain');\n inputError = signal<string | null>(null);\n selectedPositionIndex: Record<string, number> = {};\n supportedSuffixes = computed(() =>\n this.chordService.suffixes().slice(0, 18).join(', '),\n );\n readonly hasResults = computed(() =>\n this.inputMode() === 'sections'\n ? this.sections().length > 0\n : this.results().length > 0,\n );\n\n resultsRow = viewChild<ElementRef<HTMLElement>>('resultsRow');\n sectionsContainer = viewChild<ElementRef<HTMLElement>>('sectionsContainer');\n\n exportPanelOpen = signal(false);\n exportBgColor = '#ffffff';\n exportLineColor = '#000000';\n exportTransparent = false;\n\n constructor(private readonly chordService: ChordService) {\n effect(() => this.runSearch(this.language()));\n }\n\n async exportPng(): Promise<void> {\n if (this.inputMode() === 'sections') {\n const container = this.sectionsContainer()?.nativeElement;\n if (!container) return;\n for (const [i, section] of this.sections().entries()) {\n const sectionEl = container.querySelector<HTMLElement>(\n `[data-section=\"${i}\"]`,\n );\n if (!sectionEl) continue;\n const svgs = Array.from(\n sectionEl.querySelectorAll<SVGSVGElement>('svg.chord-svg'),\n );\n if (!svgs.length) continue;\n const filename =\n section.name\n .trim()\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/[^a-z0-9-]/g, '') || 'section';\n await this.renderPng(svgs, filename);\n }\n } else {\n const svgs = Array.from(\n this.resultsRow()?.nativeElement.querySelectorAll<SVGSVGElement>(\n 'svg.chord-svg',\n ) ?? [],\n );\n if (!svgs.length) return;\n await this.renderPng(svgs, 'chords');\n }\n }\n\n private async renderPng(\n svgs: SVGSVGElement[],\n filename: string,\n ): Promise<void> {\n const padding = 32;\n const gap = 24;\n const width = 240;\n const height = 330;\n const scale = 2;\n\n const canvas = document.createElement('canvas');\n canvas.width =\n (padding * 2 + svgs.length * width + (svgs.length - 1) * gap) * scale;\n canvas.height = (padding * 2 + height) * scale;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n ctx.scale(scale, scale);\n\n if (!this.exportTransparent) {\n ctx.fillStyle = this.exportBgColor;\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n }\n\n const bg = this.exportTransparent ? 'transparent' : this.exportBgColor;\n const fg = this.exportLineColor;\n // ponytail: text inside filled dots/barres inverts bg; transparent defaults to white\n const fgInverse = this.exportTransparent ? '#ffffff' : this.exportBgColor;\n\n for (const [index, svg] of svgs.entries()) {\n const clone = svg.cloneNode(true) as SVGSVGElement;\n\n clone.insertAdjacentHTML(\n 'afterbegin',\n `<style>\n .chord-svg{font-family:Roboto,Arial,sans-serif;font-weight:400}\n .card-bg{fill:${bg}}.title{font-size:42px;font-weight:400;fill:${fg}}\n .grid line{stroke:${fg};stroke-width:2.6;stroke-linecap:square}\n .grid line.nut{stroke-width:7}.barres rect,.dots circle{fill:${fg}}\n .barres text,.dots text{fill:${fgInverse};font-size:16px;font-weight:400;dominant-baseline:central;alignment-baseline:middle}\n .markers text{fill:${fg};font-size:30px;font-weight:400}\n .fret-number{fill:${fg};font-size:42px;font-weight:400}\n .string-labels text{fill:${fg};font-size:18px;font-weight:400}\n </style>`,\n );\n\n const url = URL.createObjectURL(\n new Blob([new XMLSerializer().serializeToString(clone)], {\n type: 'image/svg+xml',\n }),\n );\n const image = new Image();\n image.src = url;\n await image.decode();\n\n ctx.drawImage(\n image,\n padding + index * (width + gap),\n padding,\n width,\n height,\n );\n URL.revokeObjectURL(url);\n }\n\n const link = document.createElement('a');\n link.download = `${filename}.png`;\n link.href = canvas.toDataURL('image/png');\n link.click();\n }\n\n runSearch(language: Language = this.language()): void {\n const query = this.query.trim();\n\n if (this.chordService.isSectionFormat(query)) {\n this.inputMode.set('sections');\n const { sections, error } = this.chordService.searchSections(\n query,\n language,\n );\n this.inputError.set(error);\n this.sections.set(sections);\n this.results.set([]);\n for (const section of sections) {\n for (const result of section.results) {\n if (this.selectedPositionIndex[result.id] === undefined) {\n this.selectedPositionIndex[result.id] = 0;\n }\n }\n }\n } else {\n this.inputMode.set('plain');\n this.inputError.set(null);\n const results = this.chordService.search(query, language);\n this.results.set(results);\n this.sections.set([]);\n for (const result of results) {\n if (this.selectedPositionIndex[result.id] === undefined) {\n this.selectedPositionIndex[result.id] = 0;\n }\n }\n }\n }\n\n selectPosition(resultId: string, value: string | number): void {\n this.selectedPositionIndex[resultId] = Number(value);\n }\n\n selectedIndex(resultId: string): number {\n return this.selectedPositionIndex[resultId] ?? 0;\n }\n\n selectedPosition(result: ChordSearchResult): ChordsDbPosition | null {\n if (!result.positions.length) return null;\n return (\n result.positions[this.selectedPositionIndex[result.id] ?? 0] ??\n result.positions[0]\n );\n }\n\n trackByResultId(_: number, result: ChordSearchResult): string {\n return result.id;\n }\n}\n","<main class=\"page-shell\">\n <section class=\"hero-card\">\n <p class=\"eyebrow\">{{ text().eyebrow }}</p>\n <h1>{{ text().title }}</h1>\n\n <p class=\"description\">\n {{ text().descriptionStart }}\n <strong>{{ text().descriptionLimit }}</strong>\n {{ text().descriptionEnd }}\n <strong>C, F#, C#m, Bb</strong>.\n </p>\n\n <div class=\"search-row\">\n <label for=\"chordInput\">{{ text().inputLabel }}</label>\n <input\n id=\"chordInput\"\n type=\"text\"\n [(ngModel)]=\"query\"\n (input)=\"runSearch()\"\n placeholder=\"C, F#, C#m, Bb\"\n autocomplete=\"off\"\n />\n <div class=\"export-wrap\">\n <button\n type=\"button\"\n class=\"export-btn\"\n (click)=\"exportPanelOpen.set(!exportPanelOpen())\"\n [disabled]=\"!hasResults()\"\n [class.open]=\"exportPanelOpen()\"\n >\n {{ text().exportPng }}\n <span class=\"chevron\" aria-hidden=\"true\">▾</span>\n </button>\n <div class=\"export-panel\" [class.open]=\"exportPanelOpen()\">\n <div class=\"export-panel-inner\">\n <div class=\"export-options\">\n <label class=\"color-opt\">\n <span>{{ text().bgColor }}</span>\n <div class=\"color-row\">\n <input\n type=\"color\"\n [(ngModel)]=\"exportBgColor\"\n [disabled]=\"exportTransparent\"\n />\n <label class=\"check-label\">\n <input type=\"checkbox\" [(ngModel)]=\"exportTransparent\" />\n {{ text().transparent }}\n </label>\n </div>\n </label>\n <label class=\"color-opt\">\n <span>{{ text().lineColor }}</span>\n <input type=\"color\" [(ngModel)]=\"exportLineColor\" />\n </label>\n <button\n type=\"button\"\n class=\"download-btn\"\n (click)=\"exportPng()\"\n [disabled]=\"!hasResults()\"\n >\n {{ text().download }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n @if (inputError()) {\n <p class=\"input-error\">{{ inputError() }}</p>\n }\n\n <p class=\"hint\">{{ text().availableTypes }} {{ supportedSuffixes() }}...</p>\n </section>\n\n @if (inputMode() === 'plain') {\n <section\n #resultsRow\n class=\"results-row\"\n [attr.aria-label]=\"text().resultsLabel\"\n >\n @for (result of results(); track trackByResultId($index, result)) {\n <article class=\"result-card\" [class.has-error]=\"result.error\">\n <header class=\"result-header\">\n <div>\n <p class=\"result-label\">{{ text().chord }}</p>\n <h2>{{ result.displayName }}</h2>\n @if (result.dbName && result.raw !== result.displayName) {\n <p class=\"db-name\">{{ text().normalizedAs }} {{ result.dbName }}</p>\n }\n </div>\n @if (result.positions.length > 1) {\n <label class=\"select-label\">\n {{ text().position }}\n <select\n [ngModel]=\"selectedPositionIndex[result.id]\"\n (ngModelChange)=\"selectPosition(result.id, $event)\"\n >\n @for (position of result.positions; track $index) {\n <option [value]=\"$index\">\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\n </option>\n }\n </select>\n </label>\n }\n </header>\n @if (result.error) {\n <p class=\"error-text\">{{ result.error }}</p>\n } @else if (selectedPosition(result); as position) {\n <div class=\"svg-wrap\">\n <app-chord-diagram\n [title]=\"result.displayName\"\n [position]=\"position\"\n [positionIndex]=\"selectedIndex(result.id)\"\n />\n </div>\n }\n </article>\n }\n </section>\n } @else if (inputMode() === 'sections') {\n <div #sectionsContainer class=\"sections-container\">\n @for (section of sections(); let si = $index; track si) {\n <section\n class=\"section-block\"\n [attr.data-section]=\"si\"\n [attr.aria-label]=\"section.name\"\n >\n <h2 class=\"section-name\">{{ section.name }}</h2>\n <div class=\"results-row\">\n @for (result of section.results; track trackByResultId($index, result)) {\n <article class=\"result-card\" [class.has-error]=\"result.error\">\n <header class=\"result-header\">\n <div>\n <p class=\"result-label\">{{ text().chord }}</p>\n <h2>{{ result.displayName }}</h2>\n @if (result.dbName && result.raw !== result.displayName) {\n <p class=\"db-name\">\n {{ text().normalizedAs }} {{ result.dbName }}\n </p>\n }\n </div>\n @if (result.positions.length > 1) {\n <label class=\"select-label\">\n {{ text().position }}\n <select\n [ngModel]=\"selectedPositionIndex[result.id]\"\n (ngModelChange)=\"selectPosition(result.id, $event)\"\n >\n @for (position of result.positions; track $index) {\n <option [value]=\"$index\">\n {{ $index + 1 }} {{ text().of }} {{ result.positions.length }}\n </option>\n }\n </select>\n </label>\n }\n </header>\n @if (result.error) {\n <p class=\"error-text\">{{ result.error }}</p>\n } @else if (selectedPosition(result); as position) {\n <div class=\"svg-wrap\">\n <app-chord-diagram\n [title]=\"result.displayName\"\n [position]=\"position\"\n [positionIndex]=\"selectedIndex(result.id)\"\n />\n </div>\n }\n </article>\n }\n </div>\n </section>\n }\n </div>\n }\n\n <footer class=\"app-footer\">\n <p>\n {{ text().openSource }}\n <a\n href=\"https://github.com/elparaquecosadeque/chord-generator\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n elparaquecosadeque/chord-generator </a\n >. {{ text().contributions }}\n </p>\n <p>{{ text().assistance }}</p>\n </footer>\n</main>\n","/*\n * Public API Surface of chord-finder\n */\n\nexport * from './lib/chord-finder';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1.ChordService"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gblp/chord-finder",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -10,10 +10,14 @@
10
10
  "@angular/forms": "^22.0.0"
11
11
  },
12
12
  "dependencies": {
13
- "@tombatossals/chords-db": "0.5.1",
13
+ "@gblp/chords-db": "1.0.0",
14
14
  "tslib": "^2.8.1"
15
15
  },
16
16
  "sideEffects": false,
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/elparaquecosadeque/chord-finder"
20
+ },
17
21
  "module": "fesm2022/gblp-chord-finder.mjs",
18
22
  "typings": "types/gblp-chord-finder.d.ts",
19
23
  "exports": {